mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-16 09:36:00 +00:00
Fixed:
- No longer unexpected `Unhandled Rejections` during P2P operations (waiting acceptance). CLI new features - P2P sync has been implemented.
This commit is contained in:
Generated
+491
-12
@@ -20,6 +20,7 @@
|
|||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2",
|
||||||
"idb": "^8.0.3",
|
"idb": "^8.0.3",
|
||||||
"minimatch": "^10.2.2",
|
"minimatch": "^10.2.2",
|
||||||
|
"node-datachannel": "^0.32.1",
|
||||||
"octagonal-wheels": "^0.1.45",
|
"octagonal-wheels": "^0.1.45",
|
||||||
"pouchdb-adapter-leveldb": "^9.0.0",
|
"pouchdb-adapter-leveldb": "^9.0.0",
|
||||||
"qrcode-generator": "^1.4.4",
|
"qrcode-generator": "^1.4.4",
|
||||||
@@ -7245,6 +7246,12 @@
|
|||||||
"url": "https://paulmillr.com/funding/"
|
"url": "https://paulmillr.com/funding/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chownr": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/cjs-module-lexer": {
|
"node_modules/cjs-module-lexer": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
|
||||||
@@ -7595,6 +7602,21 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/decompress-response": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mimic-response": "^3.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/deep-eql": {
|
"node_modules/deep-eql": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
|
||||||
@@ -7604,6 +7626,15 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/deep-extend": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/deep-is": {
|
"node_modules/deep-is": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||||
@@ -8092,7 +8123,6 @@
|
|||||||
"version": "1.4.5",
|
"version": "1.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||||
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"once": "^1.4.0"
|
"once": "^1.4.0"
|
||||||
@@ -8890,6 +8920,15 @@
|
|||||||
"bare-events": "^2.7.0"
|
"bare-events": "^2.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expand-template": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
|
||||||
|
"license": "(MIT OR WTFPL)",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expect-type": {
|
"node_modules/expect-type": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
||||||
@@ -9204,6 +9243,12 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-constants": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fs-extra": {
|
"node_modules/fs-extra": {
|
||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||||
@@ -9468,6 +9513,12 @@
|
|||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/github-from-package": {
|
||||||
|
"version": "0.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||||
|
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "13.0.6",
|
"version": "13.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||||
@@ -9909,6 +9960,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/ini": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/interface-datastore": {
|
"node_modules/interface-datastore": {
|
||||||
"version": "8.3.2",
|
"version": "8.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/interface-datastore/-/interface-datastore-8.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/interface-datastore/-/interface-datastore-8.3.2.tgz",
|
||||||
@@ -11649,6 +11706,18 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mimic-response": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "10.2.4",
|
"version": "10.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
|
||||||
@@ -11711,6 +11780,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/mkdirp-classic": {
|
||||||
|
"version": "0.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||||
|
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/modern-tar": {
|
"node_modules/modern-tar": {
|
||||||
"version": "0.7.5",
|
"version": "0.7.5",
|
||||||
"resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.5.tgz",
|
||||||
@@ -11834,6 +11909,12 @@
|
|||||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/napi-build-utils": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/napi-macros": {
|
"node_modules/napi-macros": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz",
|
||||||
@@ -11855,6 +11936,31 @@
|
|||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-abi": {
|
||||||
|
"version": "3.88.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.88.0.tgz",
|
||||||
|
"integrity": "sha512-At6b4UqIEVudaqPsXjmUO1r/N5BUr4yhDGs5PkBE8/oG5+TfLPhFechiskFsnT6Ql0VfUXbalUUCbfXxtj7K+w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"semver": "^7.3.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-datachannel": {
|
||||||
|
"version": "0.32.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-datachannel/-/node-datachannel-0.32.1.tgz",
|
||||||
|
"integrity": "sha512-r4UdtA0lCsz6XrG84pJ6lntAyw/MHpmBOhEkg5UQcmWTEpANqCPkMos6rj/QZDdq3GBUsdI/wst5acwWUiibCA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MPL 2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"prebuild-install": "^7.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
||||||
@@ -12053,7 +12159,6 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
@@ -13028,6 +13133,119 @@
|
|||||||
"integrity": "sha512-fXqsVn+rmlPtxaAIGaQP5TkiaT39OMwvMk+ScLLtHrmfXD2KBO6fe/qBl38N/rpTn0h/A058dPN4fLAHt550zA==",
|
"integrity": "sha512-fXqsVn+rmlPtxaAIGaQP5TkiaT39OMwvMk+ScLLtHrmfXD2KBO6fe/qBl38N/rpTn0h/A058dPN4fLAHt550zA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/prebuild-install": {
|
||||||
|
"version": "7.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
|
||||||
|
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
|
||||||
|
"deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^2.0.0",
|
||||||
|
"expand-template": "^2.0.3",
|
||||||
|
"github-from-package": "0.0.0",
|
||||||
|
"minimist": "^1.2.3",
|
||||||
|
"mkdirp-classic": "^0.5.3",
|
||||||
|
"napi-build-utils": "^2.0.0",
|
||||||
|
"node-abi": "^3.3.0",
|
||||||
|
"pump": "^3.0.0",
|
||||||
|
"rc": "^1.2.7",
|
||||||
|
"simple-get": "^4.0.0",
|
||||||
|
"tar-fs": "^2.0.0",
|
||||||
|
"tunnel-agent": "^0.6.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"prebuild-install": "bin.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prebuild-install/node_modules/bl": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer": "^5.5.0",
|
||||||
|
"inherits": "^2.0.4",
|
||||||
|
"readable-stream": "^3.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prebuild-install/node_modules/buffer": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.1.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prebuild-install/node_modules/detect-libc": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prebuild-install/node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prebuild-install/node_modules/tar-fs": {
|
||||||
|
"version": "2.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
|
||||||
|
"integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"chownr": "^1.1.1",
|
||||||
|
"mkdirp-classic": "^0.5.2",
|
||||||
|
"pump": "^3.0.0",
|
||||||
|
"tar-stream": "^2.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prebuild-install/node_modules/tar-stream": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bl": "^4.0.3",
|
||||||
|
"end-of-stream": "^1.4.1",
|
||||||
|
"fs-constants": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^3.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prelude-ls": {
|
"node_modules/prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||||
@@ -13171,7 +13389,6 @@
|
|||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
|
||||||
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
|
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"end-of-stream": "^1.1.0",
|
"end-of-stream": "^1.1.0",
|
||||||
@@ -13251,6 +13468,30 @@
|
|||||||
"integrity": "sha512-Mt2NznMgepLfORijhQMncE26IhkmjEphig+/1fKC0OtaKwys/gpvpmswSjoN01SS+VO951mj0L4VIDXdXsjnfA==",
|
"integrity": "sha512-Mt2NznMgepLfORijhQMncE26IhkmjEphig+/1fKC0OtaKwys/gpvpmswSjoN01SS+VO951mj0L4VIDXdXsjnfA==",
|
||||||
"license": "Apache-2.0 OR MIT"
|
"license": "Apache-2.0 OR MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/rc": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||||
|
"license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
|
||||||
|
"dependencies": {
|
||||||
|
"deep-extend": "^0.6.0",
|
||||||
|
"ini": "~1.3.0",
|
||||||
|
"minimist": "^1.2.0",
|
||||||
|
"strip-json-comments": "~2.0.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rc": "cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rc/node_modules/strip-json-comments": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readable-stream": {
|
"node_modules/readable-stream": {
|
||||||
"version": "4.7.0",
|
"version": "4.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
||||||
@@ -13682,7 +13923,6 @@
|
|||||||
"version": "7.7.4",
|
"version": "7.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
@@ -13899,6 +14139,51 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/simple-concat": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/simple-get": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"decompress-response": "^6.0.0",
|
||||||
|
"once": "^1.3.1",
|
||||||
|
"simple-concat": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sirv": {
|
"node_modules/sirv": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
|
||||||
@@ -15221,6 +15506,18 @@
|
|||||||
"@esbuild/win32-x64": "0.27.3"
|
"@esbuild/win32-x64": "0.27.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tunnel-agent": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
@@ -16621,7 +16918,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/write-stream": {
|
"node_modules/write-stream": {
|
||||||
@@ -21716,6 +22012,11 @@
|
|||||||
"readdirp": "^4.0.1"
|
"readdirp": "^4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"chownr": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||||
|
},
|
||||||
"cjs-module-lexer": {
|
"cjs-module-lexer": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
|
||||||
@@ -21960,11 +22261,24 @@
|
|||||||
"integrity": "sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==",
|
"integrity": "sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"decompress-response": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||||
|
"requires": {
|
||||||
|
"mimic-response": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"deep-eql": {
|
"deep-eql": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
|
||||||
"integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="
|
"integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="
|
||||||
},
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||||
|
},
|
||||||
"deep-is": {
|
"deep-is": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||||
@@ -22280,7 +22594,6 @@
|
|||||||
"version": "1.4.5",
|
"version": "1.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||||
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"once": "^1.4.0"
|
"once": "^1.4.0"
|
||||||
}
|
}
|
||||||
@@ -22840,6 +23153,11 @@
|
|||||||
"bare-events": "^2.7.0"
|
"bare-events": "^2.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"expand-template": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
|
||||||
|
},
|
||||||
"expect-type": {
|
"expect-type": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
||||||
@@ -23072,6 +23390,11 @@
|
|||||||
"signal-exit": "^4.0.1"
|
"signal-exit": "^4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fs-constants": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||||
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||||
@@ -23245,6 +23568,11 @@
|
|||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"github-from-package": {
|
||||||
|
"version": "0.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||||
|
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
|
||||||
|
},
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "13.0.6",
|
"version": "13.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||||
@@ -23533,6 +23861,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
},
|
},
|
||||||
|
"ini": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
|
||||||
|
},
|
||||||
"interface-datastore": {
|
"interface-datastore": {
|
||||||
"version": "8.3.2",
|
"version": "8.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/interface-datastore/-/interface-datastore-8.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/interface-datastore/-/interface-datastore-8.3.2.tgz",
|
||||||
@@ -24747,6 +25080,11 @@
|
|||||||
"mime-db": "1.52.0"
|
"mime-db": "1.52.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mimic-response": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
|
||||||
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "10.2.4",
|
"version": "10.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
|
||||||
@@ -24787,6 +25125,11 @@
|
|||||||
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"mkdirp-classic": {
|
||||||
|
"version": "0.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||||
|
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
||||||
|
},
|
||||||
"modern-tar": {
|
"modern-tar": {
|
||||||
"version": "0.7.5",
|
"version": "0.7.5",
|
||||||
"resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.5.tgz",
|
||||||
@@ -24870,6 +25213,11 @@
|
|||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"napi-build-utils": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="
|
||||||
|
},
|
||||||
"napi-macros": {
|
"napi-macros": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz",
|
||||||
@@ -24886,6 +25234,22 @@
|
|||||||
"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
|
||||||
"integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="
|
"integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="
|
||||||
},
|
},
|
||||||
|
"node-abi": {
|
||||||
|
"version": "3.88.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.88.0.tgz",
|
||||||
|
"integrity": "sha512-At6b4UqIEVudaqPsXjmUO1r/N5BUr4yhDGs5PkBE8/oG5+TfLPhFechiskFsnT6Ql0VfUXbalUUCbfXxtj7K+w==",
|
||||||
|
"requires": {
|
||||||
|
"semver": "^7.3.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node-datachannel": {
|
||||||
|
"version": "0.32.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-datachannel/-/node-datachannel-0.32.1.tgz",
|
||||||
|
"integrity": "sha512-r4UdtA0lCsz6XrG84pJ6lntAyw/MHpmBOhEkg5UQcmWTEpANqCPkMos6rj/QZDdq3GBUsdI/wst5acwWUiibCA==",
|
||||||
|
"requires": {
|
||||||
|
"prebuild-install": "^7.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node-fetch": {
|
"node-fetch": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
||||||
@@ -25012,7 +25376,6 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@@ -25699,6 +26062,84 @@
|
|||||||
"integrity": "sha512-fXqsVn+rmlPtxaAIGaQP5TkiaT39OMwvMk+ScLLtHrmfXD2KBO6fe/qBl38N/rpTn0h/A058dPN4fLAHt550zA==",
|
"integrity": "sha512-fXqsVn+rmlPtxaAIGaQP5TkiaT39OMwvMk+ScLLtHrmfXD2KBO6fe/qBl38N/rpTn0h/A058dPN4fLAHt550zA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"prebuild-install": {
|
||||||
|
"version": "7.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
|
||||||
|
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
|
||||||
|
"requires": {
|
||||||
|
"detect-libc": "^2.0.0",
|
||||||
|
"expand-template": "^2.0.3",
|
||||||
|
"github-from-package": "0.0.0",
|
||||||
|
"minimist": "^1.2.3",
|
||||||
|
"mkdirp-classic": "^0.5.3",
|
||||||
|
"napi-build-utils": "^2.0.0",
|
||||||
|
"node-abi": "^3.3.0",
|
||||||
|
"pump": "^3.0.0",
|
||||||
|
"rc": "^1.2.7",
|
||||||
|
"simple-get": "^4.0.0",
|
||||||
|
"tar-fs": "^2.0.0",
|
||||||
|
"tunnel-agent": "^0.6.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bl": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||||
|
"requires": {
|
||||||
|
"buffer": "^5.5.0",
|
||||||
|
"inherits": "^2.0.4",
|
||||||
|
"readable-stream": "^3.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"buffer": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.1.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"detect-libc": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"requires": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tar-fs": {
|
||||||
|
"version": "2.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
|
||||||
|
"integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
|
||||||
|
"requires": {
|
||||||
|
"chownr": "^1.1.1",
|
||||||
|
"mkdirp-classic": "^0.5.2",
|
||||||
|
"pump": "^3.0.0",
|
||||||
|
"tar-stream": "^2.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tar-stream": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||||
|
"requires": {
|
||||||
|
"bl": "^4.0.3",
|
||||||
|
"end-of-stream": "^1.4.1",
|
||||||
|
"fs-constants": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^3.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"prelude-ls": {
|
"prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||||
@@ -25805,7 +26246,6 @@
|
|||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
|
||||||
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
|
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"end-of-stream": "^1.1.0",
|
"end-of-stream": "^1.1.0",
|
||||||
"once": "^1.3.1"
|
"once": "^1.3.1"
|
||||||
@@ -25855,6 +26295,24 @@
|
|||||||
"resolved": "https://registry.npmjs.org/race-signal/-/race-signal-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/race-signal/-/race-signal-1.1.3.tgz",
|
||||||
"integrity": "sha512-Mt2NznMgepLfORijhQMncE26IhkmjEphig+/1fKC0OtaKwys/gpvpmswSjoN01SS+VO951mj0L4VIDXdXsjnfA=="
|
"integrity": "sha512-Mt2NznMgepLfORijhQMncE26IhkmjEphig+/1fKC0OtaKwys/gpvpmswSjoN01SS+VO951mj0L4VIDXdXsjnfA=="
|
||||||
},
|
},
|
||||||
|
"rc": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||||
|
"requires": {
|
||||||
|
"deep-extend": "^0.6.0",
|
||||||
|
"ini": "~1.3.0",
|
||||||
|
"minimist": "^1.2.0",
|
||||||
|
"strip-json-comments": "~2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"strip-json-comments": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "4.7.0",
|
"version": "4.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
||||||
@@ -26139,8 +26597,7 @@
|
|||||||
"semver": {
|
"semver": {
|
||||||
"version": "7.7.4",
|
"version": "7.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"serialize-error": {
|
"serialize-error": {
|
||||||
"version": "12.0.0",
|
"version": "12.0.0",
|
||||||
@@ -26282,6 +26739,21 @@
|
|||||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"simple-concat": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
|
||||||
|
},
|
||||||
|
"simple-get": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
|
||||||
|
"requires": {
|
||||||
|
"decompress-response": "^6.0.0",
|
||||||
|
"once": "^1.3.1",
|
||||||
|
"simple-concat": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"sirv": {
|
"sirv": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
|
||||||
@@ -27057,6 +27529,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tunnel-agent": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"type-check": {
|
"type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
@@ -27856,8 +28336,7 @@
|
|||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"write-stream": {
|
"write-stream": {
|
||||||
"version": "0.4.3",
|
"version": "0.4.3",
|
||||||
|
|||||||
@@ -134,6 +134,7 @@
|
|||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2",
|
||||||
"idb": "^8.0.3",
|
"idb": "^8.0.3",
|
||||||
"minimatch": "^10.2.2",
|
"minimatch": "^10.2.2",
|
||||||
|
"node-datachannel": "^0.32.1",
|
||||||
"octagonal-wheels": "^0.1.45",
|
"octagonal-wheels": "^0.1.45",
|
||||||
"pouchdb-adapter-leveldb": "^9.0.0",
|
"pouchdb-adapter-leveldb": "^9.0.0",
|
||||||
"qrcode-generator": "^1.4.4",
|
"qrcode-generator": "^1.4.4",
|
||||||
|
|||||||
@@ -148,6 +148,9 @@ Options:
|
|||||||
Commands:
|
Commands:
|
||||||
init-settings [path] Create settings JSON from DEFAULT_SETTINGS
|
init-settings [path] Create settings JSON from DEFAULT_SETTINGS
|
||||||
sync Run one replication cycle and exit
|
sync Run one replication cycle and exit
|
||||||
|
p2p-peers <timeout> Show discovered peers as [peer]<TAB><peer-id><TAB><peer-name>
|
||||||
|
p2p-sync <peer> <timeout> Synchronise with specified peer-id or peer-name
|
||||||
|
p2p-host Start P2P host mode and wait until interrupted (Ctrl+C)
|
||||||
push <src> <dst> Push local file <src> into local database path <dst>
|
push <src> <dst> Push local file <src> into local database path <dst>
|
||||||
pull <src> <dst> Pull file <src> from local database into local file <dst>
|
pull <src> <dst> Pull file <src> from local database into local file <dst>
|
||||||
pull-rev <src> <dst> <revision> Pull specific revision into local file <dst>
|
pull-rev <src> <dst> <revision> Pull specific revision into local file <dst>
|
||||||
@@ -177,6 +180,32 @@ npm run --silent cli -- [database-path] [options] [command] [command-args]
|
|||||||
```
|
```
|
||||||
Note: `*` indicates if the file has conflicts.
|
Note: `*` indicates if the file has conflicts.
|
||||||
|
|
||||||
|
##### p2p-peers
|
||||||
|
|
||||||
|
`p2p-peers <timeout>` waits for the specified number of seconds, then prints each discovered peer on a separate line:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[peer]<TAB><peer-id><TAB><peer-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
Use this command to select a target for `p2p-sync`.
|
||||||
|
|
||||||
|
##### p2p-sync
|
||||||
|
|
||||||
|
`p2p-sync <peer> <timeout>` discovers peers up to the specified timeout and synchronises with the selected peer.
|
||||||
|
|
||||||
|
- `<peer>` accepts either `peer-id` or `peer-name` from `p2p-peers` output.
|
||||||
|
- On success, the command prints a completion message to standard error and exits with status code `0`.
|
||||||
|
- On failure, the command prints an error message and exits non-zero.
|
||||||
|
|
||||||
|
##### p2p-host
|
||||||
|
|
||||||
|
`p2p-host` starts the local P2P host and keeps running until interrupted.
|
||||||
|
|
||||||
|
- Other peers can discover and synchronise with this host while it is running.
|
||||||
|
- Stop the host with `Ctrl+C`.
|
||||||
|
- In CLI mode, behaviour is non-interactive and acceptance follows settings.
|
||||||
|
|
||||||
##### info
|
##### info
|
||||||
|
|
||||||
`info` output fields:
|
`info` output fields:
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
import type { LiveSyncBaseCore } from "../../../LiveSyncBaseCore";
|
||||||
|
import { P2P_DEFAULT_SETTINGS, SETTING_KEY_P2P_DEVICE_NAME, type EntryDoc } from "@lib/common/types";
|
||||||
|
import type { ServiceContext } from "@lib/services/base/ServiceBase";
|
||||||
|
import { TrysteroReplicator } from "@lib/replication/trystero/TrysteroReplicator";
|
||||||
|
|
||||||
|
type CLIP2PPeer = {
|
||||||
|
peerId: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function delay(ms: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseTimeoutSeconds(value: string, commandName: string): number {
|
||||||
|
const timeoutSec = Number(value);
|
||||||
|
if (!Number.isFinite(timeoutSec) || timeoutSec < 0) {
|
||||||
|
throw new Error(`${commandName} requires a non-negative timeout in seconds`);
|
||||||
|
}
|
||||||
|
return timeoutSec;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateP2PSettings(core: LiveSyncBaseCore<ServiceContext, any>) {
|
||||||
|
const settings = core.services.setting.currentSettings();
|
||||||
|
if (!settings.P2P_Enabled) {
|
||||||
|
throw new Error("P2P is disabled in settings (P2P_Enabled=false)");
|
||||||
|
}
|
||||||
|
if (!settings.P2P_AppID) {
|
||||||
|
settings.P2P_AppID = P2P_DEFAULT_SETTINGS.P2P_AppID;
|
||||||
|
}
|
||||||
|
// CLI mode is non-interactive.
|
||||||
|
settings.P2P_IsHeadless = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createReplicator(core: LiveSyncBaseCore<ServiceContext, any>): Promise<TrysteroReplicator> {
|
||||||
|
validateP2PSettings(core);
|
||||||
|
const getSettings = () => core.services.setting.currentSettings();
|
||||||
|
const getDB = () => core.services.database.localDatabase.localDatabase;
|
||||||
|
const getSimpleStore = () => core.services.keyValueDB.openSimpleStore("p2p-sync");
|
||||||
|
const getDeviceName = () =>
|
||||||
|
core.services.config.getSmallConfig(SETTING_KEY_P2P_DEVICE_NAME) || core.services.vault.getVaultName();
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
get settings() {
|
||||||
|
return getSettings();
|
||||||
|
},
|
||||||
|
get db() {
|
||||||
|
return getDB();
|
||||||
|
},
|
||||||
|
get simpleStore() {
|
||||||
|
return getSimpleStore();
|
||||||
|
},
|
||||||
|
get deviceName() {
|
||||||
|
return getDeviceName();
|
||||||
|
},
|
||||||
|
get platform() {
|
||||||
|
return core.services.API.getPlatform();
|
||||||
|
},
|
||||||
|
get confirm() {
|
||||||
|
return core.services.API.confirm;
|
||||||
|
},
|
||||||
|
processReplicatedDocs: async (docs: EntryDoc[]) => {
|
||||||
|
await core.services.replication.parseSynchroniseResult(docs as any);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return new TrysteroReplicator(env as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSortedPeers(replicator: TrysteroReplicator): CLIP2PPeer[] {
|
||||||
|
return [...replicator.knownAdvertisements]
|
||||||
|
.map((peer) => ({ peerId: peer.peerId, name: peer.name }))
|
||||||
|
.sort((a, b) => a.peerId.localeCompare(b.peerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function collectPeers(
|
||||||
|
core: LiveSyncBaseCore<ServiceContext, any>,
|
||||||
|
timeoutSec: number
|
||||||
|
): Promise<CLIP2PPeer[]> {
|
||||||
|
const replicator = await createReplicator(core);
|
||||||
|
await replicator.open();
|
||||||
|
try {
|
||||||
|
await delay(timeoutSec * 1000);
|
||||||
|
return getSortedPeers(replicator);
|
||||||
|
} finally {
|
||||||
|
await replicator.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolvePeer(peers: CLIP2PPeer[], peerToken: string): CLIP2PPeer | undefined {
|
||||||
|
const byId = peers.find((peer) => peer.peerId === peerToken);
|
||||||
|
if (byId) {
|
||||||
|
return byId;
|
||||||
|
}
|
||||||
|
const byName = peers.filter((peer) => peer.name === peerToken);
|
||||||
|
if (byName.length > 1) {
|
||||||
|
throw new Error(`Multiple peers matched by name '${peerToken}'. Use peer-id instead.`);
|
||||||
|
}
|
||||||
|
if (byName.length === 1) {
|
||||||
|
return byName[0];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function syncWithPeer(
|
||||||
|
core: LiveSyncBaseCore<ServiceContext, any>,
|
||||||
|
peerToken: string,
|
||||||
|
timeoutSec: number
|
||||||
|
): Promise<CLIP2PPeer> {
|
||||||
|
const replicator = await createReplicator(core);
|
||||||
|
await replicator.open();
|
||||||
|
try {
|
||||||
|
const timeoutMs = timeoutSec * 1000;
|
||||||
|
const start = Date.now();
|
||||||
|
let targetPeer: CLIP2PPeer | undefined;
|
||||||
|
|
||||||
|
while (Date.now() - start <= timeoutMs) {
|
||||||
|
const peers = getSortedPeers(replicator);
|
||||||
|
targetPeer = resolvePeer(peers, peerToken);
|
||||||
|
if (targetPeer) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await delay(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetPeer) {
|
||||||
|
throw new Error(`Peer '${peerToken}' was not found within ${timeoutSec} seconds`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pullResult = await replicator.replicateFrom(targetPeer.peerId, false);
|
||||||
|
if (pullResult && "error" in pullResult && pullResult.error) {
|
||||||
|
throw pullResult.error;
|
||||||
|
}
|
||||||
|
const pushResult = (await replicator.requestSynchroniseToPeer(targetPeer.peerId)) as any;
|
||||||
|
if (!pushResult || pushResult.ok !== true) {
|
||||||
|
throw pushResult?.error ?? new Error("P2P sync failed while requesting remote sync");
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetPeer;
|
||||||
|
} finally {
|
||||||
|
await replicator.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function openP2PHost(core: LiveSyncBaseCore<ServiceContext, any>): Promise<TrysteroReplicator> {
|
||||||
|
const replicator = await createReplicator(core);
|
||||||
|
await replicator.open();
|
||||||
|
return replicator;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { parseTimeoutSeconds } from "./p2p";
|
||||||
|
|
||||||
|
describe("p2p command helpers", () => {
|
||||||
|
it("accepts non-negative timeout", () => {
|
||||||
|
expect(parseTimeoutSeconds("0", "p2p-peers")).toBe(0);
|
||||||
|
expect(parseTimeoutSeconds("2.5", "p2p-sync")).toBe(2.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects invalid timeout values", () => {
|
||||||
|
expect(() => parseTimeoutSeconds("-1", "p2p-peers")).toThrow(
|
||||||
|
"p2p-peers requires a non-negative timeout in seconds"
|
||||||
|
);
|
||||||
|
expect(() => parseTimeoutSeconds("abc", "p2p-sync")).toThrow(
|
||||||
|
"p2p-sync requires a non-negative timeout in seconds"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -6,6 +6,7 @@ import { DEFAULT_SETTINGS, type FilePathWithPrefix, type ObsidianLiveSyncSetting
|
|||||||
import { stripAllPrefixes } from "@lib/string_and_binary/path";
|
import { stripAllPrefixes } from "@lib/string_and_binary/path";
|
||||||
import type { CLICommandContext, CLIOptions } from "./types";
|
import type { CLICommandContext, CLIOptions } from "./types";
|
||||||
import { promptForPassphrase, readStdinAsUtf8, toArrayBuffer, toVaultRelativePath } from "./utils";
|
import { promptForPassphrase, readStdinAsUtf8, toArrayBuffer, toVaultRelativePath } from "./utils";
|
||||||
|
import { collectPeers, openP2PHost, parseTimeoutSeconds, syncWithPeer } from "./p2p";
|
||||||
import { performFullScan } from "@lib/serviceFeatures/offlineScanner";
|
import { performFullScan } from "@lib/serviceFeatures/offlineScanner";
|
||||||
import { UnresolvedErrorManager } from "@lib/services/base/UnresolvedErrorManager";
|
import { UnresolvedErrorManager } from "@lib/services/base/UnresolvedErrorManager";
|
||||||
|
|
||||||
@@ -23,6 +24,42 @@ export async function runCommand(options: CLIOptions, context: CLICommandContext
|
|||||||
return !!result;
|
return !!result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.command === "p2p-peers") {
|
||||||
|
if (options.commandArgs.length < 1) {
|
||||||
|
throw new Error("p2p-peers requires one argument: <timeout>");
|
||||||
|
}
|
||||||
|
const timeoutSec = parseTimeoutSeconds(options.commandArgs[0], "p2p-peers");
|
||||||
|
console.error(`[Command] p2p-peers timeout=${timeoutSec}s`);
|
||||||
|
const peers = await collectPeers(core as any, timeoutSec);
|
||||||
|
if (peers.length > 0) {
|
||||||
|
process.stdout.write(peers.map((peer) => `[peer]\t${peer.peerId}\t${peer.name}`).join("\n") + "\n");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.command === "p2p-sync") {
|
||||||
|
if (options.commandArgs.length < 2) {
|
||||||
|
throw new Error("p2p-sync requires two arguments: <peer> <timeout>");
|
||||||
|
}
|
||||||
|
const peerToken = options.commandArgs[0].trim();
|
||||||
|
if (!peerToken) {
|
||||||
|
throw new Error("p2p-sync requires a non-empty <peer>");
|
||||||
|
}
|
||||||
|
const timeoutSec = parseTimeoutSeconds(options.commandArgs[1], "p2p-sync");
|
||||||
|
console.error(`[Command] p2p-sync peer=${peerToken} timeout=${timeoutSec}s`);
|
||||||
|
const peer = await syncWithPeer(core as any, peerToken, timeoutSec);
|
||||||
|
console.error(`[Done] P2P sync completed with ${peer.name} (${peer.peerId})`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.command === "p2p-host") {
|
||||||
|
console.error("[Command] p2p-host");
|
||||||
|
await openP2PHost(core as any);
|
||||||
|
console.error("[Ready] P2P host is running. Press Ctrl+C to stop.");
|
||||||
|
await new Promise(() => {});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (options.command === "push") {
|
if (options.command === "push") {
|
||||||
if (options.commandArgs.length < 2) {
|
if (options.commandArgs.length < 2) {
|
||||||
throw new Error("push requires two arguments: <src> <dst>");
|
throw new Error("push requires two arguments: <src> <dst>");
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import { ServiceContext } from "@lib/services/base/ServiceBase";
|
|||||||
export type CLICommand =
|
export type CLICommand =
|
||||||
| "daemon"
|
| "daemon"
|
||||||
| "sync"
|
| "sync"
|
||||||
|
| "p2p-peers"
|
||||||
|
| "p2p-sync"
|
||||||
|
| "p2p-host"
|
||||||
| "push"
|
| "push"
|
||||||
| "pull"
|
| "pull"
|
||||||
| "pull-rev"
|
| "pull-rev"
|
||||||
@@ -36,6 +39,9 @@ export interface CLICommandContext {
|
|||||||
|
|
||||||
export const VALID_COMMANDS = new Set([
|
export const VALID_COMMANDS = new Set([
|
||||||
"sync",
|
"sync",
|
||||||
|
"p2p-peers",
|
||||||
|
"p2p-sync",
|
||||||
|
"p2p-host",
|
||||||
"push",
|
"push",
|
||||||
"pull",
|
"pull",
|
||||||
"pull-rev",
|
"pull-rev",
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
import polyfill from "node-datachannel/polyfill";
|
||||||
import { main } from "./main";
|
import { main } from "./main";
|
||||||
|
|
||||||
|
for (const prop in polyfill) {
|
||||||
|
// @ts-ignore Applying polyfill to globalThis
|
||||||
|
globalThis[prop] = (polyfill as any)[prop];
|
||||||
|
}
|
||||||
|
|
||||||
main().catch((error) => {
|
main().catch((error) => {
|
||||||
console.error(`[Fatal Error]`, error);
|
console.error(`[Fatal Error]`, error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
+22
-4
@@ -27,7 +27,14 @@ import { initialiseServiceModulesCLI } from "./serviceModules/CLIServiceModules"
|
|||||||
import { DEFAULT_SETTINGS, LOG_LEVEL_VERBOSE, type LOG_LEVEL, type ObsidianLiveSyncSettings } from "@lib/common/types";
|
import { DEFAULT_SETTINGS, LOG_LEVEL_VERBOSE, type LOG_LEVEL, type ObsidianLiveSyncSettings } from "@lib/common/types";
|
||||||
import type { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub";
|
import type { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub";
|
||||||
import type { InjectableSettingService } from "@/lib/src/services/implements/injectable/InjectableSettingService";
|
import type { InjectableSettingService } from "@/lib/src/services/implements/injectable/InjectableSettingService";
|
||||||
import { LOG_LEVEL_DEBUG, setGlobalLogFunction, defaultLoggerEnv, LOG_LEVEL_INFO, LOG_LEVEL_URGENT, LOG_LEVEL_NOTICE } from "octagonal-wheels/common/logger";
|
import {
|
||||||
|
LOG_LEVEL_DEBUG,
|
||||||
|
setGlobalLogFunction,
|
||||||
|
defaultLoggerEnv,
|
||||||
|
LOG_LEVEL_INFO,
|
||||||
|
LOG_LEVEL_URGENT,
|
||||||
|
LOG_LEVEL_NOTICE,
|
||||||
|
} from "octagonal-wheels/common/logger";
|
||||||
import { runCommand } from "./commands/runCommand";
|
import { runCommand } from "./commands/runCommand";
|
||||||
import { VALID_COMMANDS } from "./commands/types";
|
import { VALID_COMMANDS } from "./commands/types";
|
||||||
import type { CLICommand, CLIOptions } from "./commands/types";
|
import type { CLICommand, CLIOptions } from "./commands/types";
|
||||||
@@ -36,6 +43,7 @@ import { stripAllPrefixes } from "@lib/string_and_binary/path";
|
|||||||
|
|
||||||
const SETTINGS_FILE = ".livesync/settings.json";
|
const SETTINGS_FILE = ".livesync/settings.json";
|
||||||
defaultLoggerEnv.minLogLevel = LOG_LEVEL_DEBUG;
|
defaultLoggerEnv.minLogLevel = LOG_LEVEL_DEBUG;
|
||||||
|
|
||||||
// DI the log again.
|
// DI the log again.
|
||||||
// const recentLogEntries = reactiveSource<LogEntry[]>([]);
|
// const recentLogEntries = reactiveSource<LogEntry[]>([]);
|
||||||
// const globalLogFunction = (message: any, level?: number, key?: string) => {
|
// const globalLogFunction = (message: any, level?: number, key?: string) => {
|
||||||
@@ -65,6 +73,10 @@ Arguments:
|
|||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
sync Run one replication cycle and exit
|
sync Run one replication cycle and exit
|
||||||
|
p2p-peers <timeout> Show discovered peers as [peer]<TAB><peer-id><TAB><peer-name>
|
||||||
|
p2p-sync <peer> <timeout>
|
||||||
|
Sync with the specified peer-id or peer-name
|
||||||
|
p2p-host Start P2P host mode and wait until interrupted
|
||||||
push <src> <dst> Push local file <src> into local database path <dst>
|
push <src> <dst> Push local file <src> into local database path <dst>
|
||||||
pull <src> <dst> Pull file <src> from local database into local file <dst>
|
pull <src> <dst> Pull file <src> from local database into local file <dst>
|
||||||
pull-rev <src> <dst> <rev> Pull file <src> at specific revision <rev> into local file <dst>
|
pull-rev <src> <dst> <rev> Pull file <src> at specific revision <rev> into local file <dst>
|
||||||
@@ -78,6 +90,9 @@ Commands:
|
|||||||
resolve <path> <rev> Resolve conflicts by keeping <rev> and deleting others
|
resolve <path> <rev> Resolve conflicts by keeping <rev> and deleting others
|
||||||
Examples:
|
Examples:
|
||||||
livesync-cli ./my-database sync
|
livesync-cli ./my-database sync
|
||||||
|
livesync-cli ./my-database p2p-peers 5
|
||||||
|
livesync-cli ./my-database p2p-sync my-peer-name 15
|
||||||
|
livesync-cli ./my-database p2p-host
|
||||||
livesync-cli ./my-database --settings ./custom-settings.json push ./note.md folder/note.md
|
livesync-cli ./my-database --settings ./custom-settings.json push ./note.md folder/note.md
|
||||||
livesync-cli ./my-database pull folder/note.md ./exports/note.md
|
livesync-cli ./my-database pull folder/note.md ./exports/note.md
|
||||||
livesync-cli ./my-database pull-rev folder/note.md ./exports/note.old.md 3-abcdef
|
livesync-cli ./my-database pull-rev folder/note.md ./exports/note.old.md 3-abcdef
|
||||||
@@ -213,21 +228,22 @@ export async function main() {
|
|||||||
options.command === "cat" ||
|
options.command === "cat" ||
|
||||||
options.command === "cat-rev" ||
|
options.command === "cat-rev" ||
|
||||||
options.command === "ls" ||
|
options.command === "ls" ||
|
||||||
|
options.command === "p2p-peers" ||
|
||||||
options.command === "info" ||
|
options.command === "info" ||
|
||||||
options.command === "rm" ||
|
options.command === "rm" ||
|
||||||
options.command === "resolve";
|
options.command === "resolve";
|
||||||
const infoLog = avoidStdoutNoise ? console.error : console.log;
|
const infoLog = avoidStdoutNoise ? console.error : console.log;
|
||||||
if(options.debug){
|
if (options.debug) {
|
||||||
setGlobalLogFunction((msg, level) => {
|
setGlobalLogFunction((msg, level) => {
|
||||||
console.error(`[${level}] ${typeof msg === "string" ? msg : JSON.stringify(msg)}`);
|
console.error(`[${level}] ${typeof msg === "string" ? msg : JSON.stringify(msg)}`);
|
||||||
if (msg instanceof Error) {
|
if (msg instanceof Error) {
|
||||||
console.error(msg);
|
console.error(msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}else{
|
} else {
|
||||||
setGlobalLogFunction((msg, level) => {
|
setGlobalLogFunction((msg, level) => {
|
||||||
// NO OP, leave it to logFunction
|
// NO OP, leave it to logFunction
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (options.command === "init-settings") {
|
if (options.command === "init-settings") {
|
||||||
await createDefaultSettingsFile(options);
|
await createDefaultSettingsFile(options);
|
||||||
@@ -421,4 +437,6 @@ export async function main() {
|
|||||||
console.error(`[Error] Failed to start:`, error);
|
console.error(`[Error] Failed to start:`, error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
// To prevent unexpected hanging in webRTC connections.
|
||||||
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,4 +58,31 @@ describe("CLI parseArgs", () => {
|
|||||||
expect(combined).toContain("Usage:");
|
expect(combined).toContain("Usage:");
|
||||||
expect(combined).toContain("livesync-cli [database-path]");
|
expect(combined).toContain("livesync-cli [database-path]");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("parses p2p-peers command and timeout", () => {
|
||||||
|
process.argv = ["node", "livesync-cli", "./vault", "p2p-peers", "5"];
|
||||||
|
const parsed = parseArgs();
|
||||||
|
|
||||||
|
expect(parsed.databasePath).toBe("./vault");
|
||||||
|
expect(parsed.command).toBe("p2p-peers");
|
||||||
|
expect(parsed.commandArgs).toEqual(["5"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("parses p2p-sync command with peer and timeout", () => {
|
||||||
|
process.argv = ["node", "livesync-cli", "./vault", "p2p-sync", "peer-1", "12"];
|
||||||
|
const parsed = parseArgs();
|
||||||
|
|
||||||
|
expect(parsed.databasePath).toBe("./vault");
|
||||||
|
expect(parsed.command).toBe("p2p-sync");
|
||||||
|
expect(parsed.commandArgs).toEqual(["peer-1", "12"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("parses p2p-host command", () => {
|
||||||
|
process.argv = ["node", "livesync-cli", "./vault", "p2p-host"];
|
||||||
|
const parsed = parseArgs();
|
||||||
|
|
||||||
|
expect(parsed.databasePath).toBe("./vault");
|
||||||
|
expect(parsed.command).toBe("p2p-host");
|
||||||
|
expect(parsed.commandArgs).toEqual([]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,16 +11,20 @@
|
|||||||
"cli": "node dist/index.cjs",
|
"cli": "node dist/index.cjs",
|
||||||
"buildRun": "npm run build && npm run cli --",
|
"buildRun": "npm run build && npm run cli --",
|
||||||
"check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json",
|
"check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json",
|
||||||
"test:unit": "cd ../../.. && npx vitest run --config vitest.config.unit.ts src/apps/cli/main.unit.spec.ts src/apps/cli/commands/utils.unit.spec.ts src/apps/cli/commands/runCommand.unit.spec.ts",
|
"test:unit": "cd ../../.. && npx vitest run --config vitest.config.unit.ts src/apps/cli/main.unit.spec.ts src/apps/cli/commands/utils.unit.spec.ts src/apps/cli/commands/runCommand.unit.spec.ts src/apps/cli/commands/p2p.unit.spec.ts",
|
||||||
"test:e2e:two-vaults": "bash test/test-e2e-two-vaults-with-docker-linux.sh",
|
"test:e2e:two-vaults": "bash test/test-e2e-two-vaults-with-docker-linux.sh",
|
||||||
"test:e2e:two-vaults:common": "bash test/test-e2e-two-vaults-common.sh",
|
"test:e2e:two-vaults:common": "bash test/test-e2e-two-vaults-common.sh",
|
||||||
"test:e2e:two-vaults:matrix": "bash test/test-e2e-two-vaults-matrix.sh",
|
"test:e2e:two-vaults:matrix": "bash test/test-e2e-two-vaults-matrix.sh",
|
||||||
"test:e2e:push-pull": "bash test/test-push-pull-linux.sh",
|
"test:e2e:push-pull": "bash test/test-push-pull-linux.sh",
|
||||||
"test:e2e:setup-put-cat": "bash test/test-setup-put-cat-linux.sh",
|
"test:e2e:setup-put-cat": "bash test/test-setup-put-cat-linux.sh",
|
||||||
"test:e2e:sync-two-local": "bash test/test-sync-two-local-databases-linux.sh",
|
"test:e2e:sync-two-local": "bash test/test-sync-two-local-databases-linux.sh",
|
||||||
|
"test:e2e:p2p": "bash test/test-p2p-three-nodes-conflict-linux.sh",
|
||||||
|
"test:e2e:p2p-host": "bash test/test-p2p-host-linux.sh",
|
||||||
|
"test:e2e:p2p-sync": "bash test/test-p2p-sync-linux.sh",
|
||||||
|
"test:e2e:p2p-peers:local-relay": "bash test/test-p2p-peers-local-relay.sh",
|
||||||
"test:e2e:mirror": "bash test/test-mirror-linux.sh",
|
"test:e2e:mirror": "bash test/test-mirror-linux.sh",
|
||||||
"pretest:e2e:all": "npm run build",
|
"pretest:e2e:all": "npm run build",
|
||||||
"test:e2e:all": " export RUN_BUILD=0 && npm run test:e2e:setup-put-cat && npm run test:e2e:push-pull && npm run test:e2e:sync-two-local && npm run test:e2e:mirror && npm run test:e2e:two-vaults"
|
"test:e2e:all": " export RUN_BUILD=0 && npm run test:e2e:setup-put-cat && npm run test:e2e:push-pull && npm run test:e2e:sync-two-local && npm run test:e2e:p2p && npm run test:e2e:mirror && npm run test:e2e:two-vaults && npm run test:e2e:p2p-host && npm run test:e2e:p2p"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {}
|
"devDependencies": {}
|
||||||
|
|||||||
@@ -101,9 +101,7 @@ class NodeFileKeyValueDatabase implements KeyValueDatabase {
|
|||||||
private load() {
|
private load() {
|
||||||
try {
|
try {
|
||||||
const loaded = JSON.parse(nodeFs.readFileSync(this.filePath, "utf-8")) as Record<string, unknown>;
|
const loaded = JSON.parse(nodeFs.readFileSync(this.filePath, "utf-8")) as Record<string, unknown>;
|
||||||
this.data = new Map(
|
this.data = new Map(Object.entries(loaded).map(([key, value]) => [key, deserializeFromNodeKV(value)]));
|
||||||
Object.entries(loaded).map(([key, value]) => [key, deserializeFromNodeKV(value)])
|
|
||||||
);
|
|
||||||
} catch {
|
} catch {
|
||||||
this.data = new Map();
|
this.data = new Map();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,6 +211,57 @@ fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2), "utf-8");
|
|||||||
NODE
|
NODE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cli_test_apply_p2p_settings() {
|
||||||
|
local settings_file="$1"
|
||||||
|
local room_id="$2"
|
||||||
|
local passphrase="$3"
|
||||||
|
local app_id="${4:-self-hosted-livesync-cli-tests}"
|
||||||
|
local relays="${5:-ws://localhost:4000/}"
|
||||||
|
local auto_accept="${6:-~.*}"
|
||||||
|
SETTINGS_FILE="$settings_file" \
|
||||||
|
P2P_ROOM_ID="$room_id" \
|
||||||
|
P2P_PASSPHRASE="$passphrase" \
|
||||||
|
P2P_APP_ID="$app_id" \
|
||||||
|
P2P_RELAYS="$relays" \
|
||||||
|
P2P_AUTO_ACCEPT="$auto_accept" \
|
||||||
|
node <<'NODE'
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const settingsPath = process.env.SETTINGS_FILE;
|
||||||
|
const data = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
||||||
|
|
||||||
|
data.P2P_Enabled = true;
|
||||||
|
data.P2P_AutoStart = false;
|
||||||
|
data.P2P_AutoBroadcast = false;
|
||||||
|
data.P2P_AppID = process.env.P2P_APP_ID;
|
||||||
|
data.P2P_roomID = process.env.P2P_ROOM_ID;
|
||||||
|
data.P2P_passphrase = process.env.P2P_PASSPHRASE;
|
||||||
|
data.P2P_relays = process.env.P2P_RELAYS;
|
||||||
|
data.P2P_AutoAcceptingPeers = process.env.P2P_AUTO_ACCEPT;
|
||||||
|
data.P2P_AutoDenyingPeers = "";
|
||||||
|
data.P2P_IsHeadless = true;
|
||||||
|
data.isConfigured = true;
|
||||||
|
|
||||||
|
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2), "utf-8");
|
||||||
|
NODE
|
||||||
|
}
|
||||||
|
|
||||||
|
cli_test_is_local_p2p_relay() {
|
||||||
|
local relay_url="$1"
|
||||||
|
[[ "$relay_url" == "ws://localhost:4000" || "$relay_url" == "ws://localhost:4000/" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
cli_test_stop_p2p_relay() {
|
||||||
|
bash "$CLI_DIR/util/p2p-stop.sh" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
cli_test_start_p2p_relay() {
|
||||||
|
echo "[INFO] stopping leftover P2P relay container if present"
|
||||||
|
cli_test_stop_p2p_relay
|
||||||
|
|
||||||
|
echo "[INFO] starting local P2P relay container"
|
||||||
|
bash "$CLI_DIR/util/p2p-start.sh"
|
||||||
|
}
|
||||||
|
|
||||||
cli_test_stop_couchdb() {
|
cli_test_stop_couchdb() {
|
||||||
bash "$CLI_DIR/util/couchdb-stop.sh" >/dev/null 2>&1 || true
|
bash "$CLI_DIR/util/couchdb-stop.sh" >/dev/null 2>&1 || true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
# This test should be run with P2P client, please refer to the test-p2p-three-nodes-conflict-linux.sh test for more details.
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CLI_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
cd "$CLI_DIR"
|
||||||
|
source "$SCRIPT_DIR/test-helpers.sh"
|
||||||
|
display_test_info
|
||||||
|
|
||||||
|
RUN_BUILD="${RUN_BUILD:-1}"
|
||||||
|
VERBOSE_TEST_LOGGING="${VERBOSE_TEST_LOGGING:-0}"
|
||||||
|
KEEP_TEST_DATA="${KEEP_TEST_DATA:-0}"
|
||||||
|
|
||||||
|
RELAY="${RELAY:-ws://localhost:4000/}"
|
||||||
|
USE_INTERNAL_RELAY="${USE_INTERNAL_RELAY:-1}"
|
||||||
|
ROOM_ID="${ROOM_ID:-1}"
|
||||||
|
PASSPHRASE="${PASSPHRASE:-test}"
|
||||||
|
APP_ID="${APP_ID:-self-hosted-livesync-cli-tests}"
|
||||||
|
|
||||||
|
cli_test_init_cli_cmd
|
||||||
|
|
||||||
|
if [[ "$RUN_BUILD" == "1" ]]; then
|
||||||
|
echo "[INFO] building CLI"
|
||||||
|
npm run build
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/livesync-cli-p2p-host.XXXXXX")"
|
||||||
|
VAULT="$WORK_DIR/vault-host"
|
||||||
|
SETTINGS="$WORK_DIR/settings-host.json"
|
||||||
|
mkdir -p "$VAULT"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
local exit_code=$?
|
||||||
|
if [[ "${P2P_RELAY_STARTED:-0}" == "1" ]]; then
|
||||||
|
cli_test_stop_p2p_relay
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$KEEP_TEST_DATA" != "1" ]]; then
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
else
|
||||||
|
echo "[INFO] KEEP_TEST_DATA=1, preserving artefacts at $WORK_DIR"
|
||||||
|
fi
|
||||||
|
exit "$exit_code"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
if [[ "$USE_INTERNAL_RELAY" == "1" ]]; then
|
||||||
|
if cli_test_is_local_p2p_relay "$RELAY"; then
|
||||||
|
cli_test_start_p2p_relay
|
||||||
|
P2P_RELAY_STARTED=1
|
||||||
|
else
|
||||||
|
echo "[INFO] USE_INTERNAL_RELAY=1 but RELAY is not local ($RELAY), skipping local relay startup"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[INFO] preparing settings"
|
||||||
|
echo "[INFO] relay=$RELAY room=$ROOM_ID app=$APP_ID"
|
||||||
|
cli_test_init_settings_file "$SETTINGS"
|
||||||
|
cli_test_apply_p2p_settings "$SETTINGS" "$ROOM_ID" "$PASSPHRASE" "$APP_ID" "$RELAY"
|
||||||
|
|
||||||
|
echo "[CASE] start p2p-host"
|
||||||
|
echo "[INFO] press Ctrl+C to stop"
|
||||||
|
run_cli "$VAULT" --settings "$SETTINGS" p2p-host
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CLI_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
cd "$CLI_DIR"
|
||||||
|
|
||||||
|
source "$SCRIPT_DIR/test-helpers.sh"
|
||||||
|
|
||||||
|
RUN_BUILD="${RUN_BUILD:-0}"
|
||||||
|
KEEP_TEST_DATA="${KEEP_TEST_DATA:-0}"
|
||||||
|
RELAY="${RELAY:-ws://localhost:7777}"
|
||||||
|
ROOM_ID="${ROOM_ID:-1}"
|
||||||
|
PASSPHRASE="${PASSPHRASE:-test}"
|
||||||
|
TIMEOUT_SECONDS="${TIMEOUT_SECONDS:-8}"
|
||||||
|
DEBUG_FLAG="${DEBUG_FLAG:--d}"
|
||||||
|
|
||||||
|
if [[ "$RUN_BUILD" == "1" ]]; then
|
||||||
|
echo "[INFO] building CLI"
|
||||||
|
npm run build
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/livesync-cli-p2p-peers-local-relay.XXXXXX")"
|
||||||
|
VAULT="$WORK_DIR/vault"
|
||||||
|
SETTINGS="$WORK_DIR/settings.json"
|
||||||
|
mkdir -p "$VAULT"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
local exit_code=$?
|
||||||
|
if [[ "$KEEP_TEST_DATA" != "1" ]]; then
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
else
|
||||||
|
echo "[INFO] KEEP_TEST_DATA=1, preserving artefacts at $WORK_DIR"
|
||||||
|
fi
|
||||||
|
exit "$exit_code"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
cli_test_init_cli_cmd
|
||||||
|
|
||||||
|
echo "[INFO] creating settings at $SETTINGS"
|
||||||
|
run_cli init-settings --force "$SETTINGS" >/dev/null
|
||||||
|
|
||||||
|
SETTINGS_FILE="$SETTINGS" \
|
||||||
|
P2P_ROOM_ID="$ROOM_ID" \
|
||||||
|
P2P_PASSPHRASE="$PASSPHRASE" \
|
||||||
|
P2P_RELAYS="$RELAY" \
|
||||||
|
node <<'NODE'
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const settingsPath = process.env.SETTINGS_FILE;
|
||||||
|
const data = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
||||||
|
|
||||||
|
data.P2P_Enabled = true;
|
||||||
|
data.P2P_AutoStart = false;
|
||||||
|
data.P2P_AutoBroadcast = false;
|
||||||
|
data.P2P_roomID = process.env.P2P_ROOM_ID;
|
||||||
|
data.P2P_passphrase = process.env.P2P_PASSPHRASE;
|
||||||
|
data.P2P_relays = process.env.P2P_RELAYS;
|
||||||
|
data.P2P_AutoAcceptingPeers = "~.*";
|
||||||
|
data.P2P_AutoDenyingPeers = "";
|
||||||
|
data.P2P_IsHeadless = true;
|
||||||
|
data.isConfigured = true;
|
||||||
|
|
||||||
|
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2), "utf-8");
|
||||||
|
NODE
|
||||||
|
|
||||||
|
echo "[INFO] relay=$RELAY room=$ROOM_ID timeout=${TIMEOUT_SECONDS}s"
|
||||||
|
echo "[INFO] running p2p-peers"
|
||||||
|
|
||||||
|
set +e
|
||||||
|
OUTPUT="$(run_cli "$DEBUG_FLAG" "$VAULT" --settings "$SETTINGS" p2p-peers "$TIMEOUT_SECONDS" 2>&1)"
|
||||||
|
EXIT_CODE=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "$OUTPUT"
|
||||||
|
|
||||||
|
if [[ "$EXIT_CODE" -ne 0 ]]; then
|
||||||
|
echo "[FAIL] p2p-peers exited with code $EXIT_CODE" >&2
|
||||||
|
exit "$EXIT_CODE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$OUTPUT" ]]; then
|
||||||
|
echo "[WARN] command completed but output was empty"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[PASS] p2p-peers finished"
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# This test should be run with P2P client, please refer to the test-p2p-three-nodes-conflict-linux.sh test for more details.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CLI_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
cd "$CLI_DIR"
|
||||||
|
source "$SCRIPT_DIR/test-helpers.sh"
|
||||||
|
display_test_info
|
||||||
|
|
||||||
|
RUN_BUILD="${RUN_BUILD:-1}"
|
||||||
|
VERBOSE_TEST_LOGGING="${VERBOSE_TEST_LOGGING:-0}"
|
||||||
|
KEEP_TEST_DATA="${KEEP_TEST_DATA:-0}"
|
||||||
|
|
||||||
|
RELAY="${RELAY:-ws://localhost:4000/}"
|
||||||
|
USE_INTERNAL_RELAY="${USE_INTERNAL_RELAY:-1}"
|
||||||
|
ROOM_ID="${ROOM_ID:-1}"
|
||||||
|
PASSPHRASE="${PASSPHRASE:-test}"
|
||||||
|
APP_ID="${APP_ID:-self-hosted-livesync-cli-tests}"
|
||||||
|
PEERS_TIMEOUT="${PEERS_TIMEOUT:-12}"
|
||||||
|
SYNC_TIMEOUT="${SYNC_TIMEOUT:-15}"
|
||||||
|
TARGET_PEER="${TARGET_PEER:-}"
|
||||||
|
|
||||||
|
cli_test_init_cli_cmd
|
||||||
|
|
||||||
|
if [[ "$RUN_BUILD" == "1" ]]; then
|
||||||
|
echo "[INFO] building CLI"
|
||||||
|
npm run build
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/livesync-cli-p2p-sync.XXXXXX")"
|
||||||
|
VAULT="$WORK_DIR/vault-sync"
|
||||||
|
SETTINGS="$WORK_DIR/settings-sync.json"
|
||||||
|
mkdir -p "$VAULT"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
local exit_code=$?
|
||||||
|
if [[ "${P2P_RELAY_STARTED:-0}" == "1" ]]; then
|
||||||
|
cli_test_stop_p2p_relay
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$KEEP_TEST_DATA" != "1" ]]; then
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
else
|
||||||
|
echo "[INFO] KEEP_TEST_DATA=1, preserving artefacts at $WORK_DIR"
|
||||||
|
fi
|
||||||
|
exit "$exit_code"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
if [[ "$USE_INTERNAL_RELAY" == "1" ]]; then
|
||||||
|
if cli_test_is_local_p2p_relay "$RELAY"; then
|
||||||
|
cli_test_start_p2p_relay
|
||||||
|
P2P_RELAY_STARTED=1
|
||||||
|
else
|
||||||
|
echo "[INFO] USE_INTERNAL_RELAY=1 but RELAY is not local ($RELAY), skipping local relay startup"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[INFO] preparing settings"
|
||||||
|
echo "[INFO] relay=$RELAY room=$ROOM_ID app=$APP_ID"
|
||||||
|
cli_test_init_settings_file "$SETTINGS"
|
||||||
|
cli_test_apply_p2p_settings "$SETTINGS" "$ROOM_ID" "$PASSPHRASE" "$APP_ID" "$RELAY"
|
||||||
|
|
||||||
|
echo "[CASE] discover peers"
|
||||||
|
PEER_LINES="$(run_cli "$VAULT" --settings "$SETTINGS" p2p-peers "$PEERS_TIMEOUT")"
|
||||||
|
if [[ -z "$PEER_LINES" ]]; then
|
||||||
|
echo "[FAIL] p2p-peers returned empty output" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! awk -F $'\t' 'NF>=3 && $1=="[peer]" { found=1 } END { exit(found ? 0 : 1) }' <<< "$PEER_LINES"; then
|
||||||
|
echo "[FAIL] p2p-peers output must include [peer]<TAB><peer-id><TAB><peer-name>" >&2
|
||||||
|
echo "$PEER_LINES" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SELECTED_PEER_ID=""
|
||||||
|
SELECTED_PEER_NAME=""
|
||||||
|
|
||||||
|
if [[ -n "$TARGET_PEER" ]]; then
|
||||||
|
while IFS=$'\t' read -r marker peer_id peer_name _; do
|
||||||
|
if [[ "$marker" != "[peer]" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ "$peer_id" == "$TARGET_PEER" || "$peer_name" == "$TARGET_PEER" ]]; then
|
||||||
|
SELECTED_PEER_ID="$peer_id"
|
||||||
|
SELECTED_PEER_NAME="$peer_name"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done <<< "$PEER_LINES"
|
||||||
|
|
||||||
|
if [[ -z "$SELECTED_PEER_ID" ]]; then
|
||||||
|
echo "[FAIL] TARGET_PEER=$TARGET_PEER was not found" >&2
|
||||||
|
echo "$PEER_LINES" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
SELECTED_PEER_ID="$(awk -F $'\t' 'NF>=3 && $1=="[peer]" {print $2; exit}' <<< "$PEER_LINES")"
|
||||||
|
SELECTED_PEER_NAME="$(awk -F $'\t' 'NF>=3 && $1=="[peer]" {print $3; exit}' <<< "$PEER_LINES")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$SELECTED_PEER_ID" ]]; then
|
||||||
|
echo "[FAIL] could not extract peer-id from p2p-peers output" >&2
|
||||||
|
echo "$PEER_LINES" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[PASS] selected peer: ${SELECTED_PEER_ID} (${SELECTED_PEER_NAME:-unknown})"
|
||||||
|
|
||||||
|
echo "[CASE] run p2p-sync"
|
||||||
|
run_cli "$VAULT" --settings "$SETTINGS" p2p-sync "$SELECTED_PEER_ID" "$SYNC_TIMEOUT" >/dev/null
|
||||||
|
|
||||||
|
echo "[PASS] p2p-sync completed"
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CLI_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
cd "$CLI_DIR"
|
||||||
|
source "$SCRIPT_DIR/test-helpers.sh"
|
||||||
|
display_test_info
|
||||||
|
|
||||||
|
RUN_BUILD="${RUN_BUILD:-1}"
|
||||||
|
KEEP_TEST_DATA="${KEEP_TEST_DATA:-0}"
|
||||||
|
VERBOSE_TEST_LOGGING="${VERBOSE_TEST_LOGGING:-0}"
|
||||||
|
|
||||||
|
RELAY="${RELAY:-ws://localhost:4000/}"
|
||||||
|
USE_INTERNAL_RELAY="${USE_INTERNAL_RELAY:-1}"
|
||||||
|
ROOM_ID_PREFIX="${ROOM_ID_PREFIX:-p2p-room}"
|
||||||
|
PASSPHRASE_PREFIX="${PASSPHRASE_PREFIX:-p2p-pass}"
|
||||||
|
APP_ID="${APP_ID:-self-hosted-livesync-cli-tests}"
|
||||||
|
PEERS_TIMEOUT="${PEERS_TIMEOUT:-10}"
|
||||||
|
SYNC_TIMEOUT="${SYNC_TIMEOUT:-15}"
|
||||||
|
|
||||||
|
ROOM_ID="${ROOM_ID_PREFIX}-$(date +%s)-$RANDOM-$RANDOM"
|
||||||
|
PASSPHRASE="${PASSPHRASE_PREFIX}-$(date +%s)-$RANDOM-$RANDOM"
|
||||||
|
|
||||||
|
cli_test_init_cli_cmd
|
||||||
|
|
||||||
|
if [[ "$RUN_BUILD" == "1" ]]; then
|
||||||
|
echo "[INFO] building CLI"
|
||||||
|
npm run build
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/livesync-cli-p2p-3nodes.XXXXXX")"
|
||||||
|
VAULT_A="$WORK_DIR/vault-a"
|
||||||
|
VAULT_B="$WORK_DIR/vault-b"
|
||||||
|
VAULT_C="$WORK_DIR/vault-c"
|
||||||
|
SETTINGS_A="$WORK_DIR/settings-a.json"
|
||||||
|
SETTINGS_B="$WORK_DIR/settings-b.json"
|
||||||
|
SETTINGS_C="$WORK_DIR/settings-c.json"
|
||||||
|
HOST_LOG="$WORK_DIR/p2p-host.log"
|
||||||
|
|
||||||
|
mkdir -p "$VAULT_A" "$VAULT_B" "$VAULT_C"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
local exit_code=$?
|
||||||
|
if [[ -n "${HOST_PID:-}" ]] && kill -0 "$HOST_PID" >/dev/null 2>&1; then
|
||||||
|
kill -TERM "$HOST_PID" >/dev/null 2>&1 || true
|
||||||
|
wait "$HOST_PID" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${P2P_RELAY_STARTED:-0}" == "1" ]]; then
|
||||||
|
cli_test_stop_p2p_relay
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$KEEP_TEST_DATA" != "1" ]]; then
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
else
|
||||||
|
echo "[INFO] KEEP_TEST_DATA=1, preserving artefacts at $WORK_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit "$exit_code"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
if [[ "$USE_INTERNAL_RELAY" == "1" ]]; then
|
||||||
|
if cli_test_is_local_p2p_relay "$RELAY"; then
|
||||||
|
cli_test_start_p2p_relay
|
||||||
|
P2P_RELAY_STARTED=1
|
||||||
|
else
|
||||||
|
echo "[INFO] USE_INTERNAL_RELAY=1 but RELAY is not local ($RELAY), skipping local relay startup"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_cli_a() {
|
||||||
|
run_cli "$VAULT_A" --settings "$SETTINGS_A" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_cli_b() {
|
||||||
|
run_cli "$VAULT_B" --settings "$SETTINGS_B" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_cli_c() {
|
||||||
|
run_cli "$VAULT_C" --settings "$SETTINGS_C" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "[INFO] preparing settings"
|
||||||
|
echo "[INFO] relay=$RELAY room=$ROOM_ID app=$APP_ID"
|
||||||
|
cli_test_init_settings_file "$SETTINGS_A"
|
||||||
|
cli_test_init_settings_file "$SETTINGS_B"
|
||||||
|
cli_test_init_settings_file "$SETTINGS_C"
|
||||||
|
cli_test_apply_p2p_settings "$SETTINGS_A" "$ROOM_ID" "$PASSPHRASE" "$APP_ID" "$RELAY"
|
||||||
|
cli_test_apply_p2p_settings "$SETTINGS_B" "$ROOM_ID" "$PASSPHRASE" "$APP_ID" "$RELAY"
|
||||||
|
cli_test_apply_p2p_settings "$SETTINGS_C" "$ROOM_ID" "$PASSPHRASE" "$APP_ID" "$RELAY"
|
||||||
|
|
||||||
|
echo "[CASE] start p2p-host on A"
|
||||||
|
run_cli_a p2p-host >"$HOST_LOG" 2>&1 &
|
||||||
|
HOST_PID=$!
|
||||||
|
|
||||||
|
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||||
|
echo "[INFO] waiting for p2p-host to start..."
|
||||||
|
if grep -Fq "P2P host is running" "$HOST_LOG"; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
if ! grep -Fq "P2P host is running" "$HOST_LOG"; then
|
||||||
|
echo "[FAIL] p2p-host did not become ready" >&2
|
||||||
|
cat "$HOST_LOG" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "[PASS] p2p-host started"
|
||||||
|
|
||||||
|
echo "[CASE] discover host peer from B"
|
||||||
|
PEERS_FROM_B="$(run_cli_b p2p-peers "$PEERS_TIMEOUT")"
|
||||||
|
HOST_PEER_ID="$(awk -F $'\t' 'NF>=3 && $1=="[peer]" {print $2; exit}' <<< "$PEERS_FROM_B")"
|
||||||
|
if [[ -z "$HOST_PEER_ID" ]]; then
|
||||||
|
echo "[FAIL] B could not find host peer" >&2
|
||||||
|
echo "$PEERS_FROM_B" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "[PASS] B discovered host peer: $HOST_PEER_ID"
|
||||||
|
|
||||||
|
echo "[CASE] discover host peer from C"
|
||||||
|
PEERS_FROM_C="$(run_cli_c p2p-peers "$PEERS_TIMEOUT")"
|
||||||
|
HOST_PEER_ID_FROM_C="$(awk -F $'\t' 'NF>=3 && $1=="[peer]" {print $2; exit}' <<< "$PEERS_FROM_C")"
|
||||||
|
if [[ -z "$HOST_PEER_ID_FROM_C" ]]; then
|
||||||
|
echo "[FAIL] C could not find host peer" >&2
|
||||||
|
echo "$PEERS_FROM_C" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "[PASS] C discovered host peer: $HOST_PEER_ID_FROM_C"
|
||||||
|
|
||||||
|
TARGET_PATH="p2p/conflicted-from-two-clients.txt"
|
||||||
|
|
||||||
|
echo "[CASE] B creates file and syncs"
|
||||||
|
printf 'from-client-b-v1\n' | run_cli_b put "$TARGET_PATH" >/dev/null
|
||||||
|
run_cli_b p2p-sync "$HOST_PEER_ID" "$SYNC_TIMEOUT" >/dev/null
|
||||||
|
|
||||||
|
echo "[CASE] C syncs and can see B file"
|
||||||
|
run_cli_c p2p-sync "$HOST_PEER_ID_FROM_C" "$SYNC_TIMEOUT" >/dev/null
|
||||||
|
VISIBLE_ON_C=""
|
||||||
|
for _ in 1 2 3 4 5; do
|
||||||
|
if VISIBLE_ON_C="$(run_cli_c cat "$TARGET_PATH" 2>/dev/null | cli_test_sanitise_cat_stdout)"; then
|
||||||
|
if [[ "$VISIBLE_ON_C" == "from-client-b-v1" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
run_cli_c p2p-sync "$HOST_PEER_ID_FROM_C" "$SYNC_TIMEOUT" >/dev/null
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
cli_test_assert_equal "from-client-b-v1" "$VISIBLE_ON_C" "C should see file created by B"
|
||||||
|
|
||||||
|
echo "[CASE] B and C modify file independently"
|
||||||
|
printf 'from-client-b-v2\n' | run_cli_b put "$TARGET_PATH" >/dev/null
|
||||||
|
printf 'from-client-c-v2\n' | run_cli_c put "$TARGET_PATH" >/dev/null
|
||||||
|
|
||||||
|
echo "[CASE] B and C sync to host concurrently"
|
||||||
|
set +e
|
||||||
|
run_cli_b p2p-sync "$HOST_PEER_ID" "$SYNC_TIMEOUT" >/dev/null &
|
||||||
|
SYNC_B_PID=$!
|
||||||
|
run_cli_c p2p-sync "$HOST_PEER_ID_FROM_C" "$SYNC_TIMEOUT" >/dev/null &
|
||||||
|
SYNC_C_PID=$!
|
||||||
|
wait "$SYNC_B_PID"
|
||||||
|
SYNC_B_EXIT=$?
|
||||||
|
wait "$SYNC_C_PID"
|
||||||
|
SYNC_C_EXIT=$?
|
||||||
|
set -e
|
||||||
|
if [[ "$SYNC_B_EXIT" -ne 0 || "$SYNC_C_EXIT" -ne 0 ]]; then
|
||||||
|
echo "[FAIL] concurrent sync failed: B=$SYNC_B_EXIT C=$SYNC_C_EXIT" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[CASE] sync back to clients"
|
||||||
|
run_cli_b p2p-sync "$HOST_PEER_ID" "$SYNC_TIMEOUT" >/dev/null
|
||||||
|
run_cli_c p2p-sync "$HOST_PEER_ID_FROM_C" "$SYNC_TIMEOUT" >/dev/null
|
||||||
|
|
||||||
|
echo "[CASE] B info shows conflict"
|
||||||
|
INFO_JSON_B_BEFORE="$(run_cli_b info "$TARGET_PATH")"
|
||||||
|
CONFLICTS_B_BEFORE="$(printf '%s' "$INFO_JSON_B_BEFORE" | cli_test_json_string_field_from_stdin conflicts)"
|
||||||
|
KEEP_REV_B="$(printf '%s' "$INFO_JSON_B_BEFORE" | cli_test_json_string_field_from_stdin revision)"
|
||||||
|
if [[ "$CONFLICTS_B_BEFORE" == "N/A" || -z "$CONFLICTS_B_BEFORE" ]]; then
|
||||||
|
echo "[FAIL] expected conflicts on B after two-client sync" >&2
|
||||||
|
echo "$INFO_JSON_B_BEFORE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -z "$KEEP_REV_B" ]]; then
|
||||||
|
echo "[FAIL] could not read current revision on B for resolve" >&2
|
||||||
|
echo "$INFO_JSON_B_BEFORE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "[PASS] conflict detected on B"
|
||||||
|
|
||||||
|
echo "[CASE] C info shows conflict"
|
||||||
|
INFO_JSON_C_BEFORE="$(run_cli_c info "$TARGET_PATH")"
|
||||||
|
CONFLICTS_C_BEFORE="$(printf '%s' "$INFO_JSON_C_BEFORE" | cli_test_json_string_field_from_stdin conflicts)"
|
||||||
|
KEEP_REV_C="$(printf '%s' "$INFO_JSON_C_BEFORE" | cli_test_json_string_field_from_stdin revision)"
|
||||||
|
if [[ "$CONFLICTS_C_BEFORE" == "N/A" || -z "$CONFLICTS_C_BEFORE" ]]; then
|
||||||
|
echo "[FAIL] expected conflicts on C after two-client sync" >&2
|
||||||
|
echo "$INFO_JSON_C_BEFORE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -z "$KEEP_REV_C" ]]; then
|
||||||
|
echo "[FAIL] could not read current revision on C for resolve" >&2
|
||||||
|
echo "$INFO_JSON_C_BEFORE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "[PASS] conflict detected on C"
|
||||||
|
|
||||||
|
echo "[CASE] resolve conflict on B and C"
|
||||||
|
run_cli_b resolve "$TARGET_PATH" "$KEEP_REV_B" >/dev/null
|
||||||
|
run_cli_c resolve "$TARGET_PATH" "$KEEP_REV_C" >/dev/null
|
||||||
|
|
||||||
|
INFO_JSON_B_AFTER="$(run_cli_b info "$TARGET_PATH")"
|
||||||
|
CONFLICTS_B_AFTER="$(printf '%s' "$INFO_JSON_B_AFTER" | cli_test_json_string_field_from_stdin conflicts)"
|
||||||
|
if [[ "$CONFLICTS_B_AFTER" != "N/A" ]]; then
|
||||||
|
echo "[FAIL] conflict still remains on B after resolve" >&2
|
||||||
|
echo "$INFO_JSON_B_AFTER" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
INFO_JSON_C_AFTER="$(run_cli_c info "$TARGET_PATH")"
|
||||||
|
CONFLICTS_C_AFTER="$(printf '%s' "$INFO_JSON_C_AFTER" | cli_test_json_string_field_from_stdin conflicts)"
|
||||||
|
if [[ "$CONFLICTS_C_AFTER" != "N/A" ]]; then
|
||||||
|
echo "[FAIL] conflict still remains on C after resolve" >&2
|
||||||
|
echo "$INFO_JSON_C_AFTER" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
FINAL_CONTENT_B="$(run_cli_b cat "$TARGET_PATH" | cli_test_sanitise_cat_stdout)"
|
||||||
|
FINAL_CONTENT_C="$(run_cli_c cat "$TARGET_PATH" | cli_test_sanitise_cat_stdout)"
|
||||||
|
if [[ "$FINAL_CONTENT_B" != "from-client-b-v2" && "$FINAL_CONTENT_B" != "from-client-c-v2" ]]; then
|
||||||
|
echo "[FAIL] unexpected final content on B after resolve" >&2
|
||||||
|
echo "[FAIL] final content on B: $FINAL_CONTENT_B" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$FINAL_CONTENT_C" != "from-client-b-v2" && "$FINAL_CONTENT_C" != "from-client-c-v2" ]]; then
|
||||||
|
echo "[FAIL] unexpected final content on C after resolve" >&2
|
||||||
|
echo "[FAIL] final content on C: $FINAL_CONTENT_C" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[PASS] conflicts resolved on B and C"
|
||||||
|
echo "[PASS] all 3-node P2P conflict scenarios passed"
|
||||||
Executable
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "P2P Init - No additional initialization required."
|
||||||
Executable
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
docker run -d --name relay-test -p 4000:8080 scsibug/nostr-rs-relay:latest
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
docker stop relay-test
|
||||||
|
docker rm relay-test
|
||||||
@@ -5,7 +5,16 @@ import { readFileSync } from "node:fs";
|
|||||||
const packageJson = JSON.parse(readFileSync("../../../package.json", "utf-8"));
|
const packageJson = JSON.parse(readFileSync("../../../package.json", "utf-8"));
|
||||||
const manifestJson = JSON.parse(readFileSync("../../../manifest.json", "utf-8"));
|
const manifestJson = JSON.parse(readFileSync("../../../manifest.json", "utf-8"));
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
const defaultExternal = ["obsidian", "electron", "crypto", "pouchdb-adapter-leveldb", "commander", "punycode"];
|
const defaultExternal = [
|
||||||
|
"obsidian",
|
||||||
|
"electron",
|
||||||
|
"crypto",
|
||||||
|
"pouchdb-adapter-leveldb",
|
||||||
|
"commander",
|
||||||
|
"punycode",
|
||||||
|
"node-datachannel",
|
||||||
|
"node-datachannel/polyfill",
|
||||||
|
];
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [svelte()],
|
plugins: [svelte()],
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -43,6 +52,7 @@ export default defineConfig({
|
|||||||
if (id === "fs" || id === "fs/promises" || id === "path" || id === "crypto" || id === "worker_threads")
|
if (id === "fs" || id === "fs/promises" || id === "path" || id === "crypto" || id === "worker_threads")
|
||||||
return true;
|
return true;
|
||||||
if (id.startsWith("pouchdb-")) return true;
|
if (id.startsWith("pouchdb-")) return true;
|
||||||
|
if (id.startsWith("node-datachannel")) return true;
|
||||||
if (id.startsWith("node:")) return true;
|
if (id.startsWith("node:")) return true;
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
Submodule src/lib updated: f77404c926...90da1d4fb4
+3
-1
@@ -5,9 +5,11 @@ The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsid
|
|||||||
|
|
||||||
## -- unreleased --
|
## -- unreleased --
|
||||||
|
|
||||||
### New features
|
### CLI new features
|
||||||
|
|
||||||
- `mirror` command has been added to the CLI. This command is intended to mirror the storage to the local database.
|
- `mirror` command has been added to the CLI. This command is intended to mirror the storage to the local database.
|
||||||
|
- `p2p-sync`, `p2p-peers`, and `p2p-host` commands have been added to the CLI. These commands are intended for P2P synchronisation.
|
||||||
|
- Yes, no more need for a [LiveSync PeerServer](https://github.com/vrtmrz/livesync-serverpeer) for virtual environments! The CLI can handle it by itself.
|
||||||
|
|
||||||
## 0.25.52-patched-1
|
## 0.25.52-patched-1
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user