mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-11 02:01:52 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4354cc3054 | ||
|
|
0664427c63 | ||
|
|
49c4736d69 | ||
|
|
f0ce8f0e05 | ||
|
|
0a70afc5a3 | ||
|
|
431239a736 | ||
|
|
1ceb671683 | ||
|
|
ea40e5918c | ||
|
|
64681729ff | ||
|
|
830f2f25d1 | ||
|
|
05f0abebf0 | ||
|
|
842da980d7 | ||
|
|
d8ecbb593b | ||
|
|
8d66c372e1 |
78
.github/ISSUE_TEMPLATE/issue-report.md
vendored
Normal file
78
.github/ISSUE_TEMPLATE/issue-report.md
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
name: Issue report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Thank you for taking the time to report this issue!
|
||||||
|
To improve the process, I would like to ask you to let me know the information in advance.
|
||||||
|
|
||||||
|
All instructions and examples, and empty entries can be deleted.
|
||||||
|
Just for your information, a [filled example](https://docs.vrtmrz.net/LiveSync/hintandtrivia/Issue+example) is also written.
|
||||||
|
|
||||||
|
## Abstract
|
||||||
|
The synchronisation hung up immediately after connecting.
|
||||||
|
|
||||||
|
## Expected behaviour
|
||||||
|
- Synchronisation ends with the message `Replication completed`
|
||||||
|
- Everything synchronised
|
||||||
|
|
||||||
|
## Actually happened
|
||||||
|
- Synchronisation has been cancelled with the message `TypeError ... ` (captured in the attached log, around LL.10-LL.12)
|
||||||
|
- No files synchronised
|
||||||
|
|
||||||
|
## Reproducing procedure
|
||||||
|
|
||||||
|
1. Configure LiveSync as in the attached material.
|
||||||
|
2. Click the replication button on the ribbon.
|
||||||
|
3. Synchronising has begun.
|
||||||
|
4. About two or three seconds later, we got the error `TypeError ... `.
|
||||||
|
5. Replication has been stopped. No files synchronised.
|
||||||
|
|
||||||
|
Note: If you do not catch the reproducing procedure, please let me know the frequency and signs.
|
||||||
|
|
||||||
|
## Report materials
|
||||||
|
If the information is not available, do not hesitate to report it as it is. You can also of course omit it if you think this is indeed unnecessary. If it is necessary, I will ask you.
|
||||||
|
|
||||||
|
### Report from the LiveSync
|
||||||
|
For more information, please refer to [Making the report](https://docs.vrtmrz.net/LiveSync/hintandtrivia/Making+the+report).
|
||||||
|
<details>
|
||||||
|
<summary>Report from hatch</summary>
|
||||||
|
|
||||||
|
```
|
||||||
|
<!-- paste here -->
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Obsidian debug info
|
||||||
|
<details>
|
||||||
|
<summary>Debug info</summary>
|
||||||
|
|
||||||
|
```
|
||||||
|
<!-- paste here -->
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Plug-in log
|
||||||
|
We can see the log by tapping the Document box icon. If you noticed something suspicious, please let me know.
|
||||||
|
Note: **Please enable `Verbose Log`**. For detail, refer to [Logging](https://docs.vrtmrz.net/LiveSync/hintandtrivia/Logging), please.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Plug-in log</summary>
|
||||||
|
|
||||||
|
```
|
||||||
|
<!-- paste here -->
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Network log
|
||||||
|
Network logs displayed in DevTools will possibly help with connection-related issues. To capture that, please refer to [DevTools](https://docs.vrtmrz.net/LiveSync/hintandtrivia/DevTools).
|
||||||
|
|
||||||
|
### Screenshots
|
||||||
|
If applicable, please add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
### Other information, insights and intuition.
|
||||||
|
Please provide any additional context or information about the problem.
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
# Setup CouchDB to your server
|
# Setup a CouchDB server
|
||||||
|
|
||||||
|
## Configure
|
||||||
|
|
||||||
## Install CouchDB and access from a PC or Mac
|
The easiest way to set up a CouchDB instance is using the official [docker image](https://hub.docker.com/_/couchdb).
|
||||||
|
|
||||||
The easiest way to set up the CouchDB is using the [docker image]((https://hub.docker.com/_/couchdb)).
|
Some initial configuration is required. Create a `local.ini` to use Self-hosted LiveSync as follows:
|
||||||
|
|
||||||
But some additional configurations are required in `local.ini` to use from Self-hosted LiveSync, like below:
|
```ini
|
||||||
|
|
||||||
```
|
|
||||||
[couchdb]
|
[couchdb]
|
||||||
single_node=true
|
single_node=true
|
||||||
max_document_size = 50000000
|
max_document_size = 50000000
|
||||||
@@ -32,28 +31,62 @@ methods = GET, PUT, POST, HEAD, DELETE
|
|||||||
max_age = 3600
|
max_age = 3600
|
||||||
```
|
```
|
||||||
|
|
||||||
Make `local.ini` and run with docker run like this, you can launch the CouchDB.
|
## Run
|
||||||
|
|
||||||
|
### Docker CLI
|
||||||
|
|
||||||
|
You can launch CouchDB using your `local.ini` like this:
|
||||||
```
|
```
|
||||||
$ docker run --rm -it -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password -v /path/to/local.ini:/opt/couchdb/etc/local.ini -p 5984:5984 couchdb
|
$ docker run --rm -it -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password -v /path/to/local.ini:/opt/couchdb/etc/local.ini -p 5984:5984 couchdb
|
||||||
```
|
```
|
||||||
*Remember to replace the path with the path to your local.ini*
|
*Remember to replace the path with the path to your local.ini*
|
||||||
Note: At this time, the file owner of local.ini became 5984:5984. It's the limitation docker image. please change the owner before editing local.ini again.
|
|
||||||
|
|
||||||
If you could confirm that Self-hosted LiveSync can sync with the server, launch the docker image as a background as you like.
|
Run in detached mode:
|
||||||
|
|
||||||
Example to run docker in detached mode:
|
|
||||||
```
|
```
|
||||||
$ docker run -d --restart always -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password -v /path/to/local.ini:/opt/couchdb/etc/local.ini -p 5984:5984 couchdb
|
$ docker run -d --restart always -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password -v /path/to/local.ini:/opt/couchdb/etc/local.ini -p 5984:5984 couchdb
|
||||||
```
|
```
|
||||||
*Remember to replace the path with the path to your local.ini*
|
*Remember to replace the path with the path to your local.ini*
|
||||||
|
|
||||||
|
### Docker Compose
|
||||||
|
Create a directory, place your `local.ini` within it, and create a `docker-compose.yml` alongside it. The directory structure should look similar to this:
|
||||||
|
```
|
||||||
|
obsidian-livesync
|
||||||
|
├── docker-compose.yml
|
||||||
|
└── local.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
A good place to start for `docker-compose.yml`:
|
||||||
|
```yaml
|
||||||
|
version: "2.1"
|
||||||
|
services:
|
||||||
|
couchdb:
|
||||||
|
image: couchdb
|
||||||
|
container_name: obsidian-livesync
|
||||||
|
user: 1000:1000
|
||||||
|
environment:
|
||||||
|
- COUCHDB_USER=admin
|
||||||
|
- COUCHDB_PASSWORD=password
|
||||||
|
volumes:
|
||||||
|
- ./data:/opt/couchdb/data
|
||||||
|
- ./local.ini:/opt/couchdb/etc/local.ini
|
||||||
|
ports:
|
||||||
|
- 5984:5984
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
And finally launch the container
|
||||||
|
```
|
||||||
|
# -d will launch detached so the container runs in background
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
## Access from a mobile device
|
## Access from a mobile device
|
||||||
If you want to access Self-hosted LiveSync from mobile devices, you need a valid SSL certificate.
|
If you want to access Self-hosted LiveSync from mobile devices, you need a valid SSL certificate.
|
||||||
|
|
||||||
### Testing from a mobile
|
### Testing from a mobile
|
||||||
In the testing phase, [localhost.run](http://localhost.run/) or something like services is very useful.
|
In the testing phase, [localhost.run](https://localhost.run/) or something like services is very useful.
|
||||||
|
|
||||||
example on using localhost.run)
|
example using localhost.run:
|
||||||
```
|
```
|
||||||
$ ssh -R 80:localhost:5984 nokey@localhost.run
|
$ ssh -R 80:localhost:5984 nokey@localhost.run
|
||||||
Warning: Permanently added the RSA host key for IP address '35.171.254.69' to the list of known hosts.
|
Warning: Permanently added the RSA host key for IP address '35.171.254.69' to the list of known hosts.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.19.2",
|
"version": "0.19.6",
|
||||||
"minAppVersion": "0.9.12",
|
"minAppVersion": "0.9.12",
|
||||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"author": "vorotamoroz",
|
"author": "vorotamoroz",
|
||||||
|
|||||||
112
package-lock.json
generated
112
package-lock.json
generated
@@ -1,22 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.19.2",
|
"version": "0.19.6",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.19.2",
|
"version": "0.19.6",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
"esbuild": "0.15.15",
|
|
||||||
"esbuild-svelte": "^0.7.3",
|
|
||||||
"idb": "^7.1.1",
|
"idb": "^7.1.1",
|
||||||
"xxhash-wasm": "^0.4.2"
|
"xxhash-wasm": "^0.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tsconfig/svelte": "^4.0.1",
|
||||||
"@types/diff-match-patch": "^1.0.32",
|
"@types/diff-match-patch": "^1.0.32",
|
||||||
|
"@types/node": "^20.2.5",
|
||||||
"@types/pouchdb": "^6.4.0",
|
"@types/pouchdb": "^6.4.0",
|
||||||
"@types/pouchdb-browser": "^6.1.3",
|
"@types/pouchdb-browser": "^6.1.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||||
@@ -39,11 +39,11 @@
|
|||||||
"pouchdb-mapreduce": "^8.0.1",
|
"pouchdb-mapreduce": "^8.0.1",
|
||||||
"pouchdb-replication": "^8.0.1",
|
"pouchdb-replication": "^8.0.1",
|
||||||
"pouchdb-utils": "^8.0.1",
|
"pouchdb-utils": "^8.0.1",
|
||||||
"svelte": "^3.55.1",
|
"svelte": "^3.59.1",
|
||||||
"svelte-preprocess": "^5.0.1",
|
"svelte-preprocess": "^5.0.3",
|
||||||
"transform-pouch": "^2.0.0",
|
"transform-pouch": "^2.0.0",
|
||||||
"tslib": "^2.5.0",
|
"tslib": "^2.5.0",
|
||||||
"typescript": "^4.9.5"
|
"typescript": "^5.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/state": {
|
"node_modules/@codemirror/state": {
|
||||||
@@ -203,6 +203,12 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tsconfig/svelte": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-B+XlGpmuAQzJqDoBATNCvEPqQg0HkO7S8pM14QDI5NsmtymzRexQ1N+nX2H6RTtFbuFgaZD4I8AAi8voGg0GLg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/codemirror": {
|
"node_modules/@types/codemirror": {
|
||||||
"version": "0.0.108",
|
"version": "0.0.108",
|
||||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.108.tgz",
|
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.108.tgz",
|
||||||
@@ -252,9 +258,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "14.17.21",
|
"version": "20.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz",
|
||||||
"integrity": "sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA==",
|
"integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/pouchdb": {
|
"node_modules/@types/pouchdb": {
|
||||||
@@ -443,15 +449,6 @@
|
|||||||
"integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==",
|
"integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/sass": {
|
|
||||||
"version": "1.43.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.43.1.tgz",
|
|
||||||
"integrity": "sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/semver": {
|
"node_modules/@types/semver": {
|
||||||
"version": "7.3.13",
|
"version": "7.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
|
||||||
@@ -3719,23 +3716,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte": {
|
"node_modules/svelte": {
|
||||||
"version": "3.55.1",
|
"version": "3.59.1",
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.1.tgz",
|
||||||
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==",
|
"integrity": "sha512-pKj8fEBmqf6mq3/NfrB9SLtcJcUvjYSWyePlfCqN9gujLB25RitWK8PvFzlwim6hD/We35KbPlRteuA6rnPGcQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte-preprocess": {
|
"node_modules/svelte-preprocess": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.0.4.tgz",
|
||||||
"integrity": "sha512-0HXyhCoc9rsW4zGOgtInylC6qj259E1hpFnJMJWTf+aIfeqh4O/QHT31KT2hvPEqQfdjmqBR/kO2JDkkciBLrQ==",
|
"integrity": "sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/pug": "^2.0.6",
|
"@types/pug": "^2.0.6",
|
||||||
"@types/sass": "^1.43.1",
|
|
||||||
"detect-indent": "^6.1.0",
|
"detect-indent": "^6.1.0",
|
||||||
"magic-string": "^0.27.0",
|
"magic-string": "^0.27.0",
|
||||||
"sorcery": "^0.11.0",
|
"sorcery": "^0.11.0",
|
||||||
@@ -3754,8 +3750,8 @@
|
|||||||
"sass": "^1.26.8",
|
"sass": "^1.26.8",
|
||||||
"stylus": "^0.55.0",
|
"stylus": "^0.55.0",
|
||||||
"sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
|
"sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
|
||||||
"svelte": "^3.23.0",
|
"svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0",
|
||||||
"typescript": "^3.9.5 || ^4.0.0"
|
"typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@babel/core": {
|
"@babel/core": {
|
||||||
@@ -3916,16 +3912,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.9.5",
|
"version": "5.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
|
||||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
"integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.2.0"
|
"node": ">=12.20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/unbox-primitive": {
|
"node_modules/unbox-primitive": {
|
||||||
@@ -4087,9 +4083,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/yaml": {
|
"node_modules/yaml": {
|
||||||
"version": "2.1.3",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
|
||||||
"integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==",
|
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
@@ -4233,6 +4229,12 @@
|
|||||||
"fastq": "^1.6.0"
|
"fastq": "^1.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@tsconfig/svelte": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-B+XlGpmuAQzJqDoBATNCvEPqQg0HkO7S8pM14QDI5NsmtymzRexQ1N+nX2H6RTtFbuFgaZD4I8AAi8voGg0GLg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/codemirror": {
|
"@types/codemirror": {
|
||||||
"version": "0.0.108",
|
"version": "0.0.108",
|
||||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.108.tgz",
|
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.108.tgz",
|
||||||
@@ -4282,9 +4284,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "14.17.21",
|
"version": "20.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz",
|
||||||
"integrity": "sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA==",
|
"integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/pouchdb": {
|
"@types/pouchdb": {
|
||||||
@@ -4473,15 +4475,6 @@
|
|||||||
"integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==",
|
"integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/sass": {
|
|
||||||
"version": "1.43.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.43.1.tgz",
|
|
||||||
"integrity": "sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/semver": {
|
"@types/semver": {
|
||||||
"version": "7.3.13",
|
"version": "7.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
|
||||||
@@ -6787,19 +6780,18 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"svelte": {
|
"svelte": {
|
||||||
"version": "3.55.1",
|
"version": "3.59.1",
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.1.tgz",
|
||||||
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==",
|
"integrity": "sha512-pKj8fEBmqf6mq3/NfrB9SLtcJcUvjYSWyePlfCqN9gujLB25RitWK8PvFzlwim6hD/We35KbPlRteuA6rnPGcQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"svelte-preprocess": {
|
"svelte-preprocess": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.0.4.tgz",
|
||||||
"integrity": "sha512-0HXyhCoc9rsW4zGOgtInylC6qj259E1hpFnJMJWTf+aIfeqh4O/QHT31KT2hvPEqQfdjmqBR/kO2JDkkciBLrQ==",
|
"integrity": "sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/pug": "^2.0.6",
|
"@types/pug": "^2.0.6",
|
||||||
"@types/sass": "^1.43.1",
|
|
||||||
"detect-indent": "^6.1.0",
|
"detect-indent": "^6.1.0",
|
||||||
"magic-string": "^0.27.0",
|
"magic-string": "^0.27.0",
|
||||||
"sorcery": "^0.11.0",
|
"sorcery": "^0.11.0",
|
||||||
@@ -6910,9 +6902,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.9.5",
|
"version": "5.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
|
||||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
"integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"unbox-primitive": {
|
"unbox-primitive": {
|
||||||
@@ -7047,9 +7039,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"yaml": {
|
"yaml": {
|
||||||
"version": "2.1.3",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
|
||||||
"integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==",
|
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"yocto-queue": {
|
"yocto-queue": {
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.19.2",
|
"version": "0.19.6",
|
||||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -13,7 +13,9 @@
|
|||||||
"author": "vorotamoroz",
|
"author": "vorotamoroz",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tsconfig/svelte": "^4.0.1",
|
||||||
"@types/diff-match-patch": "^1.0.32",
|
"@types/diff-match-patch": "^1.0.32",
|
||||||
|
"@types/node": "^20.2.5",
|
||||||
"@types/pouchdb": "^6.4.0",
|
"@types/pouchdb": "^6.4.0",
|
||||||
"@types/pouchdb-browser": "^6.1.3",
|
"@types/pouchdb-browser": "^6.1.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||||
@@ -36,16 +38,14 @@
|
|||||||
"pouchdb-mapreduce": "^8.0.1",
|
"pouchdb-mapreduce": "^8.0.1",
|
||||||
"pouchdb-replication": "^8.0.1",
|
"pouchdb-replication": "^8.0.1",
|
||||||
"pouchdb-utils": "^8.0.1",
|
"pouchdb-utils": "^8.0.1",
|
||||||
"svelte": "^3.55.1",
|
"svelte": "^3.59.1",
|
||||||
"svelte-preprocess": "^5.0.1",
|
"svelte-preprocess": "^5.0.3",
|
||||||
"transform-pouch": "^2.0.0",
|
"transform-pouch": "^2.0.0",
|
||||||
"tslib": "^2.5.0",
|
"tslib": "^2.5.0",
|
||||||
"typescript": "^4.9.5"
|
"typescript": "^5.0.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
"esbuild": "0.15.15",
|
|
||||||
"esbuild-svelte": "^0.7.3",
|
|
||||||
"idb": "^7.1.1",
|
"idb": "^7.1.1",
|
||||||
"xxhash-wasm": "^0.4.2"
|
"xxhash-wasm": "^0.4.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { Notice, PluginManifest, stringifyYaml, parseYaml } from "./deps";
|
import { Notice, type PluginManifest, parseYaml } from "./deps";
|
||||||
|
|
||||||
import { EntryDoc, LoadedEntry, LOG_LEVEL, InternalFileEntry, FilePathWithPrefix, FilePath, DocumentID } from "./lib/src/types";
|
import type { EntryDoc, LoadedEntry, InternalFileEntry, FilePathWithPrefix, FilePath, DocumentID, AnyEntry } from "./lib/src/types";
|
||||||
|
import { LOG_LEVEL } from "./lib/src/types";
|
||||||
import { ICXHeader, PERIODIC_PLUGIN_SWEEP, } from "./types";
|
import { ICXHeader, PERIODIC_PLUGIN_SWEEP, } from "./types";
|
||||||
import { delay, getDocData } from "./lib/src/utils";
|
import { Parallels, delay, getDocData } from "./lib/src/utils";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
|
||||||
import { WrappedNotice } from "./lib/src/wrapper";
|
import { WrappedNotice } from "./lib/src/wrapper";
|
||||||
import { base64ToArrayBuffer, arrayBufferToBase64, readString, writeString, uint8ArrayToHexString } from "./lib/src/strbin";
|
import { base64ToArrayBuffer, arrayBufferToBase64, readString, uint8ArrayToHexString } from "./lib/src/strbin";
|
||||||
import { runWithLock } from "./lib/src/lock";
|
import { runWithLock } from "./lib/src/lock";
|
||||||
import { LiveSyncCommands } from "./LiveSyncCommands";
|
import { LiveSyncCommands } from "./LiveSyncCommands";
|
||||||
import { stripAllPrefixes } from "./lib/src/path";
|
import { stripAllPrefixes } from "./lib/src/path";
|
||||||
@@ -17,13 +17,29 @@ import { PluginDialogModal } from "./dialogs";
|
|||||||
import { JsonResolveModal } from "./JsonResolveModal";
|
import { JsonResolveModal } from "./JsonResolveModal";
|
||||||
|
|
||||||
|
|
||||||
|
function serialize<T>(obj: T): string {
|
||||||
|
return JSON.stringify(obj, null, 1);
|
||||||
|
}
|
||||||
|
function deserialize<T>(str: string, def: T) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(str) as T;
|
||||||
|
} catch (ex) {
|
||||||
|
try {
|
||||||
|
return parseYaml(str);
|
||||||
|
} catch (ex) {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const pluginList = writable([] as PluginDataExDisplay[]);
|
export const pluginList = writable([] as PluginDataExDisplay[]);
|
||||||
export const pluginIsEnumerating = writable(false);
|
export const pluginIsEnumerating = writable(false);
|
||||||
|
|
||||||
|
const encoder = new TextEncoder();
|
||||||
const hashString = (async (key: string) => {
|
const hashString = (async (key: string) => {
|
||||||
const buff = writeString(key);
|
// const buff = writeString(key);
|
||||||
|
const buff = encoder.encode(key);
|
||||||
const digest = await crypto.subtle.digest('SHA-256', buff);
|
const digest = await crypto.subtle.digest('SHA-256', buff);
|
||||||
return uint8ArrayToHexString(new Uint8Array(digest));
|
return uint8ArrayToHexString(new Uint8Array(digest));
|
||||||
})
|
})
|
||||||
@@ -145,7 +161,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
if (this.plugin.suspended) {
|
if (this.plugin.suspended) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.settings.autoSweepPlugins) {
|
if (this.settings.autoSweepPlugins && this.settings.usePluginSync) {
|
||||||
await this.scanAllConfigFiles(false);
|
await this.scanAllConfigFiles(false);
|
||||||
}
|
}
|
||||||
this.periodicPluginSweepProcessor.enable(this.settings.autoSweepPluginsPeriodic && !this.settings.watchInternalFileChanges ? (PERIODIC_PLUGIN_SWEEP * 1000) : 0);
|
this.periodicPluginSweepProcessor.enable(this.settings.autoSweepPluginsPeriodic && !this.settings.watchInternalFileChanges ? (PERIODIC_PLUGIN_SWEEP * 1000) : 0);
|
||||||
@@ -171,7 +187,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
const entries = [] as PluginDataExDisplay[]
|
const entries = [] as PluginDataExDisplay[]
|
||||||
const plugins = this.localDatabase.findEntries(ICXHeader + "", `${ICXHeader}\u{10ffff}`, { include_docs: true });
|
const plugins = this.localDatabase.findEntries(ICXHeader + "", `${ICXHeader}\u{10ffff}`, { include_docs: true });
|
||||||
const semaphore = Semaphore(4);
|
const semaphore = Semaphore(4);
|
||||||
const processes = [] as Promise<void>[];
|
const para = Parallels();
|
||||||
let count = 0;
|
let count = 0;
|
||||||
pluginIsEnumerating.set(true);
|
pluginIsEnumerating.set(true);
|
||||||
let processed = false;
|
let processed = false;
|
||||||
@@ -184,16 +200,18 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
processed = true;
|
processed = true;
|
||||||
const oldEntry = (this.pluginList.find(e => e.documentPath == path));
|
const oldEntry = (this.pluginList.find(e => e.documentPath == path));
|
||||||
if (oldEntry && oldEntry.mtime == plugin.mtime) continue;
|
if (oldEntry && oldEntry.mtime == plugin.mtime) continue;
|
||||||
processes.push((async (v) => {
|
await para.wait(5);
|
||||||
|
para.add((async (v) => {
|
||||||
|
|
||||||
const release = await semaphore.acquire(1);
|
const release = await semaphore.acquire(1);
|
||||||
try {
|
try {
|
||||||
Logger(`Enumerating files... ${count++}`, logLevel, "get-plugins");
|
count++;
|
||||||
|
if (count % 10 == 0) Logger(`Enumerating files... ${count}`, logLevel, "get-plugins");
|
||||||
|
|
||||||
Logger(`plugin-${path}`, LOG_LEVEL.VERBOSE);
|
Logger(`plugin-${path}`, LOG_LEVEL.VERBOSE);
|
||||||
const wx = await this.localDatabase.getDBEntry(path, null, false, false);
|
const wx = await this.localDatabase.getDBEntry(path, null, false, false);
|
||||||
if (wx) {
|
if (wx) {
|
||||||
const data = parseYaml(getDocData(wx.data)) as PluginDataEx;
|
const data = deserialize(getDocData(wx.data), {}) as PluginDataEx;
|
||||||
const xFiles = [] as PluginDataExFile[];
|
const xFiles = [] as PluginDataExFile[];
|
||||||
for (const file of data.files) {
|
for (const file of data.files) {
|
||||||
const work = { ...file };
|
const work = { ...file };
|
||||||
@@ -217,7 +235,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
}
|
}
|
||||||
)(plugin));
|
)(plugin));
|
||||||
}
|
}
|
||||||
await Promise.all(processes);
|
await para.all();
|
||||||
let newList = [...this.pluginList];
|
let newList = [...this.pluginList];
|
||||||
for (const item of entries) {
|
for (const item of entries) {
|
||||||
newList = newList.filter(x => x.documentPath != item.documentPath);
|
newList = newList.filter(x => x.documentPath != item.documentPath);
|
||||||
@@ -241,9 +259,9 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
const docB = await this.localDatabase.getDBEntry(dataB.documentPath);
|
const docB = await this.localDatabase.getDBEntry(dataB.documentPath);
|
||||||
|
|
||||||
if (docA && docB) {
|
if (docA && docB) {
|
||||||
const pluginDataA = parseYaml(getDocData(docA.data)) as PluginDataEx;
|
const pluginDataA = deserialize(getDocData(docA.data), {}) as PluginDataEx;
|
||||||
pluginDataA.documentPath = dataA.documentPath;
|
pluginDataA.documentPath = dataA.documentPath;
|
||||||
const pluginDataB = parseYaml(getDocData(docB.data)) as PluginDataEx;
|
const pluginDataB = deserialize(getDocData(docB.data), {}) as PluginDataEx;
|
||||||
pluginDataB.documentPath = dataB.documentPath;
|
pluginDataB.documentPath = dataB.documentPath;
|
||||||
|
|
||||||
// Use outer structure to wrap each data.
|
// Use outer structure to wrap each data.
|
||||||
@@ -282,7 +300,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
if (dx == false) {
|
if (dx == false) {
|
||||||
throw "Not found on database"
|
throw "Not found on database"
|
||||||
}
|
}
|
||||||
const loadedData = parseYaml(getDocData(dx.data)) as PluginDataEx;
|
const loadedData = deserialize(getDocData(dx.data), {}) as PluginDataEx;
|
||||||
for (const f of loadedData.files) {
|
for (const f of loadedData.files) {
|
||||||
Logger(`Applying ${f.filename} of ${data.displayName || data.name}..`);
|
Logger(`Applying ${f.filename} of ${data.displayName || data.name}..`);
|
||||||
try {
|
try {
|
||||||
@@ -354,7 +372,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
async parseReplicationResultItem(docs: PouchDB.Core.ExistingDocument<EntryDoc>) {
|
async parseReplicationResultItem(docs: PouchDB.Core.ExistingDocument<EntryDoc>) {
|
||||||
if (docs._id.startsWith(ICXHeader)) {
|
if (docs._id.startsWith(ICXHeader)) {
|
||||||
if (this.plugin.settings.usePluginSync) {
|
if (this.plugin.settings.usePluginSync) {
|
||||||
await this.updatePluginList(false, docs.path ? docs.path : this.getPath(docs));
|
await this.updatePluginList(false, (docs as AnyEntry).path ? (docs as AnyEntry).path : this.getPath((docs as AnyEntry)));
|
||||||
}
|
}
|
||||||
if (this.plugin.settings.usePluginSync && this.plugin.settings.notifyPluginOrSettingUpdated) {
|
if (this.plugin.settings.usePluginSync && this.plugin.settings.notifyPluginOrSettingUpdated) {
|
||||||
if (!this.pluginDialog || (this.pluginDialog && !this.pluginDialog.isOpened())) {
|
if (!this.pluginDialog || (this.pluginDialog && !this.pluginDialog.isOpened())) {
|
||||||
@@ -497,7 +515,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
for (const target of fileTargets) {
|
for (const target of fileTargets) {
|
||||||
const data = await this.makeEntryFromFile(target);
|
const data = await this.makeEntryFromFile(target);
|
||||||
if (data == false) {
|
if (data == false) {
|
||||||
Logger(`Config: skipped: ${target} `, LOG_LEVEL.VERBOSE);
|
// Logger(`Config: skipped: ${target} `, LOG_LEVEL.VERBOSE);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (data.version) {
|
if (data.version) {
|
||||||
@@ -520,7 +538,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = stringifyYaml(dt);
|
const content = serialize(dt);
|
||||||
try {
|
try {
|
||||||
const old = await this.localDatabase.getDBEntryMeta(prefixedFileName, null, false);
|
const old = await this.localDatabase.getDBEntryMeta(prefixedFileName, null, false);
|
||||||
let saveData: LoadedEntry;
|
let saveData: LoadedEntry;
|
||||||
@@ -567,6 +585,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
|
|
||||||
}
|
}
|
||||||
async watchVaultRawEventsAsync(path: FilePath) {
|
async watchVaultRawEventsAsync(path: FilePath) {
|
||||||
|
if (!this.settings.usePluginSync) return false;
|
||||||
if (!this.isTargetPath(path)) return false;
|
if (!this.isTargetPath(path)) return false;
|
||||||
const stat = await this.app.vault.adapter.stat(path);
|
const stat = await this.app.vault.adapter.stat(path);
|
||||||
// Make sure that target is a file.
|
// Make sure that target is a file.
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import { Notice, normalizePath, PluginManifest } from "./deps";
|
import { Notice, normalizePath, PluginManifest } from "./deps";
|
||||||
import { EntryDoc, LoadedEntry, LOG_LEVEL, InternalFileEntry, FilePathWithPrefix, FilePath } from "./lib/src/types";
|
import { EntryDoc, LoadedEntry, LOG_LEVEL, InternalFileEntry, FilePathWithPrefix, FilePath } from "./lib/src/types";
|
||||||
import { InternalFileInfo, ICHeader, ICHeaderEnd } from "./types";
|
import { InternalFileInfo, ICHeader, ICHeaderEnd } from "./types";
|
||||||
import { delay, isDocContentSame } from "./lib/src/utils";
|
import { Parallels, delay, isDocContentSame } from "./lib/src/utils";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
||||||
import { disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask, isInternalMetadata, PeriodicProcessor } from "./utils";
|
import { disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask, isInternalMetadata, PeriodicProcessor } from "./utils";
|
||||||
import { WrappedNotice } from "./lib/src/wrapper";
|
import { WrappedNotice } from "./lib/src/wrapper";
|
||||||
import { base64ToArrayBuffer, arrayBufferToBase64 } from "./lib/src/strbin";
|
import { base64ToArrayBuffer, arrayBufferToBase64 } from "./lib/src/strbin";
|
||||||
import { runWithLock } from "./lib/src/lock";
|
import { runWithLock } from "./lib/src/lock";
|
||||||
import { Semaphore } from "./lib/src/semaphore";
|
|
||||||
import { JsonResolveModal } from "./JsonResolveModal";
|
import { JsonResolveModal } from "./JsonResolveModal";
|
||||||
import { LiveSyncCommands } from "./LiveSyncCommands";
|
import { LiveSyncCommands } from "./LiveSyncCommands";
|
||||||
import { addPrefix, stripAllPrefixes } from "./lib/src/path";
|
import { addPrefix, stripAllPrefixes } from "./lib/src/path";
|
||||||
@@ -254,37 +253,35 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
c = pieces.shift();
|
c = pieces.shift();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const p = [] as Promise<void>[];
|
|
||||||
const semaphore = Semaphore(10);
|
|
||||||
// Cache update time information for files which have already been processed (mainly for files that were skipped due to the same content)
|
// Cache update time information for files which have already been processed (mainly for files that were skipped due to the same content)
|
||||||
let caches: { [key: string]: { storageMtime: number; docMtime: number; }; } = {};
|
let caches: { [key: string]: { storageMtime: number; docMtime: number; }; } = {};
|
||||||
caches = await this.kvDB.get<{ [key: string]: { storageMtime: number; docMtime: number; }; }>("diff-caches-internal") || {};
|
caches = await this.kvDB.get<{ [key: string]: { storageMtime: number; docMtime: number; }; }>("diff-caches-internal") || {};
|
||||||
|
const filesMap = files.reduce((acc, cur) => {
|
||||||
|
acc[cur.path] = cur;
|
||||||
|
return acc;
|
||||||
|
}, {} as { [key: string]: InternalFileInfo; });
|
||||||
|
const filesOnDBMap = filesOnDB.reduce((acc, cur) => {
|
||||||
|
acc[stripAllPrefixes(this.getPath(cur))] = cur;
|
||||||
|
return acc;
|
||||||
|
}, {} as { [key: string]: InternalFileEntry; });
|
||||||
|
const para = Parallels();
|
||||||
for (const filename of allFileNames) {
|
for (const filename of allFileNames) {
|
||||||
if (!filename) continue;
|
|
||||||
processed++;
|
processed++;
|
||||||
if (processed % 100 == 0)
|
if (processed % 100 == 0) {
|
||||||
Logger(`Hidden file: ${processed}/${fileCount}`, logLevel, "sync_internal");
|
Logger(`Hidden file: ${processed}/${fileCount}`, logLevel, "sync_internal");
|
||||||
|
}
|
||||||
|
if (!filename) continue;
|
||||||
if (ignorePatterns.some(e => filename.match(e)))
|
if (ignorePatterns.some(e => filename.match(e)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const fileOnStorage = files.find(e => e.path == filename);
|
const fileOnStorage = filename in filesMap ? filesMap[filename] : undefined;
|
||||||
const fileOnDatabase = filesOnDB.find(e => stripAllPrefixes(this.getPath(e)) == filename);
|
const fileOnDatabase = filename in filesOnDBMap ? filesOnDBMap[filename] : undefined;
|
||||||
const addProc = async (p: () => Promise<void>): Promise<void> => {
|
|
||||||
const releaser = await semaphore.acquire(1);
|
|
||||||
try {
|
|
||||||
return p();
|
|
||||||
} catch (ex) {
|
|
||||||
Logger("Some process failed", logLevel);
|
|
||||||
Logger(ex);
|
|
||||||
} finally {
|
|
||||||
releaser();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const cache = filename in caches ? caches[filename] : { storageMtime: 0, docMtime: 0 };
|
const cache = filename in caches ? caches[filename] : { storageMtime: 0, docMtime: 0 };
|
||||||
|
|
||||||
p.push(addProc(async () => {
|
await para.wait(5);
|
||||||
const xFileOnStorage = fileOnStorage;
|
const proc = (async (xFileOnStorage: InternalFileInfo, xFileOnDatabase: InternalFileEntry) => {
|
||||||
const xFileOnDatabase = fileOnDatabase;
|
|
||||||
if (xFileOnStorage && xFileOnDatabase) {
|
if (xFileOnStorage && xFileOnDatabase) {
|
||||||
// Both => Synchronize
|
// Both => Synchronize
|
||||||
if ((direction != "pullForce" && direction != "pushForce") && xFileOnDatabase.mtime == cache.docMtime && xFileOnStorage.mtime == cache.storageMtime) {
|
if ((direction != "pullForce" && direction != "pushForce") && xFileOnDatabase.mtime == cache.docMtime && xFileOnStorage.mtime == cache.storageMtime) {
|
||||||
@@ -326,9 +323,11 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
throw new Error("Invalid state on hidden file sync");
|
throw new Error("Invalid state on hidden file sync");
|
||||||
// Something corrupted?
|
// Something corrupted?
|
||||||
}
|
}
|
||||||
}));
|
|
||||||
|
});
|
||||||
|
para.add(proc(fileOnStorage, fileOnDatabase))
|
||||||
}
|
}
|
||||||
await Promise.all(p);
|
await para.all();
|
||||||
await this.kvDB.set("diff-caches-internal", caches);
|
await this.kvDB.set("diff-caches-internal", caches);
|
||||||
|
|
||||||
// When files has been retrieved from the database. they must be reloaded.
|
// When files has been retrieved from the database. they must be reloaded.
|
||||||
@@ -495,7 +494,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
const mtime = new Date().getTime();
|
const mtime = new Date().getTime();
|
||||||
await runWithLock("file-" + prefixedFileName, false, async () => {
|
await runWithLock("file-" + prefixedFileName, false, async () => {
|
||||||
try {
|
try {
|
||||||
const old = await this.localDatabase.getDBEntry(prefixedFileName, null, false, false) as InternalFileEntry | false;
|
const old = await this.localDatabase.getDBEntryMeta(prefixedFileName, null, true) as InternalFileEntry | false;
|
||||||
let saveData: InternalFileEntry;
|
let saveData: InternalFileEntry;
|
||||||
if (old === false) {
|
if (old === false) {
|
||||||
saveData = {
|
saveData = {
|
||||||
@@ -541,7 +540,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
try {
|
try {
|
||||||
// Check conflicted status
|
// Check conflicted status
|
||||||
//TODO option
|
//TODO option
|
||||||
const fileOnDB = await this.localDatabase.getDBEntry(prefixedFileName, { conflicts: true }, false, false);
|
const fileOnDB = await this.localDatabase.getDBEntry(prefixedFileName, { conflicts: true }, false, true);
|
||||||
if (fileOnDB === false)
|
if (fileOnDB === false)
|
||||||
throw new Error(`File not found on database.:${filename}`);
|
throw new Error(`File not found on database.:${filename}`);
|
||||||
// Prevent overwrite for Prevent overwriting while some conflicted revision exists.
|
// Prevent overwrite for Prevent overwriting while some conflicted revision exists.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { normalizePath, PluginManifest } from "./deps";
|
import { normalizePath, type PluginManifest } from "./deps";
|
||||||
import { DocumentID, EntryDoc, FilePathWithPrefix, LoadedEntry, LOG_LEVEL } from "./lib/src/types";
|
import type { DocumentID, EntryDoc, FilePathWithPrefix, LoadedEntry } from "./lib/src/types";
|
||||||
import { PluginDataEntry, PERIODIC_PLUGIN_SWEEP, PluginList, DevicePluginList, PSCHeader, PSCHeaderEnd } from "./types";
|
import { LOG_LEVEL } from "./lib/src/types";
|
||||||
|
import { type PluginDataEntry, PERIODIC_PLUGIN_SWEEP, type PluginList, type DevicePluginList, PSCHeader, PSCHeaderEnd } from "./types";
|
||||||
import { getDocData, isDocContentSame } from "./lib/src/utils";
|
import { getDocData, isDocContentSame } from "./lib/src/utils";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntryDoc, ObsidianLiveSyncSettings, LOG_LEVEL, DEFAULT_SETTINGS } from "./lib/src/types";
|
import { type EntryDoc, type ObsidianLiveSyncSettings, LOG_LEVEL, DEFAULT_SETTINGS } from "./lib/src/types";
|
||||||
import { configURIBase } from "./types";
|
import { configURIBase } from "./types";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
||||||
@@ -284,8 +284,22 @@ Of course, we are able to disable these features.`
|
|||||||
this.plugin.settings.syncAfterMerge = false;
|
this.plugin.settings.syncAfterMerge = false;
|
||||||
//this.suspendExtraSync();
|
//this.suspendExtraSync();
|
||||||
}
|
}
|
||||||
|
async askUseNewAdapter() {
|
||||||
|
if (!this.plugin.settings.useIndexedDBAdapter) {
|
||||||
|
const message = `Now this plugin has been configured to use the old database adapter for keeping compatibility. Do you want to deactivate it?`;
|
||||||
|
const CHOICE_YES = "Yes, disable and use latest";
|
||||||
|
const CHOICE_NO = "No, keep compatibility";
|
||||||
|
const choices = [CHOICE_YES, CHOICE_NO];
|
||||||
|
|
||||||
|
const ret = await confirmWithMessage(this.plugin, "Database adapter", message, choices, CHOICE_YES, 10);
|
||||||
|
if (ret == CHOICE_YES) {
|
||||||
|
this.plugin.settings.useIndexedDBAdapter = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
async fetchLocal() {
|
async fetchLocal() {
|
||||||
this.suspendExtraSync();
|
this.suspendExtraSync();
|
||||||
|
this.askUseNewAdapter();
|
||||||
await this.plugin.realizeSettingSyncMode();
|
await this.plugin.realizeSettingSyncMode();
|
||||||
await this.plugin.resetLocalDatabase();
|
await this.plugin.resetLocalDatabase();
|
||||||
await delay(1000);
|
await delay(1000);
|
||||||
@@ -313,6 +327,7 @@ Of course, we are able to disable these features.`
|
|||||||
}
|
}
|
||||||
async rebuildEverything() {
|
async rebuildEverything() {
|
||||||
this.suspendExtraSync();
|
this.suspendExtraSync();
|
||||||
|
this.askUseNewAdapter();
|
||||||
await this.plugin.realizeSettingSyncMode();
|
await this.plugin.realizeSettingSyncMode();
|
||||||
await this.plugin.resetLocalDatabase();
|
await this.plugin.resetLocalDatabase();
|
||||||
await delay(1000);
|
await delay(1000);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Diff, DIFF_DELETE, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
import { type Diff, DIFF_DELETE, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
||||||
import type { FilePath, LoadedEntry } from "./lib/src/types";
|
import type { FilePath, LoadedEntry } from "./lib/src/types";
|
||||||
import { base64ToString } from "./lib/src/strbin";
|
import { base64ToString } from "./lib/src/strbin";
|
||||||
import { getDocData } from "./lib/src/utils";
|
import { getDocData } from "./lib/src/utils";
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
import type { PluginDataExDisplay } from "./CmdConfigSync";
|
import type { PluginDataExDisplay } from "./CmdConfigSync";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { versionNumberString2Number } from "./lib/src/strbin";
|
import { versionNumberString2Number } from "./lib/src/strbin";
|
||||||
import { FilePath, LOG_LEVEL } from "./lib/src/types";
|
import { type FilePath, LOG_LEVEL } from "./lib/src/types";
|
||||||
import { getDocData } from "./lib/src/utils";
|
import { getDocData } from "./lib/src/utils";
|
||||||
import type ObsidianLiveSyncPlugin from "./main";
|
import type ObsidianLiveSyncPlugin from "./main";
|
||||||
import { askString, scheduleTask } from "./utils";
|
import { askString, scheduleTask } from "./utils";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import ObsidianLiveSyncPlugin from "./main";
|
import ObsidianLiveSyncPlugin from "./main";
|
||||||
import { PluginDataExDisplay, pluginIsEnumerating, pluginList } from "./CmdConfigSync";
|
import { type PluginDataExDisplay, pluginIsEnumerating, pluginList } from "./CmdConfigSync";
|
||||||
import PluginCombo from "./PluginCombo.svelte";
|
import PluginCombo from "./PluginCombo.svelte";
|
||||||
export let plugin: ObsidianLiveSyncPlugin;
|
export let plugin: ObsidianLiveSyncPlugin;
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (this.plugin.settings.batchSave) {
|
if (this.plugin.settings.batchSave && !this.plugin.settings.liveSync) {
|
||||||
// if the latest event is the same type, omit that
|
// if the latest event is the same type, omit that
|
||||||
// a.md MODIFY <- this should be cancelled when a.md MODIFIED
|
// a.md MODIFY <- this should be cancelled when a.md MODIFIED
|
||||||
// b.md MODIFY <- this should be cancelled when b.md MODIFIED
|
// b.md MODIFY <- this should be cancelled when b.md MODIFIED
|
||||||
|
|||||||
2
src/lib
2
src/lib
Submodule src/lib updated: fb3070851f...63fa0074fe
101
src/main.ts
101
src/main.ts
@@ -1,10 +1,10 @@
|
|||||||
const isDebug = false;
|
const isDebug = false;
|
||||||
|
|
||||||
import { Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
import { type Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
||||||
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, RequestUrlParam, RequestUrlResponse, requestUrl } from "./deps";
|
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, type RequestUrlParam, type RequestUrlResponse, requestUrl } from "./deps";
|
||||||
import { EntryDoc, LoadedEntry, ObsidianLiveSyncSettings, diff_check_result, diff_result_leaf, EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, SALT_OF_PASSPHRASE, ConfigPassphraseStore, CouchDBConnection, FLAGMD_REDFLAG2, FLAGMD_REDFLAG3, PREFIXMD_LOGFILE, DatabaseConnectingStatus, EntryHasPath, DocumentID, FilePathWithPrefix, FilePath, AnyEntry } from "./lib/src/types";
|
import { type EntryDoc, type LoadedEntry, type ObsidianLiveSyncSettings, type diff_check_result, type diff_result_leaf, type EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, type diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, SALT_OF_PASSPHRASE, type ConfigPassphraseStore, type CouchDBConnection, FLAGMD_REDFLAG2, FLAGMD_REDFLAG3, PREFIXMD_LOGFILE, type DatabaseConnectingStatus, type EntryHasPath, type DocumentID, type FilePathWithPrefix, type FilePath, type AnyEntry } from "./lib/src/types";
|
||||||
import { InternalFileInfo, queueItem, CacheData, FileEventItem, FileWatchEventQueueMax } from "./types";
|
import { type InternalFileInfo, type queueItem, type CacheData, type FileEventItem, FileWatchEventQueueMax } from "./types";
|
||||||
import { getDocData, isDocContentSame } from "./lib/src/utils";
|
import { getDocData, isDocContentSame, Parallels } from "./lib/src/utils";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
||||||
import { LogDisplayModal } from "./LogDisplayModal";
|
import { LogDisplayModal } from "./LogDisplayModal";
|
||||||
@@ -22,9 +22,9 @@ import { addPrefix, isPlainText, shouldBeIgnored, stripAllPrefixes } from "./lib
|
|||||||
import { runWithLock } from "./lib/src/lock";
|
import { runWithLock } from "./lib/src/lock";
|
||||||
import { Semaphore } from "./lib/src/semaphore";
|
import { Semaphore } from "./lib/src/semaphore";
|
||||||
import { StorageEventManager, StorageEventManagerObsidian } from "./StorageEventManager";
|
import { StorageEventManager, StorageEventManagerObsidian } from "./StorageEventManager";
|
||||||
import { LiveSyncLocalDB, LiveSyncLocalDBEnv } from "./lib/src/LiveSyncLocalDB";
|
import { LiveSyncLocalDB, type LiveSyncLocalDBEnv } from "./lib/src/LiveSyncLocalDB";
|
||||||
import { LiveSyncDBReplicator, LiveSyncReplicatorEnv } from "./lib/src/LiveSyncReplicator";
|
import { LiveSyncDBReplicator, type LiveSyncReplicatorEnv } from "./lib/src/LiveSyncReplicator";
|
||||||
import { KeyValueDatabase, OpenKeyValueDatabase } from "./KeyValueDB";
|
import { type KeyValueDatabase, OpenKeyValueDatabase } from "./KeyValueDB";
|
||||||
import { LiveSyncCommands } from "./LiveSyncCommands";
|
import { LiveSyncCommands } from "./LiveSyncCommands";
|
||||||
import { HiddenFileSync } from "./CmdHiddenFileSync";
|
import { HiddenFileSync } from "./CmdHiddenFileSync";
|
||||||
import { SetupLiveSync } from "./CmdSetupLiveSync";
|
import { SetupLiveSync } from "./CmdSetupLiveSync";
|
||||||
@@ -87,7 +87,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
processReplication = (e: PouchDB.Core.ExistingDocument<EntryDoc>[]) => this.parseReplicationResult(e);
|
processReplication = (e: PouchDB.Core.ExistingDocument<EntryDoc>[]) => this.parseReplicationResult(e);
|
||||||
async connectRemoteCouchDB(uri: string, auth: { username: string; password: string }, disableRequestURI: boolean, passphrase: string | false, useDynamicIterationCount: boolean): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> {
|
async connectRemoteCouchDB(uri: string, auth: { username: string; password: string }, disableRequestURI: boolean, passphrase: string | false, useDynamicIterationCount: boolean, performSetup: boolean, skipInfo: boolean): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> {
|
||||||
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
|
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
|
||||||
if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters.";
|
if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters.";
|
||||||
if (uri.indexOf(" ") !== -1) return "Remote URI and database name could not contain spaces.";
|
if (uri.indexOf(" ") !== -1) return "Remote URI and database name could not contain spaces.";
|
||||||
@@ -104,6 +104,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
const conf: PouchDB.HttpAdapter.HttpAdapterConfiguration = {
|
const conf: PouchDB.HttpAdapter.HttpAdapterConfiguration = {
|
||||||
adapter: "http",
|
adapter: "http",
|
||||||
auth,
|
auth,
|
||||||
|
skip_setup: !performSetup,
|
||||||
fetch: async (url: string | Request, opts: RequestInit) => {
|
fetch: async (url: string | Request, opts: RequestInit) => {
|
||||||
let size = "";
|
let size = "";
|
||||||
const localURL = url.toString().substring(uri.length);
|
const localURL = url.toString().substring(uri.length);
|
||||||
@@ -192,6 +193,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
if (passphrase !== "false" && typeof passphrase === "string") {
|
if (passphrase !== "false" && typeof passphrase === "string") {
|
||||||
enableEncryption(db, passphrase, useDynamicIterationCount);
|
enableEncryption(db, passphrase, useDynamicIterationCount);
|
||||||
}
|
}
|
||||||
|
if (skipInfo) {
|
||||||
|
return { db: db, info: { db_name: "", doc_count: 0, update_seq: "" } };
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const info = await db.info();
|
const info = await db.info();
|
||||||
return { db: db, info: info };
|
return { db: db, info: info };
|
||||||
@@ -878,7 +882,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
|
|
||||||
async procFileEvent(applyBatch?: boolean) {
|
async procFileEvent(applyBatch?: boolean) {
|
||||||
if (!this.isReady) return;
|
if (!this.isReady) return;
|
||||||
if (this.settings.batchSave) {
|
if (this.settings.batchSave && !this.settings.liveSync) {
|
||||||
if (!applyBatch && this.vaultManager.getQueueLength() < FileWatchEventQueueMax) {
|
if (!applyBatch && this.vaultManager.getQueueLength() < FileWatchEventQueueMax) {
|
||||||
// Defer till applying batch save or queue has been grown enough.
|
// Defer till applying batch save or queue has been grown enough.
|
||||||
// or 30 seconds after.
|
// or 30 seconds after.
|
||||||
@@ -964,7 +968,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
async applyBatchChange() {
|
async applyBatchChange() {
|
||||||
if (this.settings.batchSave) {
|
if (this.settings.batchSave && !this.settings.liveSync) {
|
||||||
return await this.procFileEvent(true);
|
return await this.procFileEvent(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1364,8 +1368,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
// If `Read chunks online` is disabled, chunks should be transferred before here.
|
// If `Read chunks online` is disabled, chunks should be transferred before here.
|
||||||
// However, in some cases, chunks are after that. So, if missing chunks exist, we have to wait for them.
|
// However, in some cases, chunks are after that. So, if missing chunks exist, we have to wait for them.
|
||||||
if ((!this.settings.readChunksOnline) && "children" in doc) {
|
if ((!this.settings.readChunksOnline) && "children" in doc) {
|
||||||
const c = await this.localDatabase.collectChunksWithCache(doc.children)
|
const c = await this.localDatabase.collectChunksWithCache(doc.children as DocumentID[]);
|
||||||
const missing = c.filter((e) => !e.chunk).map((e) => e.id);
|
const missing = c.filter((e) => e.chunk === false).map((e) => e.id);
|
||||||
if (missing.length > 0) Logger(`${path} (${doc._id}, ${doc._rev}) Queued (waiting ${missing.length} items)`, LOG_LEVEL.VERBOSE);
|
if (missing.length > 0) Logger(`${path} (${doc._id}, ${doc._rev}) Queued (waiting ${missing.length} items)`, LOG_LEVEL.VERBOSE);
|
||||||
newQueue.missingChildren = missing;
|
newQueue.missingChildren = missing;
|
||||||
this.queuedFiles.push(newQueue);
|
this.queuedFiles.push(newQueue);
|
||||||
@@ -1381,15 +1385,15 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
const docsSorted = docs.sort((a, b) => b.mtime - a.mtime);
|
const docsSorted = docs.sort((a, b) => b.mtime - a.mtime);
|
||||||
L1:
|
L1:
|
||||||
for (const change of docsSorted) {
|
for (const change of docsSorted) {
|
||||||
|
if (isChunk(change._id)) {
|
||||||
|
await this.parseIncomingChunk(change);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (const proc of this.addOns) {
|
for (const proc of this.addOns) {
|
||||||
if (await proc.parseReplicationResultItem(change)) {
|
if (await proc.parseReplicationResultItem(change)) {
|
||||||
continue L1;
|
continue L1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isChunk(change._id)) {
|
|
||||||
await this.parseIncomingChunk(change);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (change._id == SYNCINFO_ID) {
|
if (change._id == SYNCINFO_ID) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1466,9 +1470,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
this.statusBar.title = e.syncStatus;
|
this.statusBar.title = e.syncStatus;
|
||||||
let waiting = "";
|
let waiting = "";
|
||||||
if (this.settings.batchSave) {
|
if (this.settings.batchSave && !this.settings.liveSync) {
|
||||||
waiting = " " + "🛫".repeat(this.vaultManager.getQueueLength());
|
const len = this.vaultManager.getQueueLength();
|
||||||
waiting = waiting.replace(/(🛫){10}/g, "🚀");
|
if (len != 0) {
|
||||||
|
waiting = ` 🛫${len}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let queued = "";
|
let queued = "";
|
||||||
const queue = Object.entries(e.queuedItems).filter((e) => !e[1].warned);
|
const queue = Object.entries(e.queuedItems).filter((e) => !e[1].warned);
|
||||||
@@ -1476,7 +1482,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
|
|
||||||
if (queuedCount) {
|
if (queuedCount) {
|
||||||
const pieces = queue.map((e) => e[1].missingChildren).reduce((prev, cur) => prev + cur.length, 0);
|
const pieces = queue.map((e) => e[1].missingChildren).reduce((prev, cur) => prev + cur.length, 0);
|
||||||
queued = ` 🧩 ${queuedCount} (${pieces})`;
|
queued = ` 🧩${queuedCount} (${pieces})`;
|
||||||
}
|
}
|
||||||
const processes = e.count;
|
const processes = e.count;
|
||||||
const processesDisp = processes == 0 ? "" : ` ⏳${processes}`;
|
const processesDisp = processes == 0 ? "" : ` ⏳${processes}`;
|
||||||
@@ -1488,19 +1494,20 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
return proc.substring(0, p);
|
return proc.substring(0, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pendingTask = e.pending.length
|
const pendingTask = e.pending.length
|
||||||
? "\nPending: " +
|
? e.pending.length < 10 ? ("\nPending: " +
|
||||||
Object.entries(e.pending.reduce((p, c) => ({ ...p, [getProcKind(c)]: (p[getProcKind(c)] ?? 0) + 1 }), {} as { [key: string]: number }))
|
Object.entries(e.pending.reduce((p, c) => ({ ...p, [getProcKind(c)]: (p[getProcKind(c)] ?? 0) + 1 }), {} as { [key: string]: number }))
|
||||||
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
||||||
.join(", ")
|
.join(", ")
|
||||||
: "";
|
) : `\n Pending: ${e.pending.length}` : "";
|
||||||
|
|
||||||
const runningTask = e.running.length
|
const runningTask = e.running.length
|
||||||
? "\nRunning: " +
|
? e.running.length < 10 ? ("\nRunning: " +
|
||||||
Object.entries(e.running.reduce((p, c) => ({ ...p, [getProcKind(c)]: (p[getProcKind(c)] ?? 0) + 1 }), {} as { [key: string]: number }))
|
Object.entries(e.running.reduce((p, c) => ({ ...p, [getProcKind(c)]: (p[getProcKind(c)] ?? 0) + 1 }), {} as { [key: string]: number }))
|
||||||
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
||||||
.join(", ")
|
.join(", ")
|
||||||
: "";
|
) : `\n Running: ${e.running.length}` : "";
|
||||||
this.setStatusBarText(message + pendingTask + runningTask);
|
this.setStatusBarText(message + pendingTask + runningTask);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1638,13 +1645,15 @@ Or if you are sure know what had been happened, we can unlock the database from
|
|||||||
const filesStorageName = filesStorage.map((e) => e.path);
|
const filesStorageName = filesStorage.map((e) => e.path);
|
||||||
Logger("Collecting local files on the DB", LOG_LEVEL.VERBOSE);
|
Logger("Collecting local files on the DB", LOG_LEVEL.VERBOSE);
|
||||||
const filesDatabase = [] as FilePathWithPrefix[]
|
const filesDatabase = [] as FilePathWithPrefix[]
|
||||||
|
let count = 0;
|
||||||
for await (const doc of this.localDatabase.findAllNormalDocs()) {
|
for await (const doc of this.localDatabase.findAllNormalDocs()) {
|
||||||
|
count++;
|
||||||
|
if (count % 25 == 0) Logger(`Collecting local files on the DB: ${count}`, showingNotice ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO, "syncAll");
|
||||||
const path = getPath(doc);
|
const path = getPath(doc);
|
||||||
if (isValidPath(path) && this.isTargetFile(path)) {
|
if (isValidPath(path) && this.isTargetFile(path)) {
|
||||||
filesDatabase.push(path);
|
filesDatabase.push(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger("Opening the key-value database", LOG_LEVEL.VERBOSE);
|
Logger("Opening the key-value database", LOG_LEVEL.VERBOSE);
|
||||||
const isInitialized = await (this.kvDB.get<boolean>("initialized")) || false;
|
const isInitialized = await (this.kvDB.get<boolean>("initialized")) || false;
|
||||||
// Make chunk bigger if it is the initial scan. There must be non-active docs.
|
// Make chunk bigger if it is the initial scan. There must be non-active docs.
|
||||||
@@ -1663,25 +1672,23 @@ Or if you are sure know what had been happened, we can unlock the database from
|
|||||||
|
|
||||||
const runAll = async<T>(procedureName: string, objects: T[], callback: (arg: T) => Promise<void>) => {
|
const runAll = async<T>(procedureName: string, objects: T[], callback: (arg: T) => Promise<void>) => {
|
||||||
Logger(procedureName);
|
Logger(procedureName);
|
||||||
const semaphore = Semaphore(25);
|
|
||||||
if (!this.localDatabase.isReady) throw Error("Database is not ready!");
|
if (!this.localDatabase.isReady) throw Error("Database is not ready!");
|
||||||
const processes = objects.map(e => (async (v) => {
|
const para = Parallels();
|
||||||
const releaser = await semaphore.acquire(1, procedureName);
|
for (const v of objects) {
|
||||||
|
await para.wait(10);
|
||||||
|
para.add((async (v) => {
|
||||||
|
try {
|
||||||
|
await callback(v);
|
||||||
|
} catch (ex) {
|
||||||
|
Logger(`Error while ${procedureName}`, LOG_LEVEL.NOTICE);
|
||||||
|
Logger(ex);
|
||||||
|
}
|
||||||
|
})(v));
|
||||||
|
|
||||||
try {
|
|
||||||
await callback(v);
|
|
||||||
} catch (ex) {
|
|
||||||
Logger(`Error while ${procedureName}`, LOG_LEVEL.NOTICE);
|
|
||||||
Logger(ex);
|
|
||||||
} finally {
|
|
||||||
releaser();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)(e));
|
await para.all();
|
||||||
await Promise.all(processes);
|
|
||||||
|
|
||||||
Logger(`${procedureName} done.`);
|
Logger(`${procedureName} done.`);
|
||||||
};
|
}
|
||||||
|
|
||||||
await runAll("UPDATE DATABASE", onlyInStorage, async (e) => {
|
await runAll("UPDATE DATABASE", onlyInStorage, async (e) => {
|
||||||
Logger(`UPDATE DATABASE ${e.path}`);
|
Logger(`UPDATE DATABASE ${e.path}`);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { DataWriteOptions, normalizePath, TFile, Platform, TAbstractFile, App, Plugin_2, RequestUrlParam, requestUrl } from "./deps";
|
import { type DataWriteOptions, normalizePath, TFile, Platform, TAbstractFile, App, Plugin_2, type RequestUrlParam, requestUrl } from "./deps";
|
||||||
import { path2id_base, id2path_base, isValidFilenameInLinux, isValidFilenameInDarwin, isValidFilenameInWidows, isValidFilenameInAndroid, stripAllPrefixes } from "./lib/src/path";
|
import { path2id_base, id2path_base, isValidFilenameInLinux, isValidFilenameInDarwin, isValidFilenameInWidows, isValidFilenameInAndroid, stripAllPrefixes } from "./lib/src/path";
|
||||||
|
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { AnyEntry, DocumentID, EntryDoc, EntryHasPath, FilePath, FilePathWithPrefix, LOG_LEVEL, NewEntry } from "./lib/src/types";
|
import { type AnyEntry, type DocumentID, type EntryDoc, type EntryHasPath, type FilePath, type FilePathWithPrefix, LOG_LEVEL, type NewEntry } from "./lib/src/types";
|
||||||
import { CHeader, ICHeader, ICHeaderLength, PSCHeader } from "./types";
|
import { CHeader, ICHeader, ICHeaderLength, PSCHeader } from "./types";
|
||||||
import { InputStringDialog, PopoverSelectString } from "./dialogs";
|
import { InputStringDialog, PopoverSelectString } from "./dialogs";
|
||||||
import ObsidianLiveSyncPlugin from "./main";
|
import ObsidianLiveSyncPlugin from "./main";
|
||||||
@@ -535,7 +535,7 @@ export const localDatabaseCleanUp = async (plugin: ObsidianLiveSyncPlugin, force
|
|||||||
await performRebuildDB(plugin, "localOnly");
|
await performRebuildDB(plugin, "localOnly");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
Logger("This feature requires enabling `Use new adapter`. Please enable it", LOG_LEVEL.NOTICE, "clean-up-db");
|
Logger("This feature requires disabling `Use an old adapter for compatibility`.", LOG_LEVEL.NOTICE, "clean-up-db");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -252,3 +252,7 @@ div.sls-setting-menu-btn {
|
|||||||
.sls-item-dirty::before {
|
.sls-item-dirty::before {
|
||||||
content: "✏";
|
content: "✏";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sls-setting-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||||
|
"inlineSourceMap": true,
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
@@ -6,6 +8,10 @@
|
|||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
|
"types": [
|
||||||
|
"svelte",
|
||||||
|
"node"
|
||||||
|
],
|
||||||
// "importsNotUsedAsValues": "error",
|
// "importsNotUsedAsValues": "error",
|
||||||
"importHelpers": false,
|
"importHelpers": false,
|
||||||
"alwaysStrict": true,
|
"alwaysStrict": true,
|
||||||
|
|||||||
34
updates.md
34
updates.md
@@ -24,5 +24,39 @@ I hope you will give it a try.
|
|||||||
- Improved:
|
- Improved:
|
||||||
- Showing status is now thinned for performance.
|
- Showing status is now thinned for performance.
|
||||||
- Enhance caching while collecting chunks.
|
- Enhance caching while collecting chunks.
|
||||||
|
- 0.19.3
|
||||||
|
- Improved:
|
||||||
|
- Now replication will be paced by collecting chunks. If synchronisation has been deadlocked, please enable `Do not pace synchronization` once.
|
||||||
|
- 0.19.4
|
||||||
|
- Improved:
|
||||||
|
- Reduced remote database checking to improve speed and reduce bandwidth.
|
||||||
|
- Fixed:
|
||||||
|
- Chunks which previously misinterpreted are now interpreted correctly.
|
||||||
|
- No more missing chunks which not be found forever, except if it has been actually missing.
|
||||||
|
- Deleted file detection on hidden file synchronising now works fine.
|
||||||
|
- Now the Customisation sync is surely quiet while it has been disabled.
|
||||||
|
- 0.19.5
|
||||||
|
- Fixed:
|
||||||
|
- Now hidden file synchronisation would not be hanged, even if so many files exist.
|
||||||
|
- Improved:
|
||||||
|
- Customisation sync works more smoothly.
|
||||||
|
- Note: Concurrent processing has been rollbacked into the original implementation. As a result, the total number of processes is no longer shown next to the hourglass icon. However, only the processes that are running concurrently are shown.
|
||||||
|
- 0.19.6
|
||||||
|
- Fixed:
|
||||||
|
- Logging has been tweaked.
|
||||||
|
- No more too many planes and rockets.
|
||||||
|
- The batch database update now surely only works in non-live mode.
|
||||||
|
- Internal things:
|
||||||
|
- Some frameworks has been upgraded.
|
||||||
|
- Import declaration has been fixed.
|
||||||
|
- Improved:
|
||||||
|
- The plug-in now asks to enable a new adaptor, when rebuilding, if it is not enabled yet.
|
||||||
|
- The setting dialogue refined.
|
||||||
|
- Configurations for compatibilities have been moved under the hatch.
|
||||||
|
- Made it clear that disabled is the default.
|
||||||
|
- Ambiguous names configuration have been renamed.
|
||||||
|
- Items that have no meaning in the settings are no longer displayed.
|
||||||
|
- Some items have been reordered for clarity.
|
||||||
|
- Each configuration has been grouped.
|
||||||
|
|
||||||
... To continue on to `updates_old.md`.
|
... To continue on to `updates_old.md`.
|
||||||
|
|||||||
Reference in New Issue
Block a user