diff --git a/.changeset/eighty-jars-shave.md b/.changeset/eighty-jars-shave.md deleted file mode 100644 index cb05104fd..000000000 --- a/.changeset/eighty-jars-shave.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -"@hyperbook/cloud": patch ---- - -Make the cloud server easier to run and safer to update: - -- Published as a container image per release. `docker compose up -d` is the - whole deployment, and `docker compose pull && docker compose up -d` the whole - update. An image tag is something to pin, which tracking `main` never was. - `docker-compose.yml`, a `Dockerfile` and an `ecosystem.config.js` for - non-container deployments are committed rather than pasted from the README. -- The schema is versioned with `PRAGMA user_version` instead of re-deriving - itself from `CREATE TABLE IF NOT EXISTS` and column probes on every boot. - Each migration runs at most once, in order, and the version is recorded only - after the step succeeds, so an interrupted upgrade retries rather than skips. -- **The database is copied aside before any migration that has work to do**, - with `VACUUM INTO`. If the copy cannot be written, the migration does not - run. The five most recent backups are kept. -- Starting a build older than the one that last wrote the database is refused, - instead of silently reading a schema it does not understand. -- In production the server refuses to start when `JWT_SECRET` is missing or - still the placeholder, when it is shorter than 32 characters, when `BASE_URL` - is unset, or when SMTP is half configured. It prints every problem at once - and suggests a generated secret. Previously a deployment that forgot - `JWT_SECRET` ran with a value published in this repository, and said nothing. -- `engines` now records the Node versions `better-sqlite3` can be built - against, so a mismatch fails at install rather than at `require`. diff --git a/.changeset/lucky-moons-shave.md b/.changeset/lucky-moons-shave.md deleted file mode 100644 index 1b5f29084..000000000 --- a/.changeset/lucky-moons-shave.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -"@hyperbook/cloud": patch ---- - -Fix event replay corrupting the state it reconstructs: - -- The primary key is now parsed out of the Dexie schema string. Taking the - first field verbatim left the markers on (`++id`, `&email`, `*tags`), so the - key matched no row property at all and every update and delete missed - silently while creates piled up duplicates. Compound keys (`[a+b]`) and - outbound keys are handled too. -- A created row can no longer end up without its primary key, which made it - unreachable to every later event. -- Primary keys keep their type. `prim_key` stored `String(primKey)`, flattening - numeric and compound keys so they no longer matched the rows they addressed; - events now also carry `prim_key_json`. -- Replay is idempotent. `create` appended unconditionally, so a retried or - replayed batch duplicated rows; it now upserts. -- Dexie's dotted update paths (`update(id, {"state.zoom": 2})`) are applied to - the nested field instead of being written as a literal `"state.zoom"` - property. -- Events carry the client's Dexie primary-key schema, so a table with no prior - snapshot is no longer assumed to be keyed by `id`. Tables keyed by something - else — `bookmarks` by `path`, `onlineide` by `scriptId` — had a bogus `id` - stamped onto every row. -- The `afterEventId` check and the append now share one transaction. Two - clients writing at the same moment could both pass the check and interleave. -- A malformed batch is rejected with 400 instead of failing a CHECK constraint - and returning 500, which the client treated as transient and retried forever. - -Both new columns are added by migration; existing databases keep working. diff --git a/.changeset/olive-pumas-repeat.md b/.changeset/olive-pumas-repeat.md deleted file mode 100644 index 35ed95d3d..000000000 --- a/.changeset/olive-pumas-repeat.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -"@hyperbook/markdown": patch -"hyperbook": patch ---- - -Improve the cloud sync indicator: - -- Each sync state now has its own badge shape on the toolbar icon. Every state - previously rendered the identical person glyph, distinguished only by fill - color, so amber "unsynced" and green "synced" were indistinguishable to a - red-green color blind reader. -- Sync states a reader can act on are now surfaced outside the user drawer. - Being offline or failing to save was only visible in a drawer most readers - never open. The notice offers a retry when a save fails, and says how much is - waiting while offline. Successful saves stay silent. -- A sync conflict no longer reloads the page without warning. The merge is - explained first, with a "Reload now" button, and the reload follows a few - seconds later. Reloading is still necessary because directives read the store - once at startup, but it is no longer a surprise. -- The toolbar button carries an accessible name that tracks the sync state, and - the status line is an `aria-live` region, so a change is announced rather than - only shown. -- The status line reports what it already knew but discarded: how long ago the - last save landed, and how many batches are waiting while offline. "Saved - locally" alone read as "you are all done". -- The toolbar icon no longer stops updating when the shell is rendered without - the user drawer. -- Status colors meet WCAG AA contrast and follow the light/dark theme; they - were fixed mid-tones at roughly 2-3:1 on white. -- `hyperbook.i18n.get` honors its fallback argument. Ten call sites in the - cloud UI passed one, but the parameter did not exist, so a missing key - rendered the raw key id. -- The impersonation banner offsets only the element after it, not every - following sibling, and builds its markup as nodes instead of interpolating - the username into `innerHTML`. diff --git a/.changeset/rotten-donkeys-repeat.md b/.changeset/rotten-donkeys-repeat.md deleted file mode 100644 index 5a09bcecc..000000000 --- a/.changeset/rotten-donkeys-repeat.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -"@hyperbook/markdown": patch -"hyperbook": patch ---- - -Fix several data-loss and corruption bugs in cloud sync: - -- Loading from the cloud no longer fails whenever the server has events but no - snapshot. The reconstruction the server builds from an event log alone - carries a placeholder database version, which `import` rejected outright — - so a user's work synced up but never came back down, silently, until - something happened to post a full snapshot. -- The event watermark is now stored per hyperbook. Two hyperbooks served from - the same origin shared one `localStorage` key, so each sent the other's - `afterEventId` and got stuck in a permanent conflict loop. -- A sync conflict no longer discards local work. The client now pulls the - server state, replays its pending events on top of it locally and remotely, - and only then reloads. -- Offline batches now chain onto the watermark each one returns. Every batch - after the first previously carried a watermark recorded before the flush and - was rejected, discarding the whole queue. -- Events now carry their Dexie primary-key schema, so the server can replay - onto a table it has no snapshot for without assuming the key field is `id`. -- Ephemeral `currentState` (cursor, scroll, window size) is no longer included - in cloud snapshots; it was already excluded from events. -- A batch rejected with a 4xx is dropped instead of being retried forever, and - a 404 from the cloud no longer throws a `TypeError` that looked transient. -- Concurrent `online` events can no longer start two overlapping queue flushes. -- Closing the tab now flushes pending changes with a `keepalive` request - instead of only warning. Anything changed inside the debounce window was - lost when the tab closed, and `beforeunload` never fires at all on mobile - Safari or when a background tab is discarded. diff --git a/packages/hyperbook/CHANGELOG.md b/packages/hyperbook/CHANGELOG.md index 0afdd240d..d70d4d8c8 100644 --- a/packages/hyperbook/CHANGELOG.md +++ b/packages/hyperbook/CHANGELOG.md @@ -1,5 +1,68 @@ # hyperbook +## 0.100.4 + +### Patch Changes + +- [`d91255b`](https://github.com/openpatch/hyperbook/commit/d91255bb77ce954056c8caa7780a1e7048e60b9d) Thanks [@mikebarkmin](https://github.com/mikebarkmin)! - Improve the cloud sync indicator: + + - Each sync state now has its own badge shape on the toolbar icon. Every state + previously rendered the identical person glyph, distinguished only by fill + color, so amber "unsynced" and green "synced" were indistinguishable to a + red-green color blind reader. + - Sync states a reader can act on are now surfaced outside the user drawer. + Being offline or failing to save was only visible in a drawer most readers + never open. The notice offers a retry when a save fails, and says how much is + waiting while offline. Successful saves stay silent. + - A sync conflict no longer reloads the page without warning. The merge is + explained first, with a "Reload now" button, and the reload follows a few + seconds later. Reloading is still necessary because directives read the store + once at startup, but it is no longer a surprise. + - The toolbar button carries an accessible name that tracks the sync state, and + the status line is an `aria-live` region, so a change is announced rather than + only shown. + - The status line reports what it already knew but discarded: how long ago the + last save landed, and how many batches are waiting while offline. "Saved + locally" alone read as "you are all done". + - The toolbar icon no longer stops updating when the shell is rendered without + the user drawer. + - Status colors meet WCAG AA contrast and follow the light/dark theme; they + were fixed mid-tones at roughly 2-3:1 on white. + - `hyperbook.i18n.get` honors its fallback argument. Ten call sites in the + cloud UI passed one, but the parameter did not exist, so a missing key + rendered the raw key id. + - The impersonation banner offsets only the element after it, not every + following sibling, and builds its markup as nodes instead of interpolating + the username into `innerHTML`. + +- [`d91255b`](https://github.com/openpatch/hyperbook/commit/d91255bb77ce954056c8caa7780a1e7048e60b9d) Thanks [@mikebarkmin](https://github.com/mikebarkmin)! - Fix several data-loss and corruption bugs in cloud sync: + + - Loading from the cloud no longer fails whenever the server has events but no + snapshot. The reconstruction the server builds from an event log alone + carries a placeholder database version, which `import` rejected outright — + so a user's work synced up but never came back down, silently, until + something happened to post a full snapshot. + - The event watermark is now stored per hyperbook. Two hyperbooks served from + the same origin shared one `localStorage` key, so each sent the other's + `afterEventId` and got stuck in a permanent conflict loop. + - A sync conflict no longer discards local work. The client now pulls the + server state, replays its pending events on top of it locally and remotely, + and only then reloads. + - Offline batches now chain onto the watermark each one returns. Every batch + after the first previously carried a watermark recorded before the flush and + was rejected, discarding the whole queue. + - Events now carry their Dexie primary-key schema, so the server can replay + onto a table it has no snapshot for without assuming the key field is `id`. + - Ephemeral `currentState` (cursor, scroll, window size) is no longer included + in cloud snapshots; it was already excluded from events. + - A batch rejected with a 4xx is dropped instead of being retried forever, and + a 404 from the cloud no longer throws a `TypeError` that looked transient. + - Concurrent `online` events can no longer start two overlapping queue flushes. + - Closing the tab now flushes pending changes with a `keepalive` request + instead of only warning. Anything changed inside the debounce window was + lost when the tab closed, and `beforeunload` never fires at all on mobile + Safari or when a background tab is discarded. + ## 0.100.3 ### Patch Changes diff --git a/packages/hyperbook/package.json b/packages/hyperbook/package.json index ea1d2a56b..5f2ef4eec 100644 --- a/packages/hyperbook/package.json +++ b/packages/hyperbook/package.json @@ -1,6 +1,6 @@ { "name": "hyperbook", - "version": "0.100.3", + "version": "0.100.4", "author": "Mike Barkmin", "homepage": "https://github.com/openpatch/hyperbook#readme", "license": "MIT", diff --git a/packages/markdown/CHANGELOG.md b/packages/markdown/CHANGELOG.md index 68388d84a..23dccc83e 100644 --- a/packages/markdown/CHANGELOG.md +++ b/packages/markdown/CHANGELOG.md @@ -1,5 +1,68 @@ # @hyperbook/markdown +## 0.71.4 + +### Patch Changes + +- [`d91255b`](https://github.com/openpatch/hyperbook/commit/d91255bb77ce954056c8caa7780a1e7048e60b9d) Thanks [@mikebarkmin](https://github.com/mikebarkmin)! - Improve the cloud sync indicator: + + - Each sync state now has its own badge shape on the toolbar icon. Every state + previously rendered the identical person glyph, distinguished only by fill + color, so amber "unsynced" and green "synced" were indistinguishable to a + red-green color blind reader. + - Sync states a reader can act on are now surfaced outside the user drawer. + Being offline or failing to save was only visible in a drawer most readers + never open. The notice offers a retry when a save fails, and says how much is + waiting while offline. Successful saves stay silent. + - A sync conflict no longer reloads the page without warning. The merge is + explained first, with a "Reload now" button, and the reload follows a few + seconds later. Reloading is still necessary because directives read the store + once at startup, but it is no longer a surprise. + - The toolbar button carries an accessible name that tracks the sync state, and + the status line is an `aria-live` region, so a change is announced rather than + only shown. + - The status line reports what it already knew but discarded: how long ago the + last save landed, and how many batches are waiting while offline. "Saved + locally" alone read as "you are all done". + - The toolbar icon no longer stops updating when the shell is rendered without + the user drawer. + - Status colors meet WCAG AA contrast and follow the light/dark theme; they + were fixed mid-tones at roughly 2-3:1 on white. + - `hyperbook.i18n.get` honors its fallback argument. Ten call sites in the + cloud UI passed one, but the parameter did not exist, so a missing key + rendered the raw key id. + - The impersonation banner offsets only the element after it, not every + following sibling, and builds its markup as nodes instead of interpolating + the username into `innerHTML`. + +- [`d91255b`](https://github.com/openpatch/hyperbook/commit/d91255bb77ce954056c8caa7780a1e7048e60b9d) Thanks [@mikebarkmin](https://github.com/mikebarkmin)! - Fix several data-loss and corruption bugs in cloud sync: + + - Loading from the cloud no longer fails whenever the server has events but no + snapshot. The reconstruction the server builds from an event log alone + carries a placeholder database version, which `import` rejected outright — + so a user's work synced up but never came back down, silently, until + something happened to post a full snapshot. + - The event watermark is now stored per hyperbook. Two hyperbooks served from + the same origin shared one `localStorage` key, so each sent the other's + `afterEventId` and got stuck in a permanent conflict loop. + - A sync conflict no longer discards local work. The client now pulls the + server state, replays its pending events on top of it locally and remotely, + and only then reloads. + - Offline batches now chain onto the watermark each one returns. Every batch + after the first previously carried a watermark recorded before the flush and + was rejected, discarding the whole queue. + - Events now carry their Dexie primary-key schema, so the server can replay + onto a table it has no snapshot for without assuming the key field is `id`. + - Ephemeral `currentState` (cursor, scroll, window size) is no longer included + in cloud snapshots; it was already excluded from events. + - A batch rejected with a 4xx is dropped instead of being retried forever, and + a 404 from the cloud no longer throws a `TypeError` that looked transient. + - Concurrent `online` events can no longer start two overlapping queue flushes. + - Closing the tab now flushes pending changes with a `keepalive` request + instead of only warning. Anything changed inside the debounce window was + lost when the tab closed, and `beforeunload` never fires at all on mobile + Safari or when a background tab is discarded. + ## 0.71.3 ### Patch Changes diff --git a/packages/markdown/package.json b/packages/markdown/package.json index 84e902b3f..9deee21a0 100644 --- a/packages/markdown/package.json +++ b/packages/markdown/package.json @@ -1,6 +1,6 @@ { "name": "@hyperbook/markdown", - "version": "0.71.3", + "version": "0.71.4", "author": "Mike Barkmin", "homepage": "https://github.com/openpatch/hyperbook#readme", "license": "MIT", diff --git a/platforms/cloud/CHANGELOG.md b/platforms/cloud/CHANGELOG.md index b15898971..0378aa729 100644 --- a/platforms/cloud/CHANGELOG.md +++ b/platforms/cloud/CHANGELOG.md @@ -1,5 +1,61 @@ # @hyperbook/cloud +## 0.0.2 + +### Patch Changes + +- [`9df0010`](https://github.com/openpatch/hyperbook/commit/9df00101a871f9298664790411ee33ad81218d45) Thanks [@mikebarkmin](https://github.com/mikebarkmin)! - Make the cloud server easier to run and safer to update: + + - Published as a container image per release. `docker compose up -d` is the + whole deployment, and `docker compose pull && docker compose up -d` the whole + update. An image tag is something to pin, which tracking `main` never was. + `docker-compose.yml`, a `Dockerfile` and an `ecosystem.config.js` for + non-container deployments are committed rather than pasted from the README. + - The schema is versioned with `PRAGMA user_version` instead of re-deriving + itself from `CREATE TABLE IF NOT EXISTS` and column probes on every boot. + Each migration runs at most once, in order, and the version is recorded only + after the step succeeds, so an interrupted upgrade retries rather than skips. + - **The database is copied aside before any migration that has work to do**, + with `VACUUM INTO`. If the copy cannot be written, the migration does not + run. The five most recent backups are kept. + - Starting a build older than the one that last wrote the database is refused, + instead of silently reading a schema it does not understand. + - In production the server refuses to start when `JWT_SECRET` is missing or + still the placeholder, when it is shorter than 32 characters, when `BASE_URL` + is unset, or when SMTP is half configured. It prints every problem at once + and suggests a generated secret. Previously a deployment that forgot + `JWT_SECRET` ran with a value published in this repository, and said nothing. + - `engines` now records the Node versions `better-sqlite3` can be built + against, so a mismatch fails at install rather than at `require`. + +- [`d91255b`](https://github.com/openpatch/hyperbook/commit/d91255bb77ce954056c8caa7780a1e7048e60b9d) Thanks [@mikebarkmin](https://github.com/mikebarkmin)! - Fix event replay corrupting the state it reconstructs: + + - The primary key is now parsed out of the Dexie schema string. Taking the + first field verbatim left the markers on (`++id`, `&email`, `*tags`), so the + key matched no row property at all and every update and delete missed + silently while creates piled up duplicates. Compound keys (`[a+b]`) and + outbound keys are handled too. + - A created row can no longer end up without its primary key, which made it + unreachable to every later event. + - Primary keys keep their type. `prim_key` stored `String(primKey)`, flattening + numeric and compound keys so they no longer matched the rows they addressed; + events now also carry `prim_key_json`. + - Replay is idempotent. `create` appended unconditionally, so a retried or + replayed batch duplicated rows; it now upserts. + - Dexie's dotted update paths (`update(id, {"state.zoom": 2})`) are applied to + the nested field instead of being written as a literal `"state.zoom"` + property. + - Events carry the client's Dexie primary-key schema, so a table with no prior + snapshot is no longer assumed to be keyed by `id`. Tables keyed by something + else — `bookmarks` by `path`, `onlineide` by `scriptId` — had a bogus `id` + stamped onto every row. + - The `afterEventId` check and the append now share one transaction. Two + clients writing at the same moment could both pass the check and interleave. + - A malformed batch is rejected with 400 instead of failing a CHECK constraint + and returning 500, which the client treated as transient and retried forever. + + Both new columns are added by migration; existing databases keep working. + ## 0.0.1 ### Patch Changes diff --git a/platforms/cloud/package.json b/platforms/cloud/package.json index f258e6556..ebd9f677d 100644 --- a/platforms/cloud/package.json +++ b/platforms/cloud/package.json @@ -1,6 +1,6 @@ { "name": "@hyperbook/cloud", - "version": "0.0.1", + "version": "0.0.2", "private": true, "engines": { "node": ">=20" diff --git a/platforms/vscode/CHANGELOG.md b/platforms/vscode/CHANGELOG.md index e18512e4e..7995773fc 100644 --- a/platforms/vscode/CHANGELOG.md +++ b/platforms/vscode/CHANGELOG.md @@ -1,5 +1,12 @@ # @hyperbook/vscode-extension +## 0.51.7 + +### Patch Changes + +- Updated dependencies [[`d91255b`](https://github.com/openpatch/hyperbook/commit/d91255bb77ce954056c8caa7780a1e7048e60b9d), [`d91255b`](https://github.com/openpatch/hyperbook/commit/d91255bb77ce954056c8caa7780a1e7048e60b9d)]: + - @hyperbook/markdown@0.71.4 + ## 0.51.6 ### Patch Changes diff --git a/platforms/vscode/package.json b/platforms/vscode/package.json index 2af40a141..bb4e251f8 100644 --- a/platforms/vscode/package.json +++ b/platforms/vscode/package.json @@ -11,7 +11,7 @@ }, "homepage": "https://hyperbook.openpatch.org", "main": "./out/extension.js", - "version": "0.51.6", + "version": "0.51.7", "engines": { "vscode": "^1.71.0" },