mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-08-25 12:57:07 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c36898628 |
+1
-2
@@ -33,8 +33,7 @@
|
|||||||
const id = params.get('id');
|
const id = params.get('id');
|
||||||
const total = parseInt(params.get('n') || '0');
|
const total = parseInt(params.get('n') || '0');
|
||||||
const index = parseInt(params.get('i') || '-1');
|
const index = parseInt(params.get('i') || '-1');
|
||||||
// Keep the chunk percent-encoded so URI delimiters remain part of the settings payload.
|
const data = params.get('d');
|
||||||
const data = hash.match(/(?:^|&)d=([^&]*)/)?.[1];
|
|
||||||
|
|
||||||
const app = document.getElementById('app');
|
const app = document.getElementById('app');
|
||||||
|
|
||||||
|
|||||||
@@ -42,20 +42,10 @@ probe. CouchDB's API documentation says that `limit=0` has the same effect as
|
|||||||
no result rows and leave the complete count in `pending`. Fast Fetch therefore
|
no result rows and leave the complete count in `pending`. Fast Fetch therefore
|
||||||
uses an explicit one-row normal probe and includes no document bodies.
|
uses an explicit one-row normal probe and includes no document bodies.
|
||||||
|
|
||||||
Finite continuous-feed completion differs across supported CouchDB releases.
|
A continuous feed with a heartbeat remains open at the current tail. It closes
|
||||||
CouchDB 3.5.0 was observed to close a heartbeat-enabled feed with a
|
with a `{ "last_seq": ... }` line only after its finite `limit` has been met.
|
||||||
`{ "last_seq": ... }` line when its finite `limit` is met. CouchDB 3.2 instead
|
Consequently, a limit larger than the workload already known to be available
|
||||||
continues to wait for database updates after the limit has been consumed. With
|
can leave initialisation waiting for future writes.
|
||||||
a heartbeat configured, each wait emits another heartbeat and the page can
|
|
||||||
remain open indefinitely, even after every requested row has arrived.
|
|
||||||
|
|
||||||
On CouchDB 3.2, an explicit `timeout` without a heartbeat has different
|
|
||||||
semantics from a total request deadline. Shard-result waits may emit blank
|
|
||||||
keep-alive lines and continue processing. Once the currently available changes
|
|
||||||
have been exhausted and the feed is waiting for another database update, the
|
|
||||||
timeout stops that wait and returns the feed-level `last_seq`. The timeout can
|
|
||||||
therefore terminate a finite page without limiting the duration of an active
|
|
||||||
page transfer.
|
|
||||||
|
|
||||||
The CouchDB sequence token is opaque and must be handled using CouchDB's
|
The CouchDB sequence token is opaque and must be handled using CouchDB's
|
||||||
sequence semantics, without parsing, ordering, or comparison. On clustered
|
sequence semantics, without parsing, ordering, or comparison. On clustered
|
||||||
@@ -83,16 +73,8 @@ request starts again from that same cursor.
|
|||||||
The number currently available is `results.length + pending`. If it is zero,
|
The number currently available is `results.length + pending`. If it is zero,
|
||||||
Fast Fetch is caught up and completes without opening another stream. Otherwise,
|
Fast Fetch is caught up and completes without opening another stream. Otherwise,
|
||||||
the next continuous request uses the smaller of that count and 10,000 as its
|
the next continuous request uses the smaller of that count and 10,000 as its
|
||||||
finite `limit`.
|
finite `limit`. This prevents a heartbeat-enabled request from waiting for
|
||||||
|
future changes merely to fill an oversized page.
|
||||||
Each finite page omits `heartbeat` and sets `timeout=1000`. This lets CouchDB
|
|
||||||
3.2 return the page's terminator one second after it exhausts the currently
|
|
||||||
available changes, rather than keeping the request open for future writes. The
|
|
||||||
client immediately reconnects from that terminator while another normal probe
|
|
||||||
reports available work. This bounded cycle also preserves the intent of the
|
|
||||||
earlier iOS and iPadOS heartbeat workaround: Fast Fetch no longer depends on a
|
|
||||||
silent continuous request eventually closing at CouchDB's default 60-second
|
|
||||||
timeout.
|
|
||||||
|
|
||||||
The probe and page are separate HTTP requests, not a transactional snapshot.
|
The probe and page are separate HTTP requests, not a transactional snapshot.
|
||||||
New writes, replica selection, or administrative changes may alter the rows
|
New writes, replica selection, or administrative changes may alter the rows
|
||||||
@@ -270,8 +252,6 @@ writer. Verify that:
|
|||||||
`pending` is zero;
|
`pending` is zero;
|
||||||
- every probe uses `limit=1`, excludes document bodies, and is repeated from the
|
- every probe uses `limit=1`, excludes document bodies, and is repeated from the
|
||||||
previous page's opaque terminator;
|
previous page's opaque terminator;
|
||||||
- each bounded page omits `heartbeat`, uses `timeout=1000`, and can complete
|
|
||||||
under CouchDB 3.2 after its current rows have been delivered;
|
|
||||||
- deletion and document-less rows consume a page slot;
|
- deletion and document-less rows consume a page slot;
|
||||||
- a row count cannot complete a page without its `last_seq` terminator;
|
- a row count cannot complete a page without its `last_seq` terminator;
|
||||||
- a page terminator cannot advance the checkpoint before its batch is durable;
|
- a page terminator cannot advance the checkpoint before its batch is durable;
|
||||||
@@ -306,14 +286,11 @@ existing setup sequence and cleanup.
|
|||||||
|
|
||||||
Commonlib's CouchDB integration test remains responsible for the real HTTP
|
Commonlib's CouchDB integration test remains responsible for the real HTTP
|
||||||
changes feed, opaque sequence tokens, deletion rows, and local batch
|
changes feed, opaque sequence tokens, deletion rows, and local batch
|
||||||
persistence. It should use the maintained CI CouchDB release, a two-shard
|
persistence. It should use a two-shard database, include a data set large enough
|
||||||
database, and a data set large enough to cross a local batch boundary, and
|
to cross a local batch boundary, and confirm that the final checkpoint can be
|
||||||
confirm that the final checkpoint can be passed back to CouchDB as `since` with
|
passed back to CouchDB as `since` with no result rows or pending changes. The
|
||||||
no result rows or pending changes. The test must not compare that token's
|
test must not compare that token's representation with a separately requested
|
||||||
representation with a separately requested target or changes-row token. The
|
target or changes-row token.
|
||||||
focused regression test covers CouchDB 3.2's page-tail behaviour; compatibility
|
|
||||||
with a real CouchDB 3.2 server can be confirmed manually without expanding the
|
|
||||||
permanent CI matrix.
|
|
||||||
|
|
||||||
LiveSync's real Obsidian Setup URI workflow remains responsible for the actual
|
LiveSync's real Obsidian Setup URI workflow remains responsible for the actual
|
||||||
Fast Fetch selection, E2EE passphrase, Vault reflection, ordinary file round
|
Fast Fetch selection, E2EE passphrase, Vault reflection, ordinary file round
|
||||||
@@ -333,9 +310,6 @@ This follows [Real Obsidian E2E](2026_06_real_obsidian_e2e.md).
|
|||||||
or local persistence failures which cannot repair themselves.
|
or local persistence failures which cannot repair themselves.
|
||||||
- Progress totals remain approximate and may grow when a later probe observes
|
- Progress totals remain approximate and may grow when a later probe observes
|
||||||
new work, without affecting correctness.
|
new work, without affecting correctness.
|
||||||
- A completed page can spend up to one second waiting for its terminator before
|
|
||||||
Fast Fetch probes and reconnects. Active page transfer is not constrained to
|
|
||||||
one second.
|
|
||||||
- The implementation requires coordinated changes in Commonlib and LiveSync.
|
- The implementation requires coordinated changes in Commonlib and LiveSync.
|
||||||
Commonlib remains the authoritative package for streaming and rebuilder
|
Commonlib remains the authoritative package for streaming and rebuilder
|
||||||
behaviour; LiveSync consumes an immutable Commonlib release and owns its setup
|
behaviour; LiveSync consumes an immutable Commonlib release and owns its setup
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "1.0.9",
|
"version": "1.0.8",
|
||||||
"minAppVersion": "1.7.2",
|
"minAppVersion": "1.7.2",
|
||||||
"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",
|
||||||
|
|||||||
Generated
+9
-9
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "1.0.9",
|
"version": "1.0.8",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "1.0.9",
|
"version": "1.0.8",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"src/apps/cli",
|
"src/apps/cli",
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
"@smithy/types": "^4.14.3",
|
"@smithy/types": "^4.14.3",
|
||||||
"@smithy/util-retry": "^4.4.5",
|
"@smithy/util-retry": "^4.4.5",
|
||||||
"@vrtmrz/browser-ui-kit": "0.1.0",
|
"@vrtmrz/browser-ui-kit": "0.1.0",
|
||||||
"@vrtmrz/livesync-commonlib": "0.1.8",
|
"@vrtmrz/livesync-commonlib": "0.1.7",
|
||||||
"@vrtmrz/obsidian-plugin-kit": "0.1.3",
|
"@vrtmrz/obsidian-plugin-kit": "0.1.3",
|
||||||
"@vrtmrz/ui-interactions": "0.1.2",
|
"@vrtmrz/ui-interactions": "0.1.2",
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
@@ -4775,9 +4775,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vrtmrz/livesync-commonlib": {
|
"node_modules/@vrtmrz/livesync-commonlib": {
|
||||||
"version": "0.1.8",
|
"version": "0.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/@vrtmrz/livesync-commonlib/-/livesync-commonlib-0.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/@vrtmrz/livesync-commonlib/-/livesync-commonlib-0.1.7.tgz",
|
||||||
"integrity": "sha512-Kn1AF41h2Dog37ThU7KgLcKxItCCerLEBWg1eSGAUoTk3TyPBYynvtmVFwWhy6LCePcuwB/+x7EQNTL3Sgzkyg==",
|
"integrity": "sha512-xwRXuPqYmPbWlSaVrQqeVacZz4X5km3fgoUee6Odl8/C3dNx+pFndxoHvXIDo3HmbMKcz/JA09JTKvfxVqBwQQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.808.0",
|
"@aws-sdk/client-s3": "^3.808.0",
|
||||||
@@ -15924,7 +15924,7 @@
|
|||||||
},
|
},
|
||||||
"src/apps/cli": {
|
"src/apps/cli": {
|
||||||
"name": "self-hosted-livesync-cli",
|
"name": "self-hosted-livesync-cli",
|
||||||
"version": "1.0.9-cli",
|
"version": "1.0.8-cli",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.0",
|
"chokidar": "^4.0.0",
|
||||||
"minimatch": "^10.2.5",
|
"minimatch": "^10.2.5",
|
||||||
@@ -15949,7 +15949,7 @@
|
|||||||
},
|
},
|
||||||
"src/apps/webapp": {
|
"src/apps/webapp": {
|
||||||
"name": "livesync-webapp",
|
"name": "livesync-webapp",
|
||||||
"version": "1.0.9-webapp",
|
"version": "1.0.8-webapp",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"octagonal-wheels": "^0.1.52"
|
"octagonal-wheels": "^0.1.52"
|
||||||
},
|
},
|
||||||
@@ -15961,7 +15961,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/apps/webpeer": {
|
"src/apps/webpeer": {
|
||||||
"version": "1.0.9-webpeer",
|
"version": "1.0.8-webpeer",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"octagonal-wheels": "^0.1.52"
|
"octagonal-wheels": "^0.1.52"
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "1.0.9",
|
"version": "1.0.8",
|
||||||
"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",
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
"@smithy/types": "^4.14.3",
|
"@smithy/types": "^4.14.3",
|
||||||
"@smithy/util-retry": "^4.4.5",
|
"@smithy/util-retry": "^4.4.5",
|
||||||
"@vrtmrz/browser-ui-kit": "0.1.0",
|
"@vrtmrz/browser-ui-kit": "0.1.0",
|
||||||
"@vrtmrz/livesync-commonlib": "0.1.8",
|
"@vrtmrz/livesync-commonlib": "0.1.7",
|
||||||
"@vrtmrz/obsidian-plugin-kit": "0.1.3",
|
"@vrtmrz/obsidian-plugin-kit": "0.1.3",
|
||||||
"@vrtmrz/ui-interactions": "0.1.2",
|
"@vrtmrz/ui-interactions": "0.1.2",
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "self-hosted-livesync-cli",
|
"name": "self-hosted-livesync-cli",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.9-cli",
|
"version": "1.0.8-cli",
|
||||||
"main": "dist/index.cjs",
|
"main": "dist/index.cjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "livesync-webapp",
|
"name": "livesync-webapp",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.9-webapp",
|
"version": "1.0.8-webapp",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Browser-based Self-hosted LiveSync using FileSystem API",
|
"description": "Browser-based Self-hosted LiveSync using FileSystem API",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "webpeer",
|
"name": "webpeer",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.9-webpeer",
|
"version": "1.0.8-webpeer",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -30,15 +30,12 @@ Deno.test({
|
|||||||
const aggregator = await browser.newPage();
|
const aggregator = await browser.newPage();
|
||||||
const assertNoAggregatorFailures = observePageFailures(aggregator);
|
const assertNoAggregatorFailures = observePageFailures(aggregator);
|
||||||
const assertNoAggregatorNetworkFailures = observeNetworkFailures(aggregator);
|
const assertNoAggregatorNetworkFailures = observeNetworkFailures(aggregator);
|
||||||
await aggregator.goto(new URL("aggregator.html#id=pages-smoke&n=2&i=0&d=before%2", server.baseUrl).href);
|
await aggregator.goto(new URL("aggregator.html#id=pages-smoke&n=2&i=0&d=first-", server.baseUrl).href);
|
||||||
await aggregator.getByText("1 / 2 Loaded", { exact: true }).waitFor();
|
await aggregator.getByText("1 / 2 Loaded", { exact: true }).waitFor();
|
||||||
await aggregator.goto(
|
await aggregator.goto(new URL("aggregator.html#id=pages-smoke&n=2&i=1&d=second", server.baseUrl).href);
|
||||||
new URL("aggregator.html#id=pages-smoke&n=2&i=1&d=3after%26amp%2Bplus%25percent", server.baseUrl)
|
|
||||||
.href
|
|
||||||
);
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
await aggregator.getByRole("link", { name: "Open Obsidian to complete setup" }).getAttribute("href"),
|
await aggregator.getByRole("link", { name: "Open Obsidian to complete setup" }).getAttribute("href"),
|
||||||
"obsidian://setuplivesync?settingsQR=before%23after%26amp%2Bplus%25percent"
|
"obsidian://setuplivesync?settingsQR=first-second"
|
||||||
);
|
);
|
||||||
assertNoAggregatorFailures();
|
assertNoAggregatorFailures();
|
||||||
assertNoAggregatorNetworkFailures();
|
assertNoAggregatorNetworkFailures();
|
||||||
|
|||||||
-15
@@ -12,25 +12,10 @@ Earlier releases remain available in the 0.25 release history and the legacy rel
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
## 1.0.9
|
|
||||||
|
|
||||||
8th August, 2026
|
|
||||||
|
|
||||||
For the first time in a while, I published a release that could not be promoted to a stable release. Sorry about that! I am glad that we caught it while it was still a pre-release.
|
|
||||||
|
|
||||||
### Setup and compatibility
|
|
||||||
|
|
||||||
#### Fixed
|
|
||||||
|
|
||||||
- Multi-part settings QR codes now preserve special characters in passwords, passphrases, and other settings (PR #1083). Thank you to @calvinbui for the improvement!
|
|
||||||
- Fast Setup now sizes each finite CouchDB changes page from a one-row status probe, counts the returned result together with `pending`, and resumes from the page's opaque `last_seq` without comparing token representations. Each page uses a one-second idle timeout instead of a heartbeat, allowing CouchDB 3.2 to return its terminator after the currently available rows have been persisted.
|
|
||||||
|
|
||||||
## 1.0.8
|
## 1.0.8
|
||||||
|
|
||||||
8th August, 2026
|
8th August, 2026
|
||||||
|
|
||||||
This version was published for pre-release validation only and was not promoted to a stable release.
|
|
||||||
|
|
||||||
### Setup and compatibility
|
### Setup and compatibility
|
||||||
|
|
||||||
#### Fixed
|
#### Fixed
|
||||||
|
|||||||
+1
-2
@@ -20,6 +20,5 @@
|
|||||||
"1.0.5": "1.7.2",
|
"1.0.5": "1.7.2",
|
||||||
"1.0.6": "1.7.2",
|
"1.0.6": "1.7.2",
|
||||||
"1.0.7": "1.7.2",
|
"1.0.7": "1.7.2",
|
||||||
"1.0.8": "1.7.2",
|
"1.0.8": "1.7.2"
|
||||||
"1.0.9": "1.7.2"
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user