diff --git a/DESIGN.md b/DESIGN.md index adc5df09..2fea7d86 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -86,6 +86,8 @@ The reason we have two components here is so the `Identifier` doesn't need to si Typically the `Agent` will use a temporary session key pair and the `Identifier` is the more permanent identifier. Example flow: Identifier delegates to agent, agent contacts remote account service, account service issues UCANs addressed to identifier. Those UCANs are then used throughout the SDK to check for capabilities, etc. +The storage DID used for the file system is always the identifier DID. + ### Authority This component is responsible for providing and requesting authority, which technically means providing and requesting UCANs and file system secrets (access keys). diff --git a/package-lock.json b/package-lock.json index 9730e7ee..70cfd1e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,68 +9,70 @@ "version": "0.40.0-prerelease", "license": "Apache-2.0", "dependencies": { - "@chainsafe/libp2p-noise": "^13.0.0", - "@chainsafe/libp2p-yamux": "^5.0.0", - "@ipld/dag-cbor": "^9.0.4", + "@ipld/dag-cbor": "^9.0.5", "@ipld/dag-pb": "^4.0.5", - "@libp2p/mplex": "^9.0.4", - "@libp2p/peer-id": "^3.0.2", - "@libp2p/websockets": "^7.0.4", - "@libp2p/webtransport": "^3.0.6", - "@multiformats/multiaddr": "^12.1.7", "@ucans/core": "^0.12.0", - "blockstore-level": "^1.1.3", "debounce-promise": "^3.1.2", "emittery": "^1.0.1", - "events": "^3.3.0", - "fission-bloom-filters": "1.7.1", - "interface-blockstore": "^5.2.5", - "interface-datastore": "^8.2.4", - "ipfs-bitswap": "^19.0.0", - "ipfs-unixfs": "^11.0.1", - "ipfs-unixfs-exporter": "^13.1.6", - "ipfs-unixfs-importer": "^15.1.7", - "iso-base": "^2.0.0", - "iso-did": "^1.3.1", - "iso-signatures": "^0.1.9", + "interface-blockstore": "^5.2.6", + "ipfs-unixfs": "^11.1.0", + "ipfs-unixfs-exporter": "^13.2.1", + "ipfs-unixfs-importer": "^15.2.1", + "iso-base": "^2.0.1", + "iso-did": "^1.3.3", + "iso-signatures": "^0.1.10", "it-all": "^3.0.3", - "libp2p": "^0.46.6", - "localforage": "^1.10.0", - "multiformats": "^12.0.1", - "one-webcrypto": "^1.0.3", + "multiformats": "^12.1.1", "uint8arrays": "^4.0.6", - "wnfs": "0.1.24" + "wnfs": "0.1.27" }, "devDependencies": { "@esbuild-plugins/node-globals-polyfill": "^0.2.3", - "@ipld/car": "^5.2.0", + "@ipld/car": "^5.2.3", "@types/debounce-promise": "^3.1.6", "@types/expect": "^24.3.0", "@types/mocha": "^10.0.1", - "@types/node": "^20.5.0", - "@typescript-eslint/eslint-plugin": "^6.4.0", - "@typescript-eslint/parser": "^6.4.0", - "assert": "^2.0.0", + "@types/node": "^20.6.0", + "@typescript-eslint/eslint-plugin": "^6.7.0", + "@typescript-eslint/parser": "^6.7.0", + "assert": "^2.1.0", "copyfiles": "^2.4.1", - "dprint": "^0.40.2", + "dprint": "^0.41.0", "esbuild": "^0.19.2", "esbuild-plugin-wasm": "^1.1.0", - "eslint": "^8.47.0", - "fast-check": "^3.12.0", + "eslint": "^8.49.0", + "fast-check": "^3.13.0", "globby": "^13.2.2", "madge": "^6.1.0", "mocha": "^10.2.0", "rimraf": "^5.0.1", "ts-node": "^10.9.1", - "tslib": "^2.6.1", - "typedoc": "^0.24.8", - "typedoc-plugin-missing-exports": "^2.0.1", - "typedoc-plugin-rename-defaults": "^0.6.5", - "typescript": "^5.1.6", + "tslib": "^2.6.2", + "typedoc": "^0.25.1", + "typedoc-plugin-missing-exports": "^2.1.0", + "typedoc-plugin-rename-defaults": "^0.6.6", + "typescript": "^5.2.2", "util": "^0.12.4" }, "engines": { "node": ">=16" + }, + "peerDependencies": { + "@chainsafe/libp2p-noise": "^13.0.1", + "@chainsafe/libp2p-yamux": "^5.0.0", + "@libp2p/mplex": "^9.0.5", + "@libp2p/peer-id": "^3.0.2", + "@libp2p/websockets": "^7.0.6", + "@libp2p/webtransport": "^3.0.10", + "@localfirst/relay-client": "^3.6.2", + "@multiformats/multiaddr": "^12.1.7", + "@noble/ciphers": "^0.3.0", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.2", + "blockstore-level": "^1.1.4", + "ipfs-bitswap": "^19.0.0", + "libp2p": "^0.46.10", + "localforage": "^1.10.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -86,6 +88,7 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@achingbrain/nat-port-mapper/-/nat-port-mapper-1.0.11.tgz", "integrity": "sha512-Y2lwx0zmrwEl+IGu+V/QiVBdcdsWscYq1PMMEjvyuuaXnmnppbLWilO8LK1yoLdncxwJBuS0zZtHbpFeWBusRg==", + "peer": true, "dependencies": { "@achingbrain/ssdp": "^4.0.1", "@libp2p/logger": "^3.0.0", @@ -105,6 +108,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@achingbrain/ssdp/-/ssdp-4.0.4.tgz", "integrity": "sha512-fY/ShiYJmhLdr45Vn2+f88xTqZjBSH3X3F+EJu/89cjB1JIkMCVtD5CQaaS38YknIL8cEcNhjMZM4cdE3ckSSQ==", + "peer": true, "dependencies": { "event-iterator": "^2.0.0", "freeport-promise": "^2.0.0", @@ -120,6 +124,7 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "peer": true, "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -254,18 +259,20 @@ "node_modules/@chainsafe/is-ip": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@chainsafe/is-ip/-/is-ip-2.0.2.tgz", - "integrity": "sha512-ndGqEMG1W5WkGagaqOZHpPU172AGdxr+LD15sv3WIUvT5oCFUrG1Y0CW/v2Egwj4JXEvSibaIIIqImsm98y1nA==" + "integrity": "sha512-ndGqEMG1W5WkGagaqOZHpPU172AGdxr+LD15sv3WIUvT5oCFUrG1Y0CW/v2Egwj4JXEvSibaIIIqImsm98y1nA==", + "peer": true }, "node_modules/@chainsafe/libp2p-noise": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/@chainsafe/libp2p-noise/-/libp2p-noise-13.0.0.tgz", - "integrity": "sha512-+kRW5GSTGYB42WjFa1f7Wc/1+VWLffOhwChi+CbPceidMHM5pbOQNb+xQM2/aqLre+A+WnBOKEopME7dnoqLNQ==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@chainsafe/libp2p-noise/-/libp2p-noise-13.0.1.tgz", + "integrity": "sha512-eeOFubXyS9sK0oBg/qRfve6LVGzZX1vyULVidaKGTJr8Y4dtyU4+Btqw/aVo3o1lhdvb/qoY+p/Ep2pUsvJKhg==", + "peer": true, "dependencies": { "@libp2p/crypto": "^2.0.0", "@libp2p/interface": "^0.1.0", "@libp2p/logger": "^3.0.0", "@libp2p/peer-id": "^3.0.0", - "@noble/ciphers": "^0.1.4", + "@noble/ciphers": "^0.3.0", "@noble/curves": "^1.1.0", "@noble/hashes": "^1.3.1", "it-byte-stream": "^1.0.0", @@ -287,6 +294,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/@chainsafe/libp2p-yamux/-/libp2p-yamux-5.0.0.tgz", "integrity": "sha512-aWTnBPR2hJt0A2y579sMtZVB6IqgSSHlZ6Eg+WDxNZQ0zcexafuruZQDj+z3FUTNPz+E8IeuyCi7tjI4IEehjw==", + "peer": true, "dependencies": { "@libp2p/interface": "^0.1.0", "@libp2p/logger": "^3.0.0", @@ -305,6 +313,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@chainsafe/netmask/-/netmask-2.0.0.tgz", "integrity": "sha512-I3Z+6SWUoaljh3TBzCnCxjlUyN8tA+NAk5L6m9IxvCf1BENQTePzPMis97CoN/iMW1St3WN+AWCCRp+TTBRiDg==", + "peer": true, "dependencies": { "@chainsafe/is-ip": "^2.0.1" } @@ -322,9 +331,9 @@ } }, "node_modules/@dprint/darwin-arm64": { - "version": "0.40.2", - "resolved": "https://registry.npmjs.org/@dprint/darwin-arm64/-/darwin-arm64-0.40.2.tgz", - "integrity": "sha512-qharMFhxpNq9brgvHLbqzzAgVgPWSHLfzNLwWWhKcGOUUDUIilfAo3SlvOz6w4nQiIifLpYZOvZqK7Lpf9mSSw==", + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@dprint/darwin-arm64/-/darwin-arm64-0.41.0.tgz", + "integrity": "sha512-P9PtcQI0mrI4U6yyd+/iI664BHSqC9KTS6ogq0ptEdnLtlaWzf09D1nv6FBaHiG9m3conuBRlPsoUqt3j6PZ2w==", "cpu": [ "arm64" ], @@ -343,54 +352,6 @@ "esbuild": "*" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz", - "integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz", - "integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz", - "integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/darwin-arm64": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz", @@ -407,294 +368,6 @@ "node": ">=12" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz", - "integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz", - "integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz", - "integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz", - "integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz", - "integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz", - "integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz", - "integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz", - "integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz", - "integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz", - "integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz", - "integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz", - "integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz", - "integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz", - "integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz", - "integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz", - "integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz", - "integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz", - "integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -743,18 +416,18 @@ } }, "node_modules/@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", + "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -785,14 +458,14 @@ "dev": true }, "node_modules/@ipld/car": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@ipld/car/-/car-5.2.0.tgz", - "integrity": "sha512-Y4DiyVoPaeGxY6gKV/0A/73SlIIuDu7fl25NdlrO6BYhyTN6v59KqcilmMXbiBA/zcf7cZr1GZVPHRyG2+nmAw==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@ipld/car/-/car-5.2.3.tgz", + "integrity": "sha512-nU4PF9Iivf/HJJyRRvBDaVeMdMdUqUynFZuK1Vcyk7Qru+MlA9jwH8RTpylW2VlJH/MvFBqMXfqNPUiNG7pfRw==", "dev": true, "dependencies": { "@ipld/dag-cbor": "^9.0.0", - "cborg": "^1.9.0", - "multiformats": "^11.0.0", + "cborg": "^4.0.0", + "multiformats": "^12.1.0", "varint": "^6.0.0" }, "engines": { @@ -800,22 +473,21 @@ "npm": ">=7.0.0" } }, - "node_modules/@ipld/car/node_modules/multiformats": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-11.0.2.tgz", - "integrity": "sha512-b5mYMkOkARIuVZCpvijFj9a6m5wMVLC7cf/jIPd5D/ARDOfLC5+IFkbgDXQgcU2goIsTD/O9NY4DI/Mt4OGvlg==", + "node_modules/@ipld/car/node_modules/cborg": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cborg/-/cborg-4.0.1.tgz", + "integrity": "sha512-jbQyS14n3d1W3QyK3WXLTzSrnJnpZphrr0PbxYpT2VXcTpzl6QQ5JeAMfRWvdqt/Vbsxp56OXe/RIYFl069LGQ==", "dev": true, - "engines": { - "node": ">=16.0.0", - "npm": ">=7.0.0" + "bin": { + "cborg": "lib/bin.js" } }, "node_modules/@ipld/dag-cbor": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@ipld/dag-cbor/-/dag-cbor-9.0.4.tgz", - "integrity": "sha512-HBNVngk/47pKNLTAelN6ORWgKkjJtQj96Xb+jIBtRShJGCsXgghj1TzTynTTIp1dZxwPe5rVIL6yjZmvdyP2Wg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@ipld/dag-cbor/-/dag-cbor-9.0.5.tgz", + "integrity": "sha512-TyqgtxEojc98rvxg4NGM+73JzQeM4+tK2VQes/in2mdyhO+1wbGuBijh1tvi9BErQ/dEblxs9v4vEQSX8mFCIw==", "dependencies": { - "cborg": "^2.0.1", + "cborg": "^4.0.0", "multiformats": "^12.0.1" }, "engines": { @@ -824,11 +496,11 @@ } }, "node_modules/@ipld/dag-cbor/node_modules/cborg": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/cborg/-/cborg-2.0.3.tgz", - "integrity": "sha512-f1IbyqgRLQK4ruNM+V3WikfYfXQg/f/zC1oneOw1P7F/Dn2OJX6MaXIdei3JMpz361IjY7OENBKcE53nkJFVCQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cborg/-/cborg-4.0.1.tgz", + "integrity": "sha512-jbQyS14n3d1W3QyK3WXLTzSrnJnpZphrr0PbxYpT2VXcTpzl6QQ5JeAMfRWvdqt/Vbsxp56OXe/RIYFl069LGQ==", "bin": { - "cborg": "cli.js" + "cborg": "lib/bin.js" } }, "node_modules/@ipld/dag-pb": { @@ -984,6 +656,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/@libp2p/crypto/-/crypto-2.0.3.tgz", "integrity": "sha512-VLhjdkJe8b/vedHp7SosDs62Yxq1i05Ej/YdVaEdWQdJsBRHCwbRlS4hPg3vm21U5hLF0g958r/927Vd/wamZw==", + "peer": true, "dependencies": { "@libp2p/interface": "^0.1.2", "@noble/curves": "^1.1.0", @@ -999,6 +672,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/@libp2p/interface/-/interface-0.1.2.tgz", "integrity": "sha512-Q5t27434Mvn+R6AUJlRH+q/jSXarDpP+KXVkyGY7S1fKPI2berqoFPqT61bRRBYsCH2OPZiKBB53VUzxL9uEvg==", + "peer": true, "dependencies": { "@multiformats/multiaddr": "^12.1.5", "abortable-iterator": "^5.0.1", @@ -1013,6 +687,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/@libp2p/interface-internal/-/interface-internal-0.1.4.tgz", "integrity": "sha512-fRa8AUeCVOqfjgJgpIWupOsc7nAnJuI/VjWL2ZfRqbz7CPLD9c/ZAKXC140THSxlNdNQ9kGpo/C2z/yCGLy4ig==", + "peer": true, "dependencies": { "@libp2p/interface": "^0.1.2", "@libp2p/peer-collections": "^4.0.3", @@ -1024,6 +699,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@libp2p/keychain/-/keychain-3.0.3.tgz", "integrity": "sha512-mt3Pq8pPUDchoYlTXyNFMSIZ2/gbGZUJIr1qDQGphLZKxZ3Ejsqps2Dgo0t6yBxvJQ0581tXfptAXzw75Y2LIA==", + "peer": true, "dependencies": { "@libp2p/crypto": "^2.0.3", "@libp2p/interface": "^0.1.2", @@ -1039,6 +715,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/@libp2p/logger/-/logger-3.0.2.tgz", "integrity": "sha512-2JtRGBXiGfm1t5XneUIXQ2JusW7QwyYmxsW7hSAYS5J73RQJUicpt5le5obVRt7+OM39ei+nWEuC6Xvm1ugHkw==", + "peer": true, "dependencies": { "@libp2p/interface": "^0.1.2", "@multiformats/multiaddr": "^12.1.5", @@ -1048,9 +725,10 @@ } }, "node_modules/@libp2p/mplex": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@libp2p/mplex/-/mplex-9.0.4.tgz", - "integrity": "sha512-vUoj4XJtQJocN4ZdokfoMXVlATeBmnHDUtk1CtluimwAPhHkIhO9IhzIWEYmb8C5j7Yim+824tPj+TJ6vfBYlQ==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@libp2p/mplex/-/mplex-9.0.5.tgz", + "integrity": "sha512-cwg8ueB7xzwQbWt2da/Q5oPsm/6wk1GwNiGBiUsvy75h3qr6b1RNDuFJYkCSySI4mLvdTrkvthJCXHQMAW4Tvg==", + "peer": true, "dependencies": { "@libp2p/interface": "^0.1.2", "@libp2p/logger": "^3.0.2", @@ -1069,6 +747,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-2.0.1.tgz", "integrity": "sha512-euvmpuulJstK5+xNuI4S1KfnxJnbI5QP52RXIR3GZ3/ZMkOsEK2AgCtFpNvEQLXMxMx2o0qcyevK1fJwOZJagQ==", + "peer": true, "dependencies": { "uint8arraylist": "^2.0.0", "uint8arrays": "^4.0.2" @@ -1078,6 +757,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@libp2p/multistream-select/-/multistream-select-4.0.2.tgz", "integrity": "sha512-Ss3kPD+1Z8RFLUT+oN9I2ynEtp/Yj2+rOngU1XjIxustg1nt5lq0kk9hvWJyBexzmuML0xCknNjUXovpRbFPgQ==", + "peer": true, "dependencies": { "@libp2p/interface": "^0.1.2", "@libp2p/logger": "^3.0.2", @@ -1098,6 +778,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/@libp2p/peer-collections/-/peer-collections-4.0.3.tgz", "integrity": "sha512-ahfZFdRhApN4dulnzAvkzQsPVJVX7UID3QMKC/cduK5FYWqm7zbtW6bpwDilhZY3wvjvaQYs4R0KKSysvTPiQQ==", + "peer": true, "dependencies": { "@libp2p/interface": "^0.1.2", "@libp2p/peer-id": "^3.0.2" @@ -1107,6 +788,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/@libp2p/peer-id/-/peer-id-3.0.2.tgz", "integrity": "sha512-133qGXu9UBiqsYm7nBDJaAh4eiKe79DPLKF+/aRu0Z7gKcX7I0+LewEky4kBt3olhYQSF1CAnJIzD8Dmsn40Yw==", + "peer": true, "dependencies": { "@libp2p/interface": "^0.1.2", "multiformats": "^12.0.1", @@ -1117,6 +799,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@libp2p/peer-id-factory/-/peer-id-factory-3.0.3.tgz", "integrity": "sha512-RsE1GbK0w4LxiGr9sU2fm23IHOGzCtCfmUD8LC9V8LwLgt+z62oNKbWzwbko+CeromngURDimdv3JpH9jw5OUA==", + "peer": true, "dependencies": { "@libp2p/crypto": "^2.0.3", "@libp2p/interface": "^0.1.2", @@ -1128,32 +811,44 @@ } }, "node_modules/@libp2p/peer-record": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@libp2p/peer-record/-/peer-record-6.0.3.tgz", - "integrity": "sha512-S5C4Df2uyX1vNsnduZ6RVjB7T+dUEhqnaSNhiv82VCoqMoniHQBf2ftvXlv/UqqssW9or1x4UwgFU+sL7kObkw==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@libp2p/peer-record/-/peer-record-6.0.4.tgz", + "integrity": "sha512-O4YPbLPiavKGzNrfYMegGRzu4ez+Xm+2w9xQra3a36SKUcJlpHO/9gcmvkVfl3wBDnhuQ4TS5vWHlR42GuoS8A==", + "peer": true, "dependencies": { "@libp2p/crypto": "^2.0.3", "@libp2p/interface": "^0.1.2", "@libp2p/peer-id": "^3.0.2", - "@libp2p/utils": "^4.0.2", + "@libp2p/utils": "^4.0.3", "@multiformats/multiaddr": "^12.1.5", "protons-runtime": "^5.0.0", - "uint8-varint": "^1.0.2", + "uint8-varint": "^2.0.0", "uint8arraylist": "^2.4.3", "uint8arrays": "^4.0.6" } }, + "node_modules/@libp2p/peer-record/node_modules/uint8-varint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-2.0.1.tgz", + "integrity": "sha512-euvmpuulJstK5+xNuI4S1KfnxJnbI5QP52RXIR3GZ3/ZMkOsEK2AgCtFpNvEQLXMxMx2o0qcyevK1fJwOZJagQ==", + "peer": true, + "dependencies": { + "uint8arraylist": "^2.0.0", + "uint8arrays": "^4.0.2" + } + }, "node_modules/@libp2p/peer-store": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@libp2p/peer-store/-/peer-store-9.0.3.tgz", - "integrity": "sha512-7vSAUvKAzzWRwcMxOUvyGNw8V59t9l9l1Ugxa+VHCKKhvAEn9eXjf8We8BLGT3KnUG6aJ5HpODPK4RbW6BNGfA==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@libp2p/peer-store/-/peer-store-9.0.4.tgz", + "integrity": "sha512-i5qAxCBpBOMK3oXjTH2Cg64tg8DrkgpNLK1KQvg3CEdqMhwdrywPt2AMFh4VLga6H+EjYvok2GOHczNVxvzeiQ==", + "peer": true, "dependencies": { "@libp2p/interface": "^0.1.2", "@libp2p/logger": "^3.0.2", "@libp2p/peer-collections": "^4.0.3", "@libp2p/peer-id": "^3.0.2", "@libp2p/peer-id-factory": "^3.0.3", - "@libp2p/peer-record": "^6.0.3", + "@libp2p/peer-record": "^6.0.4", "@multiformats/multiaddr": "^12.1.5", "interface-datastore": "^8.2.0", "it-all": "^3.0.2", @@ -1165,14 +860,16 @@ } }, "node_modules/@libp2p/utils": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@libp2p/utils/-/utils-4.0.2.tgz", - "integrity": "sha512-M6ARf4NhzFqpw15BOG0FQVXanjWdnta/s91OzhtdZhsp1A/FmUDlxwdIeshs2x/6TfNhyrMtR8Wid/BYsPpBow==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@libp2p/utils/-/utils-4.0.3.tgz", + "integrity": "sha512-jusH8y4G9YluKRm63EPIiN9fNv0hVtfKY7O0nsLI14o0/W/WJhTsQWm+kPOfvoAgCIqAVrxefBqAmFGiiYPnvg==", + "peer": true, "dependencies": { "@chainsafe/is-ip": "^2.0.2", "@libp2p/interface": "^0.1.2", "@libp2p/logger": "^3.0.2", "@multiformats/multiaddr": "^12.1.5", + "@multiformats/multiaddr-matcher": "^1.0.1", "is-loopback-addr": "^2.0.1", "it-stream-types": "^2.0.1", "private-ip": "^3.0.0", @@ -1180,13 +877,14 @@ } }, "node_modules/@libp2p/websockets": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@libp2p/websockets/-/websockets-7.0.4.tgz", - "integrity": "sha512-DtwsZhYH//3bIMtzBpzi7ZWPeHtfEgbDOCaoVORcb4152ZvC68C7u+c3k7S2TyXY1ceqLTAt9LI9hDMM0WNfcQ==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@libp2p/websockets/-/websockets-7.0.6.tgz", + "integrity": "sha512-Hoe6KJMCKkLv00xWtCCZcdcoRX5GoR0ebdsc+PlMPPpPNhkbTa1OH1I8bcLU6VBMaaWLWuTyIZE6rWTu/+k/9g==", + "peer": true, "dependencies": { "@libp2p/interface": "^0.1.2", "@libp2p/logger": "^3.0.2", - "@libp2p/utils": "^4.0.2", + "@libp2p/utils": "^4.0.3", "@multiformats/mafmt": "^12.1.2", "@multiformats/multiaddr": "^12.1.5", "@multiformats/multiaddr-to-uri": "^9.0.2", @@ -1199,9 +897,10 @@ } }, "node_modules/@libp2p/webtransport": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@libp2p/webtransport/-/webtransport-3.0.6.tgz", - "integrity": "sha512-JNr5oIimsuVNwChdnkMtyLeEQy49Y3qTvVroVoVznczGDDC08xmrDhd7DyIjIs7kfRKzMY5Wftu8TjzKXtC3aw==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@libp2p/webtransport/-/webtransport-3.0.10.tgz", + "integrity": "sha512-66v2F8iJmdZyniIK94VOerBnfCTJSORkGJ4LhfQ+kMDOKxEvON7bMVQdNkp2jIfiob4npaxhHACFpWCF4rebVg==", + "peer": true, "dependencies": { "@chainsafe/libp2p-noise": "^13.0.0", "@libp2p/interface": "^0.1.2", @@ -1214,10 +913,22 @@ "uint8arrays": "^4.0.6" } }, + "node_modules/@localfirst/relay-client": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@localfirst/relay-client/-/relay-client-3.6.2.tgz", + "integrity": "sha512-8+/2WC9EoWnhaOpAMhM1zV2IIfaac7wpiOlabb98+i56TiuqTc4vG/x9EmAfqZ57TvAt5O3NDbfiVgLGqg+FQg==", + "peer": true, + "dependencies": { + "cuid": "2", + "debug": "4", + "eventemitter3": "4" + } + }, "node_modules/@multiformats/mafmt": { "version": "12.1.6", "resolved": "https://registry.npmjs.org/@multiformats/mafmt/-/mafmt-12.1.6.tgz", "integrity": "sha512-tlJRfL21X+AKn9b5i5VnaTD6bNttpSpcqwKVmDmSHLwxoz97fAHaepqFOk/l1fIu94nImIXneNbhsJx/RQNIww==", + "peer": true, "dependencies": { "@multiformats/multiaddr": "^12.0.0" } @@ -1226,6 +937,7 @@ "version": "12.1.7", "resolved": "https://registry.npmjs.org/@multiformats/multiaddr/-/multiaddr-12.1.7.tgz", "integrity": "sha512-MZRj+uUrtF2WqgByrsPolrdyPDSFstw7Fe0ewabWgWl27fcOmfDOSrEt2aUVkSzapXbyCG7JQh0QvimmTF4aMA==", + "peer": true, "dependencies": { "@chainsafe/is-ip": "^2.0.1", "@chainsafe/netmask": "^2.0.0", @@ -1244,6 +956,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@multiformats/multiaddr-matcher/-/multiaddr-matcher-1.0.1.tgz", "integrity": "sha512-ZzqwTH8tP5Py/k8eNKprO0i6tuwgrbp7KWz+ttxvzkPl43BlU9Yd5joq+M5grCt158rpAc2uhPobzfXgPxW5XQ==", + "peer": true, "dependencies": { "@chainsafe/is-ip": "^2.0.1", "@multiformats/multiaddr": "^12.0.0", @@ -1254,6 +967,7 @@ "version": "9.0.2", "resolved": "https://registry.npmjs.org/@multiformats/multiaddr-to-uri/-/multiaddr-to-uri-9.0.2.tgz", "integrity": "sha512-vrWmfFadmix5Ab9l//oRQdQ7O3J5bGJpJRMSm21bHlQB0XV4xtNU6vMZBVXeu3Su79LgflEp37cjTFE3yKf3Hw==", + "peer": true, "dependencies": { "@multiformats/multiaddr": "^11.0.0" }, @@ -1266,6 +980,7 @@ "version": "11.6.1", "resolved": "https://registry.npmjs.org/@multiformats/multiaddr/-/multiaddr-11.6.1.tgz", "integrity": "sha512-doST0+aB7/3dGK9+U5y3mtF3jq85KGbke1QiH0KE1F5mGQ9y56mFebTeu2D9FNOm+OT6UHb8Ss8vbSnpGjeLNw==", + "peer": true, "dependencies": { "@chainsafe/is-ip": "^2.0.1", "dns-over-http-resolver": "^2.1.0", @@ -1283,6 +998,7 @@ "version": "11.0.2", "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-11.0.2.tgz", "integrity": "sha512-b5mYMkOkARIuVZCpvijFj9a6m5wMVLC7cf/jIPd5D/ARDOfLC5+IFkbgDXQgcU2goIsTD/O9NY4DI/Mt4OGvlg==", + "peer": true, "engines": { "node": ">=16.0.0", "npm": ">=7.0.0" @@ -1292,6 +1008,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-2.0.1.tgz", "integrity": "sha512-euvmpuulJstK5+xNuI4S1KfnxJnbI5QP52RXIR3GZ3/ZMkOsEK2AgCtFpNvEQLXMxMx2o0qcyevK1fJwOZJagQ==", + "peer": true, "dependencies": { "uint8arraylist": "^2.0.0", "uint8arrays": "^4.0.2" @@ -1320,19 +1037,21 @@ } }, "node_modules/@noble/ciphers": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.1.4.tgz", - "integrity": "sha512-d3ZR8vGSpy3v/nllS+bD/OMN5UZqusWiQqkyj7AwzTnhXFH72pF5oB4Ach6DQ50g5kXxC28LdaYBEpsyv9KOUQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.3.0.tgz", + "integrity": "sha512-ldbrnOjmNRwFdXcTM6uXDcxpMIFrbzAWNnpBPp4oTJTFF0XByGD6vf45WrehZGXRQTRVV+Zm8YP+EgEf+e4cWA==", + "peer": true, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/curves": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", - "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "peer": true, "dependencies": { - "@noble/hashes": "1.3.1" + "@noble/hashes": "1.3.2" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -1350,9 +1069,10 @@ ] }, "node_modules/@noble/hashes": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", - "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "peer": true, "engines": { "node": ">= 16" }, @@ -1542,19 +1262,14 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.0.tgz", - "integrity": "sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==" - }, - "node_modules/@types/retry": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", - "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" + "version": "20.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.0.tgz", + "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==" }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", "dev": true }, "node_modules/@types/stack-utils": { @@ -1567,6 +1282,7 @@ "version": "8.5.5", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", + "peer": true, "dependencies": { "@types/node": "*" } @@ -1587,16 +1303,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.0.tgz", - "integrity": "sha512-62o2Hmc7Gs3p8SLfbXcipjWAa6qk2wZGChXG2JbBtYpwSRmti/9KHLqfbLs9uDigOexG+3PaQ9G2g3201FWLKg==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz", + "integrity": "sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.4.0", - "@typescript-eslint/type-utils": "6.4.0", - "@typescript-eslint/utils": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/type-utils": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1622,9 +1338,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz", + "integrity": "sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1635,12 +1351,12 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz", + "integrity": "sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/types": "6.7.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1652,15 +1368,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.4.0.tgz", - "integrity": "sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.0.tgz", + "integrity": "sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.4.0", - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/typescript-estree": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", "debug": "^4.3.4" }, "engines": { @@ -1680,9 +1396,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz", + "integrity": "sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1693,13 +1409,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", - "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz", + "integrity": "sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1720,12 +1436,12 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz", + "integrity": "sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/types": "6.7.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1766,13 +1482,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz", - "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.0.tgz", + "integrity": "sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0" + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1783,9 +1499,9 @@ } }, "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz", + "integrity": "sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1796,12 +1512,12 @@ } }, "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz", + "integrity": "sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/types": "6.7.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1813,13 +1529,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.4.0.tgz", - "integrity": "sha512-TvqrUFFyGY0cX3WgDHcdl2/mMCWCDv/0thTtx/ODMY1QhEiyFtv/OlLaNIiYLwRpAxAtOLOY9SUf1H3Q3dlwAg==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.0.tgz", + "integrity": "sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.4.0", - "@typescript-eslint/utils": "6.4.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/utils": "6.7.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1840,9 +1556,9 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz", + "integrity": "sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1853,13 +1569,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", - "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz", + "integrity": "sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1880,12 +1596,12 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz", + "integrity": "sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/types": "6.7.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2016,17 +1732,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.4.0.tgz", - "integrity": "sha512-BvvwryBQpECPGo8PwF/y/q+yacg8Hn/2XS+DqL/oRsOPK+RPt29h5Ui5dqOKHDlbXrAeHUTnyG3wZA0KTDxRZw==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.0.tgz", + "integrity": "sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.4.0", - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/typescript-estree": "6.4.0", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", "semver": "^7.5.4" }, "engines": { @@ -2041,9 +1757,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz", + "integrity": "sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2054,13 +1770,13 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", - "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz", + "integrity": "sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2081,12 +1797,12 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz", + "integrity": "sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/types": "6.7.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2170,12 +1886,14 @@ "node_modules/@vascosantos/moving-average": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@vascosantos/moving-average/-/moving-average-1.1.0.tgz", - "integrity": "sha512-MVEJ4vWAPNbrGLjz7ITnHYg+YXZ6ijAqtH5/cHwSoCpbvuJ98aLXwFfPKAUfZpJMQR5uXB58UJajbY130IRF/w==" + "integrity": "sha512-MVEJ4vWAPNbrGLjz7ITnHYg+YXZ6ijAqtH5/cHwSoCpbvuJ98aLXwFfPKAUfZpJMQR5uXB58UJajbY130IRF/w==", + "peer": true }, "node_modules/abortable-iterator": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/abortable-iterator/-/abortable-iterator-5.0.1.tgz", "integrity": "sha512-hlZ5Z8UwqrKsJcelVPEqDduZowJPBQJ9ZhBC2FXpja3lXy8X6MoI5uMzIgmrA8+3jcVnp8TF/tx+IBBqYJNUrg==", + "peer": true, "dependencies": { "get-iterator": "^2.0.0", "it-stream-types": "^2.0.1" @@ -2189,6 +1907,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", + "peer": true, "dependencies": { "buffer": "^6.0.3", "catering": "^2.1.0", @@ -2297,6 +2016,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/any-signal/-/any-signal-4.1.1.tgz", "integrity": "sha512-iADenERppdC+A2YKbOXXB2WUeABLaM6qnpZ70kZbPZ1cZMMJ7eF+3CaYm+/PhBizgkzlvssC7QuHS30oOiQYWA==", + "peer": true, "engines": { "node": ">=16.0.0", "npm": ">=7.0.0" @@ -2343,15 +2063,16 @@ } }, "node_modules/assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dev": true, "dependencies": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "node_modules/ast-module-types": { @@ -2409,6 +2130,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", "integrity": "sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==", + "peer": true, "dependencies": { "lodash": "^4.17.4", "platform": "^1.3.3" @@ -2445,6 +2167,7 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/blockstore-core/-/blockstore-core-4.3.3.tgz", "integrity": "sha512-+CyaLkkI7fUKCV/zTQTUmRjejgl1cyynjMZtoeYMXzp/024oy7HlUQZbcoLjxiqzlejMq2S4LD0iyRae2k67+g==", + "peer": true, "dependencies": { "@libp2p/logger": "^3.0.0", "err-code": "^3.0.1", @@ -2463,19 +2186,16 @@ } }, "node_modules/blockstore-level": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/blockstore-level/-/blockstore-level-1.1.3.tgz", - "integrity": "sha512-wRfxExp3VH5JiLPjq67nmPy2tydhDmxrMB/NIcydgjmK5T66v5bRTkRDP6bfK2tqi26GP8WkwXpfxr0tSOaRbA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/blockstore-level/-/blockstore-level-1.1.4.tgz", + "integrity": "sha512-tB8osXZ42Ab4OEAH89Z4W3xgntuefInqHmIpP1pFSPmRpErEIQOKWo2tzWeXgH9+banU3TnrllmmEutiVdAMdQ==", + "peer": true, "dependencies": { "blockstore-core": "^4.0.0", "interface-blockstore": "^5.0.0", "interface-store": "^5.0.0", "level": "^8.0.0", "multiformats": "^12.0.1" - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=7.0.0" } }, "node_modules/brace-expansion": { @@ -2504,6 +2224,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", + "peer": true, "dependencies": { "abstract-level": "^1.0.2", "catering": "^2.1.1", @@ -2556,6 +2277,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/byte-access/-/byte-access-1.0.1.tgz", "integrity": "sha512-GKYa+lvxnzhgHWj9X+LCsQ4s2/C5uvib573eAOiQKywXMkzFFErY2+yQdzmdE5iWVpmqecsRx3bOtOY4/1eINw==", + "peer": true, "dependencies": { "uint8arraylist": "^2.0.0" }, @@ -2602,19 +2324,11 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", + "peer": true, "engines": { "node": ">=6" } }, - "node_modules/cborg": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/cborg/-/cborg-1.10.0.tgz", - "integrity": "sha512-/eM0JCaL99HDHxjySNQJLaolZFVdl6VA0/hEKIoiQPcQzE5LrG5QHdml0HaBt31brgB9dNe1zMr3f8IVrpotRQ==", - "dev": true, - "bin": { - "cborg": "cli.js" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2675,6 +2389,7 @@ "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.2.0.tgz", "integrity": "sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg==", "hasInstallScript": true, + "peer": true, "dependencies": { "abstract-level": "^1.0.2", "catering": "^2.1.0", @@ -2813,15 +2528,18 @@ "node": ">= 8" } }, - "node_modules/cuint": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", - "integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==" + "node_modules/cuid": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/cuid/-/cuid-2.1.8.tgz", + "integrity": "sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg==", + "deprecated": "Cuid and other k-sortable and non-cryptographic ids (Ulid, ObjectId, KSUID, all UUIDs) are all insecure. Use @paralleldrive/cuid2 instead.", + "peer": true }, "node_modules/datastore-core": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/datastore-core/-/datastore-core-9.2.2.tgz", "integrity": "sha512-WFB1wVlD3Tr2yBZpJutPedBc18A4t0HvLOSksokYr/2nHBapplgnwkg2esI6xxctma+76FghhXx7G26khx2Uxg==", + "peer": true, "dependencies": { "@libp2p/logger": "^3.0.0", "err-code": "^3.0.1", @@ -2894,6 +2612,7 @@ "version": "7.2.2", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-7.2.2.tgz", "integrity": "sha512-AD7TrdNNPXRZIGw63dw+lnGmT4v7ggZC5NHNJgAYWm5njrwoze1q5JSAW9YuLy2tjnoLUG/r8FEB93MCh9QJPg==", + "peer": true, "dependencies": { "execa": "^7.1.1" }, @@ -2933,6 +2652,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/delay/-/delay-6.0.0.tgz", "integrity": "sha512-2NJozoOHQ4NuZuVIr5CWd0iiLVIRSDepakaovIN+9eIDHEhdCAEvSy2cuf1DCrPPQLvHmbqTHODlhHg8UCy4zw==", + "peer": true, "engines": { "node": ">=16" }, @@ -3214,6 +2934,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/dns-over-http-resolver/-/dns-over-http-resolver-2.1.0.tgz", "integrity": "sha512-eb8RGy6k54JdD7Rjw8g65y1MyA4z3m3IIYh7uazkgZuKIdFn8gYt8dydMm3op+2UshDdk9EexrXcDluKNY/CDg==", + "peer": true, "dependencies": { "debug": "^4.3.1", "native-fetch": "^4.0.2", @@ -3237,21 +2958,21 @@ } }, "node_modules/dprint": { - "version": "0.40.2", - "resolved": "https://registry.npmjs.org/dprint/-/dprint-0.40.2.tgz", - "integrity": "sha512-3LdyUV0itEW59UPtsRA2StOWOu8FyOW+BgvJpH/tACRHKi0z5gaQnvSxdS3mbG7dgtEhdRnGg6JoiQyGib6NTg==", + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/dprint/-/dprint-0.41.0.tgz", + "integrity": "sha512-9Ctv6EnwOy5Ai566DczI/QhAC6y+AhWDA2gFU8Zz4xezUy1BevHaIYhfdLWZQxh4Qf4H28lRu1Lq+hhIm1US9w==", "dev": true, "hasInstallScript": true, "bin": { "dprint": "bin.js" }, "optionalDependencies": { - "@dprint/darwin-arm64": "0.40.2", - "@dprint/darwin-x64": "0.40.2", - "@dprint/linux-arm64-glibc": "0.40.2", - "@dprint/linux-x64-glibc": "0.40.2", - "@dprint/linux-x64-musl": "0.40.2", - "@dprint/win32-x64": "0.40.2" + "@dprint/darwin-arm64": "0.41.0", + "@dprint/darwin-x64": "0.41.0", + "@dprint/linux-arm64-glibc": "0.41.0", + "@dprint/linux-x64-glibc": "0.41.0", + "@dprint/linux-x64-musl": "0.41.0", + "@dprint/win32-x64": "0.41.0" } }, "node_modules/eastasianwidth": { @@ -3277,6 +2998,29 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "peer": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.12.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", @@ -3350,12 +3094,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", - "dev": true - }, "node_modules/esbuild": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.2.tgz", @@ -3510,16 +3248,16 @@ } }, "node_modules/eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", + "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint/js": "8.49.0", + "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.12.4", @@ -3684,7 +3422,8 @@ "node_modules/event-iterator": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/event-iterator/-/event-iterator-2.0.0.tgz", - "integrity": "sha512-KGft0ldl31BZVV//jj+IAIGCxkvvUkkON+ScH6zfoX+l+omX6001ggyRSpI0Io2Hlro0ThXotswCtfzS8UkIiQ==" + "integrity": "sha512-KGft0ldl31BZVV//jj+IAIGCxkvvUkkON+ScH6zfoX+l+omX6001ggyRSpI0Io2Hlro0ThXotswCtfzS8UkIiQ==", + "peer": true }, "node_modules/eventemitter3": { "version": "4.0.7", @@ -3695,6 +3434,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "peer": true, "engines": { "node": ">=0.8.x" } @@ -3703,6 +3443,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "peer": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.1", @@ -3725,6 +3466,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "peer": true, "engines": { "node": ">=12" }, @@ -3736,6 +3478,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "peer": true, "dependencies": { "mimic-fn": "^4.0.0" }, @@ -3762,9 +3505,9 @@ } }, "node_modules/fast-check": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.12.0.tgz", - "integrity": "sha512-SqahE9mlL3+lhjJ39joMLwcj6F+24hfZdf/tchlNO8sHcTdrUUdA5P/ZbSFZM9Xpzs36XaneGwE0FWepm/zyOA==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.13.0.tgz", + "integrity": "sha512-m6+3gZ/yTiCWTuV/1e/UuPPjyyyHdQ5gu0pMd84C6705VTDjAgAE6nqFT5jhgegFllCJ95yOzBpqvJSs2DZAxQ==", "dev": true, "funding": [ { @@ -3924,21 +3667,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fission-bloom-filters": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/fission-bloom-filters/-/fission-bloom-filters-1.7.1.tgz", - "integrity": "sha512-AAVWxwqgSDK+/3Tn2kx+a9j/ND/pyVNVZgn/rL5pfQaX7w0qfP81PlLCNKhM4XKOhcg1kFXNcoWkQKg3MyyULw==", - "dependencies": { - "buffer": "^6.0.3", - "is-buffer": "^2.0.4", - "lodash": "^4.17.15", - "lodash.eq": "^4.0.0", - "lodash.indexof": "^4.0.5", - "reflect-metadata": "^0.1.13", - "seedrandom": "^3.0.5", - "xxhashjs": "^0.2.2" - } - }, "node_modules/flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", @@ -4030,6 +3758,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/freeport-promise/-/freeport-promise-2.0.0.tgz", "integrity": "sha512-dwWpT1DdQcwrhmRwnDnPM/ZFny+FtzU+k50qF2eid3KxaQDsMiBrwo1i0G3qSugkN5db6Cb0zgfc68QeTOpEFg==", + "peer": true, "engines": { "node": ">=16.0.0", "npm": ">=7.0.0" @@ -4127,7 +3856,8 @@ "node_modules/get-iterator": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-iterator/-/get-iterator-2.0.0.tgz", - "integrity": "sha512-BDJawD5PU2gZv6Vlp8O28H4GnZcsr3h9gZUvnAP5xXP3WOy/QAoOsyMepSkw21jur+4t5Vppde72ChjhTIzxzg==" + "integrity": "sha512-BDJawD5PU2gZv6Vlp8O28H4GnZcsr3h9gZUvnAP5xXP3WOy/QAoOsyMepSkw21jur+4t5Vppde72ChjhTIzxzg==", + "peer": true }, "node_modules/get-own-enumerable-property-symbols": { "version": "3.0.2", @@ -4139,6 +3869,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "peer": true, "engines": { "node": ">=10" }, @@ -4350,6 +4081,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "peer": true, "engines": { "node": ">=14.18.0" } @@ -4385,7 +4117,8 @@ "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "peer": true }, "node_modules/import-fresh": { "version": "3.3.0", @@ -4440,22 +4173,19 @@ "dev": true }, "node_modules/interface-blockstore": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/interface-blockstore/-/interface-blockstore-5.2.5.tgz", - "integrity": "sha512-xqu9/8Q3spKUe21eLl7+AGFlzsWZxMNKwvbTJdYJIenG3pL4QKlZQl18RNzVVzMK8GXOgDWDe8gSIP/rldHJAg==", + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/interface-blockstore/-/interface-blockstore-5.2.6.tgz", + "integrity": "sha512-Cp6+QPY81kunaxQV/j5BWk3uSW6kpos8dLveJ3P2lbmA/yX2XeMwVlNOnQyGg0evhp1qmMLhf6eCswNiSMW3CA==", "dependencies": { "interface-store": "^5.0.0", "multiformats": "^12.0.1" - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=7.0.0" } }, "node_modules/interface-datastore": { "version": "8.2.4", "resolved": "https://registry.npmjs.org/interface-datastore/-/interface-datastore-8.2.4.tgz", "integrity": "sha512-5ng8eSfuynvywa6/5FHbYdyBMrzMdRqcH+xk48hZMr1F+wmLM5Qkh9QuLtYIlTkpUn5INB4vNBONC+swHiLgpA==", + "peer": true, "dependencies": { "interface-store": "^5.0.0", "nanoid": "^4.0.0", @@ -4493,6 +4223,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-5.0.0.tgz", "integrity": "sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==", + "peer": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -4504,6 +4235,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", + "peer": true, "engines": { "node": ">= 10" } @@ -4512,6 +4244,7 @@ "version": "19.0.0", "resolved": "https://registry.npmjs.org/ipfs-bitswap/-/ipfs-bitswap-19.0.0.tgz", "integrity": "sha512-X0u7Y9dnRlP57EGNOF+RUtm2UkB3Nrx/NLLeUZ/VEEMWBC5iOFpBDvTxSNdEK366khbG8mXk2NJ1ko8jcKXETw==", + "peer": true, "dependencies": { "@libp2p/interface": "^0.1.1", "@libp2p/logger": "^3.0.1", @@ -4539,9 +4272,9 @@ } }, "node_modules/ipfs-unixfs": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/ipfs-unixfs/-/ipfs-unixfs-11.0.1.tgz", - "integrity": "sha512-SD9dqn14bfgMfkPstsR/2Av3zCzYMj2ntQJab4HZucgX4nNV6K7guZh4Hf3kiL8ONff1Ogft1ekFU083DIKEdQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/ipfs-unixfs/-/ipfs-unixfs-11.1.0.tgz", + "integrity": "sha512-Lq37nKLJOpRFjx3rcg3y+ZwUxBX7jluKfIt5UPp6wb1L3dP0sj1yaLR0Yg2CdGYvHWyUpZD1iTnT8upL0ToDOw==", "dependencies": { "err-code": "^3.0.1", "protons-runtime": "^5.0.0", @@ -4553,9 +4286,9 @@ } }, "node_modules/ipfs-unixfs-exporter": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/ipfs-unixfs-exporter/-/ipfs-unixfs-exporter-13.1.6.tgz", - "integrity": "sha512-pfFQThwa4/mbnLriHQso3wt25xcx5V0mBAOBPWx2jQv0QhgDtJSASyQrwwA2xE8F9zn0N3CWn7WDHhmM+EQ3sg==", + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/ipfs-unixfs-exporter/-/ipfs-unixfs-exporter-13.2.1.tgz", + "integrity": "sha512-sR0zA05VVXbRjslZ6PTQFxqVvvC6XXD0qkIo/RQb4VH+t13fbQhvhItn3PtFOETaUsUhUBi8tgH/LlqSeXsplA==", "dependencies": { "@ipld/dag-cbor": "^9.0.0", "@ipld/dag-pb": "^4.0.0", @@ -4570,7 +4303,7 @@ "it-parallel": "^3.0.0", "it-pipe": "^3.0.1", "it-pushable": "^3.1.0", - "multiformats": "^11.0.0", + "multiformats": "^12.0.1", "p-queue": "^7.3.0", "progress-events": "^1.0.0", "uint8arrays": "^4.0.2" @@ -4580,19 +4313,10 @@ "npm": ">=7.0.0" } }, - "node_modules/ipfs-unixfs-exporter/node_modules/multiformats": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-11.0.2.tgz", - "integrity": "sha512-b5mYMkOkARIuVZCpvijFj9a6m5wMVLC7cf/jIPd5D/ARDOfLC5+IFkbgDXQgcU2goIsTD/O9NY4DI/Mt4OGvlg==", - "engines": { - "node": ">=16.0.0", - "npm": ">=7.0.0" - } - }, "node_modules/ipfs-unixfs-importer": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/ipfs-unixfs-importer/-/ipfs-unixfs-importer-15.1.7.tgz", - "integrity": "sha512-dhcgfW5tigCcL3GhCCzEnNliU2M35mj23KYlcAHh7SCEMOHjNPZjm7kT8prrV24lJLite1QvS//BdcFS/yYY4Q==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/ipfs-unixfs-importer/-/ipfs-unixfs-importer-15.2.1.tgz", + "integrity": "sha512-9ArBh7Xfz8gUSe8pq9c9ilBOXd1bbT3L+4xnI6w/usWLwnNT14p8WbFZjDD0MO1/PrD0PTUZuHNDS2l4EO+wPg==", "dependencies": { "@ipld/dag-pb": "^4.0.0", "@multiformats/murmur3": "^2.0.0", @@ -4605,7 +4329,7 @@ "it-batch": "^3.0.2", "it-first": "^3.0.2", "it-parallel-batch": "^3.0.1", - "multiformats": "^11.0.0", + "multiformats": "^12.0.1", "progress-events": "^1.0.0", "rabin-wasm": "^0.1.4", "uint8arraylist": "^2.4.3", @@ -4616,15 +4340,6 @@ "npm": ">=7.0.0" } }, - "node_modules/ipfs-unixfs-importer/node_modules/multiformats": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-11.0.2.tgz", - "integrity": "sha512-b5mYMkOkARIuVZCpvijFj9a6m5wMVLC7cf/jIPd5D/ARDOfLC5+IFkbgDXQgcU2goIsTD/O9NY4DI/Mt4OGvlg==", - "engines": { - "node": ">=16.0.0", - "npm": ">=7.0.0" - } - }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -4699,6 +4414,7 @@ "url": "https://feross.org/support" } ], + "peer": true, "engines": { "node": ">=4" } @@ -4745,7 +4461,8 @@ "node_modules/is-electron": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.1.tgz", - "integrity": "sha512-r8EEQQsqT+Gn0aXFx7lTFygYQhILLCB+wn0WCDL5LZRINeLH/Rvw1j2oKodELLXYNImQ3CRlVsY8wW4cGOsyuw==" + "integrity": "sha512-r8EEQQsqT+Gn0aXFx7lTFygYQhILLCB+wn0WCDL5LZRINeLH/Rvw1j2oKodELLXYNImQ3CRlVsY8wW4cGOsyuw==", + "peer": true }, "node_modules/is-extglob": { "version": "2.1.1", @@ -4804,7 +4521,8 @@ "node_modules/is-loopback-addr": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-loopback-addr/-/is-loopback-addr-2.0.2.tgz", - "integrity": "sha512-26POf2KRCno/KTNL5Q0b/9TYnL00xEsSaLfiFRmjM7m7Lw7ZMmFybzzuX4CcsLAluZGd+niLUiMRxEooVE3aqg==" + "integrity": "sha512-26POf2KRCno/KTNL5Q0b/9TYnL00xEsSaLfiFRmjM7m7Lw7ZMmFybzzuX4CcsLAluZGd+niLUiMRxEooVE3aqg==", + "peer": true }, "node_modules/is-nan": { "version": "1.3.2", @@ -4931,6 +4649,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "peer": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -5041,56 +4760,39 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/iso-base": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/iso-base/-/iso-base-2.0.0.tgz", - "integrity": "sha512-XcrJx8bcs0SFjWX7LRWJFPfI7KD/KReHylzX0Rf2ph9QtOBEAHwKX3NabL7GZCLHUeAZslW912mdJpkquyirMA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/iso-base/-/iso-base-2.0.1.tgz", + "integrity": "sha512-ip0nUW9oZP+LG6mslSgdJPt9NWZOq1qIOKpoUIdm8sq/87VwY8WZXw9ptCkhdJclY+r4feFbZuu9P/OFDqCvkQ==", "dependencies": { "base-x": "^4.0.0", "bigint-mod-arith": "^3.3.1" } }, "node_modules/iso-did": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/iso-did/-/iso-did-1.3.1.tgz", - "integrity": "sha512-M+TMjOpfnlz2ZpQsEmHJJPkDwsI7klPU4eXa9re6bhRr8nAdFnnBTyCNb8xr28rAJk1OfpUN5c4ZRlnawKW5fA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/iso-did/-/iso-did-1.3.3.tgz", + "integrity": "sha512-ekMGlODhBvlt90rEXPfJHCrKLUsSd4jk7ejoWsCn5dPiS4WCFbqxjIyKdkCn16CztbgkmzrmzzOt/4kKQ6jbUw==", "dependencies": { "did-resolver": "^4.1.0", - "iso-base": "^1.1.2", - "multiformats": "^12.0.1" - } - }, - "node_modules/iso-did/node_modules/iso-base": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iso-base/-/iso-base-1.1.2.tgz", - "integrity": "sha512-cseSNMTn889oh9l/Aw4f1a5dDHmNnYGVZAp//G+UKfSJ5xC6WxQgYsp/O4McGYhmZJxPVenqlhNrt4ki2nRfZQ==", - "dependencies": { - "base-x": "^4.0.0", - "bigint-mod-arith": "^3.3.1" + "iso-base": "^2.0.1", + "multiformats": "^12.1.1" } }, "node_modules/iso-signatures": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/iso-signatures/-/iso-signatures-0.1.9.tgz", - "integrity": "sha512-PrdLbUHQTHgW7EwrKmxVTIyUbmiIfZsUn3UC4CeQICV1qoGuRafft591xyuSd45SOmLovkBmXOVvzO3b3GORVw==", + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/iso-signatures/-/iso-signatures-0.1.10.tgz", + "integrity": "sha512-eMjPmJnSqmswLgBOaUtUqVhI75H+hMeLAOLKVWyab0nTXNNkiuLInLN35Dq+rWocuQuUpWCtMK5cY8bTDitQrw==", "dependencies": { "@noble/ed25519": "^2.0.0", - "iso-base": "^1.1.2", - "iso-did": "^1.3.1" - } - }, - "node_modules/iso-signatures/node_modules/iso-base": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iso-base/-/iso-base-1.1.2.tgz", - "integrity": "sha512-cseSNMTn889oh9l/Aw4f1a5dDHmNnYGVZAp//G+UKfSJ5xC6WxQgYsp/O4McGYhmZJxPVenqlhNrt4ki2nRfZQ==", - "dependencies": { - "base-x": "^4.0.0", - "bigint-mod-arith": "^3.3.1" + "iso-base": "^2.0.1", + "iso-did": "^1.3.3" } }, "node_modules/iso-url": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iso-url/-/iso-url-1.2.1.tgz", "integrity": "sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng==", + "peer": true, "engines": { "node": ">=12" } @@ -5113,6 +4815,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/it-batched-bytes/-/it-batched-bytes-2.0.3.tgz", "integrity": "sha512-vUhr1K6NerlrSbSKpBGW9bDd3s64GPUQePWUzoUF0zkYw2ufFpCXEYCZAtJMP45n6BJNChWDYTYwxAZvQG0b0g==", + "peer": true, "dependencies": { "p-defer": "^4.0.0", "uint8arraylist": "^2.4.1" @@ -5126,6 +4829,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/it-byte-stream/-/it-byte-stream-1.0.0.tgz", "integrity": "sha512-zQjgsP5kWTnMrchFOMAiiA9FcO9xSdkTdpBvdsvuqwwSNCZ8eVeyHGlc9lE3gUUSeZzzwk5OSd/18y2A4GaREg==", + "peer": true, "dependencies": { "it-pushable": "^3.2.0", "it-stream-types": "^2.0.1", @@ -5136,6 +4840,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/it-drain/-/it-drain-3.0.2.tgz", "integrity": "sha512-0hJvS/4Ktt9wT/bktmovjjMAY8r6FCsXqpL3zjqBBNwoL21VgQfguEnwbLSGuCip9Zq1vfU43cbHkmaRZdBfOg==", + "peer": true, "engines": { "node": ">=16.0.0", "npm": ">=7.0.0" @@ -5166,6 +4871,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/it-foreach/-/it-foreach-2.0.3.tgz", "integrity": "sha512-rpkhyHMSSe9pkmTtPcDoA5+NKhMUDqddwdXakUzNn/aOIp3vNnGBH4P4xncefxZM29iwzKBnK7AGcYVYoIG8gQ==", + "peer": true, "dependencies": { "it-peekable": "^3.0.0" }, @@ -5178,6 +4884,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/it-handshake/-/it-handshake-4.1.3.tgz", "integrity": "sha512-V6Lt9A9usox9iduOX+edU1Vo94E6v9Lt9dOvg3ubFaw1qf5NCxXLi93Ao4fyCHWDYd8Y+DUhadwNtWVyn7qqLg==", + "peer": true, "dependencies": { "it-pushable": "^3.1.0", "it-reader": "^6.0.1", @@ -5203,6 +4910,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/it-length-prefixed/-/it-length-prefixed-9.0.1.tgz", "integrity": "sha512-ZBD8ZFLERj8d1q9CeBtk0eJ4EpeI3qwnkmWtemBSm3ZI2dM8PUweNVk5haZ2vw3EIq2uYQiabV9YwNm6EASM4A==", + "peer": true, "dependencies": { "err-code": "^3.0.1", "it-stream-types": "^2.0.1", @@ -5219,6 +4927,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/it-length-prefixed-stream/-/it-length-prefixed-stream-1.0.0.tgz", "integrity": "sha512-d+WJC2AkQRENrWr0XoZ0Tm+NBFpMiC/hQ1ZhhBhpq/L8/zFh4XNBv/28ticXVdgA2u33N69k43zJ+ZbAophNyg==", + "peer": true, "dependencies": { "it-byte-stream": "^1.0.0", "it-length-prefixed": "^9.0.1", @@ -5255,6 +4964,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/it-pair/-/it-pair-2.0.6.tgz", "integrity": "sha512-5M0t5RAcYEQYNG5BV7d7cqbdwbCAp5yLdzvkxsZmkuZsLbTdZzah6MQySYfaAQjNDCq6PUnDt0hqBZ4NwMfW6g==", + "peer": true, "dependencies": { "it-stream-types": "^2.0.1", "p-defer": "^4.0.0" @@ -5315,6 +5025,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/it-protobuf-stream/-/it-protobuf-stream-1.0.0.tgz", "integrity": "sha512-y3FfOQ+u2xY3DLs5FH8xXz1YH/50WJx40a9zWEk73DhNSpaLaLHjZqEiTyHa5mKjP0NaYgum+Ne6wo8yFPeoNw==", + "peer": true, "dependencies": { "it-length-prefixed-stream": "^1.0.0", "it-stream-types": "^2.0.1", @@ -5343,6 +5054,7 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/it-reader/-/it-reader-6.0.4.tgz", "integrity": "sha512-XCWifEcNFFjjBHtor4Sfaj8rcpt+FkY0L6WdhD578SCDhV4VUm7fCkF3dv5a+fTcfQqvN9BsxBTvWbYO6iCjTg==", + "peer": true, "dependencies": { "it-stream-types": "^2.0.1", "uint8arraylist": "^2.0.0" @@ -5356,6 +5068,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/it-sort/-/it-sort-3.0.2.tgz", "integrity": "sha512-gRvHyXkn13hyXIoiGkvg7Mf1Yg8JUB+ArKjMrGCYfd/4MQ8mQlMCOE6H8itjshwdVEAUDrppb786zODndYyjSg==", + "peer": true, "dependencies": { "it-all": "^3.0.0" }, @@ -5377,6 +5090,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/it-take/-/it-take-3.0.2.tgz", "integrity": "sha512-HgtnQYW45iV+lOJIk54dhKWNi+puAeutUehIWQE9tRkM91nlCn0abbDU2xG/FZV3cVnEG4hGwxOEImnMMKwhmg==", + "peer": true, "engines": { "node": ">=16.0.0", "npm": ">=7.0.0" @@ -5386,6 +5100,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/it-ws/-/it-ws-6.0.1.tgz", "integrity": "sha512-tWsIEN/hYlBGgvikP3B/afBBR0nZesw6mwQjyeBfpOK69mKYNMOqWn/OxurQaK3TLhxTmbAoy/yLX6jYEqcQVw==", + "peer": true, "dependencies": { "event-iterator": "^2.0.0", "iso-url": "^1.1.2", @@ -5535,12 +5250,14 @@ "node_modules/just-debounce-it": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/just-debounce-it/-/just-debounce-it-3.1.1.tgz", - "integrity": "sha512-oPsuRyWp99LJaQ4KXC3A42tQNqkRTcPy0A8BCkRZ5cPCgsx81upB2KUrmHZvDUNhnCDKe7MshfTuWFQB9iXwDg==" + "integrity": "sha512-oPsuRyWp99LJaQ4KXC3A42tQNqkRTcPy0A8BCkRZ5cPCgsx81upB2KUrmHZvDUNhnCDKe7MshfTuWFQB9iXwDg==", + "peer": true }, "node_modules/level": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", + "peer": true, "dependencies": { "browser-level": "^1.0.1", "classic-level": "^1.2.0" @@ -5557,6 +5274,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", + "peer": true, "engines": { "node": ">=12" } @@ -5565,6 +5283,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", + "peer": true, "dependencies": { "buffer": "^6.0.3", "module-error": "^1.0.1" @@ -5587,9 +5306,10 @@ } }, "node_modules/libp2p": { - "version": "0.46.6", - "resolved": "https://registry.npmjs.org/libp2p/-/libp2p-0.46.6.tgz", - "integrity": "sha512-5zDUpi4Foj30s+I/f6UC+wrO2u1CMLVFXby+AnCl1cEt4r+z92rLlN1Td0gUQjiAw1I3EL9yI+ASt3/Pts5IPw==", + "version": "0.46.10", + "resolved": "https://registry.npmjs.org/libp2p/-/libp2p-0.46.10.tgz", + "integrity": "sha512-zwvulgpyEtru9Z2MWA3b+6/g0t0/qO1Z6ON0LFzO0prjFzXbKa3R57tAktef8JIhiCriZbtPc4Bex3Q8PUL2wQ==", + "peer": true, "dependencies": { "@achingbrain/nat-port-mapper": "^1.0.9", "@libp2p/crypto": "^2.0.3", @@ -5601,13 +5321,12 @@ "@libp2p/peer-collections": "^4.0.3", "@libp2p/peer-id": "^3.0.2", "@libp2p/peer-id-factory": "^3.0.3", - "@libp2p/peer-record": "^6.0.3", - "@libp2p/peer-store": "^9.0.3", - "@libp2p/utils": "^4.0.2", + "@libp2p/peer-record": "^6.0.4", + "@libp2p/peer-store": "^9.0.4", + "@libp2p/utils": "^4.0.3", "@multiformats/mafmt": "^12.1.2", "@multiformats/multiaddr": "^12.1.5", "@multiformats/multiaddr-matcher": "^1.0.0", - "abortable-iterator": "^5.0.1", "any-signal": "^4.1.1", "datastore-core": "^9.0.1", "delay": "^6.0.0", @@ -5629,20 +5348,49 @@ "multiformats": "^12.0.1", "p-defer": "^4.0.0", "p-queue": "^7.3.4", - "p-retry": "^5.0.0", + "p-retry": "^6.0.0", "private-ip": "^3.0.0", "protons-runtime": "^5.0.0", - "rate-limiter-flexible": "^2.3.11", + "rate-limiter-flexible": "^3.0.0", "uint8arraylist": "^2.4.3", "uint8arrays": "^4.0.6", "wherearewe": "^2.0.1", "xsalsa20": "^1.1.0" } }, + "node_modules/libp2p/node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "peer": true + }, + "node_modules/libp2p/node_modules/p-retry": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.0.0.tgz", + "integrity": "sha512-6NuuXu8Upembd4sNdo4PRbs+M6aHgBTrFE6lkH0YKjVzne3cDW4gkncB98ty/bkMxLxLVNeD5bX9FyWjM7WZ+A==", + "peer": true, + "dependencies": { + "@types/retry": "0.12.2", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/libp2p/node_modules/rate-limiter-flexible": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-3.0.0.tgz", + "integrity": "sha512-janAJkWxWxmLka0hV+XvCTo0M8keeSeOuz8ZL33cTXrkS4ek9mQ2VJm9ri7fm03oTVth19Sfqb1ijCmo7K/vAg==", + "peer": true + }, "node_modules/lie": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "peer": true, "dependencies": { "immediate": "~3.0.5" } @@ -5651,6 +5399,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "peer": true, "dependencies": { "lie": "3.1.1" } @@ -5673,17 +5422,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.eq": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.eq/-/lodash.eq-4.0.0.tgz", - "integrity": "sha512-vbrJpXL6kQNG6TkInxX12DZRfuYVllSxhwYqjYB78g2zF3UI15nFO/0AgmZnZRnaQ38sZtjCiVjGr2rnKt4v0g==" - }, - "node_modules/lodash.indexof": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/lodash.indexof/-/lodash.indexof-4.0.5.tgz", - "integrity": "sha512-t9wLWMQsawdVmf6/IcAgVGqAJkNzYVcn4BHYZKTPW//l7N5Oq7Bq138BaVk19agcsPZePcidSgTTw4NqS1nUAw==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "peer": true }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -5716,6 +5456,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/longbits/-/longbits-1.1.0.tgz", "integrity": "sha512-22U2exkkYy7sr7nuQJYx2NEZ2kEMsC69+BxM5h8auLvkVIJa+LwAB5mFIExnuW2dFuYXFOWsFMKXjaWiq/htYQ==", + "peer": true, "dependencies": { "byte-access": "^1.0.1", "uint8arraylist": "^2.0.0" @@ -5813,6 +5554,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "peer": true, "dependencies": { "is-plain-obj": "^2.1.0" }, @@ -5823,7 +5565,8 @@ "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "peer": true }, "node_modules/merge2": { "version": "1.4.1", @@ -6055,6 +5798,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", + "peer": true, "engines": { "node": ">=10" } @@ -6088,6 +5832,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mortice/-/mortice-3.0.1.tgz", "integrity": "sha512-eyDUsl1nCR9+JtNksKnaESLP9MgAXCA4w1LTtsmOSQNsThnv++f36rrBu5fC/fdGIwTJZmbiaR/QewptH93pYA==", + "peer": true, "dependencies": { "nanoid": "^4.0.0", "observable-webworkers": "^2.0.1", @@ -6105,9 +5850,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multiformats": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-12.0.1.tgz", - "integrity": "sha512-s01wijBJoDUqESWSzePY0lvTw7J3PVO9x2Cc6ASI5AMZM2Gnhh7BC17+nlFhHKU7dDzaCaRfb+NiqNzOsgPUoQ==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-12.1.1.tgz", + "integrity": "sha512-GBSToTmri2vJYs8wqcZQ8kB21dCaeTOzHTIAlr8J06C1eL6UbzqURXFZ5Fl0EYm9GAFz1IlYY8SxGOs9G9NJRg==", "engines": { "node": ">=16.0.0", "npm": ">=7.0.0" @@ -6125,6 +5870,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz", "integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==", + "peer": true, "bin": { "nanoid": "bin/nanoid.js" }, @@ -6135,12 +5881,14 @@ "node_modules/napi-macros": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", - "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==" + "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", + "peer": true }, "node_modules/native-fetch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/native-fetch/-/native-fetch-4.0.2.tgz", "integrity": "sha512-4QcVlKFtv2EYVS5MBgsGX5+NWKtbDbIECdUXDBGDMAZXq3Jkv9zf+y8iS7Ub8fEdga3GpYeazp9gauNqXHJOCg==", + "peer": true, "peerDependencies": { "undici": "*" } @@ -6155,6 +5903,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "peer": true, "engines": { "node": ">= 0.4.0" } @@ -6182,6 +5931,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "peer": true, "engines": { "node": ">= 6.13.0" } @@ -6190,6 +5940,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "peer": true, "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -6249,6 +6000,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "peer": true, "dependencies": { "path-key": "^4.0.0" }, @@ -6263,6 +6015,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "peer": true, "engines": { "node": ">=12" }, @@ -6326,6 +6079,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/observable-webworkers/-/observable-webworkers-2.0.1.tgz", "integrity": "sha512-JI1vB0u3pZjoQKOK1ROWzp0ygxSi7Yb0iR+7UNsw4/Zn4cQ0P3R7XL38zac/Dy2tEA7Lg88/wIJTjF8vYXZ0uw==", + "peer": true, "engines": { "node": ">=16.0.0", "npm": ">=7.0.0" @@ -6340,11 +6094,6 @@ "wrappy": "1" } }, - "node_modules/one-webcrypto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/one-webcrypto/-/one-webcrypto-1.0.3.tgz", - "integrity": "sha512-fu9ywBVBPx0gS9K0etIROTiCkvI5S1TDjFsYFb3rC1ewFxeOqsbzq7aIMBHsYfrTHBcGXJaONXXjTl8B01cW1Q==" - }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -6502,25 +6251,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-retry": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-5.1.1.tgz", - "integrity": "sha512-i69WkEU5ZAL8mrmdmVviWwU+DN+IUF8f4sSJThoJ3z5A7Nn5iuO5ROX3Boye0u+uYQLOSfgFl7SuFZCjlAVbQA==", - "dependencies": { - "@types/retry": "0.12.1", - "retry": "^0.13.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-timeout": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==", + "peer": true, "engines": { "node": ">=14.16" }, @@ -6636,7 +6371,8 @@ "node_modules/platform": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "peer": true }, "node_modules/pluralize": { "version": "8.0.0", @@ -7076,6 +6812,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/private-ip/-/private-ip-3.0.1.tgz", "integrity": "sha512-Ezc16ANuhSHmWAE6lbXUKburNzGpR0J5X0Zh5Um/PZ/s57Fp+HYqYe6BYPH2QbqKr/5WebfzJQ1jq6Kj5dbRmA==", + "peer": true, "dependencies": { "@chainsafe/is-ip": "^2.0.1", "ip-regex": "^5.0.0", @@ -7214,7 +6951,8 @@ "node_modules/rate-limiter-flexible": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-2.4.1.tgz", - "integrity": "sha512-dgH4T44TzKVO9CLArNto62hJOwlWJMLUjVVr/ii0uUzZXEXthDNr7/yefW5z/1vvHAfycc1tnuiYyNJ8CTRB3g==" + "integrity": "sha512-dgH4T44TzKVO9CLArNto62hJOwlWJMLUjVVr/ii0uUzZXEXthDNr7/yefW5z/1vvHAfycc1tnuiYyNJ8CTRB3g==", + "peer": true }, "node_modules/rc": { "version": "1.2.8", @@ -7275,15 +7013,11 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/receptacle/-/receptacle-1.3.2.tgz", "integrity": "sha512-HrsFvqZZheusncQRiEE7GatOAETrARKV/lnfYicIm8lbvp/JQOdADOfhjBd2DajvoszEyxSM6RlAAIZgEoeu/A==", + "peer": true, "dependencies": { "ms": "^2.1.1" } }, - "node_modules/reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" - }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -7387,12 +7121,14 @@ "node_modules/retimer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/retimer/-/retimer-3.0.0.tgz", - "integrity": "sha512-WKE0j11Pa0ZJI5YIk0nflGI7SQsfl2ljihVy7ogh7DeQSeYAUi0ubZ/yEueGtDfUPk6GH5LRw1hBdLq4IwUBWA==" + "integrity": "sha512-WKE0j11Pa0ZJI5YIk0nflGI7SQsfl2ljihVy7ogh7DeQSeYAUi0ubZ/yEueGtDfUPk6GH5LRw1hBdLq4IwUBWA==", + "peer": true }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "peer": true, "engines": { "node": ">= 4" } @@ -7512,6 +7248,7 @@ "url": "https://feross.org/support" } ], + "peer": true, "dependencies": { "queue-microtask": "^1.2.2" } @@ -7549,10 +7286,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true, + "peer": true + }, "node_modules/sanitize-filename": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "peer": true, "dependencies": { "truncate-utf8-bytes": "^1.0.0" } @@ -7581,12 +7326,8 @@ "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "node_modules/seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "peer": true }, "node_modules/semver": { "version": "7.5.4", @@ -7854,6 +7595,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "peer": true, "engines": { "node": ">=12" }, @@ -7984,6 +7726,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/timeout-abort-controller/-/timeout-abort-controller-3.0.0.tgz", "integrity": "sha512-O3e+2B8BKrQxU2YRyEjC/2yFdb33slI22WRdUaDx6rvysfi9anloNZyR2q0l6LnePo5qH7gSM7uZtvvwZbc2yA==", + "peer": true, "dependencies": { "retimer": "^3.0.0" } @@ -8009,6 +7752,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "peer": true, "dependencies": { "utf8-byte-length": "^1.0.1" } @@ -8103,9 +7847,9 @@ } }, "node_modules/tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, "node_modules/type-check": { @@ -8133,42 +7877,42 @@ } }, "node_modules/typedoc": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", - "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.1.tgz", + "integrity": "sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==", "dev": true, "dependencies": { "lunr": "^2.3.9", "marked": "^4.3.0", - "minimatch": "^9.0.0", + "minimatch": "^9.0.3", "shiki": "^0.14.1" }, "bin": { "typedoc": "bin/typedoc" }, "engines": { - "node": ">= 14.14" + "node": ">= 16" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x" } }, "node_modules/typedoc-plugin-missing-exports": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-2.0.1.tgz", - "integrity": "sha512-+A78kT78uC0Dbv2EFB9RXHO3iywJ5x89jd4z0bLL7Z8DlOSQjJxhRHf8otXsHZbgRUWAuYqYMA9LzxfuSOc87A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-2.1.0.tgz", + "integrity": "sha512-+1DhqZCEu7Vu5APnrqpPwl31D+hXpt1fV0Le9ycCRL1eLVdatdl6KVt4SEVwPxnEpKwgOn2dNX6I9+0F1aO2aA==", "dev": true, "peerDependencies": { - "typedoc": "0.24.x" + "typedoc": "0.24.x || 0.25.x" } }, "node_modules/typedoc-plugin-rename-defaults": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/typedoc-plugin-rename-defaults/-/typedoc-plugin-rename-defaults-0.6.5.tgz", - "integrity": "sha512-DwkgwRMxgu3UrDR3VUAdnF9jYzM6p7rw6UcVIh4MD7yjEmFDR8WWyOlk6oYgELmRYHxTDx0f0GK6iSgoxSh/Qw==", + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/typedoc-plugin-rename-defaults/-/typedoc-plugin-rename-defaults-0.6.6.tgz", + "integrity": "sha512-8TCDrxMG8cR0IRhMZbxMCXlmQrEwFFH0she4eHsaUGd1TPfrkk8GLO4n2qlSISd66OtT28e17ysiVZPbQnLGMg==", "dev": true, "peerDependencies": { - "typedoc": "0.22.x || 0.23.x || 0.24.x" + "typedoc": "0.22.x || 0.23.x || 0.24.x || 0.25.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -8181,9 +7925,9 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz", - "integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -8196,9 +7940,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -8212,6 +7956,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-1.0.6.tgz", "integrity": "sha512-Z0ujO4rxPwxTdLsSI5ke+bdl9hjJ1xiOakBPZeWUI/u6YBGCEGTW6b90SMlhxSGButKVPkL9fMFUDnqThQYTGg==", + "peer": true, "dependencies": { "byte-access": "^1.0.0", "longbits": "^1.1.0", @@ -8297,7 +8042,8 @@ "node_modules/utf8-byte-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", - "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==" + "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==", + "peer": true }, "node_modules/util": { "version": "0.12.5", @@ -8332,6 +8078,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/varint-decoder/-/varint-decoder-1.0.0.tgz", "integrity": "sha512-JkOvdztASWGUAsXshCFHrB9f6AgR2Q8W08CEyJ+43b1qtFocmI8Sp1R/M0E/hDOY2FzVIqk63tOYLgDYWuJ7IQ==", + "peer": true, "dependencies": { "varint": "^5.0.0" }, @@ -8343,7 +8090,8 @@ "node_modules/varint-decoder/node_modules/varint": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", - "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==", + "peer": true }, "node_modules/vscode-oniguruma": { "version": "1.7.0", @@ -8393,6 +8141,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/wherearewe/-/wherearewe-2.0.1.tgz", "integrity": "sha512-XUguZbDxCA2wBn2LoFtcEhXL6AXo+hVjGonwhSTTTU9SzbWG8Xu3onNIpzf9j/mYUcJQ0f+m37SzG77G851uFw==", + "peer": true, "dependencies": { "is-electron": "^2.2.0" }, @@ -8452,9 +8201,9 @@ } }, "node_modules/wnfs": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/wnfs/-/wnfs-0.1.24.tgz", - "integrity": "sha512-EHCztoKtUsREL2Iio0SfldGQAfskJLGG8RkLefRws/8jWJN1liRu4DBG+AdAZo91LB4iEHx4Bxpet1NYs/y0Jg==" + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/wnfs/-/wnfs-0.1.27.tgz", + "integrity": "sha512-9cC53nL3Nd303gykf6WNLYoxBsGkR9sgG2PMLMRcaMD3Ygzd6Q9sUFz7dPhYNWUPIH9VUGLM58MA16c03HrX0Q==" }, "node_modules/word-wrap": { "version": "1.2.5", @@ -8516,6 +8265,7 @@ "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -8536,6 +8286,7 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "peer": true, "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -8548,6 +8299,7 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "peer": true, "engines": { "node": ">=4.0" } @@ -8555,7 +8307,8 @@ "node_modules/xsalsa20": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/xsalsa20/-/xsalsa20-1.2.0.tgz", - "integrity": "sha512-FIr/DEeoHfj7ftfylnoFt3rAIRoWXpx2AoDfrT2qD2wtp7Dp+COajvs/Icb7uHqRW9m60f5iXZwdsJJO3kvb7w==" + "integrity": "sha512-FIr/DEeoHfj7ftfylnoFt3rAIRoWXpx2AoDfrT2qD2wtp7Dp+COajvs/Icb7uHqRW9m60f5iXZwdsJJO3kvb7w==", + "peer": true }, "node_modules/xtend": { "version": "4.0.2", @@ -8566,14 +8319,6 @@ "node": ">=0.4" } }, - "node_modules/xxhashjs": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", - "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", - "dependencies": { - "cuint": "^0.2.2" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 7c945605..54f58120 100644 --- a/package.json +++ b/package.json @@ -82,64 +82,66 @@ "publish-latest": "npm publish --tag latest" }, "dependencies": { - "@chainsafe/libp2p-noise": "^13.0.0", - "@chainsafe/libp2p-yamux": "^5.0.0", - "@ipld/dag-cbor": "^9.0.4", + "@ipld/dag-cbor": "^9.0.5", "@ipld/dag-pb": "^4.0.5", - "@libp2p/mplex": "^9.0.4", - "@libp2p/peer-id": "^3.0.2", - "@libp2p/websockets": "^7.0.4", - "@libp2p/webtransport": "^3.0.6", - "@multiformats/multiaddr": "^12.1.7", "@ucans/core": "^0.12.0", - "blockstore-level": "^1.1.3", "debounce-promise": "^3.1.2", "emittery": "^1.0.1", - "events": "^3.3.0", - "fission-bloom-filters": "1.7.1", - "interface-blockstore": "^5.2.5", - "interface-datastore": "^8.2.4", - "ipfs-bitswap": "^19.0.0", - "ipfs-unixfs": "^11.0.1", - "ipfs-unixfs-exporter": "^13.1.6", - "ipfs-unixfs-importer": "^15.1.7", - "iso-base": "^2.0.0", - "iso-did": "^1.3.1", - "iso-signatures": "^0.1.9", + "interface-blockstore": "^5.2.6", + "ipfs-unixfs": "^11.1.0", + "ipfs-unixfs-exporter": "^13.2.1", + "ipfs-unixfs-importer": "^15.2.1", + "iso-base": "^2.0.1", + "iso-did": "^1.3.3", + "iso-signatures": "^0.1.10", "it-all": "^3.0.3", - "libp2p": "^0.46.6", - "localforage": "^1.10.0", - "multiformats": "^12.0.1", - "one-webcrypto": "^1.0.3", + "multiformats": "^12.1.1", "uint8arrays": "^4.0.6", - "wnfs": "0.1.24" + "wnfs": "0.1.27" }, "devDependencies": { "@esbuild-plugins/node-globals-polyfill": "^0.2.3", - "@ipld/car": "^5.2.0", + "@ipld/car": "^5.2.3", "@types/debounce-promise": "^3.1.6", "@types/expect": "^24.3.0", "@types/mocha": "^10.0.1", - "@types/node": "^20.5.0", - "@typescript-eslint/eslint-plugin": "^6.4.0", - "@typescript-eslint/parser": "^6.4.0", - "assert": "^2.0.0", + "@types/node": "^20.6.0", + "@typescript-eslint/eslint-plugin": "^6.7.0", + "@typescript-eslint/parser": "^6.7.0", + "assert": "^2.1.0", "copyfiles": "^2.4.1", - "dprint": "^0.40.2", + "dprint": "^0.41.0", "esbuild": "^0.19.2", "esbuild-plugin-wasm": "^1.1.0", - "eslint": "^8.47.0", - "fast-check": "^3.12.0", + "eslint": "^8.49.0", + "fast-check": "^3.13.0", "globby": "^13.2.2", "madge": "^6.1.0", "mocha": "^10.2.0", "rimraf": "^5.0.1", "ts-node": "^10.9.1", - "tslib": "^2.6.1", - "typedoc": "^0.24.8", - "typedoc-plugin-missing-exports": "^2.0.1", - "typedoc-plugin-rename-defaults": "^0.6.5", - "typescript": "^5.1.6", + "tslib": "^2.6.2", + "typedoc": "^0.25.1", + "typedoc-plugin-missing-exports": "^2.1.0", + "typedoc-plugin-rename-defaults": "^0.6.6", + "typescript": "^5.2.2", "util": "^0.12.4" + }, + "peerDependencies": { + "@chainsafe/libp2p-noise": "^13.0.1", + "@chainsafe/libp2p-yamux": "^5.0.0", + "@libp2p/mplex": "^9.0.5", + "@libp2p/peer-id": "^3.0.2", + "@libp2p/websockets": "^7.0.6", + "@libp2p/webtransport": "^3.0.10", + "@localfirst/relay-client": "^3.6.2", + "@multiformats/multiaddr": "^12.1.7", + "@noble/ciphers": "^0.3.0", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.2", + "blockstore-level": "^1.1.4", + "ipfs-bitswap": "^19.0.0", + "libp2p": "^0.46.10", + "localforage": "^1.10.0" } } diff --git a/scripts/build-minified.js b/scripts/build-minified.js index 4b91de8f..56699d02 100644 --- a/scripts/build-minified.js +++ b/scripts/build-minified.js @@ -10,7 +10,7 @@ import zlib from "zlib" const globalName = "oddjs" const CONFIG = { - entryPoints: ["src/index.ts", "src/compositions/fission.ts"], + entryPoints: ["src/index.ts", "src/compositions/local.ts"], outdir: "dist", bundle: true, splitting: true, @@ -40,8 +40,8 @@ await esbuild.build(CONFIG) fs.renameSync("dist/index.js", "dist/index.esm.min.js") fs.renameSync("dist/index.js.map", "dist/index.esm.min.js.map") -fs.renameSync("dist/compositions/fission.js", "dist/compositions/fission.esm.min.js") -fs.renameSync("dist/compositions/fission.js.map", "dist/compositions/fission.esm.min.js.map") +fs.renameSync("dist/compositions/local.js", "dist/compositions/local.esm.min.js") +fs.renameSync("dist/compositions/local.js.map", "dist/compositions/local.esm.min.js.map") // GZIP diff --git a/src/accessKey.ts b/src/accessKey.ts new file mode 100644 index 00000000..687ff4ec --- /dev/null +++ b/src/accessKey.ts @@ -0,0 +1,7 @@ +import * as Path from "./path/index.js" + +export type AccessKeyWithContext = { + did: string + key: Uint8Array + path: Path.Distinctive +} diff --git a/src/agent/did.ts b/src/agent/did.ts index e968190a..3d7338f8 100644 --- a/src/agent/did.ts +++ b/src/agent/did.ts @@ -9,7 +9,7 @@ import * as Agent from "../components/agent/implementation.js" */ export async function exchange(agent: Agent.Implementation): Promise { const pubKey = await agent.exchangeKey().then(exportPublicKey).then(spki.decode) - const ksAlg = await agent.keyAlgorithm() + const ksAlg = agent.keyAlgorithm() return DIDKey.fromPublicKey(ksAlg, pubKey).toString() } @@ -24,7 +24,7 @@ export { exchange as sharing } */ export async function signing(agent: Agent.Implementation): Promise { const pubKey = await agent.signingKey().then(exportPublicKey).then(spki.decode) - const ksAlg = await agent.keyAlgorithm() + const ksAlg = agent.keyAlgorithm() return DIDKey.fromPublicKey(ksAlg, pubKey).toString() } diff --git a/src/auth.ts b/src/auth.ts index 42e15770..e209d6f4 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -1,33 +1,39 @@ import { Cabinet } from "./repositories/cabinet.js" -import { Account, Agent, Authority, Identifier } from "./components.js" +import { Account, Agent, Clerk, Identifier } from "./components.js" import { AnnexParentType } from "./components/account/implementation.js" +import { Names } from "./repositories/names.js" ////////////// // REGISTER // ////////////// export const register = ( - { account, agent, authority, identifier, cabinet }: { + { account, agent, clerk, identifier, cabinet, names }: { account: Account.Implementation agent: Agent.Implementation - authority: Authority.Implementation + clerk: Clerk.Implementation identifier: Identifier.Implementation cabinet: Cabinet + names: Names } ) => async (formValues: Record): Promise< { registered: true } | { registered: false; reason: string } > => { - // Do delegation from identifier to agent - const agentDelegation = await authority.clerk.tickets.misc.identifierToAgentDelegation(identifier, agent) - await cabinet.addTicket("misc", agentDelegation) - // Call account register implementation - const result = await account.register(formValues, agentDelegation) + const result = await account.register(identifier, names, formValues) if (result.registered) { - await cabinet.addTickets("account", result.tickets) + // Do delegation from identifier to agent + const agentDelegation = await clerk.tickets.misc.identifierToAgentDelegation( + identifier, + agent, + result.tickets + ) + + await cabinet.addTicket("agent", agentDelegation, clerk.tickets.cid) + await cabinet.addTickets("account", result.tickets, clerk.tickets.cid) return { registered: true } } else { return result diff --git a/src/authority/query.ts b/src/authority/query.ts index 6c9ebd68..f1e4569c 100644 --- a/src/authority/query.ts +++ b/src/authority/query.ts @@ -1,3 +1,4 @@ +import { isObject, isString } from "../common/type-checks.js" import * as Path from "../path/index.js" //////// @@ -21,6 +22,7 @@ export type FileSystemAbility = typeof ALLOWED_FILE_SYSTEM_ABILITIES[number] export type FileSystemQuery = { query: "fileSystem" ability: FileSystemAbility + id: { did: string } | { name: string } path: Path.Distinctive> } @@ -33,22 +35,28 @@ export const account: AccountQuery = { } export const fileSystem = { - rootAccess: [{ - query: "fileSystem", - ability: "*", - path: Path.directory("public"), - }, { - query: "fileSystem", - ability: "*", - path: Path.directory("private"), - }] as FileSystemQuery[], + rootAccess(id: { did: string } | { name: string }): FileSystemQuery[] { + return [{ + query: "fileSystem", + ability: "*", + id, + path: Path.directory("public"), + }, { + query: "fileSystem", + ability: "*", + id, + path: Path.directory("private"), + }] + }, limitedAccess( ability: typeof ALLOWED_FILE_SYSTEM_ABILITIES[number], + id: { did: string } | { name: string }, path: Path.Distinctive> ): FileSystemQuery { return { query: "fileSystem", ability, + id, path, } }, @@ -58,6 +66,36 @@ export const fileSystem = { // 🛠️ // //////// +export function isContained({ parent, child }: { parent: Query; child: Query }): boolean { + if (parent.query === "account") return child.query === "account" + + // File System + if (parent.query === "fileSystem") { + if (child.query !== "fileSystem") return false + + const ability = parent.ability === "*" + ? true + : (child.ability === "*" ? false : parent.ability === child.ability) + + const id = JSON.stringify(parent.id) === JSON.stringify(child.id) + + const unwrappedParentPath = Path.unwrap(parent.path) + const path = Path.unwrap(child.path).reduce( + (acc, part, idx) => { + if (!acc) return acc + if (idx + 1 > unwrappedParentPath.length) return true + return part === unwrappedParentPath[idx] + }, + true + ) + + return ability && id && path + } + + // ? + return false +} + export function needsWriteAccess(query: FileSystemQuery): boolean { return query.ability !== "read" } @@ -66,8 +104,8 @@ export function needsWriteAccess(query: FileSystemQuery): boolean { // ENCODING // ////////////// -export function fromJSON(query: string): Query { - const obj = JSON.parse(query) +export function fromJSON(query: string | Record): Query { + const obj = isString(query) ? JSON.parse(query) : query switch (obj.query) { case "account": @@ -101,25 +139,37 @@ function fileSystemQueryFromJSON(obj: Record): FileSystemQuery throw new Error(`Expected a path with a partition (private or public), got: ${obj.path}`) } + if (!isObject(obj.id)) { + throw new Error("Expected a `id` object") + } + + if (!isString(obj.id.did) && !isString(obj.id.name)) { + throw new Error("Expected the `id` object to have a `did` or a `name` property") + } + return { query: "fileSystem", ability: obj.ability as FileSystemAbility, + id: isString(obj.id.did) + ? { did: obj.id.did } + : { name: obj.id.name as string }, path: partitionedPath, } } -export function toJSON(query: Query): string { +export function toJSON(query: Query): object { switch (query.query) { case "account": - return JSON.stringify({ + return { query: query.query, - }) + } case "fileSystem": - return JSON.stringify({ + return { query: query.query, ability: query.ability, + id: query.id, path: Path.toPosix(query.path), - }) + } } } diff --git a/src/channel.ts b/src/channel.ts index eff2a98c..e0a6195c 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -7,9 +7,9 @@ export type Channel = { send: (data: ChannelData) => void } -export type ChannelOptions = { - context: Context - onmessage: (event: MessageEvent) => void +export type ChannelOptions = { + onmessage: (event: MessageEvent, channel: Channel) => void + topic: string } export type ChannelData = string | ArrayBufferLike | Blob | ArrayBufferView diff --git a/src/channel/websocket.ts b/src/channel/websocket.ts index e1b3949b..67c27c20 100644 --- a/src/channel/websocket.ts +++ b/src/channel/websocket.ts @@ -5,22 +5,25 @@ import { Manners } from "../components.js" // 🛠️ // //////// -export const createWebsocketChannel = async ( +export const createWebsocketChannel = async ( manners: Manners.Implementation, socketEndpoint: string, - options: ChannelOptions + options: ChannelOptions ): Promise => { const { onmessage } = options const socket = new WebSocket(socketEndpoint) await waitForOpenConnection(socket) - socket.onmessage = onmessage manners.log("📣 Channel established", socket) - return { + const channel = { send: publish(socket), close: close(socket), } + + socket.onmessage = event => onmessage(event, channel) + + return channel } const waitForOpenConnection = async (socket: WebSocket): Promise => { @@ -31,15 +34,11 @@ const waitForOpenConnection = async (socket: WebSocket): Promise => { } export const close = (socket: WebSocket): () => void => { - return () => socket.close(1000) + return () => socket.close() } export const publish = (socket: WebSocket): (data: ChannelData) => void => { return function(data: ChannelData) { - const binary = typeof data === "string" - ? new TextEncoder().encode(data).buffer - : data - - socket.send(binary) + socket.send(data) } } diff --git a/src/components.ts b/src/components.ts index baa05fa7..8ee571dd 100644 --- a/src/components.ts +++ b/src/components.ts @@ -2,6 +2,7 @@ import * as Account from "./components/account/implementation.js" import * as Agent from "./components/agent/implementation.js" import * as Authority from "./components/authority/implementation.js" import * as Channel from "./components/channel/implementation.js" +import * as Clerk from "./components/clerk/implementation.js" import * as Depot from "./components/depot/implementation.js" import * as DNS from "./components/dns/implementation.js" import * as Identifier from "./components/identifier/implementation.js" @@ -14,11 +15,16 @@ import { FileSystem } from "./fs/class.js" // COMPONENTS // //////////////// -export type Components = { +export type Components< + Annex extends Account.AnnexParentType, + AuthorityProvideResponse, + AuthorityRequestResponse, +> = { account: Account.Implementation agent: Agent.Implementation - authority: Authority.Implementation - channel: Channel.Implementation + authority: Authority.Implementation + channel: Channel.Implementation + clerk: Clerk.Implementation depot: Depot.Implementation dns: DNS.Implementation identifier: Identifier.Implementation @@ -29,4 +35,4 @@ export type Components = // CONVENIENCE EXPORTS // ///////////////////////// -export { Account, Agent, Authority, Channel, Depot, DNS, Identifier, Manners, Storage } +export { Account, Agent, Authority, Channel, Clerk, Depot, DNS, Identifier, Manners, Storage } diff --git a/src/components/account/fission/data-root.ts b/src/components/account/fission/data-root.ts index 048c3fc8..4fe3d710 100644 --- a/src/components/account/fission/data-root.ts +++ b/src/components/account/fission/data-root.ts @@ -6,7 +6,6 @@ import * as Ucan from "../../../ucan/ts-ucan/index.js" import { decodeCID } from "../../../common/cid.js" import { Agent, DNS, Identifier, Manners } from "../../../components.js" -import { cid as ticketCID } from "../../../ticket/index.js" import { Ticket } from "../../../ticket/types.js" /** @@ -84,7 +83,7 @@ export async function update( username: string ): Promise<{ updated: true } | { updated: false; reason: string }> { const cid = cidInstance.toString() - const identifierDID = await dependencies.identifier.did() + const identifierDID = dependencies.identifier.did() // Debug dependencies.manners.log("🌊 Updating your DNSLink:", cid) @@ -98,12 +97,12 @@ export async function update( // issuer: await Ucan.keyPair(dependencies.agent), FIXME: Should use agent issuer: { did: () => identifierDID, - jwtAlg: await dependencies.identifier.ucanAlgorithm(), + jwtAlg: dependencies.identifier.ucanAlgorithm(), sign: data => dependencies.identifier.sign(data), }, proofs: await Promise.all(proofs.map( - async proof => ticketCID(proof).toString() + async proof => (await Ucan.ticketCID(proof)).toString() )), }) ) diff --git a/src/components/account/fission/implementations/common.ts b/src/components/account/fission/implementations/common.ts index 5f6bdc8e..cae0b759 100644 --- a/src/components/account/fission/implementations/common.ts +++ b/src/components/account/fission/implementations/common.ts @@ -2,12 +2,25 @@ import * as Fission from "../../../../common/fission.js" import * as Ucan from "../../../../ucan/ts-ucan/index.js" import * as Identifier from "../../../identifier/implementation.js" +import { AccountQuery } from "../../../../authority/query.js" import { CID } from "../../../../common/index.js" import { Agent, DNS, Manners } from "../../../../components.js" -import { Inventory } from "../../../../ticket/inventory.js" +import { FileSystemCarrier } from "../../../../fs/types.js" +import { Inventory } from "../../../../inventory.js" +import { Names } from "../../../../repositories/names.js" import { Ticket } from "../../../../ticket/types.js" import { DataRoot, lookupUserDID } from "../index.js" +//////// +// 🏔️ // +//////// + +export const NAMES = { + fileSystem(identifierDID: string) { + return `ACCOUNT_FILE_SYSTEM_DID#${identifierDID}` + }, +} + //////// // 🧩 // //////// @@ -19,14 +32,7 @@ export type Annex = { * This method can be used to load a local-only file system before an account is registered. * When you register an account, the file system will sync with the Fission server, making it available through Fission IPFS nodes. */ - volume: (username?: string) => Promise<{ - dataRoot?: CID - dataRootUpdater: ( - dataRoot: CID, - proofs: Ticket[] - ) => Promise<{ updated: true } | { updated: false; reason: string }> - did: string - }> + volume: (username?: string) => Promise } export type Dependencies = { @@ -36,31 +42,26 @@ export type Dependencies = { manners: Manners.Implementation } -/////////////// -// DATA ROOT // -/////////////// +///////////////// +// FILE SYSTEM // +///////////////// export async function volume( endpoints: Fission.Endpoints, dependencies: Dependencies, identifier: Identifier.Implementation, - tickets: Inventory, + inventory: Inventory, + names: Names, username?: string -): Promise<{ - dataRoot?: CID - dataRootUpdater: ( - dataRoot: CID, - proofs: Ticket[] - ) => Promise<{ updated: true } | { updated: false; reason: string }> - did: string -}> { - const accountProof = findAccountProofUCAN(await identifier.did(), tickets) +): Promise { + const accountProof = findAccountProofTicket(identifier.did(), inventory) const accountUsername = accountProof ? findUsernameFact(accountProof) : null if (username && username !== accountUsername) { - return otherVolume(endpoints, dependencies, identifier, tickets, username) + throw new Error("Loading a volume of another user is currently disabled") + // TODO: return otherVolume(endpoints, dependencies, identifier, inventory, username) } else { - return accountVolume(endpoints, dependencies, identifier, tickets) + return accountVolume(endpoints, dependencies, identifier, inventory, names) } } @@ -68,42 +69,51 @@ export async function accountVolume( endpoints: Fission.Endpoints, dependencies: Dependencies, identifier: Identifier.Implementation, - tickets: Inventory -): Promise<{ - dataRoot?: CID - dataRootUpdater: ( - dataRoot: CID, - proofs: Ticket[] - ) => Promise<{ updated: true } | { updated: false; reason: string }> - did: string -}> { + inventory: Inventory, + names: Names +): Promise { const dataRootUpdater = async (dataRoot: CID, proofs: Ticket[]) => { - const { suffices } = await hasSufficientAuthority(dependencies, identifier, tickets) + const { suffices } = await hasSufficientAuthority(dependencies, identifier, inventory) if (!suffices) return { updated: false, reason: "Not authenticated yet, lacking authority." } - return updateDataRoot(endpoints, dependencies, identifier, tickets, dataRoot, proofs) + return updateDataRoot(endpoints, dependencies, identifier, inventory, dataRoot, proofs) } - const { suffices } = await hasSufficientAuthority(dependencies, identifier, tickets) - const identifierDID = await identifier.did() + const { suffices } = await hasSufficientAuthority(dependencies, identifier, inventory) + const identifierDID = identifier.did() if (!suffices) { - return { - dataRoot: undefined, - dataRootUpdater, - did: identifierDID, - } + const name = NAMES.fileSystem(identifierDID) + const did = names.subject(name) + + return did + ? { dataRootUpdater, id: { did } } + : { dataRootUpdater, id: { name } } + } + + // Find account-proof UCAN + const accountProof = findAccountProofTicket(identifierDID, inventory) + + if (!accountProof) { + throw new Error("Expected to find account proof") + } + + const name = NAMES.fileSystem(accountProof.audience) + const did = names.subject(name) + + if (!did) { + // futile, because a file system should not be loaded in this state. + return { dataRootUpdater, futile: true, id: { name } } } if (!dependencies.manners.program.online()) { return { dataRoot: undefined, dataRootUpdater, - did: await fileSystemDID(dependencies, identifier, tickets) || identifierDID, + id: { did }, } } - // Find account-proof UCAN - const accountProof = findAccountProofUCAN(identifierDID, tickets) + // TODO: Keep username in `names` as well const username = accountProof ? findUsernameFact(accountProof) : null if (!username) { @@ -115,49 +125,45 @@ export async function accountVolume( return { dataRoot: await DataRoot.lookup(endpoints, dependencies, username).then(a => a || undefined), dataRootUpdater, - did: await fileSystemDID(dependencies, identifier, tickets) || identifierDID, + id: { did }, } } -export async function otherVolume( - endpoints: Fission.Endpoints, - dependencies: Dependencies, - identifier: Identifier.Implementation, - tickets: Inventory, - username: string -): Promise<{ - dataRoot?: CID - dataRootUpdater: ( - dataRoot: CID, - proofs: Ticket[] - ) => Promise<{ updated: true } | { updated: false; reason: string }> - did: string -}> { - if (!dependencies.manners.program.online()) { - throw new Error("Cannot load another user's volume while offline") - } - - const userDID = await lookupUserDID(endpoints, dependencies.dns, username) - if (!userDID) throw new Error("User not found") - - const dataRootUpdater = async (dataRoot: CID, proofs: Ticket[]) => { - // TODO: Add ability to update data root of another user - // For this we need the account capability to update the volume pointer. - throw new Error("Not implemented yet") - } - - return { - dataRoot: await DataRoot.lookup(endpoints, dependencies, username).then(a => a || undefined), - dataRootUpdater, - did: userDID, - } -} +// TODO: Disabled because it isn't using the correct DID +// +// export async function otherVolume( +// endpoints: Fission.Endpoints, +// dependencies: Dependencies, +// identifier: Identifier.Implementation, +// inventory: Inventory, +// username: string +// ): Promise { +// if (!dependencies.manners.program.online()) { +// throw new Error("Cannot load another user's volume while offline") +// } +// +// const userDID = await lookupUserDID(endpoints, dependencies.dns, username) +// if (!userDID) throw new Error("User not found") +// +// const dataRootUpdater = async (dataRoot: CID, proofs: Ticket[]) => { +// // TODO: Add ability to update data root of another user +// // For this we need the account capability to update the volume pointer. +// throw new Error("Not implemented yet") +// } +// +// return { +// dataRoot: await DataRoot.lookup(endpoints, dependencies, username).then(a => a || undefined), +// dataRootUpdater, +// // FIXME: This DID is not correct! +// did: userDID, +// } +// } export async function updateDataRoot( endpoints: Fission.Endpoints, dependencies: Dependencies, identifier: Identifier.Implementation, - tickets: Inventory, + inventory: Inventory, dataRoot: CID, proofs: Ticket[] ): Promise<{ updated: true } | { updated: false; reason: string }> { @@ -166,7 +172,7 @@ export async function updateDataRoot( } // Find account-proof UCAN - const accountProof = findAccountProofUCAN(await identifier.did(), tickets) + const accountProof = findAccountProofTicket(identifier.did(), inventory) const username = accountProof ? findUsernameFact(accountProof) : null if (!username) { @@ -184,42 +190,56 @@ export async function updateDataRoot( export async function did( dependencies: Dependencies, identifier: Identifier.Implementation, - tickets: Inventory + inventory: Inventory ): Promise { // Find account-proof UCAN - const accountProof = findAccountProofUCAN(await identifier.did(), tickets) + const accountProof = findAccountProofTicket(identifier.did(), inventory) // DID is issuer of that username UCAN return accountProof - ? Ucan.decode(accountProof.token).payload.iss + ? accountProof.issuer : null } export async function fileSystemDID( dependencies: Dependencies, identifier: Identifier.Implementation, - tickets: Inventory + inventory: Inventory ) { // Find account-proof UCAN - const accountProof = findAccountProofUCAN(await identifier.did(), tickets) + const accountProof = findAccountProofTicket(identifier.did(), inventory) // DID is issuer of that username UCAN return accountProof - ? Ucan.decode(accountProof.token).payload.aud + ? accountProof.audience : null } export async function hasSufficientAuthority( dependencies: Dependencies, identifier: Identifier.Implementation, - tickets: Inventory + inventory: Inventory ): Promise< { suffices: true } | { suffices: false; reason: string } > { - const accountProof = findAccountProofUCAN(await identifier.did(), tickets) + const accountProof = findAccountProofTicket(identifier.did(), inventory) return accountProof ? { suffices: true } : { suffices: false, reason: "Missing the needed account capabilities" } } +export async function provideAuthority( + accountQuery: AccountQuery, + identifier: Identifier.Implementation, + inventory: Inventory +): Promise { + const maybeTicket = findAccountTicket( + identifier.did(), + inventory + ) + + if (!maybeTicket) return [] + return [maybeTicket] +} + //////// // 🛠️ // //////// @@ -228,18 +248,18 @@ export async function hasSufficientAuthority( * Find the original UCAN the user got back from the Fission server * after registration. This UCAN will have the username fact. */ -export function findAccountProofUCAN( +export function findAccountProofTicket( audience: string, - tickets: Inventory + inventory: Inventory ): Ticket | null { const matcher = (ticket: Ticket) => !!findUsernameFact(ticket) // Grab the UCANs addressed to this audience (ideally current identifier), // then look for the username fact ucan in the delegation chains of those UCANs. - return tickets.lookupByAudience(audience).reduce( + return inventory.lookupTicketsByAudience(audience).reduce( (acc: Ticket | null, ticket) => { if (acc) return acc - return tickets.descendUntilMatching(ticket, matcher, Ucan.ticketProofResolver) + return inventory.descendUntilMatchingTicket(ticket, matcher, Ucan.ticketProofResolver) }, null ) @@ -254,20 +274,20 @@ export function findAccountProofUCAN( * In other words, if the device that originally registered the account * linked to another device, it would delegate the account-proof UCAN * to the other device. If then asked for the account UCAN on that other - * device it would be the delegated UCAN. + * device it would be the delegation UCAN. */ -export function findAccountUCAN( +export function findAccountTicket( audience: string, - tickets: Inventory -) { + inventory: Inventory +): Ticket | null { const matcher = (ticket: Ticket) => !!findUsernameFact(ticket) // Grab the UCANs addressed to this audience (ideally current identifier), // then look for the username fact ucan in the delegation chains of those UCANs. - return tickets.lookupByAudience(audience).reduce( + return inventory.lookupTicketsByAudience(audience).reduce( (acc: Ticket | null, ticket) => { if (acc) return acc - const hasProof = !!tickets.descendUntilMatching(ticket, matcher, Ucan.ticketProofResolver) + const hasProof = !!inventory.descendUntilMatchingTicket(ticket, matcher, Ucan.ticketProofResolver) if (hasProof) return ticket return null }, diff --git a/src/components/account/fission/implementations/delegated.ts b/src/components/account/fission/implementations/delegated.ts index a0a48e5c..cf85329d 100644 --- a/src/components/account/fission/implementations/delegated.ts +++ b/src/components/account/fission/implementations/delegated.ts @@ -1,7 +1,8 @@ import * as Fission from "../../../../common/fission.js" +import * as Identifier from "../../../identifier/implementation.js" import * as Common from "./common.js" -import { AccountQuery } from "../../../../authority/query.js" +import { Names } from "../../../../repositories/names.js" import { Ticket } from "../../../../ticket/types.js" import { Implementation } from "../../implementation.js" import { Annex, Dependencies } from "./common.js" @@ -26,8 +27,9 @@ export async function canRegister( } export async function register( - formValues: Record, - identifierTicket: Ticket + identifier: Identifier.Implementation, + names: Names, + formValues: Record ): Promise< | { registered: true; tickets: Ticket[] } | { registered: false; reason: string } @@ -38,14 +40,6 @@ export async function register( } } -//////////////////////////// -// IDENTIFIER & AUTHORITY // -//////////////////////////// - -export async function provideAuthority(accountQuery: AccountQuery): Promise { - return [] // TODO -} - //////// // 🛳 // //////// @@ -57,8 +51,8 @@ export function implementation( const endpoints = optionalEndpoints || Fission.PRODUCTION return { - annex: (identifier, ucanDictionary) => ({ - volume: (...args) => Common.volume(endpoints, dependencies, identifier, ucanDictionary, ...args), + annex: (identifier, inventory, names) => ({ + volume: (...args) => Common.volume(endpoints, dependencies, identifier, inventory, names, ...args), }), canRegister, @@ -66,6 +60,6 @@ export function implementation( did: (...args) => Common.did(dependencies, ...args), hasSufficientAuthority: (...args) => Common.hasSufficientAuthority(dependencies, ...args), - provideAuthority, + provideAuthority: Common.provideAuthority, } } diff --git a/src/components/account/fission/implementations/standard.ts b/src/components/account/fission/implementations/standard.ts index 623ba47b..fac8e673 100644 --- a/src/components/account/fission/implementations/standard.ts +++ b/src/components/account/fission/implementations/standard.ts @@ -1,8 +1,9 @@ import * as Fission from "../../../../common/fission.js" import * as Ucan from "../../../../ucan/ts-ucan/index.js" +import * as Identifier from "../../../identifier/implementation.js" import * as Common from "./common.js" -import { AccountQuery } from "../../../../authority/query.js" +import { Names } from "../../../../repositories/names.js" import { Ticket } from "../../../../ticket/types.js" import { Implementation } from "../../implementation.js" import { isUsernameAvailable, isUsernameValid } from "../index.js" @@ -37,11 +38,10 @@ export async function requestVerificationCode( } email = email.trim() - const identifierDID = await dependencies.identifier.did() + const identifierDID = dependencies.identifier.did() const ucan = await Ucan.build({ audience: await Fission.did(endpoints, dependencies.dns), - // issuer: await Ucan.keyPair(dependencies.agent), FIXME: Should use agent issuer: { did: () => identifierDID, jwtAlg: await dependencies.identifier.ucanAlgorithm(), @@ -141,8 +141,9 @@ export async function canRegister( export async function register( endpoints: Fission.Endpoints, dependencies: Dependencies, - formValues: Record, - identifierTicket: Ticket + identifier: Identifier.Implementation, + names: Names, + formValues: Record ): Promise< | { registered: true; tickets: Ticket[] } | { registered: false; reason: string } @@ -156,9 +157,9 @@ export async function register( } if (formValues.accountType === "app") { - return registerAppAccount(endpoints, dependencies, formValues, identifierTicket) + return registerAppAccount(endpoints, dependencies, identifier, names, formValues) } else if (formValues.accountType === "verified") { - return registerVerifiedAccount(endpoints, dependencies, formValues, identifierTicket) + return registerVerifiedAccount(endpoints, dependencies, identifier, names, formValues) } else { throw new Error("Invalid account type") } @@ -167,8 +168,9 @@ export async function register( async function registerAppAccount( endpoints: Fission.Endpoints, dependencies: Dependencies, - formValues: Record, - identifierTicket: Ticket + identifier: Identifier.Implementation, + names: Names, + formValues: Record ): Promise< | { registered: true; tickets: Ticket[] } | { registered: false; reason: string } @@ -179,25 +181,24 @@ async function registerAppAccount( async function registerVerifiedAccount( endpoints: Fission.Endpoints, dependencies: Dependencies, - formValues: Record, - identifierTicket: Ticket + identifier: Identifier.Implementation, + names: Names, + formValues: Record ): Promise< | { registered: true; tickets: Ticket[] } | { registered: false; reason: string } > { const code = formValues.code.trim() - const identifierDID = await dependencies.identifier.did() + const identifierDID = dependencies.identifier.did() const token = Ucan.encode( await Ucan.build({ audience: await Fission.did(endpoints, dependencies.dns), - // issuer: await Ucan.keyPair(dependencies.agent), FIXME: Should use agent issuer: { did: () => identifierDID, jwtAlg: await dependencies.identifier.ucanAlgorithm(), sign: data => dependencies.identifier.sign(data), }, - // proofs: [Ucan.encode(identifierUcan)], proofs: [], facts: [{ code }], }) @@ -233,14 +234,6 @@ async function registerVerifiedAccount( } } -//////////////////////////// -// IDENTIFIER & AUTHORITY // -//////////////////////////// - -export async function provideAuthority(accountQuery: AccountQuery): Promise { - return [] // TODO -} - //////// // 🛳 // //////// @@ -252,9 +245,9 @@ export function implementation( const endpoints = optionalEndpoints || Fission.PRODUCTION return { - annex: (identifier, ucanDictionary) => ({ + annex: (identifier, inventory, names) => ({ requestVerificationCode: (...args) => requestVerificationCode(endpoints, dependencies, ...args), - volume: (...args) => Common.volume(endpoints, dependencies, identifier, ucanDictionary, ...args), + volume: (...args) => Common.volume(endpoints, dependencies, identifier, inventory, names, ...args), }), canRegister: (...args) => canRegister(endpoints, dependencies, ...args), @@ -262,6 +255,6 @@ export function implementation( did: (...args) => Common.did(dependencies, ...args), hasSufficientAuthority: (...args) => Common.hasSufficientAuthority(dependencies, ...args), - provideAuthority, + provideAuthority: Common.provideAuthority, } } diff --git a/src/components/account/implementation.ts b/src/components/account/implementation.ts index 9cd1681e..96710015 100644 --- a/src/components/account/implementation.ts +++ b/src/components/account/implementation.ts @@ -1,5 +1,6 @@ import { AccountQuery } from "../../authority/query.js" -import { Inventory } from "../../ticket/inventory.js" +import { Inventory } from "../../inventory.js" +import { Names } from "../../repositories/names.js" import { Ticket } from "../../ticket/types.js" import * as Identifier from "../identifier/implementation.js" @@ -13,7 +14,7 @@ export type Implementation = { /** * Additional methods you want to be part of `program.account` */ - annex: (identifier: Identifier.Implementation, tickets: Inventory) => Annex + annex: (identifier: Identifier.Implementation, inventory: Inventory, names: Names) => Annex // CREATION @@ -27,7 +28,11 @@ export type Implementation = { /** * How to register an account with this account system. */ - register: (formValues: Record, identifierTicket: Ticket) => Promise< + register: ( + identifier: Identifier.Implementation, + names: Names, + formValues: Record + ) => Promise< { registered: true; tickets: Ticket[] } | { registered: false; reason: string } > @@ -36,12 +41,18 @@ export type Implementation = { /** * The DID associated with this account. */ - did(identifier: Identifier.Implementation, tickets: Inventory): Promise + did( + identifier: Identifier.Implementation, + inventory: Inventory + ): Promise /** * Check if we have everything we need (eg. capabilities) regarding the account. */ - hasSufficientAuthority(identifier: Identifier.Implementation, tickets: Inventory): Promise< + hasSufficientAuthority( + identifier: Identifier.Implementation, + inventory: Inventory + ): Promise< { suffices: true } | { suffices: false; reason: string } > @@ -49,5 +60,9 @@ export type Implementation = { * Provides tickets to those who request authority. * Authority can be granted based on the received queries. */ - provideAuthority(query: AccountQuery): Promise + provideAuthority( + query: AccountQuery, + identifier: Identifier.Implementation, + inventory: Inventory + ): Promise } diff --git a/src/components/account/local.ts b/src/components/account/local.ts index 7a71a0ad..53693346 100644 --- a/src/components/account/local.ts +++ b/src/components/account/local.ts @@ -1,7 +1,8 @@ import * as Identifier from "../identifier/implementation.js" import { AccountQuery } from "../../authority/query.js" -import { Inventory } from "../../ticket/inventory.js" +import { Inventory } from "../../inventory.js" +import { Names } from "../../repositories/names.js" import { Ticket } from "../../ticket/types.js" import { Implementation } from "./implementation.js" @@ -22,8 +23,9 @@ export async function canRegister(): Promise< } export async function register( - formValues: Record, - identifierTicket: Ticket + identifier: Identifier.Implementation, + names: Names, + formValues: Record ): Promise< { registered: true; tickets: Ticket[] } | { registered: false; reason: string } > { @@ -36,14 +38,14 @@ export async function register( export async function did( identifier: Identifier.Implementation, - tickets: Inventory + inventory: Inventory ): Promise { return identifier.did() } export async function hasSufficientAuthority( identifier: Identifier.Implementation, - tickets: Inventory + inventory: Inventory ): Promise< { suffices: true } | { suffices: false; reason: string } > { diff --git a/src/components/agent/implementation.ts b/src/components/agent/implementation.ts index 9d9b7de5..f1bd8280 100644 --- a/src/components/agent/implementation.ts +++ b/src/components/agent/implementation.ts @@ -1,4 +1,4 @@ -import type { Alg as SignatureAlgorithm, KeyType } from "iso-did/core" +import type { Alg as SignatureAlgorithm, KeyType } from "iso-did/key" export type Implementation = { /** @@ -15,7 +15,7 @@ export type Implementation = { /** * DID associated with this agent. */ - did: () => Promise + did: () => string /** * Decrypt something with the exchange key. @@ -33,12 +33,12 @@ export type Implementation = { sign: (data: Uint8Array) => Promise /** - * This goes hand in hand with the DID `keyTypes` record from the crypto component. + * The algorithm of the signing key. */ - keyAlgorithm: () => Promise + keyAlgorithm: () => KeyType /** * The JWT algorithm string for agent UCANs. */ - ucanAlgorithm: () => Promise + ucanAlgorithm: () => SignatureAlgorithm } diff --git a/src/components/agent/web-crypto-api.ts b/src/components/agent/web-crypto-api.ts index 2c15fd49..f97cfe99 100644 --- a/src/components/agent/web-crypto-api.ts +++ b/src/components/agent/web-crypto-api.ts @@ -62,13 +62,13 @@ export async function implementation( return { exchangeKey: () => Promise.resolve(exchangeKey), signingKey: () => Promise.resolve(signingKey), - did: async () => DIDKey.fromPublicKey("RSA", exportedKey).toString(), + did: () => DIDKey.fromPublicKey("RSA", exportedKey).toString(), decrypt: data => decrypt(data, exchangeKey.privateKey), encrypt: data => encrypt(data, exchangeKey.publicKey), sign: data => sign(data, signingKey), - keyAlgorithm: () => Promise.resolve("RSA"), - ucanAlgorithm: () => Promise.resolve("RS256"), + keyAlgorithm: () => "RSA", + ucanAlgorithm: () => "RS256", } } diff --git a/src/components/authority/browser-url.ts b/src/components/authority/browser-url.ts new file mode 100644 index 00000000..43fc2967 --- /dev/null +++ b/src/components/authority/browser-url.ts @@ -0,0 +1,20 @@ +import * as Provider from "./browser-url/provider.js" +import * as Requestor from "./browser-url/requestor.js" + +import { ProvideResponse, RequestResponse } from "./browser-url/common.js" +import { Implementation } from "./implementation.js" + +//////// +// 🛳️ // +//////// + +export { ProvideResponse, RequestResponse } from "./browser-url/common.js" +export { ProvideParams } from "./browser-url/provider.js" +export { RequestParams } from "./browser-url/requestor.js" + +export function implementation(): Implementation { + return { + provide: Provider.provide, + request: Requestor.request, + } +} diff --git a/src/components/authority/browser-url/common.ts b/src/components/authority/browser-url/common.ts new file mode 100644 index 00000000..df1a601a --- /dev/null +++ b/src/components/authority/browser-url/common.ts @@ -0,0 +1,180 @@ +import { xchacha20poly1305 } from "@noble/ciphers/chacha" +import { x25519 } from "@noble/curves/ed25519" +import { hkdf } from "@noble/hashes/hkdf" +import { sha256 } from "@noble/hashes/sha256" +import { varint } from "iso-base/varint" +import { base58btc } from "multiformats/bases/base58" +import * as Uint8Arrays from "uint8arrays" + +import { isBlob, isObject, isString } from "../../../common/type-checks.js" + +//////// +// 🏔️️ // +//////// + +export const CIPHER_TEXT_ENCODING = "base64url" +export const DOMAIN_SEPARATION_TAG = Uint8Arrays.fromString( + "oddjs-qr-code", + "utf8" +) + +export const INITIAL_NONCE = new Uint8Array(0) + +//////// +// 🧩️ // +//////// + +export type Cipher = ReturnType["cipher"] + +export type Msg = { + did: string + payload: string + step: string +} & Record + +export type ProvideResponse = { + url: string +} + +export type RequestResponse = {} + +export type Step = "handshake" | "query" | "fin" +export type StepResult = { nextNonce: Uint8Array; nextStep: Step } + +//////// +// 🛠️ // +//////// + +export async function decodeChannelData( + data: unknown +): Promise { + let json + + data = isBlob(data) ? await data.text() : data + + if (!isString(data)) { + console.warn(`Received a message, but got a non-string message.`) + return null + } + + try { + json = JSON.parse(data) + } catch (err) { + console.warn(`Received a message, but couldn't decode it as JSON:`, err) + return null + } + + if (!isObject(json)) { + console.warn(`Received a message, but got some JSON that is not an object.`, json) + return null + } + + if (!isString(json.did)) { + console.warn(`Received a message, but got some JSON that does not have the \`did\` property.`) + return null + } + + if (!isString(json.step)) { + console.warn(`Received a message, but got some JSON that does not have the \`step\` property.`) + return null + } + + if (!isString(json.payload)) { + console.warn(`Received a message, but got some JSON that does not have the \`payload\` property.`) + return null + } + + return { + ...json, + did: json.did, + payload: json.payload, + step: json.step, + } +} + +export function decryptPayload( + cipher: Cipher, + encryptedPayload: string +): Uint8Array { + return cipher.decrypt( + Uint8Arrays.fromString( + encryptedPayload, + CIPHER_TEXT_ENCODING + ) + ) +} + +export function decryptJSONPayload( + cipher: Cipher, + encryptedPayload: string +) { + return JSON.parse( + Uint8Arrays.toString( + decryptPayload(cipher, encryptedPayload), + "utf8" + ) + ) +} + +export function encryptPayload( + cipher: Cipher, + payload: Uint8Array +): string { + return Uint8Arrays.toString( + cipher.encrypt( + payload + ), + CIPHER_TEXT_ENCODING + ) +} + +export function encryptJSONPayload( + cipher: Cipher, + payload: unknown +) { + return encryptPayload( + cipher, + Uint8Arrays.fromString( + JSON.stringify(payload), + "utf8" + ) + ) +} + +export function makeCipher( + { nonce, ourPrivateKey, producerPublicKey, remotePublicKey }: { + nonce: Uint8Array + ourPrivateKey: Uint8Array + producerPublicKey: Uint8Array + remotePublicKey: Uint8Array + } +) { + const sharedSecret = x25519.getSharedSecret(ourPrivateKey, remotePublicKey) + const hashedNonce = sha256(nonce) + + const okm = hkdf( + sha256, + sharedSecret, + producerPublicKey, + Uint8Arrays.concat([ + DOMAIN_SEPARATION_TAG, + Uint8Arrays.fromString(":", "utf8"), + hashedNonce, + ]), + 32 + 24 + hashedNonce.length // length = ChaCha key + IV + next-nonce + ) + + const xChaChaKey = okm.slice(0, 32) + const iv = okm.slice(32, 32 + 24) + + return { + cipher: xchacha20poly1305(xChaChaKey, iv), + nextNonce: okm.slice(32 + 24), + } +} + +export function publicKeyFromDID(did: string) { + const encodedPublicKey = base58btc.decode(did.replace(/^did\:key\:/, "")) + const [_code, size] = varint.decode(encodedPublicKey) + return encodedPublicKey.slice(size) +} diff --git a/src/components/authority/browser-url/provider.ts b/src/components/authority/browser-url/provider.ts new file mode 100644 index 00000000..de987667 --- /dev/null +++ b/src/components/authority/browser-url/provider.ts @@ -0,0 +1,369 @@ +import * as Uint8Arrays from "uint8arrays" + +import { x25519 } from "@noble/curves/ed25519" +import { randomBytes } from "iso-base/crypto" +import { tag } from "iso-base/varint" +import { base58btc } from "multiformats/bases/base58" + +import * as Query from "../../../authority/query.js" +import * as Events from "../../../events/authority.js" +import * as Path from "../../../path/index.js" +import * as Account from "../../account/implementation.js" +import * as Channel from "../../channel/implementation.js" +import * as Clerk from "../../clerk/implementation.js" +import * as Identifier from "../../identifier/implementation.js" + +import { Channel as ChannelType } from "../../../channel.js" +import { isObject, isString } from "../../../common/type-checks.js" +import { EventEmitter } from "../../../events/emitter.js" +import { Inventory } from "../../../inventory.js" +import { Names } from "../../../repositories/names.js" + +import { + CIPHER_TEXT_ENCODING, + Cipher, + Msg, + ProvideResponse, + StepResult, + decodeChannelData, + decryptJSONPayload, + decryptPayload, + encryptJSONPayload, + makeCipher, +} from "./common.js" +import { Session } from "./session.js" + +export type ProvideParams = { + dependencies: { + account: Account.Implementation + channel: Channel.Implementation + clerk: Clerk.Implementation + identifier: Identifier.Implementation + } + eventEmitter: EventEmitter + inventory: Inventory + queries: Query.Query[] + names: Names +} + +export async function provide( + params: ProvideParams +): Promise { + const challenge = randomBytes(16) + + const privateKey = x25519.utils.randomPrivateKey() + const publicKey = x25519.getPublicKey(privateKey) + + const id = base58btc.encode(tag(0xec, publicKey)) + const did = `did:key:${id}` + + const sessionsCache = {} + + params.dependencies.channel.establish({ + topic: did, + onmessage: (event: MessageEvent, channel: ChannelType) => + messageHandler({ + ...params, + challenge, + channel, + event, + ourDID: did, + ourPrivateKey: privateKey, + ourPublicKey: publicKey, + sessionsCache, + }), + }) + + // URL + const url = new URL(location.href) + url.searchParams.set("authority[challenge]", Uint8Arrays.toString(challenge, CIPHER_TEXT_ENCODING)) + url.searchParams.set("authority[publicKey]", Uint8Arrays.toString(publicKey, CIPHER_TEXT_ENCODING)) + + return { + url: url.toString(), + } +} + +////////////// +// MESSAGES // +////////////// + +type MessageHandlerParams = ProvideParams & { + challenge: Uint8Array + channel: ChannelType + event: MessageEvent + ourDID: string + ourPrivateKey: Uint8Array + ourPublicKey: Uint8Array + sessionsCache: Record> +} + +async function messageHandler( + params: MessageHandlerParams +) { + const msg = await decodeChannelData(params.event.data) + if (!msg) return + + const sessionsCache = params.sessionsCache + const sessionFromCache = sessionsCache[msg.did] + const session = sessionFromCache ? sessionFromCache : new ProviderSession({ + ...params, + remoteDID: msg.did, + }) + + sessionsCache[msg.did] = session + await session.proceed(msg) + + if (session.ended()) { + delete sessionsCache[msg.did] + } +} + +///////////// +// SESSION // +///////////// + +class ProviderSession extends Session { + challenge: Uint8Array + dependencies: MessageHandlerParams["dependencies"] + inventory: Inventory + names: Names + + constructor( + params: MessageHandlerParams & { + remoteDID: string + } + ) { + super(params) + + this.challenge = params.challenge + this.dependencies = params.dependencies + this.inventory = params.inventory + this.names = params.names + } + + //////////////////////// + // STEP 1 - HANDSHAKE // + //////////////////////// + + async handshake(msg: Msg): Promise { + let decryption = makeCipher({ + nonce: this.nonce, + producerPublicKey: this.ourPublicKey, + ourPrivateKey: this.ourPrivateKey, + remotePublicKey: this.remotePublicKey, + }) + + const payload = decryptPayload(decryption.cipher, msg.payload) + + const hasCorrectChallenge = Uint8Arrays.equals( + payload, + this.challenge + ) + + if (!hasCorrectChallenge) { + return this.earlyExit(`Challenge failed during handshake with ${msg.did}`) + } + + let { cipher, nextNonce } = makeCipher({ + nonce: decryption.nextNonce, + producerPublicKey: this.ourPublicKey, + ourPrivateKey: this.ourPrivateKey, + remotePublicKey: this.remotePublicKey, + }) + + this.sendMessage( + "handshake", + encryptJSONPayload(cipher, { approved: true }) + ) + + return { nextNonce, nextStep: "query" } + } + + //////////////////// + // STEP 2 - QUERY // + //////////////////// + + async query(msg: Msg): Promise { + let decryption = makeCipher({ + nonce: this.nonce, + producerPublicKey: this.ourPublicKey, + ourPrivateKey: this.ourPrivateKey, + remotePublicKey: this.remotePublicKey, + }) + + const payload = decryptJSONPayload(decryption.cipher, msg.payload) + + if ( + !isObject(payload) + || !Array.isArray(payload.queries) + || !payload.queries.every((p: unknown) => isString(p) || isObject(p)) + || !isString(payload.identifier) + ) { + return this.earlyExit(`Ignoring queries from ${msg.did}, improperly encoded queries.`) + } + + const remoteIdentifierDID = payload.identifier + + const queries = payload.queries.map(Query.fromJSON) + const allContained = queries.every(child => { + return queries.some(parent => Query.isContained({ parent, child })) + }) + + if (!allContained) { + return this.earlyExit(`Ignoring queries from ${msg.did}, asking for more than was provided.`) + } + + return new Promise((resolve, reject) => { + let { cipher, nextNonce } = makeCipher({ + nonce: decryption.nextNonce, + producerPublicKey: this.ourPublicKey, + ourPrivateKey: this.ourPrivateKey, + remotePublicKey: this.remotePublicKey, + }) + + this.eventEmitter.emit("provide:query", { + approve: (queries) => { + this + .approveQueries(queries, remoteIdentifierDID, cipher) + .then(() => resolve(this.end), reject) + }, + dismiss: () => { + this + .dismissQueries(cipher) + .then(() => resolve(this.end), reject) + }, + queries, + }) + }) + } + + async approveQueries( + queries: Query.Query[], + remoteIdentifierDID: string, + cipher: Cipher + ): Promise { + if (queries.length === 0) throw new Error("Cannot approve an empty list of queries.") + + const resolvedNames: Record = {} + + const fsQueries = queries.reduce( + (acc, q) => q.query === "fileSystem" ? [...acc, q] : acc, + [] as Query.FileSystemQuery[] + ) + + const accountTicketPromises = queries + .reduce( + (acc, q) => q.query === "account" ? [...acc, q] : acc, + [] as Query.AccountQuery[] + ) + .map(async q => { + const tickets = await this.dependencies.account.provideAuthority( + q, + this.dependencies.identifier, + this.inventory + ) + + const promises = tickets.map(async t => { + const ticket = await this.dependencies.clerk.tickets.delegate( + t, + this.dependencies.identifier, + remoteIdentifierDID + ) + + return this.inventory.bundleTickets( + ticket, + this.dependencies.clerk.tickets.proofResolver + ) + }) + + const nested = await Promise.all(promises) + const collectedTickets = nested.flat() + + return { + query: Query.toJSON(q), + tickets: collectedTickets, + } + }) + + const accountTicketsBundles = await Promise.all(accountTicketPromises) + const accountTickets = accountTicketsBundles + + const fileSystemTicketHolders = fsQueries + .map(async query => { + const did = this.names.resolveId(query.id) + if (!did) return [] + + if ("name" in query.id) { + resolvedNames[query.id.name] = did + } + + const ticket = await this.inventory.lookupFileSystemTicket(query.path, did) + if (!ticket) return [] + + return [{ ticket, query: Query.toJSON(query) }] + }) + + const fileSystemTicketPromises = (await Promise.all(fileSystemTicketHolders)) + .flat() + .map(async item => { + const ticket = await this.dependencies.clerk.tickets.delegate( + item.ticket, + this.dependencies.identifier, + remoteIdentifierDID + ) + + const tickets = await this.inventory.bundleTickets( + ticket, + this.dependencies.clerk.tickets.proofResolver + ) + + return { query: item.query, tickets } + }) + + const fileSystemTicketBundles = await Promise.all(fileSystemTicketPromises) + const fileSystemTickets = fileSystemTicketBundles + + // TODO: Clear out duplicate tickets + + const accessKeys = fsQueries + .filter(q => Path.isPartition("private", q.path)) + .map(q => { + const did = this.names.resolveId(q.id) + if (!did) return [] + const key = this.inventory.findAccessKey(q.path, did) + if (!key) return [] + return [{ query: Query.toJSON(q), key }] + }) + .flat() + + this.sendMessage( + "query", + encryptJSONPayload(cipher, { + accountTickets, + fileSystemTickets, + accessKeys: accessKeys.map(a => { + return { + did: a.key.did, + key: Uint8Arrays.toString(a.key.key, CIPHER_TEXT_ENCODING), + query: a.query, + path: Path.toPosix(a.key.path), + } + }), + resolvedNames, + }) + ) + + this.eventEmitter.emit("provide:authorised", { queries }) + this.eventEmitter.emit("provide:authorized", { queries }) + } + + async dismissQueries(cipher: Cipher): Promise { + this.sendMessage( + "query", + encryptJSONPayload(cipher, { dismissed: true }) + ) + + this.eventEmitter.emit("provide:dismissed") + } +} diff --git a/src/components/authority/browser-url/requestor.ts b/src/components/authority/browser-url/requestor.ts new file mode 100644 index 00000000..1ea152c7 --- /dev/null +++ b/src/components/authority/browser-url/requestor.ts @@ -0,0 +1,298 @@ +import * as Uint8Arrays from "uint8arrays" + +import { x25519 } from "@noble/curves/ed25519" +import { tag } from "iso-base/varint" +import { base58btc } from "multiformats/bases/base58" + +import * as Query from "../../../authority/query.js" +import * as Events from "../../../events/authority.js" +import * as Path from "../../../path/index.js" +import * as Channel from "../../channel/implementation.js" +import * as Identifier from "../../identifier/implementation.js" + +import { Channel as ChannelType } from "../../../channel.js" +import { isObject, isString } from "../../../common/type-checks.js" +import { EventEmitter } from "../../../events/emitter.js" +import { Ticket } from "../../../index.js" +import { isTicket } from "../../../ticket/index.js" +import { AuthorityArtefacts, RequestOptions } from "../implementation.js" +import { + CIPHER_TEXT_ENCODING, + INITIAL_NONCE, + Msg, + RequestResponse, + StepResult, + decodeChannelData, + decryptJSONPayload, + encryptJSONPayload, + encryptPayload, + makeCipher, +} from "./common.js" +import { Session } from "./session.js" + +export type RequestParams = { + dependencies: { + channel: Channel.Implementation + identifier: Identifier.Implementation + } + eventEmitter: EventEmitter + options: RequestOptions + queries: Query.Query[] +} + +export async function request( + params: RequestParams +): Promise | null> { + const url = new URL(location.href) + const challenge = url.searchParams.get("authority[challenge]") + const otherPubKeyString = url.searchParams.get("authority[publicKey]") + + // Nothing to do + if (!challenge || !otherPubKeyString) return null + + // Crypto + const privateKey = x25519.utils.randomPrivateKey() + const publicKey = x25519.getPublicKey(privateKey) + + const ourDID = `did:key:${base58btc.encode(tag(0xec, publicKey))}` + const otherPubKey = Uint8Arrays.fromString(otherPubKeyString, CIPHER_TEXT_ENCODING) + + const handshakeCipher = makeCipher({ + nonce: INITIAL_NONCE, + producerPublicKey: otherPubKey, + ourPrivateKey: privateKey, + remotePublicKey: otherPubKey, + }) + + // Timeout after 60 seconds + return new Promise((resolve, reject) => { + let timeoutId: number = 0 + let channel: ChannelType + + function setTimer() { + clearTimeout(timeoutId) + timeoutId = setTimeout(() => { + channel.close() + reject( + "Authority exchange timed out. The URL parameters could be out of date, or the provider has started a new session. Each time `.provide()` is called a new session is started, make sure you're using the correct URL or QR code." + ) + }, 60000) as unknown as number + return timeoutId + } + + // Setup channel + const topicID = base58btc.encode(tag(0xec, otherPubKey)) + const topicDID = `did:key:${topicID}` + + const sessionsCache = {} + + params.dependencies.channel.establish({ + topic: topicDID, + onmessage: (event: MessageEvent, channel: ChannelType) => + messageHandler({ + ...params, + channel, + event, + nonce: handshakeCipher.nextNonce, + ourDID, + ourPrivateKey: privateKey, + ourPublicKey: publicKey, + resolve, + sessionsCache, + resetTimeout: setTimer, + }), + }).then(c => { + channel = c + + // Send handshake message + channel.send( + JSON.stringify({ + step: "handshake", + did: ourDID, + payload: encryptPayload( + handshakeCipher.cipher, + Uint8Arrays.fromString(challenge, CIPHER_TEXT_ENCODING) + ), + }) + ) + + setTimer() + }) + }) +} + +////////////// +// MESSAGES // +////////////// + +type MessageHandlerParams = RequestParams & { + channel: ChannelType + event: MessageEvent + nonce: Uint8Array + ourDID: string + ourPrivateKey: Uint8Array + ourPublicKey: Uint8Array + resolve: (value: AuthorityArtefacts | null) => void + sessionsCache: Record + resetTimeout: () => number +} + +async function messageHandler(params: MessageHandlerParams) { + const msg = await decodeChannelData(params.event.data) + if (!msg) return + + const timeoutId = params.resetTimeout() + + const sessionsCache = params.sessionsCache + const sessionFromCache = sessionsCache[msg.did] + const session = sessionFromCache ? sessionFromCache : new RequestorSession({ + ...params, + remoteDID: msg.did, + }) + + sessionsCache[msg.did] = session + await session.proceed(msg) + + if (session.ended()) { + delete sessionsCache[msg.did] + params.channel.close() + clearTimeout(timeoutId) + params.resolve(null) + } +} + +///////////// +// SESSION // +///////////// + +class RequestorSession extends Session { + dependencies: MessageHandlerParams["dependencies"] + resolve: MessageHandlerParams["resolve"] + + constructor( + params: MessageHandlerParams & { + remoteDID: string + } + ) { + super(params) + + this.dependencies = params.dependencies + this.resolve = params.resolve + } + + //////////////////////// + // STEP 1 - HANDSHAKE // + //////////////////////// + + async handshake(msg: Msg): Promise { + let decryption = makeCipher({ + nonce: this.nonce, + producerPublicKey: this.remotePublicKey, + ourPrivateKey: this.ourPrivateKey, + remotePublicKey: this.remotePublicKey, + }) + + const payload = decryptJSONPayload(decryption.cipher, msg.payload) + + if (payload.approved !== true) { + return this.earlyExit(`Cancelling authority request, producer did not accept.`) + } + + let { cipher, nextNonce } = makeCipher({ + nonce: decryption.nextNonce, + producerPublicKey: this.remotePublicKey, + ourPrivateKey: this.ourPrivateKey, + remotePublicKey: this.remotePublicKey, + }) + + this.sendMessage( + "query", + encryptJSONPayload(cipher, { + identifier: this.dependencies.identifier.did(), + queries: this.queries.map(Query.toJSON), + }) + ) + + return { nextNonce, nextStep: "query" } + } + + //////////////////// + // STEP 2 - QUERY // + //////////////////// + + async query(msg: Msg): Promise { + let decryption = makeCipher({ + nonce: this.nonce, + producerPublicKey: this.remotePublicKey, + ourPrivateKey: this.ourPrivateKey, + remotePublicKey: this.remotePublicKey, + }) + + const payload = decryptJSONPayload(decryption.cipher, msg.payload) + + if (payload.dismissed === true) { + this.eventEmitter.emit("request:dismissed") + return this.end + } + + if ( + !isObject(payload) + || !Array.isArray(payload.accessKeys) + || !Array.isArray(payload.accountTickets) + || !Array.isArray(payload.fileSystemTickets) + || !payload.accessKeys.every((p: unknown) => + isObject(p) && isString(p.did) && isObject(p.query) && isString(p.key) && isString(p.path) + ) + || !payload.accountTickets.every((p: unknown) => isObject(p) && isObject(p.query) && Array.isArray(p.tickets)) + || !payload.fileSystemTickets.every((p: unknown) => isObject(p) && isObject(p.query) && Array.isArray(p.tickets)) + || !isObject(payload.resolvedNames) + || !Object.values(payload.resolvedNames).every((s: unknown) => isString(s)) + ) { + return this.earlyExit(`Ignoring queries from ${msg.did}, improperly encoded query approvals.`, payload) + } + + let authorisedQueries: Query.Query[] = [] + + const accountTickets = payload.accountTickets.map(i => { + const query = Query.fromJSON(i.query) + authorisedQueries.push(query) + const tickets: Ticket[] = i.tickets.reduce( + (acc: Ticket[], t: unknown) => isTicket(t) ? [...acc, t] : acc, + [] + ) + + return { query, tickets } + }) + + const fileSystemTickets = payload.fileSystemTickets.map(i => { + const query = Query.fromJSON(i.query) + authorisedQueries.push(query) + const tickets: Ticket[] = i.tickets.reduce( + (acc: Ticket[], t: unknown) => isTicket(t) ? [...acc, t] : acc, + [] + ) + + return { query, tickets } + }) + + const accessKeys = payload.accessKeys.map(a => { + return { + did: a.did, + key: Uint8Arrays.fromString(a.key, CIPHER_TEXT_ENCODING), + query: Query.fromJSON(a.query), + path: Path.fromPosix(a.path), + } + }) + + this.resolve({ + accessKeys, + accountTickets, + fileSystemTickets, + authorisedQueries, + resolvedNames: payload.resolvedNames as Record, + requestResponse: {}, + }) + + return this.end + } +} diff --git a/src/components/authority/browser-url/session.ts b/src/components/authority/browser-url/session.ts new file mode 100644 index 00000000..00ec56ef --- /dev/null +++ b/src/components/authority/browser-url/session.ts @@ -0,0 +1,103 @@ +import * as Events from "../../../events/authority.js" + +import { Query } from "../../../authority/query.js" +import { Channel as ChannelType } from "../../../channel.js" +import { EventEmitter } from "../../../events/emitter.js" +import { INITIAL_NONCE, Msg, Step, StepResult, publicKeyFromDID } from "./common.js" + +export abstract class Session { + channel: ChannelType + eventEmitter: EventEmitter + ourDID: string + ourPrivateKey: Uint8Array + ourPublicKey: Uint8Array + queries: Query[] + remoteDID: string + remotePublicKey: Uint8Array + + nonce: Uint8Array + step: Step + + constructor(params: { + channel: ChannelType + eventEmitter: EventEmitter + nonce?: Uint8Array + ourDID: string + ourPrivateKey: Uint8Array + ourPublicKey: Uint8Array + queries: Query[] + remoteDID: string + }) { + this.channel = params.channel + this.eventEmitter = params.eventEmitter + this.ourDID = params.ourDID + this.ourPrivateKey = params.ourPrivateKey + this.ourPublicKey = params.ourPublicKey + this.queries = params.queries + this.remoteDID = params.remoteDID + this.remotePublicKey = publicKeyFromDID(params.remoteDID) + + this.nonce = params.nonce || INITIAL_NONCE + this.step = "handshake" + } + + end: StepResult = { + nextNonce: INITIAL_NONCE, + nextStep: "fin", + } + + ended() { + return this.step === "fin" + } + + // Steps + + async proceed(msg: Msg): Promise { + const result = await this.#proceed(msg) + this.nonce = result.nextNonce + this.step = result.nextStep + } + + async #proceed(msg: Msg): Promise { + if (msg.did === this.ourDID) { + return this.earlyExit() + } + + if (this.step !== msg.step) { + return this.earlyExit( + `Ignoring client ${msg.did}, steps don't match. Received '${msg.step}', but the active step is '${this.step}'.` + ) + } + + if (this.remoteDID && msg.did !== this.remoteDID) { + return this.earlyExit( + `Ignoring client ${msg.did}, does not match DID in current state.` + ) + } + + switch (this.step) { + case "handshake": + return this.handshake(msg) + case "query": + return this.query(msg) + default: + throw new Error(`Invalid step: ${this.step}`) + } + } + + abstract handshake(msg: Msg): Promise + abstract query(msg: Msg): Promise + + // 🛠️ + + sendMessage(step: Step, payload: string): void { + this.channel.send(JSON.stringify({ step, did: this.ourDID, payload })) + } + + // ⚠️ + + earlyExit(...args: any): StepResult { + if (args.length) console.warn(...args) + return this.end + } +} diff --git a/src/components/authority/implementation.ts b/src/components/authority/implementation.ts index 3085ee62..04e5c6b8 100644 --- a/src/components/authority/implementation.ts +++ b/src/components/authority/implementation.ts @@ -1,71 +1,34 @@ -import * as Events from "../../events/authority.js" -import * as Path from "../../path/index.js" - -import * as Agent from "../agent/implementation.js" -import * as Identifier from "../identifier/implementation.js" +import * as Account from "../account/implementation.js" +import { AccessKeyWithContext } from "../../accessKey.js" import { Query } from "../../authority/query.js" -import { EventEmitter } from "../../events/emitter.js" import { Ticket } from "../../ticket/types.js" +import { ProvideParams } from "./browser-url/provider.js" +import { RequestParams } from "./browser-url/requestor.js" + +/////////////// +// REQUESTOR // +/////////////// + +export type AuthorityArtefacts = { + accessKeys: AccessKeyWithContext[] + accountTickets: { query: Query; tickets: Ticket[] }[] + authorisedQueries: Query[] + fileSystemTickets: { query: Query; tickets: Ticket[] }[] + resolvedNames: Record + requestResponse: RequestResponse +} export type RequestOptions = { extraParams?: Record - query?: Query returnUrl?: string } -export type Provider = { - type: "provider" - - provide(tickets: Ticket[], eventEmitter: EventEmitter): Promise -} - -export type Requestor = { - type: "requestor" - - request: (options: RequestOptions, eventEmitter: EventEmitter) => Promise -} - -/** - * Responsible for managing tickets or parts thereof. - */ -export type Clerk = { - tickets: { - fileSystem: { - /** - * Given a root path, create file system ticket. - */ - create: (path: Path.DistinctivePath, audience: string) => Promise - /** - * Given a root path, find a matching file system ticket. - */ - matcher: (path: Path.DistinctivePath, did: string) => (ticket: Ticket) => boolean - } - misc: { - /** - * Identifier → Agent - * - * Before registration a delegation is always performed - * from the identifier to the agent. - * - * The idea here is that any other delegation should always be - * done with the identifier as the audience so that identifier - * is always in control. - * - * In this function we should give the agent access to - * all capabilities the identifier can use. - */ - identifierToAgentDelegation: ( - identifier: Identifier.Implementation, - agent: Agent.Implementation - ) => Promise - } - } -} +//////////////////// +// IMPLEMENTATION // +//////////////////// -/** - * Implementation. - */ -export type Implementation = { - clerk: Clerk +export type Implementation = { + provide(params: ProvideParams): Promise + request(params: RequestParams): Promise | null> } diff --git a/src/components/authority/ts-ucan.ts b/src/components/authority/ts-ucan.ts deleted file mode 100644 index c23c6e44..00000000 --- a/src/components/authority/ts-ucan.ts +++ /dev/null @@ -1,100 +0,0 @@ -import * as AgentDID from "../../agent/did.js" -import * as Path from "../../path/index.js" -import * as Ucan from "../../ucan/ts-ucan/index.js" -import * as Agent from "../agent/implementation.js" -import * as Identifier from "../identifier/implementation.js" - -import { Ticket } from "../../ticket/types.js" -import { Implementation } from "./implementation.js" - -/////////// -// CLERK // -/////////// - -export async function createFileSystemTicket( - identifier: Identifier.Implementation, - path: Path.DistinctivePath, - audience: string -): Promise { - const identifierDID = await identifier.did() - const ucan = await Ucan.build({ - // from & to - issuer: { - did: () => identifierDID, - jwtAlg: await identifier.ucanAlgorithm(), - sign: identifier.sign, - }, - audience, - - // capabilities - capabilities: [ - { - with: { scheme: "wnfs", hierPart: `//${identifierDID}${Path.toPosix(path, { absolute: true })}` }, - can: { namespace: "fs", segments: ["*"] }, - }, - ], - }) - - return Ucan.toTicket(ucan) -} - -export async function identifierToAgentDelegation( - identifier: Identifier.Implementation, - agent: Agent.Implementation -): Promise { - const identifierDID = await identifier.did() - const ucan = await Ucan.build({ - issuer: { - did: () => identifierDID, - jwtAlg: await identifier.ucanAlgorithm(), - sign: identifier.sign, - }, - audience: await AgentDID.signing(agent), - capabilities: [ - // Powerbox concept: - // Every capability given to the identifier may be used by the agent. - { - with: { scheme: "ucan", hierPart: `${identifierDID}/*` }, - can: { namespace: "ucan", segments: ["*"] }, - }, - ], - }) - - return Ucan.toTicket(ucan) -} - -export function matchFileSystemTicket( - path: Path.DistinctivePath, - did: string -): (ticket: Ticket) => boolean { - return (ticket: Ticket): boolean => { - const hierPart = `//${did}/${Path.toPosix(path)}` - const ucan = Ucan.decode(ticket.token) - - return !!ucan.payload.att.find(cap => { - return cap.with.hierPart === hierPart && (cap.can === "*" || cap.can.namespace === "fs") - }) - } -} - -//////// -// 🛳️ // -//////// - -export function implementation( - identifier: Identifier.Implementation -): Implementation { - return { - clerk: { - tickets: { - fileSystem: { - create: (...args) => createFileSystemTicket(identifier, ...args), - matcher: matchFileSystemTicket, - }, - misc: { - identifierToAgentDelegation, - }, - }, - }, - } -} diff --git a/src/components/channel/fission.ts b/src/components/channel/fission.ts index fe157666..d668ab2c 100644 --- a/src/components/channel/fission.ts +++ b/src/components/channel/fission.ts @@ -5,12 +5,6 @@ import * as Fission from "../../common/fission.js" import { Manners } from "../../components.js" import { Implementation } from "./implementation.js" -//////// -// 🧩 // -//////// - -export type Context = { did: string } - //////// // 🛠️ // //////// @@ -18,7 +12,7 @@ export type Context = { did: string } export function establish( manners: Manners.Implementation, endpoints: Endpoints, - options: ChannelOptions + options: ChannelOptions ): Promise { const host = `${endpoints.server}${endpoints.apiPath}` .replace(/^https:\/\//, "wss://") @@ -26,7 +20,7 @@ export function establish( return createWebsocketChannel( manners, - `${host}/account/link/${options.context.did}`, + `${host}/relay/${options.topic}`, options ) } @@ -38,7 +32,7 @@ export function establish( export function implementation( manners: Manners.Implementation, optionalEndpoints?: Endpoints -): Implementation { +): Implementation { const endpoints = optionalEndpoints || Fission.PRODUCTION return { diff --git a/src/components/channel/implementation.ts b/src/components/channel/implementation.ts index 763daa61..e109a471 100644 --- a/src/components/channel/implementation.ts +++ b/src/components/channel/implementation.ts @@ -1,8 +1,8 @@ import { Channel, ChannelOptions } from "../../channel.js" -export type Implementation = { +export type Implementation = { /** * Creates a `Channel` which can be used to transfer data over. */ - establish: (options: ChannelOptions) => Promise + establish: (options: ChannelOptions) => Promise } diff --git a/src/components/channel/local-first-relay.ts b/src/components/channel/local-first-relay.ts new file mode 100644 index 00000000..c1de8d6f --- /dev/null +++ b/src/components/channel/local-first-relay.ts @@ -0,0 +1,66 @@ +import { Client } from "@localfirst/relay-client" +import * as Uint8Arrays from "uint8arrays" + +import { sha256 } from "multiformats/hashes/sha2" +import { Channel, ChannelData, ChannelOptions } from "../../channel.js" +import { Manners } from "../../components.js" +import { Implementation } from "./implementation.js" + +//////// +// 🛠️ // +//////// + +export async function establish( + manners: Manners.Implementation, + did: string, + url: string, + options: ChannelOptions +): Promise { + const topicDigest = await sha256.digest(new TextEncoder().encode(options.topic)) + const topic = Uint8Arrays.toString(topicDigest.bytes, "base64url") + + const didDigest = await sha256.digest(new TextEncoder().encode(did)) + const didHash = Uint8Arrays.toString(didDigest.bytes, "base64url") + + return new Promise((resolve, reject) => { + let s: WebSocket + let client: Client + + const channel = { + close: () => client.disconnectServer(), + send: (data: ChannelData) => s.send(data), + } + + client = new Client({ userName: didHash, url }) + .join(topic) + .on("peer.connect", ({ socket }) => { + s = socket as WebSocket + + // listen for messages + socket.addEventListener("message", (message: MessageEvent) => { + options.onmessage(message, channel) + }) + + // channel established + manners.log("📣 Channel established", url) + resolve(channel) + }) + .on("error", () => { + reject() + }) + }) +} + +//////// +// 🛳️ // +//////// + +export function implementation( + manners: Manners.Implementation, + did: string, + url: string +): Implementation { + return { + establish: (...args) => establish(manners, did, url, ...args), + } +} diff --git a/src/components/channel/local.ts b/src/components/channel/local.ts index 3ec045a5..84f56637 100644 --- a/src/components/channel/local.ts +++ b/src/components/channel/local.ts @@ -1,18 +1,12 @@ import { Channel, ChannelOptions } from "../../channel.js" import { Implementation } from "./implementation.js" -//////// -// 🧩 // -//////// - -export type Context = {} - //////// // 🛠️ // //////// export function establish( - options: ChannelOptions + options: ChannelOptions ): Promise { throw new Error("No local channel available just yet.") // NOTE: Do WebRTC implementation? } @@ -21,7 +15,7 @@ export function establish( // 🛳️ // //////// -export function implementation(): Implementation { +export function implementation(): Implementation { return { establish, } diff --git a/src/components/clerk/default.ts b/src/components/clerk/default.ts new file mode 100644 index 00000000..b0b6bd87 --- /dev/null +++ b/src/components/clerk/default.ts @@ -0,0 +1,121 @@ +import { ed25519 } from "@noble/curves/ed25519" +import { tag } from "iso-base/varint" +import { base58btc } from "multiformats/bases/base58" + +import * as AgentDID from "../../agent/did.js" +import * as Path from "../../path/index.js" +import * as Ucan from "../../ucan/ts-ucan/index.js" +import * as Agent from "../agent/implementation.js" +import * as Identifier from "../identifier/implementation.js" + +import { Ticket } from "../../ticket/types.js" +import { Implementation } from "./implementation.js" + +/////////// +// CLERK // +/////////// + +export async function createOriginFileSystemTicket( + path: Path.DistinctivePath, + audience: string +): Promise { + const privateKey = ed25519.utils.randomPrivateKey() + const publicKey = ed25519.getPublicKey(privateKey) + + const did = `did:key:${base58btc.encode(tag(0xed, publicKey))}` + + const ucan = await Ucan.build({ + // from & to + issuer: { + did: () => did, + jwtAlg: "EdDSA", + sign: async (data: Uint8Array) => ed25519.sign(data, privateKey), + }, + audience, + + // capabilities + capabilities: [ + { + with: { scheme: "wnfs", hierPart: `//${did}${Path.toPosix(path, { absolute: true })}` }, + can: { namespace: "fs", segments: ["*"] }, + }, + ], + }) + + return Ucan.toTicket(ucan) +} + +export async function delegate( + ticket: Ticket, + identifier: Identifier.Implementation, + remoteIdentifierDID: string +): Promise { + const identifierDID = identifier.did() + const identifierKeyPair = { + did: () => identifierDID, + jwtAlg: identifier.ucanAlgorithm(), + sign: identifier.sign, + } + + const ucan = await Ucan.build({ + audience: remoteIdentifierDID, + issuer: identifierKeyPair, + proofs: [(await Ucan.ticketCID(ticket)).toString()], + }) + + return Ucan.toTicket(ucan) +} + +export async function identifierToAgentDelegation( + identifier: Identifier.Implementation, + agent: Agent.Implementation, + proofs: Ticket[] +): Promise { + const identifierDID = identifier.did() + const ucan = await Ucan.build({ + issuer: { + did: () => identifierDID, + jwtAlg: identifier.ucanAlgorithm(), + sign: identifier.sign, + }, + audience: await AgentDID.signing(agent), + proofs: await Promise.all(proofs.map(t => Ucan.ticketCID(t).toString())), + }) + + return Ucan.toTicket(ucan) +} + +export function matchFileSystemTicket( + path: Path.DistinctivePath, + did: string +): (ticket: Ticket) => Promise { + return async (ticket: Ticket): Promise => { + const hierPart = `//${did}/${Path.toPosix(path)}` + const ucan = Ucan.decode(ticket.token) + + return !!ucan.payload.att.find(cap => { + return cap.with.hierPart === hierPart && (cap.can === "*" || cap.can.namespace === "fs") + }) + } +} + +//////// +// 🛳️ // +//////// + +export function implementation(): Implementation { + return { + tickets: { + cid: Ucan.ticketCID, + delegate, + proofResolver: async (ticket) => Ucan.ticketProofResolver(ticket), + fileSystem: { + origin: createOriginFileSystemTicket, + matcher: matchFileSystemTicket, + }, + misc: { + identifierToAgentDelegation, + }, + }, + } +} diff --git a/src/components/clerk/implementation.ts b/src/components/clerk/implementation.ts new file mode 100644 index 00000000..beb127d5 --- /dev/null +++ b/src/components/clerk/implementation.ts @@ -0,0 +1,59 @@ +import * as Path from "../../path/index.js" +import * as Agent from "../agent/implementation.js" +import * as Identifier from "../identifier/implementation.js" + +import { CID } from "../../common/cid.js" +import { Ticket } from "../../ticket/types.js" + +/** + * Responsible for managing tickets or parts thereof. + */ +export type Implementation = { + tickets: { + /** + * Creates a CID for a Ticket. + */ + cid(ticket: Ticket): Promise + + /** + * Delegates a ticket from the current identifier to another identifier. + */ + delegate(ticket: Ticket, identifier: Identifier.Implementation, audienceIdentifier: string): Promise + + /** + * Lists the proof CIDs for a Ticket. + */ + proofResolver(ticket: Ticket): Promise + + fileSystem: { + /** + * Given a root path, create the origin file system ticket. + */ + origin: (path: Path.DistinctivePath, audience: string) => Promise + /** + * Given a root path, find a matching file system ticket. + */ + matcher: (path: Path.DistinctivePath, did: string) => (ticket: Ticket) => Promise + } + misc: { + /** + * Identifier → Agent + * + * Before registration a delegation is always performed + * from the identifier to the agent. + * + * The idea here is that any other delegation should always be + * done with the identifier as the audience so that identifier + * is always in control. + * + * In this function we should give the agent access to + * all capabilities the identifier can use. + */ + identifierToAgentDelegation: ( + identifier: Identifier.Implementation, + agent: Agent.Implementation, + proofs: Ticket[] + ) => Promise + } + } +} diff --git a/src/components/depot/implementation.ts b/src/components/depot/implementation.ts index ab1c529a..ae49ae39 100644 --- a/src/components/depot/implementation.ts +++ b/src/components/depot/implementation.ts @@ -2,6 +2,7 @@ import { Blockstore } from "interface-blockstore" import { CID } from "multiformats/cid" import { CodecIdentifier } from "../../dag/codecs.js" +import { Inventory } from "../../index.js" import * as Path from "../../path/index.js" import { Ticket } from "../../ticket/types.js" @@ -23,7 +24,7 @@ export type Implementation = { * Here you could set up an IPFS peer connection, * or simply push all "changed" blocks to some other block store. */ - flush: (dataRoot: CID, proofs: Ticket[]) => Promise + flush: (dataRoot: CID, proofs: Ticket[], inventory: Inventory) => Promise /** * Create a permalink to some public file system content. diff --git a/src/components/depot/ipfs-bitswap-websockets.ts b/src/components/depot/ipfs-bitswap-websockets.ts index a2915df2..da4a148c 100644 --- a/src/components/depot/ipfs-bitswap-websockets.ts +++ b/src/components/depot/ipfs-bitswap-websockets.ts @@ -147,7 +147,7 @@ export async function implementation( if (await blockstore.has(cid)) return blockstore.get(cid) // TODO: Can we use CAR files to get a bunch of blocks at once? - return fetch(`${gatewayUrl.replace(/\/+$/, "")}/api/v0/block/get?arg=${cid.toString()}`) + return fetch(`${gatewayUrl.replace(/\/+$/, "")}/ipfs/${cid.toString()}?format=raw`) .then(r => { if (r.ok) return r.arrayBuffer() throw new Error("Failed to fetch block from gateway") diff --git a/src/components/identifier/implementation.ts b/src/components/identifier/implementation.ts index 42234a19..e6913362 100644 --- a/src/components/identifier/implementation.ts +++ b/src/components/identifier/implementation.ts @@ -4,7 +4,7 @@ export type Implementation = { * * This'll be associated with your account and file system. */ - did: () => Promise + did: () => string /** * For signing the agent delegation UCAN. @@ -19,5 +19,5 @@ export type Implementation = { /** * The JWT `alg` used in the agent delegation UCAN. */ - ucanAlgorithm: () => Promise + ucanAlgorithm: () => string } diff --git a/src/components/identifier/web-crypto-api.ts b/src/components/identifier/web-crypto-api.ts index de0061c7..ee404c1f 100644 --- a/src/components/identifier/web-crypto-api.ts +++ b/src/components/identifier/web-crypto-api.ts @@ -7,7 +7,9 @@ import { exportPublicKey } from "../../common/crypto.js" import { Store } from "../../common/crypto/store.js" import { Implementation } from "./implementation.js" -// 🛳️ +//////// +// 🛳️ // +//////// export async function implementation( { store }: { store: Store } @@ -21,8 +23,8 @@ export async function implementation( const exportedKey = await exportPublicKey(signingKey).then(spki.decode) return { - did: async () => DIDKey.fromPublicKey("RSA", exportedKey).toString(), + did: () => DIDKey.fromPublicKey("RSA", exportedKey).toString(), sign: async data => WebCryptoAPIAgent.sign(data, signingKey), - ucanAlgorithm: async () => "RS256", + ucanAlgorithm: () => "RS256", } } diff --git a/src/components/storage/implementation.ts b/src/components/storage/implementation.ts index 578effbb..d9202e57 100644 --- a/src/components/storage/implementation.ts +++ b/src/components/storage/implementation.ts @@ -9,6 +9,7 @@ export type Implementation = { KEYS: { CABINET: string CID_LOG: string + NAMES: string } getItem: (key: string) => Promise diff --git a/src/components/storage/keys/default.ts b/src/components/storage/keys/default.ts index c7b3e001..58ce350a 100644 --- a/src/components/storage/keys/default.ts +++ b/src/components/storage/keys/default.ts @@ -3,4 +3,5 @@ import { Implementation } from "../implementation.js" export const KEYS: Implementation["KEYS"] = { CABINET: "cabinet", CID_LOG: "cid-log", + NAMES: "names", } diff --git a/src/compositions/fission.ts b/src/compositions/fission.ts index d2678821..960220a0 100644 --- a/src/compositions/fission.ts +++ b/src/compositions/fission.ts @@ -22,8 +22,9 @@ import * as Config from "../configuration.js" import * as Account from "../components/account/fission.js" import * as Agent from "../components/agent/web-crypto-api.js" -import * as Authority from "../components/authority/ts-ucan.js" +import * as Authority from "../components/authority/browser-url.js" import * as Channel from "../components/channel/fission.js" +import * as Clerk from "../components/clerk/default.js" import * as Depot from "../components/depot/fission.js" import * as DNS from "../components/dns/dns-over-https/fission.js" import * as Identifier from "../components/identifier/web-crypto-api.js" @@ -35,7 +36,7 @@ export * from "../common/fission.js" export { Annexes } from "../components/account/fission.js" export type DefaultAnnex = Account.Annexes.Standard -export { Context as ChannelContext } from "../components/channel/fission.js" +export { ProvideParams, ProvideResponse, RequestParams, RequestResponse } from "../components/authority/browser-url.js" /** * Account type. @@ -85,27 +86,51 @@ export async function components( accountImplementation: "delegated" environment?: string | Fission.Endpoints } -): Promise> +): Promise< + Components< + Account.Annexes.Delegated, + Authority.ProvideResponse, + Authority.RequestResponse + > +> export async function components( config: Configuration, settings?: { accountImplementation: "standard" environment?: string | Fission.Endpoints } -): Promise> +): Promise< + Components< + Account.Annexes.Standard, + Authority.ProvideResponse, + Authority.RequestResponse + > +> export async function components( config: Configuration, settings?: { environment?: string | Fission.Endpoints } -): Promise> +): Promise< + Components< + DefaultAnnex, + Authority.ProvideResponse, + Authority.RequestResponse + > +> export async function components( config: Configuration, settings?: { accountType?: AccountImplementations environment?: string | Fission.Endpoints } -): Promise> { +): Promise< + Components< + Account.Annexes.Delegated | Account.Annexes.Standard, + Authority.ProvideResponse, + Authority.RequestResponse + > +> { const namespace = Config.namespace(config) // Determine environment @@ -129,13 +154,14 @@ export async function components( const agentStore = Storage.implementation({ name: `${namespace}/agent` }) const identifierStore = Storage.implementation({ name: `${namespace}/identifier` }) + const clerk = Clerk.implementation() const dns = DNS.implementation(endpoints) const manners = Manners.implementation(config) const channel = Channel.implementation(manners, endpoints) const agent = await Agent.implementation({ store: agentStore }) const identifier = await Identifier.implementation({ store: identifierStore }) const depot = await Depot.implementation(manners, storage, `${namespace}/blockstore`, endpoints) - const authority = Authority.implementation(identifier) + const authority = Authority.implementation() const account = (() => { switch (settings?.accountType || "standard") { @@ -164,6 +190,7 @@ export async function components( agent, authority, channel, + clerk, depot, dns, identifier, diff --git a/src/compositions/local.ts b/src/compositions/local.ts index 18fb8885..1f98fcb8 100644 --- a/src/compositions/local.ts +++ b/src/compositions/local.ts @@ -21,8 +21,9 @@ import * as Config from "../configuration.js" import * as Account from "../components/account/local.js" import * as Agent from "../components/agent/web-crypto-api.js" -import * as Authority from "../components/authority/ts-ucan.js" +import * as Authority from "../components/authority/browser-url.js" import * as Channel from "../components/channel/local.js" +import * as Clerk from "../components/clerk/default.js" import * as Depot from "../components/depot/local.js" import * as DNS from "../components/dns/dns-over-https/cloudflare-google.js" import * as Identifier from "../components/identifier/web-crypto-api.js" @@ -30,10 +31,9 @@ import * as Manners from "../components/manners/default.js" import * as Storage from "../components/storage/indexed-db.js" export { Annex } from "../components/account/local.js" -export { Context as ChannelContext } from "../components/channel/local.js" /** - * The default Fission stack using web crypto auth and IPFS. + * The local-only stack. * * @group 🚀 */ @@ -41,7 +41,13 @@ export async function components( settings: Configuration & { environment?: string } -): Promise> { +): Promise< + Components< + Account.Annex, + Authority.ProvideResponse, + Authority.RequestResponse + > +> { const config = Config.extract(settings) const namespace = Config.namespace(config) @@ -53,11 +59,12 @@ export async function components( const agent = await Agent.implementation({ store: agentStore }) const channel = Channel.implementation() + const clerk = Clerk.implementation() const dns = DNS.implementation() const identifier = await Identifier.implementation({ store: identifierStore }) const manners = Manners.implementation(config) const account = Account.implementation() - const authority = Authority.implementation(identifier) + const authority = Authority.implementation() // Fin return { @@ -65,6 +72,7 @@ export async function components( agent, authority, channel, + clerk, depot, dns, identifier, diff --git a/src/events/authority.ts b/src/events/authority.ts index ababf8d7..a3c9e79b 100644 --- a/src/events/authority.ts +++ b/src/events/authority.ts @@ -1,10 +1,16 @@ -export type AuthorityRequestor = { - "challenge": undefined // TODO -} +import { Query } from "../authority/query.js" + +export type Authority = { + "provide:authorised": { queries: Query[] } + "provide:authorized": { queries: Query[] } + "provide:dismissed": undefined + "provide:query": { + approve: (queries: Query[]) => void + dismiss: () => void + queries: Query[] + } -export type AuthorityProvider = { - "approved": undefined - "challenge": undefined // TODO - "dismissed": undefined - "query": Record // TODO + "request:authorised": { queries: Query[] } + "request:authorized": { queries: Query[] } + "request:dismissed": undefined } diff --git a/src/fileSystem.ts b/src/fileSystem.ts index fba66672..00f1017d 100644 --- a/src/fileSystem.ts +++ b/src/fileSystem.ts @@ -6,11 +6,11 @@ import * as Path from "./path/index.js" import * as CIDLog from "./repositories/cid-log.js" import { Maybe } from "./common/index.js" -import { Authority, Storage } from "./components.js" +import { Clerk, Identifier, Storage } from "./components.js" import { FileSystem } from "./fs/class.js" -import { Dependencies } from "./fs/types.js" -import { Inventory } from "./ticket/inventory.js" -import { Ticket } from "./ticket/types.js" +import { Dependencies, FileSystemCarrier } from "./fs/types.js" +import { Inventory } from "./inventory.js" +import { Names } from "./repositories/names.js" //////// // 🛠️ // @@ -21,22 +21,53 @@ import { Ticket } from "./ticket/types.js" */ export async function loadFileSystem(args: { cabinet: Cabinet - dataRoot?: CID - dataRootUpdater?: ( - dataRoot: CID, - proofs: Ticket[] - ) => Promise<{ updated: true } | { updated: false; reason: string }> + carrier: FileSystemCarrier dependencies: Dependencies & { - authority: Authority.Implementation + clerk: Clerk.Implementation + identifier: Identifier.Implementation + storage: Storage.Implementation + } + names: Names +}): Promise { + if (args.carrier.futile) { + throw new Error( + "Cannot load a file system using a futile carrier. This usually means you're in the middle of an important process in which the file system cannot be loaded yet." + ) + } + + // DID is known, so we expect an existing file system. + if ("did" in args.carrier.id) { + return loadExisting({ + ...args, + did: args.carrier.id.did, + }) + } + + // DID is unknown, that means a new file system. + return createNew({ + ...args, + didName: args.carrier.id.name, + }) +} + +//////// +// ㊙️ // +//////// + +async function loadExisting(args: { + cabinet: Cabinet + carrier: FileSystemCarrier + dependencies: Dependencies & { + clerk: Clerk.Implementation + identifier: Identifier.Implementation storage: Storage.Implementation } did: string }): Promise { - const { cabinet, dataRootUpdater, dependencies, did } = args - const { authority, depot, manners, storage } = dependencies + const { cabinet, carrier, dependencies, did } = args + const { clerk, depot, manners, storage } = dependencies - let cid: Maybe = args.dataRoot || null - let fs: FileSystem + let cid: Maybe = "dataRoot" in carrier ? carrier.dataRoot || null : null // Create CIDLog, namespaced by identifier const cidLog = await CIDLog.create({ storage, did }) @@ -47,8 +78,11 @@ export async function loadFileSystem(args: { if (!cid) { // No DNS CID yet cid = cidLog.newest() - if (cid) manners.log("📓 Using local CID:", cid.toString()) - else manners.log("📓 Creating a new file system") + if (cid) { + manners.log("📓 Using local CID:", cid.toString()) + } else { + throw new Error(`Expected a file system to exists for the DID: ${did}`) + } } else if (logIdx === cidLog.length() - 1) { // DNS is up to date manners.log("📓 DNSLink is up to date:", cid.toString()) @@ -68,54 +102,80 @@ export async function loadFileSystem(args: { // that are only stored locally. } - // If a file system exists, load it and return it - const tickets = new Inventory(authority, cabinet) - const updateDataRoot = dataRootUpdater + // File system class instance + const inventory = new Inventory(clerk, cabinet) + const updateDataRoot = carrier.dataRootUpdater - if (cid) { - await manners.fileSystem.hooks.beforeLoadExisting(cid, depot) + await manners.fileSystem.hooks.beforeLoadExisting(cid, depot) - fs = await FileSystem.fromCID( - cid, - { cidLog, dependencies, did, tickets, updateDataRoot } - ) + const fs = await FileSystem.fromCID( + cid, + { cidLog, dependencies, did, inventory, updateDataRoot } + ) - // Mount private nodes - await Promise.all( - (cabinet.accessKeys[fs.did] || []).map(async a => { - return fs.mountPrivateNode({ - path: Path.removePartition(a.path), - capsuleKey: a.key, - }) + // Mount private nodes + await Promise.all( + (cabinet.accessKeys[fs.did] || []).map(async a => { + return fs.mountPrivateNode({ + path: Path.removePartition(a.path), + capsuleKey: a.key, }) - ) + }) + ) - await manners.fileSystem.hooks.afterLoadExisting(fs, depot) + await manners.fileSystem.hooks.afterLoadExisting(fs, depot) - return fs + return fs +} + +async function createNew(args: { + cabinet: Cabinet + carrier: FileSystemCarrier + dependencies: Dependencies & { + clerk: Clerk.Implementation + identifier: Identifier.Implementation + storage: Storage.Implementation } + didName: string + names: Names +}) { + const { cabinet, carrier, dependencies, didName, names } = args + const { clerk, depot, manners, storage } = dependencies + + manners.log("📓 Creating a new file system") + + // Self delegate file system UCAN & add stuff to cabinet + const fileSystemTicket = await clerk.tickets.fileSystem.origin( + Path.root(), + dependencies.identifier.did() + ) + + await cabinet.addTicket("file_system", fileSystemTicket, clerk.tickets.cid) + + // The file system DID is the issuer of the initial file system ticket + const did = fileSystemTicket.issuer + + await names.add({ name: didName, subject: did }) + + // Create CIDLog, namespaced by identifier + const cidLog = await CIDLog.create({ storage, did }) + + // Create new FileSystem instance + const inventory = new Inventory(clerk, cabinet) + const updateDataRoot = carrier.dataRootUpdater - // Otherwise make a new one await manners.fileSystem.hooks.beforeLoadNew(depot) - fs = await FileSystem.empty({ + const fs = await FileSystem.empty({ cidLog, dependencies, did, - tickets, + inventory, updateDataRoot, }) const maybeMount = await manners.fileSystem.hooks.afterLoadNew(fs, depot) - // Self delegate file system UCAN & add stuff to cabinet - const fileSystemTicket = await authority.clerk.tickets.fileSystem.create( - Path.root(), - fs.did - ) - - await cabinet.addTicket("file_system", fileSystemTicket) - if (maybeMount) { await cabinet.addAccessKey({ did: fs.did, diff --git a/src/fs/class.node.test.ts b/src/fs/class.node.test.ts index 7d47740a..0c85d272 100644 --- a/src/fs/class.node.test.ts +++ b/src/fs/class.node.test.ts @@ -6,29 +6,48 @@ import * as Cabinet from "../repositories/cabinet.js" import * as CIDLog from "../repositories/cid-log.js" import * as Unix from "./unix.js" -import { account, agent, authority, depot, identifier, manners, storage } from "../../tests/helpers/components.js" +import { + account, + agent, + authority, + clerk, + depot, + identifier, + manners, + storage, +} from "../../tests/helpers/components.js" import { CID } from "../common/cid.js" -import { Inventory } from "../ticket/inventory.js" +import { Inventory } from "../inventory.js" import { Ticket } from "../ticket/types.js" import { FileSystem } from "./class.js" describe("File System Class", async () => { let fs: FileSystem + let mounts: { + path: Path.Distinctive + capsuleKey: Uint8Array + }[] const fsOpts = { dependencies: { account, agent, depot, identifier, manners }, settleTimeBeforePublish: 250, } + const fsTicket = await clerk.tickets.fileSystem.origin( + Path.root(), + identifier.did() + ) + + const did = fsTicket.issuer + // HOOKS // ----- beforeEach(async () => { - const did = await identifier.did() const cidLog = await CIDLog.create({ did, storage }) const cabinet = await Cabinet.create({ storage }) - const tickets = new Inventory(authority, cabinet) + const inventory = new Inventory(clerk, cabinet) const updateDataRoot = async ( dataRoot: CID, @@ -37,18 +56,16 @@ describe("File System Class", async () => { return { updated: true } } - fs = await FileSystem.empty({ ...fsOpts, cidLog, did, tickets, updateDataRoot }) + fs = await FileSystem.empty({ ...fsOpts, cidLog, did, inventory, updateDataRoot }) - const mounts = await fs.mountPrivateNodes([ + mounts = await fs.mountPrivateNodes([ { path: Path.root() }, ]) await cabinet.addTicket( "file_system", - await authority.clerk.tickets.fileSystem.create( - Path.root(), - did - ) + fsTicket, + clerk.tickets.cid ) }) @@ -69,15 +86,15 @@ describe("File System Class", async () => { "public" ) - const did = await identifier.did() const cidLog = await CIDLog.create({ did, storage }) - const cabinet = await Cabinet.create({ storage }) - const tickets = new Inventory(authority, cabinet) + const inventory = new Inventory(clerk, cabinet) - const loadedFs = await FileSystem.fromCID(dataRoot, { ...fsOpts, cidLog, did, tickets }) + const loadedFs = await FileSystem.fromCID(dataRoot, { ...fsOpts, cidLog, did, inventory }) await loadedFs.mountPrivateNodes([ - { path: Path.removePartition(privatePath), capsuleKey }, + // TODO: Needs to be fixed in rs-wnfs + // { path: Path.removePartition(privatePath), capsuleKey }, + { path: Path.root(), capsuleKey: mounts[0].capsuleKey }, ]) assert.equal(await loadedFs.read(publicPath, "utf8"), "public") @@ -95,13 +112,11 @@ describe("File System Class", async () => { const { dataRoot } = await fs.write(Path.file("private", "part.two"), "utf8", "private-2") const capsuleKey = await fs.capsuleKey(Path.directory("private")) - const did = await identifier.did() const cidLog = await CIDLog.create({ did, storage }) - const cabinet = await Cabinet.create({ storage }) - const tickets = new Inventory(authority, cabinet) + const inventory = new Inventory(clerk, cabinet) - const loadedFs = await FileSystem.fromCID(dataRoot, { ...fsOpts, cidLog, did, tickets }) + const loadedFs = await FileSystem.fromCID(dataRoot, { ...fsOpts, cidLog, did, inventory }) if (capsuleKey) { await loadedFs.mountPrivateNodes([ @@ -119,14 +134,12 @@ describe("File System Class", async () => { const privatePath = Path.file("private", "nested-private", "private.txt") const oldCapsuleKey = await fs.capsuleKey(Path.directory("private")) - const did = await identifier.did() const cidLog = await CIDLog.create({ did, storage }) - const cabinet = await Cabinet.create({ storage }) - const tickets = new Inventory(authority, cabinet) + const inventory = new Inventory(clerk, cabinet) const { dataRoot } = await fs.write(privatePath, "utf8", "private") - const loadedFs = await FileSystem.fromCID(dataRoot, { ...fsOpts, cidLog, did, tickets }) + const loadedFs = await FileSystem.fromCID(dataRoot, { ...fsOpts, cidLog, did, inventory }) if (oldCapsuleKey) { await loadedFs.mountPrivateNodes([ @@ -137,6 +150,10 @@ describe("File System Class", async () => { } assert.equal(await loadedFs.read(privatePath, "utf8"), "private") + + await loadedFs.write(privatePath, "utf8", "new content") + + assert.equal(await loadedFs.read(privatePath, "utf8"), "new content") }) // READING & WRITING diff --git a/src/fs/class.ts b/src/fs/class.ts index 9e7337f6..54ea8560 100644 --- a/src/fs/class.ts +++ b/src/fs/class.ts @@ -12,8 +12,8 @@ import * as Store from "./store.js" import { EventEmitter, createEmitter } from "../events/emitter.js" import { EventListener } from "../events/listen.js" +import { Inventory } from "../inventory.js" import { Partition, Partitioned, PartitionedNonEmpty, Private, Public } from "../path/index.js" -import { Inventory } from "../ticket/inventory.js" import { Ticket } from "../ticket/types.js" import { searchLatest } from "./common.js" import { findPrivateNode, partition as determinePartition } from "./mounts.js" @@ -22,6 +22,7 @@ import { AnySupportedDataType, DataForType, DataRootChange, + DataRootUpdater, DataType, Dependencies, DirectoryItem, @@ -40,9 +41,9 @@ export type FileSystemOptions = { cidLog: CIDLog dependencies: Dependencies did: string + inventory: Inventory settleTimeBeforePublish?: number - tickets: Inventory - updateDataRoot?: (dataRoot: CID, proofs: Ticket[]) => Promise<{ updated: true } | { updated: false; reason: string }> + updateDataRoot?: DataRootUpdater } /** @group File System */ @@ -51,10 +52,10 @@ export class FileSystem { #cidLog: CIDLog #dependencies: Dependencies #eventEmitter: EventEmitter - #settleTimeBeforePublish: number + #inventory: Inventory #rootTree: RootTree.RootTree - #tickets: Inventory - #updateDataRoot?: (dataRoot: CID, proofs: Ticket[]) => Promise<{ updated: true } | { updated: false; reason: string }> + #settleTimeBeforePublish: number + #updateDataRoot?: DataRootUpdater #privateNodes: MountedPrivateNodes = {} #rng: Rng.Rng @@ -67,9 +68,9 @@ export class FileSystem { cidLog: CIDLog, dependencies: Dependencies, did: string, - settleTimeBeforePublish: number, + inventory: Inventory, rootTree: RootTree.RootTree, - tickets: Inventory, + settleTimeBeforePublish: number, updateDataRoot?: ( dataRoot: CID, proofs: Ticket[] @@ -78,9 +79,9 @@ export class FileSystem { this.#blockStore = blockStore this.#cidLog = cidLog this.#dependencies = dependencies + this.#inventory = inventory this.#settleTimeBeforePublish = settleTimeBeforePublish this.#rootTree = rootTree - this.#tickets = tickets this.#updateDataRoot = updateDataRoot this.#eventEmitter = createEmitter() @@ -97,7 +98,7 @@ export class FileSystem { * @internal */ static async empty(opts: FileSystemOptions): Promise { - const { cidLog, dependencies, did, settleTimeBeforePublish, tickets, updateDataRoot } = opts + const { cidLog, dependencies, did, inventory, settleTimeBeforePublish, updateDataRoot } = opts const blockStore = Store.fromDepot(dependencies.depot) const rootTree = await RootTree.empty() @@ -107,9 +108,9 @@ export class FileSystem { cidLog, dependencies, did, - settleTimeBeforePublish || 2500, + inventory, rootTree, - tickets, + settleTimeBeforePublish || 2500, updateDataRoot ) } @@ -119,7 +120,7 @@ export class FileSystem { * @internal */ static async fromCID(cid: CID, opts: FileSystemOptions): Promise { - const { cidLog, dependencies, did, settleTimeBeforePublish, tickets, updateDataRoot } = opts + const { cidLog, dependencies, did, inventory, settleTimeBeforePublish, updateDataRoot } = opts const blockStore = Store.fromDepot(dependencies.depot) const rootTree = await RootTree.fromCID({ blockStore, cid, depot: dependencies.depot }) @@ -129,9 +130,9 @@ export class FileSystem { cidLog, dependencies, did, - settleTimeBeforePublish || 2500, + inventory, rootTree, - tickets, + settleTimeBeforePublish || 2500, updateDataRoot ) } @@ -674,10 +675,10 @@ export class FileSystem { this.#blockStore, this.#dependencies, this.did, + this.#inventory, { ...this.#privateNodes }, this.#rng, - { ...this.#rootTree }, - this.#tickets + { ...this.#rootTree } ) } @@ -692,7 +693,7 @@ export class FileSystem { async (args: [dataRoot: CID, proofs: Ticket[]][]): Promise => { const [dataRoot, proofs] = args[args.length - 1] - await this.#dependencies.depot.flush(dataRoot, proofs) + await this.#dependencies.depot.flush(dataRoot, proofs, this.#inventory) let status: PublishingStatus diff --git a/src/fs/transaction.ts b/src/fs/transaction.ts index b7b539cb..f6f3fa2d 100644 --- a/src/fs/transaction.ts +++ b/src/fs/transaction.ts @@ -6,8 +6,8 @@ import * as Queries from "./queries.js" import * as Unix from "./unix.js" import { CID } from "../common/cid.js" +import { Inventory } from "../inventory.js" import { Partition, Partitioned, PartitionedNonEmpty, Private, Public } from "../path/index.js" -import { Inventory } from "../ticket/inventory.js" import { Ticket } from "../ticket/types.js" import { addOrIncreaseNameNumber, pathSegmentsWithoutPartition, searchLatest } from "./common.js" import { dataFromBytes, dataToBytes } from "./data.js" @@ -31,10 +31,10 @@ export class TransactionContext { #blockStore: BlockStore #dependencies: Dependencies #did: string + #inventory: Inventory #privateNodes: MountedPrivateNodes #rng: Rng #rootTree: RootTree - #tickets: Inventory #changes: Set<{ type: MutationType @@ -46,10 +46,10 @@ export class TransactionContext { blockStore: BlockStore, dependencies: Dependencies, did: string, + inventory: Inventory, privateNodes: MountedPrivateNodes, rng: Rng, - rootTree: RootTree, - tickets: Inventory + rootTree: RootTree ) { this.#blockStore = blockStore this.#dependencies = dependencies @@ -57,7 +57,7 @@ export class TransactionContext { this.#privateNodes = privateNodes this.#rng = rng this.#rootTree = rootTree - this.#tickets = tickets + this.#inventory = inventory this.#changes = new Set() } @@ -75,7 +75,7 @@ export class TransactionContext { const proofs = await changes.reduce( async (accPromise: Promise, change): Promise => { const acc = await accPromise - const proof = context.#tickets.lookupFileSystemTicket( + const proof = await context.#inventory.lookupFileSystemTicket( change.path, context.#did ) @@ -108,7 +108,7 @@ export class TransactionContext { ) if (maybeNode) { - const [_, newForest] = await maybeNode.node.store(oldForest, context.#blockStore, context.#rng) + const [_newAccessKey, newForest] = await maybeNode.node.store(oldForest, context.#blockStore, context.#rng) return newForest } else { return oldForest @@ -615,7 +615,7 @@ export class TransactionContext { // Mark node as changed this.#changes.add({ type: mutType, - path: Path.withPartition("private", path), + path: path, }) // Replace forest @@ -623,9 +623,10 @@ export class TransactionContext { // Replace private node const nodePosix = Path.toPosix(priv.path, { absolute: true }) + const node = result.rootDir.asNode() this.#privateNodes[nodePosix] = { - node: result.rootDir.asNode(), + node, path: priv.path, } } diff --git a/src/fs/types.ts b/src/fs/types.ts index dd87d6a1..c4ad0fc5 100644 --- a/src/fs/types.ts +++ b/src/fs/types.ts @@ -5,6 +5,7 @@ import * as Path from "../path/index.js" import { CID } from "../common/cid.js" import { Partition, Partitioned } from "../path/index.js" +import { Ticket } from "../ticket/types.js" //////// // 🧩 // @@ -28,6 +29,12 @@ export type DataForType = D extends "bytes" ? U : D extends "utf8" ? string : never +/** @group File System */ +export type DataRootUpdater = ( + dataRoot: CID, + proofs: Ticket[] +) => Promise<{ updated: true } | { updated: false; reason: string }> + /** @internal */ export type Dependencies = { depot: Depot.Implementation @@ -46,6 +53,14 @@ export type DirectoryItemWithKind = DirectoryItem & { path: Path.Distinctive> } +/** @group File System */ +export type FileSystemCarrier = { + dataRoot?: CID + dataRootUpdater?: DataRootUpdater + futile?: boolean + id: { did: string } | { name: string } +} + /** @group File System */ export type MutationOptions = { skipPublish?: boolean diff --git a/src/identifier/did.ts b/src/identifier/did.ts new file mode 100644 index 00000000..3d7338f8 --- /dev/null +++ b/src/identifier/did.ts @@ -0,0 +1,30 @@ +import { DIDKey } from "iso-did/key" +import { spki } from "iso-signatures/spki" + +import { exportPublicKey } from "../common/crypto.js" +import * as Agent from "../components/agent/implementation.js" + +/** + * Create a DID based on the exchange key-pair of the agent. + */ +export async function exchange(agent: Agent.Implementation): Promise { + const pubKey = await agent.exchangeKey().then(exportPublicKey).then(spki.decode) + const ksAlg = agent.keyAlgorithm() + + return DIDKey.fromPublicKey(ksAlg, pubKey).toString() +} + +/** + * Alias `exchange` to `sharing` + */ +export { exchange as sharing } + +/** + * Create a DID based on the signing key-pair. + */ +export async function signing(agent: Agent.Implementation): Promise { + const pubKey = await agent.signingKey().then(exportPublicKey).then(spki.decode) + const ksAlg = agent.keyAlgorithm() + + return DIDKey.fromPublicKey(ksAlg, pubKey).toString() +} diff --git a/src/index.ts b/src/index.ts index 64ccc193..f54482e9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,37 +2,39 @@ * Documentation for `@oddjs/odd`. * * ``` - * import * as fission from "@oddjs/odd/compositions/fission" + * import * as local from "@oddjs/odd/compositions/local" * import * as odd from "@oddjs/odd" * * const config = { namespace: "odd-example" } * * odd.program( * config, - * await fission.components(config) + * await local.components(config) * ) * ``` * @module odd */ import * as Auth from "./auth.js" +import * as AuthorityEvents from "./events/authority.js" import * as Events from "./events/program.js" import * as Path from "./path/index.js" import * as Cabinet from "./repositories/cabinet.js" +import * as Names from "./repositories/names.js" import { FileSystemQuery, Query } from "./authority/query.js" -import { CID } from "./common/cid.js" import { Account } from "./components.js" import { Components } from "./components.js" import { AnnexParentType } from "./components/account/implementation.js" import { RequestOptions } from "./components/authority/implementation.js" import { Configuration, namespace } from "./configuration.js" +import { createEmitter } from "./events/emitter.js" import { ListenTo, listenTo } from "./events/listen.js" import { loadFileSystem } from "./fileSystem.js" import { FileSystem } from "./fs/class.js" import { addSampleData } from "./fs/data/sample.js" -import { Inventory } from "./ticket/inventory.js" -import { Ticket } from "./ticket/types.js" +import { FileSystemCarrier } from "./fs/types.js" +import { Inventory } from "./inventory.js" //////////////// // RE-EXPORTS // @@ -41,22 +43,23 @@ import { Ticket } from "./ticket/types.js" export * from "./appInfo.js" export * from "./common/types.js" export * from "./common/version.js" +export * from "./components.js" export * from "./configuration.js" export * from "./fs/types.js" -export * as Components from "./components.js" - export * as authority from "./authority/query.js" +export * as channel from "./channel.js" export * as events from "./events/index.js" export * as path from "./path/index.js" -export { Channel, ChannelData, ChannelOptions } from "./channel.js" export { CID, decodeCID, encodeCID } from "./common/cid.js" +export { Components } from "./components.js" export { RequestOptions } from "./components/authority/implementation.js" export { CodecIdentifier } from "./dag/codecs.js" export { FileSystem } from "./fs/class.js" export { TransactionContext } from "./fs/transaction.js" -export { Inventory } from "./ticket/inventory.js" +export { Inventory } from "./inventory.js" +export { Names } from "./repositories/names.js" export { Ticket } from "./ticket/types.js" /////////////////////// @@ -76,7 +79,11 @@ export { Ticket } from "./ticket/types.js" * * @group 🚀 */ -export type Program = +export type Program< + Annex extends Account.AnnexParentType, + AuthorityProvideResponse, + AuthorityRequestResponse, +> = & { /** * {@inheritDoc AccountCategory} @@ -86,12 +93,19 @@ export type Program = /** * {@inheritDoc AuthorityCategory} */ - authority: AuthorityCategory + authority: AuthorityCategory< + AuthorityProvideResponse, + AuthorityRequestResponse + > /** * Components used to build this program. */ - components: Components + components: Components< + Annex, + AuthorityProvideResponse, + AuthorityRequestResponse + > /** * Configuration used to build this program. @@ -131,11 +145,9 @@ export type AccountCategory = ReturnType = { /** * Does my program have the authority to work with these part of the file system? * And does the configured account system have the required authority? @@ -144,9 +156,9 @@ export type AuthorityCategory = { { has: true } | { has: false; reason: string } > - provide: () => Promise - request: (options: RequestOptions) => Promise -} + provide: (queries: Query | (Query | Query[])[]) => Promise + request: (queries: Query | (Query | Query[])[], options: RequestOptions) => Promise +} & ListenTo /** * File system. @@ -174,14 +186,7 @@ export type FileSystemCategory = { * program.fileSystem.load({ dataRoot: cid, dataRootUpdater: updateFn, did: "did:some-identifier" }) * ``` */ - load: (params: { - dataRoot?: CID - dataRootUpdater?: ( - dataRoot: CID, - proofs: Ticket[] - ) => Promise<{ updated: true } | { updated: false; reason: string }> - did: string - }) => Promise + load: (params: FileSystemCarrier) => Promise } /** @@ -221,11 +226,25 @@ export type IdentityCategory = { * * @group 🚀 */ -export async function program( +export async function program< + Annex extends AnnexParentType, + AuthorityProvideResponse, + AuthorityRequestResponse, +>( config: Configuration, - components: Components -): Promise> { - const { account, agent, authority, identifier } = components + components: Components< + Annex, + AuthorityProvideResponse, + AuthorityRequestResponse + > +): Promise< + Program< + Annex, + AuthorityProvideResponse, + AuthorityRequestResponse + > +> { + const { account, agent, authority, channel, clerk, identifier } = components // Is supported? await Promise.all( @@ -237,7 +256,9 @@ export async function program( // Create repositories const cabinet = await Cabinet.create({ storage: components.storage }) - const tickets = new Inventory(components.authority, cabinet) + const names = await Names.create({ storage: components.storage }) + + const inventory = new Inventory(components.clerk, cabinet) cabinet.events.on("collection:changed", async ({ collection }) => { // TODO: emit authority:inventory-changed event @@ -245,16 +266,19 @@ export async function program( }) // Authority - const authorityCategory = { + const authorityEmitter = createEmitter() + const authorityCategory: AuthorityCategory< + AuthorityProvideResponse, + AuthorityRequestResponse + > = { async has( query: Query | (Query | Query[])[] ): Promise<{ has: true } | { has: false; reason: string }> { - const audience = await identifier.did() const queries = (Array.isArray(query) ? query : [query]).flat() // Account access if (queries.some(q => q.query === "account")) { - const accountAccess = await account.hasSufficientAuthority(identifier, tickets) + const accountAccess = await account.hasSufficientAuthority(identifier, inventory) if (!accountAccess.suffices) { return { has: false, @@ -275,7 +299,9 @@ export async function program( const hasAccessToFsPaths = fsQueries.filter(q => Path.isPartition("private", q.path)).reduce( (acc, query) => { if (acc === false) return false - return cabinet.hasAccessKey(audience, query.path) + const did = names.resolveId(query.id) + if (!did) return false + return cabinet.hasAccessKey(did, query.path) }, true ) @@ -291,33 +317,90 @@ export async function program( return { has: true } }, - // TODO: - async provide() {}, - async request() {}, + async provide(query: Query | (Query | Query[])[]): Promise { + const queries = (Array.isArray(query) ? query : [query]).flat() + + return authority.provide({ + dependencies: { + account, + channel, + clerk, + identifier, + }, + eventEmitter: authorityEmitter, + inventory, + queries, + names, + }) + }, + + async request( + query: Query | (Query | Query[])[], + options: RequestOptions + ): Promise { + const queries = (Array.isArray(query) ? query : [query]).flat() + const response = await authority.request({ + dependencies: { + channel, + identifier, + }, + eventEmitter: authorityEmitter, + options, + queries, + }) + + if (response) { + const accountTickets = response.accountTickets.map(i => i.tickets).flat() + const fileSystemTickets = response.fileSystemTickets.map(i => i.tickets).flat() + + await cabinet.addTickets("account", accountTickets, clerk.tickets.cid) + await cabinet.addTickets("file_system", fileSystemTickets, clerk.tickets.cid) + await cabinet.addAccessKeys(response.accessKeys) + + const identifierAccountTickets = accountTickets.filter( + t => t.audience === identifier.did() + ) + + if (identifierAccountTickets.length) { + // Do delegation from identifier to agent + const agentDelegation = await clerk.tickets.misc.identifierToAgentDelegation( + identifier, + agent, + identifierAccountTickets + ) + + await cabinet.addTicket("agent", agentDelegation, clerk.tickets.cid) + } + + await names.add( + Object.entries(response.resolvedNames).map(([k, v]) => { + return { name: k, subject: v } + }) + ) + + await authorityEmitter.emit("request:authorised", { queries: response.authorisedQueries }) + await authorityEmitter.emit("request:authorized", { queries: response.authorisedQueries }) + + return response.requestResponse + } + + return null + }, + + ...listenTo(authorityEmitter), } // Other categories - const fileSystemCategory: Program["fileSystem"] = { + const fileSystemCategory: FileSystemCategory = { addSampleData: (fs: FileSystem) => addSampleData(fs), - load: async (params: { - dataRoot?: CID - dataRootUpdater?: ( - dataRoot: CID, - proofs: Ticket[] - ) => Promise<{ updated: true } | { updated: false; reason: string }> - did: string - }) => { - const dataRoot = params.dataRoot - const dataRootUpdater = params.dataRootUpdater - const did = params.did - - return loadFileSystem({ cabinet, dataRoot, dataRootUpdater, dependencies: components, did }) + load: async (params: FileSystemCarrier) => { + return loadFileSystem({ cabinet, names, carrier: params, dependencies: components }) }, } - const identityCategory: Program["identity"] = { + const identityCategory: IdentityCategory = { async account(): Promise { - return account.did(identifier, tickets) + return account.did(identifier, inventory) }, async agent() { return agent.did() @@ -341,10 +424,10 @@ export async function program( fileSystem: fileSystemCategory, account: { - register: Auth.register({ account, agent, authority, identifier, cabinet }), + register: Auth.register({ account, agent, clerk, identifier, cabinet, names }), canRegister: account.canRegister, - ...components.account.annex(identifier, tickets), + ...components.account.annex(identifier, inventory, names), }, } diff --git a/src/inventory.ts b/src/inventory.ts new file mode 100644 index 00000000..35d6d129 --- /dev/null +++ b/src/inventory.ts @@ -0,0 +1,161 @@ +import * as Path from "./path/index.js" + +import { AccessKeyWithContext } from "./accessKey.js" +import { CID } from "./common/cid.js" +import * as Clerk from "./components/clerk/implementation.js" +import { Cabinet } from "./repositories/cabinet.js" +import { Category, Ticket } from "./ticket/types.js" + +export class Inventory { + #authorityClerk: Clerk.Implementation + #cabinet: Cabinet + + /** @internal */ + constructor( + authorityClerk: Clerk.Implementation, + cabinet: Cabinet + ) { + this.#authorityClerk = authorityClerk + this.#cabinet = cabinet + } + + ///////////// + // LOOKUPS // + ///////////// + + /** + * Collect the given ticket and all its proofs. + */ + async bundleTickets( + ticket: Ticket, + proofResolver: (ticket: Ticket) => Promise + ): Promise { + return [ + ticket, + ...(await proofResolver(ticket)).map(cid => { + const t = this.lookupTicketByCID(cid) + if (!t) throw new Error(`Missing a proof in the local repository: ${cid}`) + return t + }), + ] + } + + descendUntilMatchingTicket( + ticket: Ticket, + matcher: (ticket: Ticket) => boolean, + proofResolver: (ticket: Ticket) => CID[] + ): Ticket | null { + if (matcher(ticket)) return ticket + return proofResolver(ticket).reduce( + (acc: Ticket | null, ticketCID) => { + if (acc) return acc + const prf = this.lookupTicketByCID(ticketCID.toString()) + return prf && matcher(prf) ? prf : null + }, + null + ) + } + + findAccessKey( + path: Path.Distinctive, + did: string + ): AccessKeyWithContext | null { + const unwrappedPath = Path.unwrap(path) + const pathKind = Path.kind(path) + + return unwrappedPath.reduce( + (acc: null | AccessKeyWithContext, _p, idx) => { + if (acc) return acc + const partialPath = Path.fromKind(pathKind, ...unwrappedPath.slice(0, idx + 1)) + const key = this.lookupAccessKey(partialPath, did) + return key ? { did, key: key, path: partialPath } : null + }, + null + ) + } + + lookupAccessKey(path: Path.Distinctive, did: string): Uint8Array | null { + const item = this.#cabinet.collection[`${did}/${Path.toPosix(path)}`] + return item?.type === "access-key" ? item.key : null + } + + lookupTicketsByAudience(audience: string): Ticket[] { + return (this.#cabinet.ticketsIndexedByAudience[audience]?.map(t => t.ticket) || []) + } + + lookupTicketsByCategory(category: Category): Ticket[] { + return this.#cabinet.tickets.filter(c => c.category === category).map(c => c.ticket) + } + + lookupTicketByCID(cid: string | CID): Ticket | null { + return this.#cabinet.ticketsIndexedByCID[cid.toString()]?.ticket || null + } + + lookupFileSystemTicket( + path: Path.DistinctivePath, + did: string + ): Promise { + const fsTickets = this.#cabinet.tickets + .filter(t => t.category === "file_system") + .map(t => t.ticket) + + return this.#lookupFileSystemTicket( + fsTickets, + path => this.#authorityClerk.tickets.fileSystem.matcher(path, did), + path + ) + } + + async #lookupFileSystemTicket( + fsTickets: Ticket[], + matcher: (pathSoFar: Path.Distinctive) => (ticket: Ticket) => Promise, + path: Path.DistinctivePath + ): Promise { + const pathParts = Path.unwrap(path) + + const results = await ["", ...pathParts].reduce( + async (acc: Promise, _part, idx): Promise => { + const pathSoFar = Path.fromKind(Path.kind(path), ...(pathParts.slice(0, idx))) + const match = matcher(pathSoFar) + + return [ + ...(await acc), + ...(await fsTickets.reduce( + async (acc: Promise, ticket: Ticket) => { + const list = await acc + if (await match(ticket)) return [...list, ticket] + return list + }, + Promise.resolve([]) + )), + ] + }, + Promise.resolve([]) + ) + + // TODO: Need to sort by ability level, ie. prefer super user over anything else + return results[0] || null + } + + async rootIssuer( + ticket: Ticket, + proofResolver: (ticket: Ticket) => Promise + ): Promise { + return this.rootTicket(ticket, proofResolver).then(r => r.issuer) + } + + async rootTicket( + ticket: Ticket, + proofResolver: (ticket: Ticket) => Promise + ): Promise { + const proofs = await proofResolver(ticket) + + if (proofs[0]) { + const t = this.lookupTicketByCID(proofs[0]) + if (!t) throw new Error(`Missing a proof in the local repository: ${proofs[0]}`) + return this.rootTicket(t, proofResolver) + } + + return ticket + } +} diff --git a/src/repositories/cabinet.ts b/src/repositories/cabinet.ts index 69569b26..1814c946 100644 --- a/src/repositories/cabinet.ts +++ b/src/repositories/cabinet.ts @@ -3,22 +3,22 @@ import * as Uint8Arrays from "uint8arrays" import * as Storage from "../components/storage/implementation.js" import * as Path from "../path/index.js" -import { CID, decodeCID, encodeCID } from "../common/cid.js" +import { AccessKeyWithContext } from "../accessKey.js" +import { decodeCID, encodeCID } from "../common/cid.js" +import { CID } from "../common/cid.js" import { isObject, isString } from "../common/type-checks.js" import Repository, { RepositoryOptions } from "../repository.js" -import { cid as ticketCID } from "../ticket/index.js" -import { Category, Ticket, isCategory } from "../ticket/types.js" +import { Category, Ticket, TicketWithContext, isCategory } from "../ticket/types.js" //////// // 🧩 // //////// export type CabinetItem = - | { type: "access-key"; did: string; key: Uint8Array; path: Path.Distinctive } + | { type: "access-key" } & AccessKeyWithContext | { type: "ticket" } & TicketWithContext export type CabinetCollection = Record -export type TicketWithContext = { category: Category; cid: CID; ticket: Ticket } //////// // 🛠️ // @@ -158,28 +158,33 @@ export class Repo extends Repository { // EXTRA - addTicket(category: Category, ticket: Ticket): Promise { - return this.addTickets(category, [ticket]) + addTicket(category: Category, ticket: Ticket, cidCreator: (ticket: Ticket) => Promise): Promise { + return this.addTickets(category, [ticket], cidCreator) } - async addTickets(category: Category, tickets: Ticket[]): Promise { - const items: Array<{ type: "ticket" } & TicketWithContext> = await Promise.all( + async addTickets(category: Category, tickets: Ticket[], cidCreator: (ticket: Ticket) => Promise): Promise { + const ticketsWithContext: Array<{ type: "ticket" } & TicketWithContext> = await Promise.all( tickets.map(async t => ({ type: "ticket", category, - cid: await ticketCID(t), + cid: await cidCreator(t), ticket: t, })) ) - return this.add(items) + return this.add( + // Only add tickets we don't have yet + ticketsWithContext.filter(t => { + return !this.ticketsIndexedByCID[t.cid.toString()] + }) + ) } - addAccessKey(item: { did: string; key: Uint8Array; path: Path.Distinctive }) { + addAccessKey(item: AccessKeyWithContext) { return this.addAccessKeys([item]) } - addAccessKeys(items: { did: string; key: Uint8Array; path: Path.Distinctive }[]) { + addAccessKeys(items: AccessKeyWithContext[]) { // Delete old access keys matching the same DID and path, // in case we want to make a new file system. items.forEach(item => { diff --git a/src/repositories/names.ts b/src/repositories/names.ts new file mode 100644 index 00000000..c76d0427 --- /dev/null +++ b/src/repositories/names.ts @@ -0,0 +1,69 @@ +import * as Storage from "../components/storage/implementation.js" +import Repository, { RepositoryOptions } from "../repository.js" + +//////// +// 🧩 // +//////// + +/** @internal */ +export type Item = { + name: string + subject: string // ie. the thing that is being named +} + +/** @internal */ +export type Collection = Record + +//////// +// 🛠️ // +//////// + +export function create({ storage }: { storage: Storage.Implementation }): Promise { + return Repo.create({ + storage, + storageName: storage.KEYS.NAMES, + }) +} + +/////////// +// CLASS // +/////////// + +export { Repo as Names } + +export class Repo extends Repository { + private constructor(options: RepositoryOptions) { + super(options) + } + + // IMPLEMENTATION + + emptyCollection() { + return {} + } + + mergeCollections(a: Collection, b: Collection): Collection { + return { + ...a, + ...b, + } + } + + async toCollection(item: Item): Promise { + return { [item.name]: item.subject } + } + + // 🛠️ + + resolveId(id: { did: string } | { name: string }): string | null { + if ("did" in id) { + return id.did + } else { + return this.subject(id.name) + } + } + + subject(name: string): string | null { + return this.collection[name] || null + } +} diff --git a/src/repository.ts b/src/repository.ts index b91efd1c..e9930bca 100644 --- a/src/repository.ts +++ b/src/repository.ts @@ -46,8 +46,8 @@ export default abstract class Repository { return repo } - async add(newItems: I[]): Promise { - const col = await newItems.reduce( + async add(newItems: I[] | I): Promise { + const col = await (Array.isArray(newItems) ? newItems : [newItems]).reduce( async (acc: Promise, item) => this.mergeCollections(await acc, await this.toCollection(item)), Promise.resolve(this.collection) ) diff --git a/src/ticket/index.ts b/src/ticket/index.ts index d9007b96..6b4bb034 100644 --- a/src/ticket/index.ts +++ b/src/ticket/index.ts @@ -1,18 +1,10 @@ -import * as Raw from "multiformats/codecs/raw" -import { sha256 } from "multiformats/hashes/sha2" -import * as Uint8Arrays from "uint8arrays" - -import { CID } from "../common/cid.js" +import { isObject, isString } from "../common/type-checks.js" import { Ticket } from "./types.js" //////// // 🛠️ // //////// -export async function cid(ticket: Ticket): Promise { - const multihash = await sha256.digest( - Uint8Arrays.fromString(ticket.token, "utf8") - ) - - return CID.createV1(Raw.code, multihash) +export function isTicket(ticket: unknown): ticket is Ticket { + return isObject(ticket) && isString(ticket.issuer) && isString(ticket.audience) && isString(ticket.token) } diff --git a/src/ticket/inventory.ts b/src/ticket/inventory.ts deleted file mode 100644 index 61be17c1..00000000 --- a/src/ticket/inventory.ts +++ /dev/null @@ -1,124 +0,0 @@ -import * as Path from "../path/index.js" - -import { CID } from "../common/cid.js" -import { Authority } from "../components.js" -import { Cabinet } from "../repositories/cabinet.js" -import { Ticket } from "./types.js" - -export class Inventory { - #authority: Authority.Implementation - #cabinet: Cabinet - - /** @internal */ - constructor( - authority: Authority.Implementation, - cabinet: Cabinet - ) { - this.#authority = authority - this.#cabinet = cabinet - } - - ///////////// - // LOOKUPS // - ///////////// - - descendUntilMatching( - ticket: Ticket, - matcher: (ticket: Ticket) => boolean, - descend: (ticket: Ticket) => CID[] - ): Ticket | null { - if (matcher(ticket)) return ticket - return descend(ticket).reduce( - (acc: Ticket | null, ticketCID) => { - if (acc) return acc - const prf = this.lookupByCID(ticketCID.toString()) - return prf && matcher(prf) ? prf : null - }, - null - ) - } - - // findMatching( - // matcher: (ticket: Ticket) => boolean, - // descend: (ticket: Ticket) => CID[] - // ): Ticket | null { - // return this.#cabinet.tickets.reduce( - // (acc: Ticket | null, t) => { - // if (acc) return acc - // if (matcher(t.ticket)) return t.ticket - // return this.descendUntilMatching(t.ticket, matcher, descend) - // }, - // null - // ) - // } - - lookupByAudience(audience: string): Ticket[] { - return (this.#cabinet.ticketsIndexedByAudience[audience]?.map(t => t.ticket) || []) - } - - lookupByCID(cid: string): Ticket | null { - return this.#cabinet.ticketsIndexedByCID[cid]?.ticket || null - } - - lookupFileSystemTicket( - path: Path.DistinctivePath, - did: string - ): Ticket | null { - const fsTickets = this.#cabinet.tickets - .filter(t => t.category === "file_system") - .map(t => t.ticket) - - return this.#lookupFileSystemUcan( - fsTickets, - path => this.#authority.clerk.tickets.fileSystem.matcher(path, did), - path - ) - } - - #lookupFileSystemUcan( - fsTickets: Ticket[], - matcher: (pathSoFar: Path.Distinctive) => (ticket: Ticket) => boolean, - path: Path.DistinctivePath - ): Ticket | null { - const pathParts = Path.unwrap(path) - - const results = ["", ...pathParts].reduce( - (acc: Ticket[], _part, idx): Ticket[] => { - const pathSoFar = Path.fromKind(Path.kind(path), ...(pathParts.slice(0, idx))) - - return [ - ...acc, - ...fsTickets.filter( - matcher(pathSoFar) - ), - ] - }, - [] - ) - - // TODO: Need to sort by ability level, ie. prefer super user over anything else - return results[0] || null - } - - rootIssuer( - ticket: Ticket, - proofsResolver: (ticket: Ticket) => CID[] - ): string { - const proofs = proofsResolver(ticket) - - if (proofs.length) { - // Always prefer the first proof. - // TBH, not sure what's best here. - const prf = proofs[0] - const t = this.#cabinet.ticketsIndexedByCID[prf.toString()] - if (!t) throw new Error("Missing a ticket in the inventory") - - return this.rootIssuer( - t.ticket, - proofsResolver - ) - } else { - return ticket.issuer - } - } -} diff --git a/src/ticket/types.ts b/src/ticket/types.ts index 907748d6..4314c643 100644 --- a/src/ticket/types.ts +++ b/src/ticket/types.ts @@ -1,4 +1,6 @@ -const CATEGORIES = ["account", "file_system", "misc"] as const +import { CID } from "../common/cid.js" + +const CATEGORIES = ["account", "agent", "file_system"] as const /** * A ticket is a [UCAN](https://github.com/ucan-wg/spec) or UCAN-like token. @@ -19,3 +21,8 @@ export type Category = typeof CATEGORIES[number] export function isCategory(string: string): string is Category { return CATEGORIES.includes(string as Category) } + +/** + * Ticket with context. + */ +export type TicketWithContext = { category: Category; cid: CID; ticket: Ticket } diff --git a/src/ucan/ts-ucan/common.ts b/src/ucan/ts-ucan/common.ts index 5da52412..03367aca 100644 --- a/src/ucan/ts-ucan/common.ts +++ b/src/ucan/ts-ucan/common.ts @@ -1,5 +1,6 @@ import * as Ucans from "@ucans/core" import * as UcanCaps from "@ucans/core/capability/index" +import * as Raw from "multiformats/codecs/raw" import * as Uint8arrays from "uint8arrays" import * as ECDSA from "iso-signatures/verifiers/ecdsa" @@ -13,6 +14,7 @@ import { DIDKey } from "iso-did/key" import * as AgentDID from "../../agent/did.js" import * as Agent from "../../components/agent/implementation.js" +import { sha256 } from "multiformats/hashes/sha2" import { CID, decodeCID } from "../../common/cid.js" import { Ticket } from "../../ticket/types.js" import { BuildParams, Keypair } from "./types.js" @@ -88,7 +90,7 @@ export function encode(ucan: Ucan): string { export async function isValid(agent: Agent.Implementation, ucan: Ucan): Promise { const plugs = await plugins() - const jwtAlg = await agent.ucanAlgorithm() + const jwtAlg = agent.ucanAlgorithm() const signature = Uint8arrays.fromString(ucan.signature, "base64url") const signedData = Uint8arrays.fromString(ucan.signedData, "utf8") @@ -104,7 +106,7 @@ export async function keyPair(agent: Agent.Implementation): Promise { return { did: () => did, - jwtAlg: await agent.ucanAlgorithm(), + jwtAlg: agent.ucanAlgorithm(), sign: data => agent.sign(data), } } @@ -113,6 +115,14 @@ export async function plugins(): Promise { return new Plugins([], {}) } +export async function ticketCID(ticket: Ticket): Promise { + const multihash = await sha256.digest( + Uint8arrays.fromString(ticket.token, "utf8") + ) + + return CID.createV1(Raw.code, multihash) +} + export function ticketProofResolver(ticket: Ticket): CID[] { const ucan = decode(ticket.token) return ucan.payload.prf.map(decodeCID) diff --git a/tests/helpers/components.ts b/tests/helpers/components.ts index 95967d9a..6f51bd6b 100644 --- a/tests/helpers/components.ts +++ b/tests/helpers/components.ts @@ -4,7 +4,8 @@ import { sha256 } from "multiformats/hashes/sha2" import * as LocalAccount from "../../src/components/account/local.js" import * as WebCryptoAgent from "../../src/components/agent/web-crypto-api.js" -import * as TsUcanAuthority from "../../src/components/authority/ts-ucan.js" +import * as BrowserUrlAuthority from "../../src/components/authority/browser-url.js" +import * as DefaultClerk from "../../src/components/clerk/default.js" import * as DOH from "../../src/components/dns/dns-over-https/cloudflare-google.js" import * as WebCryptoIdentifier from "../../src/components/identifier/web-crypto-api.js" import * as ProperManners from "../../src/components/manners/default.js" @@ -18,6 +19,7 @@ import { Agent, Authority, Channel, + Clerk, Components, DNS, Depot, @@ -96,10 +98,8 @@ const manners: Manners.Implementation = { // CHANNEL // ///////////// -export type ChannelContext = [] - -const channel: Channel.Implementation = { - establish: (options: ChannelOptions) => { +const channel: Channel.Implementation = { + establish: (options: ChannelOptions) => { throw new Error("Channels are not implemented for tests") }, } @@ -136,13 +136,27 @@ const identifier: Identifier.Implementation = await WebCryptoIdentifier.implemen // IDENTIFIER // //////////////// -const authority: Authority.Implementation = TsUcanAuthority.implementation(identifier) +const authority: Authority.Implementation< + BrowserUrlAuthority.ProvideResponse, + BrowserUrlAuthority.RequestResponse +> = BrowserUrlAuthority.implementation() + +/////////// +// CLERK // +/////////// + +const clerk: Clerk.Implementation = DefaultClerk.implementation() //////// // 🛳 // //////// -const components: Components = { +const components: Components< + LocalAccount.Annex, + BrowserUrlAuthority.ProvideResponse, + BrowserUrlAuthority.RequestResponse +> = { + clerk, depot, manners, storage, @@ -154,4 +168,4 @@ const components: Components = { authority, } -export { account, agent, authority, channel, components, depot, dns, identifier, manners, storage } +export { account, agent, authority, channel, clerk, components, depot, dns, identifier, manners, storage } diff --git a/tsconfig.json b/tsconfig.json index cbf3dc2a..8f786a16 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "moduleResolution": "node16", "target": "es2022", - "module": "es2022", + "module": "node16", "lib": [ "dom", "es2022",