From c117ef63ba19652d095d4e2953a5bc9ff0728cd6 Mon Sep 17 00:00:00 2001 From: haad Date: Thu, 13 Apr 2023 09:15:27 +0300 Subject: [PATCH] Add jsdocs --- .gitignore | 3 + Makefile | 1 + package-lock.json | 371 +++++++++++++++++++++++++- package.json | 4 +- src/access-controllers/index.js | 1 + src/access-controllers/ipfs.js | 4 + src/access-controllers/orbitdb.js | 4 + src/address.js | 1 + src/database.js | 6 + src/db/documents.js | 5 + src/db/events.js | 5 + src/db/keyvalue-indexed.js | 5 + src/db/keyvalue.js | 5 + src/identities/identities.js | 1 + src/identities/index.js | 1 + src/identities/providers/publickey.js | 4 + src/manifest.js | 1 + src/oplog/entry.js | 10 +- src/oplog/log.js | 108 +++++--- src/orbitdb.js | 1 + src/storage/composed.js | 5 + src/storage/index.js | 1 + src/storage/ipfs-block.js | 4 + src/storage/level.js | 4 + src/storage/lru.js | 4 + src/storage/memory.js | 4 + src/sync.js | 147 ++++++++-- 27 files changed, 649 insertions(+), 61 deletions(-) diff --git a/.gitignore b/.gitignore index 3642bfe..0266286 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ dist/ # Don't track Visual Studio Code settings .vscode + +# JSDoc output folder +out/ \ No newline at end of file diff --git a/Makefile b/Makefile index baea637..4856553 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ build: test clean: rm -rf node_modules/ rm -rf coverage/ + rm -rf out/ rm -f test/browser/bundle.js* clean-dependencies: clean diff --git a/package-lock.json b/package-lock.json index aebb768..1ea5c1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "fs-extra": "^11.1.0", "ipfs-core": "^0.18.0", "it-all": "^2.0.0", + "jsdoc": "^4.0.2", "mocha": "^10.2.0", "mocha-headless-chrome": "^4.0.0", "open-cli": "^7.1.0", @@ -392,7 +393,6 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", "dev": true, - "peer": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -1194,6 +1194,18 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@jsdoc/salty": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", + "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", @@ -2975,12 +2987,34 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", + "dev": true + }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", "dev": true }, + "node_modules/@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dev": true, + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "dev": true + }, "node_modules/@types/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", @@ -3770,6 +3804,12 @@ "npm": ">=7.0.0" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, "node_modules/bplist-parser": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", @@ -4130,6 +4170,18 @@ "node": ">=6" } }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/cborg": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/cborg/-/cborg-1.10.1.tgz", @@ -5063,6 +5115,15 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/envinfo": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", @@ -8468,12 +8529,59 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, "node_modules/jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", "dev": true }, + "node_modules/jsdoc": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", + "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -8585,6 +8693,15 @@ "node": ">=0.10.0" } }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, "node_modules/level": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", @@ -8771,6 +8888,15 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, "node_modules/load-json-file": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", @@ -9010,6 +9136,50 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, "node_modules/menoetius": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/menoetius/-/menoetius-0.0.3.tgz", @@ -10939,6 +11109,15 @@ "node": ">=0.10.0" } }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -12177,6 +12356,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, "node_modules/uint8-varint": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-1.0.6.tgz", @@ -12266,6 +12451,12 @@ "ieee754": "^1.1.13" } }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, "node_modules/undici": { "version": "5.21.0", "resolved": "https://registry.npmjs.org/undici/-/undici-5.21.0.tgz", @@ -12854,6 +13045,12 @@ "node": ">=4.0" } }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true + }, "node_modules/xmlhttprequest-ssl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", @@ -13248,8 +13445,7 @@ "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", - "dev": true, - "peer": true + "dev": true }, "@babel/template": { "version": "7.20.7", @@ -13939,6 +14135,15 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "@jsdoc/salty": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", + "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", + "dev": true, + "requires": { + "lodash": "^4.17.21" + } + }, "@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", @@ -15407,12 +15612,34 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", + "dev": true + }, "@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", "dev": true }, + "@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dev": true, + "requires": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "dev": true + }, "@types/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", @@ -16040,6 +16267,12 @@ } } }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, "bplist-parser": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", @@ -16282,6 +16515,15 @@ "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==" }, + "catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, "cborg": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/cborg/-/cborg-1.10.1.tgz", @@ -16959,6 +17201,12 @@ "tapable": "^2.2.0" } }, + "entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true + }, "envinfo": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", @@ -19455,12 +19703,52 @@ "argparse": "^2.0.1" } }, + "js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "requires": { + "xmlcreate": "^2.0.4" + } + }, "jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", "dev": true }, + "jsdoc": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", + "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "dev": true, + "requires": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -19552,6 +19840,15 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, "level": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", @@ -19702,6 +19999,15 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, "load-json-file": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", @@ -19878,6 +20184,38 @@ "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true }, + "markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "requires": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + }, + "markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "requires": {} + }, + "marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, "menoetius": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/menoetius/-/menoetius-0.0.3.tgz", @@ -21240,6 +21578,15 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, + "requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, + "requires": { + "lodash": "^4.17.21" + } + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -22113,6 +22460,12 @@ "is-typed-array": "^1.1.9" } }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, "uint8-varint": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-1.0.6.tgz", @@ -22175,6 +22528,12 @@ } } }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, "undici": { "version": "5.21.0", "resolved": "https://registry.npmjs.org/undici/-/undici-5.21.0.tgz", @@ -22597,6 +22956,12 @@ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "dev": true }, + "xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true + }, "xmlhttprequest-ssl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", diff --git a/package.json b/package.json index 820f25e..65457d8 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "fs-extra": "^11.1.0", "ipfs-core": "^0.18.0", "it-all": "^2.0.0", + "jsdoc": "^4.0.2", "mocha": "^10.2.0", "mocha-headless-chrome": "^4.0.0", "open-cli": "^7.1.0", @@ -61,7 +62,7 @@ "build:examples": "webpack --config conf/webpack.example.config.js", "build:dist": "webpack --config conf/webpack.config.js", "build:debug": "webpack --config conf/webpack.debug.config.js", - "build:docs/toc": "echo 'TODO'", + "build:docs": "jsdoc -r src/**", "build:tests": "rm -f test/browser/bundle.js* && webpack --config ./conf/webpack.tests.config.js", "prepublishOnly": "npm run build", "lint": "standard --env=mocha", @@ -74,6 +75,7 @@ "mocha" ], "ignore": [ + "out/**", "examples/**", "test/fixtures/**", "test/browser/**" diff --git a/src/access-controllers/index.js b/src/access-controllers/index.js index 2313a40..2670c43 100644 --- a/src/access-controllers/index.js +++ b/src/access-controllers/index.js @@ -1,3 +1,4 @@ +/** @module AccessControllers */ import IPFSAccessController from './ipfs.js' import OrbitDBAccessController from './orbitdb.js' diff --git a/src/access-controllers/ipfs.js b/src/access-controllers/ipfs.js index ba5fa85..0a13c2d 100644 --- a/src/access-controllers/ipfs.js +++ b/src/access-controllers/ipfs.js @@ -1,3 +1,7 @@ +/** + * @namespace AccessControllers-IPFS + * @memberof module:AccessControllers + */ import { IPFSBlockStorage, LRUStorage, ComposedStorage } from '../storage/index.js' import * as Block from 'multiformats/block' import * as dagCbor from '@ipld/dag-cbor' diff --git a/src/access-controllers/orbitdb.js b/src/access-controllers/orbitdb.js index 4d882ff..08c04e5 100644 --- a/src/access-controllers/orbitdb.js +++ b/src/access-controllers/orbitdb.js @@ -1,3 +1,7 @@ +/** + * @namespace AccessControllers-OrbitDB + * @memberof module:AccessControllers + */ import ensureACAddress from '../utils/ensure-ac-address.js' import IPFSAccessController from './ipfs.js' diff --git a/src/address.js b/src/address.js index 74eb5a7..b43f2e0 100644 --- a/src/address.js +++ b/src/address.js @@ -1,3 +1,4 @@ +/** @namespace Address */ import { CID } from 'multiformats/cid' import { base58btc } from 'multiformats/bases/base58' import { posixJoin } from './utils/path-join.js' diff --git a/src/database.js b/src/database.js index c157220..01cb6f1 100644 --- a/src/database.js +++ b/src/database.js @@ -1,3 +1,4 @@ +/** @module Database */ import { EventEmitter } from 'events' import PQueue from 'p-queue' import Sync from './sync.js' @@ -9,6 +10,11 @@ const defaultReferencesCount = 16 const defaultCacheSize = 1000 const Database = async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate }) => { + /** + * @namespace module:Database~Database + * @description The instance returned by {@link module:Database}. + */ + directory = pathJoin(directory || './orbitdb', `./${address}/`) meta = meta || {} referencesCount = referencesCount || defaultReferencesCount diff --git a/src/db/documents.js b/src/db/documents.js index 98ffc71..e21ec94 100644 --- a/src/db/documents.js +++ b/src/db/documents.js @@ -1,3 +1,8 @@ +/** + * @namespace Database-Documents + * @memberof module:Database + * @description Documents Database + */ import Database from '../database.js' const DefaultOptions = { indexBy: '_id' } diff --git a/src/db/events.js b/src/db/events.js index 12268e3..85f193a 100644 --- a/src/db/events.js +++ b/src/db/events.js @@ -1,3 +1,8 @@ +/** + * @namespace Database-Events + * @memberof module:Database + * @description Events Database + */ import Database from '../database.js' const Events = () => async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate }) => { diff --git a/src/db/keyvalue-indexed.js b/src/db/keyvalue-indexed.js index 383fb3f..d6a6ec2 100644 --- a/src/db/keyvalue-indexed.js +++ b/src/db/keyvalue-indexed.js @@ -1,3 +1,8 @@ +/** + * @namespace Database-KeyValueIndexed + * @memberof module:Database + * @description KeyValueIndexed Database + */ import { KeyValue } from './index.js' import LevelStorage from '../storage/level.js' import pathJoin from '../utils/path-join.js' diff --git a/src/db/keyvalue.js b/src/db/keyvalue.js index 1fbb7ba..b8b8962 100644 --- a/src/db/keyvalue.js +++ b/src/db/keyvalue.js @@ -1,3 +1,8 @@ +/** + * @namespace Database-KeyValue + * @memberof module:Database + * @description KeyValue Database + */ import Database from '../database.js' const KeyValue = () => async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate }) => { diff --git a/src/identities/identities.js b/src/identities/identities.js index 5b92e8b..50e5ce5 100644 --- a/src/identities/identities.js +++ b/src/identities/identities.js @@ -1,3 +1,4 @@ +/** @module Identities */ import Identity, { isIdentity, isEqual, decodeIdentity } from './identity.js' import { PublicKeyIdentityProvider } from './providers/index.js' // import DIDIdentityProvider from './identity-providers/did.js' diff --git a/src/identities/index.js b/src/identities/index.js index efaeaca..688b441 100644 --- a/src/identities/index.js +++ b/src/identities/index.js @@ -1,3 +1,4 @@ +/** @module IdentityProviders */ export { default as Identities, addIdentityProvider, diff --git a/src/identities/providers/publickey.js b/src/identities/providers/publickey.js index ed2a068..cc4c468 100644 --- a/src/identities/providers/publickey.js +++ b/src/identities/providers/publickey.js @@ -1,3 +1,7 @@ +/** + * @namespace module:IdentityProviders.IdentityProviders-PublicKey + * @description PublicKey Identity Provider + */ import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import IdentityProvider from './interface.js' import { signMessage, verifyMessage } from '../../key-store.js' diff --git a/src/manifest.js b/src/manifest.js index cb2dd6e..b4308b1 100644 --- a/src/manifest.js +++ b/src/manifest.js @@ -1,3 +1,4 @@ +/** @namespace Manifest */ import * as Block from 'multiformats/block' import * as dagCbor from '@ipld/dag-cbor' import { sha256 } from 'multiformats/hashes/sha2' diff --git a/src/oplog/entry.js b/src/oplog/entry.js index 53b5f10..46ae6af 100644 --- a/src/oplog/entry.js +++ b/src/oplog/entry.js @@ -1,14 +1,14 @@ +/** + * @namespace module:Log~Entry + * @memberof module:Log + * @description Log Entry + */ import Clock from './clock.js' import * as Block from 'multiformats/block' import * as dagCbor from '@ipld/dag-cbor' import { sha256 } from 'multiformats/hashes/sha2' import { base58btc } from 'multiformats/bases/base58' -/* - * @description - * A Log entry - */ - const codec = dagCbor const hasher = sha256 const hashStringEncoding = base58btc diff --git a/src/oplog/log.js b/src/oplog/log.js index 6823e06..915a618 100644 --- a/src/oplog/log.js +++ b/src/oplog/log.js @@ -1,3 +1,12 @@ +/** + * @module Log + * @description + * Log is a verifiable, append-only log CRDT. + * + * Implemented as a Merkle-CRDT as per the paper: + * "Merkle-CRDTs: Merkle-DAGs meet CRDTs" + * https://arxiv.org/abs/2004.00107 + */ import LRU from 'lru' import Entry from './entry.js' import Clock from './clock.js' @@ -25,17 +34,10 @@ const DefaultAccessController = async () => { } } -/** - * @description - * Log is a verifiable, append-only log CRDT. - * - * Implemented as a Merkle-CRDT as per the paper: - * "Merkle-CRDTs: Merkle-DAGs meet CRDTs" - * https://arxiv.org/abs/2004.00107 - */ - /** * Create a new Log instance + + * @function * @param {IPFS} ipfs An IPFS instance * @param {Object} identity Identity (https://github.com/orbitdb/orbit-db-identity-provider/blob/master/src/identity.js) * @param {Object} options @@ -45,9 +47,16 @@ const DefaultAccessController = async () => { * @param {Array} options.heads Set the heads of the log * @param {Clock} options.clock Set the clock of the log * @param {Function} options.sortFn The sort function - by default LastWriteWins - * @return {Log} The log instance + * @return {module:Log~Log} sync An instance of Log + * @memberof module:Log + * @instance */ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStorage, indexStorage, sortFn } = {}) => { + /** + * @namespace Log + * @description The instance returned by {@link module:Log}. + */ + if (identity == null) { throw new Error('Identity is required') } @@ -72,6 +81,8 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora /** * Returns the clock of the log. * @returns {Clock} + * @memberof module:Log~Log + * @instance */ const clock = async () => { // Find the latest clock from the heads @@ -80,8 +91,11 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora } /** - * Returns an array of entries - * @returns {Array} + * Returns the current heads of the log + * + * @returns {Array} + * @memberof module:Log~Log + * @instance */ const heads = async () => { const res = await _heads.all() @@ -89,8 +103,11 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora } /** - * Returns the values in the log. - * @returns {Promise>} + * Returns all entries in the log + * + * @returns {Array} + * @memberof module:Log~Log + * @instance */ const values = async () => { const values = [] @@ -101,9 +118,12 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora } /** - * Retrieve an entry. - * @param {string} [hash] The hash of the entry to retrieve - * @returns {Promise} + * Retrieve an entry + * + * @param {string} hash The hash of the entry to retrieve + * @returns {module:Log~Entry} + * @memberof module:Log~Log + * @instance */ const get = async (hash) => { const bytes = await _entries.get(hash) @@ -120,9 +140,14 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora } /** - * Append an new entry to the log. + * Append an new entry to the log + * * @param {data} data Payload to add to the entry - * @return {Promise} Entry that was appended + * @param {Object} options + * @param {Integer} options.referencesCount TODO + * @return {module:Log~Entry} Entry that was appended + * @memberof module:Log~Log + * @instance */ const append = async (data, options = { referencesCount: 0 }) => { // 1. Prepare entry @@ -169,10 +194,14 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora * * Joins another log into this one. * - * @param {Log} log Log to join with this Log - * @returns {Promise} This Log instance + * @param {module:Log~Log} log Log to join with this Log + * * @example + * * await log1.join(log2) + * + * @memberof module:Log~Log + * @instance */ const join = async (log) => { if (!log) { @@ -193,10 +222,14 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora /** * Join an entry into a log. * - * @param {Entry} entry Entry to join with this Log - * @returns {Promise} This Log instance + * @param {module:Log~Entry} entry Entry to join with this Log + * * @example + * * await log.join(entry) + * + * @memberof module:Log~Log + * @instance */ const joinEntry = async (entry) => { const { hash } = entry @@ -246,6 +279,8 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora /** * TODO + * @memberof module:Log~Log + * @instance */ const traverse = async function * (rootEntries, shouldStopFn) { // By default, we don't stop traversal and traverse @@ -309,15 +344,15 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora } } - /* + /** * Async iterator over the log entries * * @param {Object} options - * @param {amount} options.amount Number of entried to return - * @param {string|Array} options.gt Beginning hash of the iterator, non-inclusive - * @param {string|Array} options.gte Beginning hash of the iterator, inclusive - * @param {string|Array} options.lt Ending hash of the iterator, non-inclusive - * @param {string|Array} options.lte Ending hash of the iterator, inclusive + * @param {amount} options.amount Number of entried to return. Default: return all entries. + * @param {string} options.gt Beginning hash of the iterator, non-inclusive + * @param {string} options.gte Beginning hash of the iterator, inclusive + * @param {string} options.lt Ending hash of the iterator, non-inclusive + * @param {string} options.lte Ending hash of the iterator, inclusive * @returns {Symbol.asyncIterator} Iterator object of log entries * * @examples @@ -339,7 +374,8 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora * } * })() * - * + * @memberof module:Log~Log + * @instance */ const iterator = async function * ({ amount = -1, gt, gte, lt, lte } = {}) { // TODO: write comments on how the iterator algorithm works @@ -412,12 +448,22 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora } } + /** + * Clear all entries from the log and the underlying storages + * @memberof module:Log~Log + * @instance + */ const clear = async () => { await _index.clear() await _heads.clear() await _entries.clear() } + /** + * Close the log and underlying storages + * @memberof module:Log~Log + * @instance + */ const close = async () => { await _index.close() await _heads.close() @@ -428,6 +474,8 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora * Check if an object is a Log. * @param {Log} obj * @returns {boolean} + * @memberof module:Log~Log + * @instance */ const isLog = (obj) => { return obj && obj.id !== undefined && diff --git a/src/orbitdb.js b/src/orbitdb.js index e7eedf4..4d2e3e3 100644 --- a/src/orbitdb.js +++ b/src/orbitdb.js @@ -1,3 +1,4 @@ +/** @module OrbitDB */ import { Events, KeyValue, Documents } from './db/index.js' import KeyStore from './key-store.js' import { Identities } from './identities/index.js' diff --git a/src/storage/composed.js b/src/storage/composed.js index 5d3c45e..6cf8a4f 100644 --- a/src/storage/composed.js +++ b/src/storage/composed.js @@ -1,3 +1,8 @@ +/** + * @namespace Storage-Composed + * @memberof module:Storage + */ + // Compose storages: // const storage1 = await ComposedStorage(await LRUStorage(), await LevelStorage()) // const storage2 = await ComposedStorage(storage1, await IPFSBlockStorage()) diff --git a/src/storage/index.js b/src/storage/index.js index 04ee472..5d40653 100644 --- a/src/storage/index.js +++ b/src/storage/index.js @@ -1,3 +1,4 @@ +/** @module Storage */ export { default as ComposedStorage } from './composed.js' export { default as IPFSBlockStorage } from './ipfs-block.js' export { default as LevelStorage } from './level.js' diff --git a/src/storage/ipfs-block.js b/src/storage/ipfs-block.js index ade6d20..8f44dd3 100644 --- a/src/storage/ipfs-block.js +++ b/src/storage/ipfs-block.js @@ -1,3 +1,7 @@ +/** + * @namespace Storage-IPFS + * @memberof module:Storage + */ import { CID } from 'multiformats/cid' import { base58btc } from 'multiformats/bases/base58' diff --git a/src/storage/level.js b/src/storage/level.js index 663b29f..1717a60 100644 --- a/src/storage/level.js +++ b/src/storage/level.js @@ -1,3 +1,7 @@ +/** + * @namespace Storage-Level + * @memberof module:Storage + */ import { Level } from 'level' const defaultValueEncoding = 'view' diff --git a/src/storage/lru.js b/src/storage/lru.js index e6f0ff3..2998dbd 100644 --- a/src/storage/lru.js +++ b/src/storage/lru.js @@ -1,3 +1,7 @@ +/** + * @namespace Storage-LRU + * @memberof module:Storage + */ import LRU from 'lru' const defaultSize = 1000000 diff --git a/src/storage/memory.js b/src/storage/memory.js index a1ddbf7..8f6796b 100644 --- a/src/storage/memory.js +++ b/src/storage/memory.js @@ -1,3 +1,7 @@ +/** + * @namespace Storage-Memory + * @memberof module:Storage + */ const MemoryStorage = async () => { let memory = {} diff --git a/src/sync.js b/src/sync.js index 6766749..de934bc 100644 --- a/src/sync.js +++ b/src/sync.js @@ -7,41 +7,105 @@ import pathJoin from './utils/path-join.js' const DefaultTimeout = 30000 // 30 seconds /** + * @module Sync * @description - * Syncs an append-only, conflict-free replicated data type (CRDT) log between - * multiple peers. + * The Sync Protocol for OrbitDB synchronizes the database operations {@link module:Log} between multiple peers. * - * The sync protocol synchronizes heads between multiple peers, both during - * startup and also when new entries are appended to the log. + * The Sync Protocol sends and receives heads between multiple peers, + * both when opening a database and when a database is updated, ie. + * new entries are appended to the log. * - * When Sync is started, peers "dial" each other using libp2p's custom protocol - * handler and initiate the exchange of heads each peer currently has. Once - * initial sync has completed, peers notify one another of updates to heads - * using pubsub "subscribe" with the same log.id topic. A peer with new heads - * can broadcast changes to other peers using pubsub "publish". Peers - * subscribed to the same topic will then be notified and will update their - * heads accordingly. + * When Sync is started, a peer subscribes to a pubsub topic of the log's id. + * Upon subscribing to the topic, peers already connected to the topic receive + * the subscription message and "dial" the subscribing peer using a libp2p + * custom protocol. Once connected to the subscribing peer on a direct peer-to-peer + * connection, the dialing peer and the subscribing peer exchange the heads of the Log + * each peer currently has. Once completed, the peers have the same "local state". * - * The sync protocol only guarantees that the message is published; it does not - * guarantee the order in which messages are received or even that the message - * is recieved at all. The sync protocol only guarantees that heads will - * eventually reach consistency between all peers with the same address. + * Once the initial sync has completed, peers notify one another of updates to the + * log, ie. updates to the database, using the initially opened pubsub topic subscription. + * A peer with new heads broadcasts changes to other peers by publishing the updated heads + * to the pubsub topic. Peers subscribed to the same topic will then receive the update and + * will update their log's state, the heads, accordingly. + * + * The Sync Protocol is eventually consistent. It guarantees that once all messages + * have been sent and received, peers will observe the same log state and values. + * The Sync Protocol does not guarantee the order in which messages are received or + * even that a message is recieved at all, nor any timing on when messages are received. + * + * Note that the Sync Protocol does not retrieve the full log when synchronizing the + * heads. Rather only the "latest entries" in the log, the heads, are exchanged. In order + * to retrieve the full log and each entry, the user would call the log.traverse() or + * log.iterator() functions, which go through the log and retrieve each missing + * log entry from IPFS. + * + * @example + * // Using defaults + * const sync = await Sync({ ipfs, log, onSynced: (peerId, heads) => ... }) + * + * @example + * // Using all parameters + * const sync = await Sync({ ipfs, log, events, onSynced: (peerId, heads) => ..., start: false }) + * sync.events.on('join', (peerId, heads) => ...) + * sync.events.on('leave', (peerId) => ...) + * sync.events.on('error', (err) => ...) + * await sync.start() */ /** * Creates a Sync instance for sychronizing logs between multiple peers. + * + * @function * @param {Object} params One or more parameters for configuring Sync. - * @param {IPFS} params.ipfs An IPFS instance. Used for synchronizing peers. - * @param {Log} params.log The Log instance to sync. - * @param {Object} params.events An event emitter. Defaults to an instance of - * EventEmitter. Events emitted are 'join', 'error' and 'leave'. - * @param {Function} params.onSynced A function that is called after the peer + * @param {IPFS} params.ipfs An IPFS instance. + * @param {Log} params.log The log instance to sync. + * @param {EventEmitter} [params.events] An event emitter to use. Events emitted are 'join', 'leave' and 'error'. If the parameter is not provided, an EventEmitter will be created. + * @param {onSynced} [params.onSynced] A callback function that is called after the peer * has received heads from another peer. - * @param {Boolean} params.start True if sync should start automatically, false + * @param {Boolean} [params.start] True if sync should start automatically, false * otherwise. Defaults to true. - * @return {Sync} The Sync protocol instance. + * @return {module:Sync~Sync} sync An instance of the Sync Protocol. + * @memberof module:Sync + * @instance */ const Sync = async ({ ipfs, log, events, onSynced, start, timeout }) => { + /** + * @namespace module:Sync~Sync + * @description The instance returned by {@link module:Sync}. + */ + + /** + * Callback function when new heads have been received from other peers. + * @callback module:Sync~onSynced + * @param {PeerID} peerId PeerID of the peer who we received heads from + * @param {Entry[]} heads An array of Log entries + */ + + /** + * Event fired when new heads have been received from other peers. + * @event module:Sync~Sync#join + * @param {PeerID} peerId PeerID of the peer who we received heads from + * @param {Entry[]} heads An array of Log entries + * @example + * sync.events.on('join', (peerID, heads) => ...) + */ + + /** + * Event fired when a peer leaves the sync protocol. + * @event module:Sync~Sync#leave + * @param {PeerID} peerId PeerID of the peer who we received heads from + * @example + * sync.events.on('leave', (peerID) => ...) + */ + + /** + * Event fired when an error occurs. + * @event module:Sync~Sync#error + * @param {Error} error The error that occured + * @example + * sync.events.on('error', (error) => ...) + */ + if (!ipfs) throw new Error('An instance of ipfs is required.') if (!log) throw new Error('An instance of log is required.') @@ -49,9 +113,29 @@ const Sync = async ({ ipfs, log, events, onSynced, start, timeout }) => { const headsSyncAddress = pathJoin('/orbitdb/heads/', address) const queue = new PQueue({ concurrency: 1 }) + + /** + * Set of currently connected peers for the log for this Sync instance. + * @name peers + * @†ype Set + * @return Set set of PeerIDs + * @memberof module:Sync~Sync + * @instance + */ const peers = new Set() + /** + * Event emitter that emits updates. + * @name events + * @†ype EventEmitter + * @fires join when a peer has connected and heads were exchanged + * @fires leave when a peer disconnects + * @fires error when an error occurs + * @memberof module:Sync~Sync + * @instance + */ events = events || new EventEmitter() + timeout = timeout || DefaultTimeout let started = false @@ -145,12 +229,25 @@ const Sync = async ({ ipfs, log, events, onSynced, start, timeout }) => { queue.add(task) } + /** + * Add a log entry to the Sync Protocol to be sent to peers. + * @function add + * @param {Entry} entry Log entry + * @memberof module:Sync~Sync + * @instance + */ const add = async (entry) => { if (started) { await ipfs.pubsub.publish(address, entry.bytes) } } + /** + * Stop the Sync Protocol. + * @function stop + * @memberof module:Sync~Sync + * @instance + */ const stopSync = async () => { if (started) { await queue.onIdle() @@ -162,6 +259,12 @@ const Sync = async ({ ipfs, log, events, onSynced, start, timeout }) => { } } + /** + * Start the Sync Protocol. + * @function start + * @memberof module:Sync~Sync + * @instance + */ const startSync = async () => { if (!started) { // Exchange head entries with peers when connected