Skip to content

Release - #515

Merged
mbret merged 24 commits into
masterfrom
develop
Jul 26, 2026
Merged

Release#515
mbret merged 24 commits into
masterfrom
develop

Conversation

@mbret

Copy link
Copy Markdown
Owner

No description provided.

mbretand others added 23 commits July 20, 2026 13:52
* feat: upgrade @prose-reader/* to 1.332.0
Bump every @prose-reader/* dependency from 1.324.0 to 1.332.0 across web,
api and archive-metadata, and migrate the web reader to the breaking API
changes shipped in that range.
Reader lifecycle (prose #229/#230/#231):
- createReader() now takes the manifest (and optional cfi) at construction
and mount(containerElement) is a one-shot DOM attachment; reader.load()
is gone. useCreateReader now creates + mounts + destroys the reader in a
single effect (destroy() is the true inverse of create + mount, so the
effect is strict-mode safe), and useLoadReader is removed.
- The restored reading location (cfi) is read through a live ref so later
progress writes flowing back into the book query never destroy/recreate
the reader.
- reader.state$ ("idle"|"ready") is replaced by reader.mounted$; the book
loading overlay now keys off !mounted.
Pin react/react-dom to 19.1.8 in the web app only:
- react-dom 19.2 dev builds recursively read component props and access
$$typeof on every object, throwing SecurityError on our gapi cross-origin
iframe (react/react#34840). The uncaught
throw aborts passive-effect mounts (dead reader quick-menu) and corrupts
the work loop (crash on back navigation). Prod is unaffected; dev is
unusable.
- 19.1.x predates the offending logger and the web app uses no 19.2-only
APIs. admin/landing stay on 19.2.x (@Mantine 9.4 needs useEffectEvent),
so the pin is web-only, held together by resolve.dedupe in vite.config.
- Documented in AGENTS.md with removal steps for when the react bug is fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(reader): read book once via enabled gate instead of useLiveRef
The enabled predicate already stops observing after the first result, so
progress-sync writes to the same book document never change the value.
Drop the redundant useLiveRef, read bookOnce directly, and name the
enabled predicate to state the intent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: unpin apps/web react and bump react to 19.2.7 project-wide
apps/web was pinned to react 19.1.8 to dodge a dev-only React 19.2 logger
crash with the gapi iframe. The pin diverged web from the rest of the repo
and left @types on 19.2.x, creating a runtime/types skew. Align everything
to the latest 19.2.7 (react/react-dom) and 19.2.17 (@types/react) across
web, admin and landing.
Remove the now-unneeded scaffolding: the vite resolve.dedupe entry and the
AGENTS.md "React version pin" section. The dev-only reader crash returns at
parity with develop/master; production is unaffected (logger is dev-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…oS (#507)
Updates @nestjs/platform-express 11.1.27 -> 11.1.28, which pulls multer
2.1.1 -> 2.2.0 (multer is consumed only by platform-express). Resolves the
two multer denial-of-service advisories on the API file-upload path:
- GHSA-72gw-mp4g-v24j (high): DoS via deeply nested field names
- GHSA-3p4h-7m6x-2hcm (moderate): DoS via incomplete cleanup of aborted uploads
Claude-Session: https://claude.ai/code/session_01WKoPAepDc2xhSFFDhHTHe5
Co-authored-by: Claude <noreply@anthropic.com>
…n frames
React 19.2's dev-only performance track deep-walks changed props and
throws a SecurityError when it reaches the reader iframe's cross-origin
window, crashing the commit (react/react#34840). Guard the prop
walkers so unreadable values log as [inaccessible] instead of throwing.
Fixed upstream by react/react#35679 (19.3 canary only); remove with
`pnpm patch-remove react-dom@19.2.8` once on a release containing it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ernal peer
Docker Desktop writes both IPv4 and IPv6 host-gateway entries, so nginx
expands host.docker.internal into two peers; the IPv6 one is unreachable
from the IPv4-only compose network and its failure accounting could mark
the whole implicit upstream down (no live upstreams -> 502 bursts during
replication). An explicit upstream with max_fails=0 lets a failed connect
fall through to the reachable address instead of disabling the group.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel's lockfile heuristic picks pnpm 9, which can't read patchedDependencies
from pnpm-workspace.yaml and fails with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
The bootstrapped pnpm 11 defers to the packageManager pin in package.json.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel prepends its detected pnpm 9 to PATH, shadowing the npm-installed
pnpm 11, so the install still hit ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pnpm-workspace.yaml references patches/react-dom@19.2.8.patch, so the
frozen install in the base stage fails with ENOENT without it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
engines.node overrides the dashboard Node version; >=24 resolves to the
latest 24.x on Vercel while staying satisfied by the node 25 used in CI,
docker and local shells.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main.ts value-imports json/urlencoded from express, which pnpm's
isolated node_modules can't resolve through @nestjs/platform-express;
dist/main crashed with MODULE_NOT_FOUND on a clean install.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
useBook is a live rxdb-backed query and the book finished dialog keeps
an always-on observer of the same key, so the disabled observer in
useCreateReader still received every progress-sync cache update and the
bookOnce effect dependency remounted the reader on each write.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…514)
The back arrow fell through to home whenever a screen had performed a
replace-only navigation, such as switching tab on the book optimize
screen. react-router rebuilds the whole history state object on replace,
so the `__obokuCanGoBack` flag was dropped, and TrackHistoryCanGoBack
only re-stamped it on pathname changes.
react-router already tracks the entry position under `history.state.idx`,
preserved across replace, which removes the need for a custom flag, the
`__obokuFallbackBack` marker and the tracker component.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Extracts createBookQueryOptions alongside the existing
createLinkQueryOptions pattern and rebuilds the reader's frozen book
snapshot on top of a reusable hook that resolves a query's first result
and ignores later cache updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hanism
Splits the one-shot idea into an observable flavour of
queryClient.ensureQueryData (cache read, else first emission seeding the
empty cache, never touching the tanstack fetch machinery) and a thin
snapshot hook on top. Drops the inert observer, so consumers no longer
re-render on post-capture cache writes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the effect+state capture with a vanilla useQuery over a
prefix-namespaced key whose queryFn is ensureQueryData$: staleTime
Infinity freezes the snapshot, gcTime 0 scopes it to the consumers'
lifetime and concurrent consumers dedupe into a single resolution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercelBot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
obokuReadyReadyPreview, CommentJul 26, 2026 12:49pm
oboku-landingReadyReadyPreview, CommentJul 26, 2026 12:49pm

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6c4259fa2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadapps/web/src/queries/useEnsureQueryData$.ts
The private useQuery dropped the source query's networkMode, so offline
the default online mode paused resolution entirely — blocking even the
warm-cache read and leaving locally available books on the loading
screen. Resolution is cache-first, so always is the correct default;
network-bound sources can pass online explicitly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mbret
mbret merged commit 481cdbc into masterJul 26, 2026
5 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@mbret
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Release by mbret · Pull Request #515 · mbret/oboku · GitHub
Skip to content

Release - #515

Merged
mbret merged 24 commits into
masterfrom
develop
Jul 26, 2026
Merged

Release#515
mbret merged 24 commits into
masterfrom
develop

Conversation

@mbret

Copy link
Copy Markdown
Owner

No description provided.

mbretand others added 23 commits July 20, 2026 13:52
* feat: upgrade @prose-reader/* to 1.332.0
Bump every @prose-reader/* dependency from 1.324.0 to 1.332.0 across web,
api and archive-metadata, and migrate the web reader to the breaking API
changes shipped in that range.
Reader lifecycle (prose #229/#230/#231):
- createReader() now takes the manifest (and optional cfi) at construction
and mount(containerElement) is a one-shot DOM attachment; reader.load()
is gone. useCreateReader now creates + mounts + destroys the reader in a
single effect (destroy() is the true inverse of create + mount, so the
effect is strict-mode safe), and useLoadReader is removed.
- The restored reading location (cfi) is read through a live ref so later
progress writes flowing back into the book query never destroy/recreate
the reader.
- reader.state$ ("idle"|"ready") is replaced by reader.mounted$; the book
loading overlay now keys off !mounted.
Pin react/react-dom to 19.1.8 in the web app only:
- react-dom 19.2 dev builds recursively read component props and access
$$typeof on every object, throwing SecurityError on our gapi cross-origin
iframe (react/react#34840). The uncaught
throw aborts passive-effect mounts (dead reader quick-menu) and corrupts
the work loop (crash on back navigation). Prod is unaffected; dev is
unusable.
- 19.1.x predates the offending logger and the web app uses no 19.2-only
APIs. admin/landing stay on 19.2.x (@Mantine 9.4 needs useEffectEvent),
so the pin is web-only, held together by resolve.dedupe in vite.config.
- Documented in AGENTS.md with removal steps for when the react bug is fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(reader): read book once via enabled gate instead of useLiveRef
The enabled predicate already stops observing after the first result, so
progress-sync writes to the same book document never change the value.
Drop the redundant useLiveRef, read bookOnce directly, and name the
enabled predicate to state the intent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: unpin apps/web react and bump react to 19.2.7 project-wide
apps/web was pinned to react 19.1.8 to dodge a dev-only React 19.2 logger
crash with the gapi iframe. The pin diverged web from the rest of the repo
and left @types on 19.2.x, creating a runtime/types skew. Align everything
to the latest 19.2.7 (react/react-dom) and 19.2.17 (@types/react) across
web, admin and landing.
Remove the now-unneeded scaffolding: the vite resolve.dedupe entry and the
AGENTS.md "React version pin" section. The dev-only reader crash returns at
parity with develop/master; production is unaffected (logger is dev-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…oS (#507)
Updates @nestjs/platform-express 11.1.27 -> 11.1.28, which pulls multer
2.1.1 -> 2.2.0 (multer is consumed only by platform-express). Resolves the
two multer denial-of-service advisories on the API file-upload path:
- GHSA-72gw-mp4g-v24j (high): DoS via deeply nested field names
- GHSA-3p4h-7m6x-2hcm (moderate): DoS via incomplete cleanup of aborted uploads
Claude-Session: https://claude.ai/code/session_01WKoPAepDc2xhSFFDhHTHe5
Co-authored-by: Claude <noreply@anthropic.com>
…n frames
React 19.2's dev-only performance track deep-walks changed props and
throws a SecurityError when it reaches the reader iframe's cross-origin
window, crashing the commit (react/react#34840). Guard the prop
walkers so unreadable values log as [inaccessible] instead of throwing.
Fixed upstream by react/react#35679 (19.3 canary only); remove with
`pnpm patch-remove react-dom@19.2.8` once on a release containing it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ernal peer
Docker Desktop writes both IPv4 and IPv6 host-gateway entries, so nginx
expands host.docker.internal into two peers; the IPv6 one is unreachable
from the IPv4-only compose network and its failure accounting could mark
the whole implicit upstream down (no live upstreams -> 502 bursts during
replication). An explicit upstream with max_fails=0 lets a failed connect
fall through to the reachable address instead of disabling the group.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel's lockfile heuristic picks pnpm 9, which can't read patchedDependencies
from pnpm-workspace.yaml and fails with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
The bootstrapped pnpm 11 defers to the packageManager pin in package.json.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel prepends its detected pnpm 9 to PATH, shadowing the npm-installed
pnpm 11, so the install still hit ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pnpm-workspace.yaml references patches/react-dom@19.2.8.patch, so the
frozen install in the base stage fails with ENOENT without it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
engines.node overrides the dashboard Node version; >=24 resolves to the
latest 24.x on Vercel while staying satisfied by the node 25 used in CI,
docker and local shells.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main.ts value-imports json/urlencoded from express, which pnpm's
isolated node_modules can't resolve through @nestjs/platform-express;
dist/main crashed with MODULE_NOT_FOUND on a clean install.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
useBook is a live rxdb-backed query and the book finished dialog keeps
an always-on observer of the same key, so the disabled observer in
useCreateReader still received every progress-sync cache update and the
bookOnce effect dependency remounted the reader on each write.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…514)
The back arrow fell through to home whenever a screen had performed a
replace-only navigation, such as switching tab on the book optimize
screen. react-router rebuilds the whole history state object on replace,
so the `__obokuCanGoBack` flag was dropped, and TrackHistoryCanGoBack
only re-stamped it on pathname changes.
react-router already tracks the entry position under `history.state.idx`,
preserved across replace, which removes the need for a custom flag, the
`__obokuFallbackBack` marker and the tracker component.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Extracts createBookQueryOptions alongside the existing
createLinkQueryOptions pattern and rebuilds the reader's frozen book
snapshot on top of a reusable hook that resolves a query's first result
and ignores later cache updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hanism
Splits the one-shot idea into an observable flavour of
queryClient.ensureQueryData (cache read, else first emission seeding the
empty cache, never touching the tanstack fetch machinery) and a thin
snapshot hook on top. Drops the inert observer, so consumers no longer
re-render on post-capture cache writes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the effect+state capture with a vanilla useQuery over a
prefix-namespaced key whose queryFn is ensureQueryData$: staleTime
Infinity freezes the snapshot, gcTime 0 scopes it to the consumers'
lifetime and concurrent consumers dedupe into a single resolution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercelBot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
obokuReadyReadyPreview, CommentJul 26, 2026 12:49pm
oboku-landingReadyReadyPreview, CommentJul 26, 2026 12:49pm

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6c4259fa2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadapps/web/src/queries/useEnsureQueryData$.ts
The private useQuery dropped the source query's networkMode, so offline
the default online mode paused resolution entirely — blocking even the
warm-cache read and leaving locally available books on the loading
screen. Resolution is cache-first, so always is the correct default;
network-bound sources can pass online explicitly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mbret
mbret merged commit 481cdbc into masterJul 26, 2026
5 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@mbret
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Release by mbret · Pull Request #515 · mbret/oboku · GitHub
Skip to content

Release - #515

Merged
mbret merged 24 commits into
masterfrom
develop
Jul 26, 2026
Merged

Release#515
mbret merged 24 commits into
masterfrom
develop

Conversation

@mbret

Copy link
Copy Markdown
Owner

No description provided.

mbretand others added 23 commits July 20, 2026 13:52
* feat: upgrade @prose-reader/* to 1.332.0
Bump every @prose-reader/* dependency from 1.324.0 to 1.332.0 across web,
api and archive-metadata, and migrate the web reader to the breaking API
changes shipped in that range.
Reader lifecycle (prose #229/#230/#231):
- createReader() now takes the manifest (and optional cfi) at construction
and mount(containerElement) is a one-shot DOM attachment; reader.load()
is gone. useCreateReader now creates + mounts + destroys the reader in a
single effect (destroy() is the true inverse of create + mount, so the
effect is strict-mode safe), and useLoadReader is removed.
- The restored reading location (cfi) is read through a live ref so later
progress writes flowing back into the book query never destroy/recreate
the reader.
- reader.state$ ("idle"|"ready") is replaced by reader.mounted$; the book
loading overlay now keys off !mounted.
Pin react/react-dom to 19.1.8 in the web app only:
- react-dom 19.2 dev builds recursively read component props and access
$$typeof on every object, throwing SecurityError on our gapi cross-origin
iframe (react/react#34840). The uncaught
throw aborts passive-effect mounts (dead reader quick-menu) and corrupts
the work loop (crash on back navigation). Prod is unaffected; dev is
unusable.
- 19.1.x predates the offending logger and the web app uses no 19.2-only
APIs. admin/landing stay on 19.2.x (@Mantine 9.4 needs useEffectEvent),
so the pin is web-only, held together by resolve.dedupe in vite.config.
- Documented in AGENTS.md with removal steps for when the react bug is fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(reader): read book once via enabled gate instead of useLiveRef
The enabled predicate already stops observing after the first result, so
progress-sync writes to the same book document never change the value.
Drop the redundant useLiveRef, read bookOnce directly, and name the
enabled predicate to state the intent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: unpin apps/web react and bump react to 19.2.7 project-wide
apps/web was pinned to react 19.1.8 to dodge a dev-only React 19.2 logger
crash with the gapi iframe. The pin diverged web from the rest of the repo
and left @types on 19.2.x, creating a runtime/types skew. Align everything
to the latest 19.2.7 (react/react-dom) and 19.2.17 (@types/react) across
web, admin and landing.
Remove the now-unneeded scaffolding: the vite resolve.dedupe entry and the
AGENTS.md "React version pin" section. The dev-only reader crash returns at
parity with develop/master; production is unaffected (logger is dev-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…oS (#507)
Updates @nestjs/platform-express 11.1.27 -> 11.1.28, which pulls multer
2.1.1 -> 2.2.0 (multer is consumed only by platform-express). Resolves the
two multer denial-of-service advisories on the API file-upload path:
- GHSA-72gw-mp4g-v24j (high): DoS via deeply nested field names
- GHSA-3p4h-7m6x-2hcm (moderate): DoS via incomplete cleanup of aborted uploads
Claude-Session: https://claude.ai/code/session_01WKoPAepDc2xhSFFDhHTHe5
Co-authored-by: Claude <noreply@anthropic.com>
…n frames
React 19.2's dev-only performance track deep-walks changed props and
throws a SecurityError when it reaches the reader iframe's cross-origin
window, crashing the commit (react/react#34840). Guard the prop
walkers so unreadable values log as [inaccessible] instead of throwing.
Fixed upstream by react/react#35679 (19.3 canary only); remove with
`pnpm patch-remove react-dom@19.2.8` once on a release containing it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ernal peer
Docker Desktop writes both IPv4 and IPv6 host-gateway entries, so nginx
expands host.docker.internal into two peers; the IPv6 one is unreachable
from the IPv4-only compose network and its failure accounting could mark
the whole implicit upstream down (no live upstreams -> 502 bursts during
replication). An explicit upstream with max_fails=0 lets a failed connect
fall through to the reachable address instead of disabling the group.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel's lockfile heuristic picks pnpm 9, which can't read patchedDependencies
from pnpm-workspace.yaml and fails with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
The bootstrapped pnpm 11 defers to the packageManager pin in package.json.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel prepends its detected pnpm 9 to PATH, shadowing the npm-installed
pnpm 11, so the install still hit ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pnpm-workspace.yaml references patches/react-dom@19.2.8.patch, so the
frozen install in the base stage fails with ENOENT without it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
engines.node overrides the dashboard Node version; >=24 resolves to the
latest 24.x on Vercel while staying satisfied by the node 25 used in CI,
docker and local shells.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main.ts value-imports json/urlencoded from express, which pnpm's
isolated node_modules can't resolve through @nestjs/platform-express;
dist/main crashed with MODULE_NOT_FOUND on a clean install.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
useBook is a live rxdb-backed query and the book finished dialog keeps
an always-on observer of the same key, so the disabled observer in
useCreateReader still received every progress-sync cache update and the
bookOnce effect dependency remounted the reader on each write.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…514)
The back arrow fell through to home whenever a screen had performed a
replace-only navigation, such as switching tab on the book optimize
screen. react-router rebuilds the whole history state object on replace,
so the `__obokuCanGoBack` flag was dropped, and TrackHistoryCanGoBack
only re-stamped it on pathname changes.
react-router already tracks the entry position under `history.state.idx`,
preserved across replace, which removes the need for a custom flag, the
`__obokuFallbackBack` marker and the tracker component.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Extracts createBookQueryOptions alongside the existing
createLinkQueryOptions pattern and rebuilds the reader's frozen book
snapshot on top of a reusable hook that resolves a query's first result
and ignores later cache updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hanism
Splits the one-shot idea into an observable flavour of
queryClient.ensureQueryData (cache read, else first emission seeding the
empty cache, never touching the tanstack fetch machinery) and a thin
snapshot hook on top. Drops the inert observer, so consumers no longer
re-render on post-capture cache writes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the effect+state capture with a vanilla useQuery over a
prefix-namespaced key whose queryFn is ensureQueryData$: staleTime
Infinity freezes the snapshot, gcTime 0 scopes it to the consumers'
lifetime and concurrent consumers dedupe into a single resolution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercelBot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
obokuReadyReadyPreview, CommentJul 26, 2026 12:49pm
oboku-landingReadyReadyPreview, CommentJul 26, 2026 12:49pm

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6c4259fa2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadapps/web/src/queries/useEnsureQueryData$.ts
The private useQuery dropped the source query's networkMode, so offline
the default online mode paused resolution entirely — blocking even the
warm-cache read and leaving locally available books on the loading
screen. Resolution is cache-first, so always is the correct default;
network-bound sources can pass online explicitly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mbret
mbret merged commit 481cdbc into masterJul 26, 2026
5 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@mbret
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Release by mbret · Pull Request #515 · mbret/oboku · GitHub
Skip to content

Release - #515

Merged
mbret merged 24 commits into
masterfrom
develop
Jul 26, 2026
Merged

Release#515
mbret merged 24 commits into
masterfrom
develop

Conversation

@mbret

Copy link
Copy Markdown
Owner

No description provided.

mbretand others added 23 commits July 20, 2026 13:52
* feat: upgrade @prose-reader/* to 1.332.0
Bump every @prose-reader/* dependency from 1.324.0 to 1.332.0 across web,
api and archive-metadata, and migrate the web reader to the breaking API
changes shipped in that range.
Reader lifecycle (prose #229/#230/#231):
- createReader() now takes the manifest (and optional cfi) at construction
and mount(containerElement) is a one-shot DOM attachment; reader.load()
is gone. useCreateReader now creates + mounts + destroys the reader in a
single effect (destroy() is the true inverse of create + mount, so the
effect is strict-mode safe), and useLoadReader is removed.
- The restored reading location (cfi) is read through a live ref so later
progress writes flowing back into the book query never destroy/recreate
the reader.
- reader.state$ ("idle"|"ready") is replaced by reader.mounted$; the book
loading overlay now keys off !mounted.
Pin react/react-dom to 19.1.8 in the web app only:
- react-dom 19.2 dev builds recursively read component props and access
$$typeof on every object, throwing SecurityError on our gapi cross-origin
iframe (react/react#34840). The uncaught
throw aborts passive-effect mounts (dead reader quick-menu) and corrupts
the work loop (crash on back navigation). Prod is unaffected; dev is
unusable.
- 19.1.x predates the offending logger and the web app uses no 19.2-only
APIs. admin/landing stay on 19.2.x (@Mantine 9.4 needs useEffectEvent),
so the pin is web-only, held together by resolve.dedupe in vite.config.
- Documented in AGENTS.md with removal steps for when the react bug is fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(reader): read book once via enabled gate instead of useLiveRef
The enabled predicate already stops observing after the first result, so
progress-sync writes to the same book document never change the value.
Drop the redundant useLiveRef, read bookOnce directly, and name the
enabled predicate to state the intent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: unpin apps/web react and bump react to 19.2.7 project-wide
apps/web was pinned to react 19.1.8 to dodge a dev-only React 19.2 logger
crash with the gapi iframe. The pin diverged web from the rest of the repo
and left @types on 19.2.x, creating a runtime/types skew. Align everything
to the latest 19.2.7 (react/react-dom) and 19.2.17 (@types/react) across
web, admin and landing.
Remove the now-unneeded scaffolding: the vite resolve.dedupe entry and the
AGENTS.md "React version pin" section. The dev-only reader crash returns at
parity with develop/master; production is unaffected (logger is dev-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…oS (#507)
Updates @nestjs/platform-express 11.1.27 -> 11.1.28, which pulls multer
2.1.1 -> 2.2.0 (multer is consumed only by platform-express). Resolves the
two multer denial-of-service advisories on the API file-upload path:
- GHSA-72gw-mp4g-v24j (high): DoS via deeply nested field names
- GHSA-3p4h-7m6x-2hcm (moderate): DoS via incomplete cleanup of aborted uploads
Claude-Session: https://claude.ai/code/session_01WKoPAepDc2xhSFFDhHTHe5
Co-authored-by: Claude <noreply@anthropic.com>
…n frames
React 19.2's dev-only performance track deep-walks changed props and
throws a SecurityError when it reaches the reader iframe's cross-origin
window, crashing the commit (react/react#34840). Guard the prop
walkers so unreadable values log as [inaccessible] instead of throwing.
Fixed upstream by react/react#35679 (19.3 canary only); remove with
`pnpm patch-remove react-dom@19.2.8` once on a release containing it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ernal peer
Docker Desktop writes both IPv4 and IPv6 host-gateway entries, so nginx
expands host.docker.internal into two peers; the IPv6 one is unreachable
from the IPv4-only compose network and its failure accounting could mark
the whole implicit upstream down (no live upstreams -> 502 bursts during
replication). An explicit upstream with max_fails=0 lets a failed connect
fall through to the reachable address instead of disabling the group.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel's lockfile heuristic picks pnpm 9, which can't read patchedDependencies
from pnpm-workspace.yaml and fails with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
The bootstrapped pnpm 11 defers to the packageManager pin in package.json.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel prepends its detected pnpm 9 to PATH, shadowing the npm-installed
pnpm 11, so the install still hit ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pnpm-workspace.yaml references patches/react-dom@19.2.8.patch, so the
frozen install in the base stage fails with ENOENT without it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
engines.node overrides the dashboard Node version; >=24 resolves to the
latest 24.x on Vercel while staying satisfied by the node 25 used in CI,
docker and local shells.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main.ts value-imports json/urlencoded from express, which pnpm's
isolated node_modules can't resolve through @nestjs/platform-express;
dist/main crashed with MODULE_NOT_FOUND on a clean install.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
useBook is a live rxdb-backed query and the book finished dialog keeps
an always-on observer of the same key, so the disabled observer in
useCreateReader still received every progress-sync cache update and the
bookOnce effect dependency remounted the reader on each write.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…514)
The back arrow fell through to home whenever a screen had performed a
replace-only navigation, such as switching tab on the book optimize
screen. react-router rebuilds the whole history state object on replace,
so the `__obokuCanGoBack` flag was dropped, and TrackHistoryCanGoBack
only re-stamped it on pathname changes.
react-router already tracks the entry position under `history.state.idx`,
preserved across replace, which removes the need for a custom flag, the
`__obokuFallbackBack` marker and the tracker component.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Extracts createBookQueryOptions alongside the existing
createLinkQueryOptions pattern and rebuilds the reader's frozen book
snapshot on top of a reusable hook that resolves a query's first result
and ignores later cache updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hanism
Splits the one-shot idea into an observable flavour of
queryClient.ensureQueryData (cache read, else first emission seeding the
empty cache, never touching the tanstack fetch machinery) and a thin
snapshot hook on top. Drops the inert observer, so consumers no longer
re-render on post-capture cache writes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the effect+state capture with a vanilla useQuery over a
prefix-namespaced key whose queryFn is ensureQueryData$: staleTime
Infinity freezes the snapshot, gcTime 0 scopes it to the consumers'
lifetime and concurrent consumers dedupe into a single resolution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercelBot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
obokuReadyReadyPreview, CommentJul 26, 2026 12:49pm
oboku-landingReadyReadyPreview, CommentJul 26, 2026 12:49pm

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6c4259fa2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadapps/web/src/queries/useEnsureQueryData$.ts
The private useQuery dropped the source query's networkMode, so offline
the default online mode paused resolution entirely — blocking even the
warm-cache read and leaving locally available books on the loading
screen. Resolution is cache-first, so always is the correct default;
network-bound sources can pass online explicitly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mbret
mbret merged commit 481cdbc into masterJul 26, 2026
5 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@mbret
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Release by mbret · Pull Request #515 · mbret/oboku · GitHub
Skip to content

Release - #515

Merged
mbret merged 24 commits into
masterfrom
develop
Jul 26, 2026
Merged

Release#515
mbret merged 24 commits into
masterfrom
develop

Conversation

@mbret

Copy link
Copy Markdown
Owner

No description provided.

mbretand others added 23 commits July 20, 2026 13:52
* feat: upgrade @prose-reader/* to 1.332.0
Bump every @prose-reader/* dependency from 1.324.0 to 1.332.0 across web,
api and archive-metadata, and migrate the web reader to the breaking API
changes shipped in that range.
Reader lifecycle (prose #229/#230/#231):
- createReader() now takes the manifest (and optional cfi) at construction
and mount(containerElement) is a one-shot DOM attachment; reader.load()
is gone. useCreateReader now creates + mounts + destroys the reader in a
single effect (destroy() is the true inverse of create + mount, so the
effect is strict-mode safe), and useLoadReader is removed.
- The restored reading location (cfi) is read through a live ref so later
progress writes flowing back into the book query never destroy/recreate
the reader.
- reader.state$ ("idle"|"ready") is replaced by reader.mounted$; the book
loading overlay now keys off !mounted.
Pin react/react-dom to 19.1.8 in the web app only:
- react-dom 19.2 dev builds recursively read component props and access
$$typeof on every object, throwing SecurityError on our gapi cross-origin
iframe (react/react#34840). The uncaught
throw aborts passive-effect mounts (dead reader quick-menu) and corrupts
the work loop (crash on back navigation). Prod is unaffected; dev is
unusable.
- 19.1.x predates the offending logger and the web app uses no 19.2-only
APIs. admin/landing stay on 19.2.x (@Mantine 9.4 needs useEffectEvent),
so the pin is web-only, held together by resolve.dedupe in vite.config.
- Documented in AGENTS.md with removal steps for when the react bug is fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(reader): read book once via enabled gate instead of useLiveRef
The enabled predicate already stops observing after the first result, so
progress-sync writes to the same book document never change the value.
Drop the redundant useLiveRef, read bookOnce directly, and name the
enabled predicate to state the intent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: unpin apps/web react and bump react to 19.2.7 project-wide
apps/web was pinned to react 19.1.8 to dodge a dev-only React 19.2 logger
crash with the gapi iframe. The pin diverged web from the rest of the repo
and left @types on 19.2.x, creating a runtime/types skew. Align everything
to the latest 19.2.7 (react/react-dom) and 19.2.17 (@types/react) across
web, admin and landing.
Remove the now-unneeded scaffolding: the vite resolve.dedupe entry and the
AGENTS.md "React version pin" section. The dev-only reader crash returns at
parity with develop/master; production is unaffected (logger is dev-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…oS (#507)
Updates @nestjs/platform-express 11.1.27 -> 11.1.28, which pulls multer
2.1.1 -> 2.2.0 (multer is consumed only by platform-express). Resolves the
two multer denial-of-service advisories on the API file-upload path:
- GHSA-72gw-mp4g-v24j (high): DoS via deeply nested field names
- GHSA-3p4h-7m6x-2hcm (moderate): DoS via incomplete cleanup of aborted uploads
Claude-Session: https://claude.ai/code/session_01WKoPAepDc2xhSFFDhHTHe5
Co-authored-by: Claude <noreply@anthropic.com>
…n frames
React 19.2's dev-only performance track deep-walks changed props and
throws a SecurityError when it reaches the reader iframe's cross-origin
window, crashing the commit (react/react#34840). Guard the prop
walkers so unreadable values log as [inaccessible] instead of throwing.
Fixed upstream by react/react#35679 (19.3 canary only); remove with
`pnpm patch-remove react-dom@19.2.8` once on a release containing it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ernal peer
Docker Desktop writes both IPv4 and IPv6 host-gateway entries, so nginx
expands host.docker.internal into two peers; the IPv6 one is unreachable
from the IPv4-only compose network and its failure accounting could mark
the whole implicit upstream down (no live upstreams -> 502 bursts during
replication). An explicit upstream with max_fails=0 lets a failed connect
fall through to the reachable address instead of disabling the group.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel's lockfile heuristic picks pnpm 9, which can't read patchedDependencies
from pnpm-workspace.yaml and fails with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
The bootstrapped pnpm 11 defers to the packageManager pin in package.json.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel prepends its detected pnpm 9 to PATH, shadowing the npm-installed
pnpm 11, so the install still hit ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pnpm-workspace.yaml references patches/react-dom@19.2.8.patch, so the
frozen install in the base stage fails with ENOENT without it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
engines.node overrides the dashboard Node version; >=24 resolves to the
latest 24.x on Vercel while staying satisfied by the node 25 used in CI,
docker and local shells.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main.ts value-imports json/urlencoded from express, which pnpm's
isolated node_modules can't resolve through @nestjs/platform-express;
dist/main crashed with MODULE_NOT_FOUND on a clean install.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
useBook is a live rxdb-backed query and the book finished dialog keeps
an always-on observer of the same key, so the disabled observer in
useCreateReader still received every progress-sync cache update and the
bookOnce effect dependency remounted the reader on each write.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…514)
The back arrow fell through to home whenever a screen had performed a
replace-only navigation, such as switching tab on the book optimize
screen. react-router rebuilds the whole history state object on replace,
so the `__obokuCanGoBack` flag was dropped, and TrackHistoryCanGoBack
only re-stamped it on pathname changes.
react-router already tracks the entry position under `history.state.idx`,
preserved across replace, which removes the need for a custom flag, the
`__obokuFallbackBack` marker and the tracker component.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Extracts createBookQueryOptions alongside the existing
createLinkQueryOptions pattern and rebuilds the reader's frozen book
snapshot on top of a reusable hook that resolves a query's first result
and ignores later cache updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hanism
Splits the one-shot idea into an observable flavour of
queryClient.ensureQueryData (cache read, else first emission seeding the
empty cache, never touching the tanstack fetch machinery) and a thin
snapshot hook on top. Drops the inert observer, so consumers no longer
re-render on post-capture cache writes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the effect+state capture with a vanilla useQuery over a
prefix-namespaced key whose queryFn is ensureQueryData$: staleTime
Infinity freezes the snapshot, gcTime 0 scopes it to the consumers'
lifetime and concurrent consumers dedupe into a single resolution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercelBot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
obokuReadyReadyPreview, CommentJul 26, 2026 12:49pm
oboku-landingReadyReadyPreview, CommentJul 26, 2026 12:49pm

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6c4259fa2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadapps/web/src/queries/useEnsureQueryData$.ts
The private useQuery dropped the source query's networkMode, so offline
the default online mode paused resolution entirely — blocking even the
warm-cache read and leaving locally available books on the loading
screen. Resolution is cache-first, so always is the correct default;
network-bound sources can pass online explicitly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mbret
mbret merged commit 481cdbc into masterJul 26, 2026
5 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@mbret
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Release by mbret · Pull Request #515 · mbret/oboku · GitHub
Skip to content

Release - #515

Merged
mbret merged 24 commits into
masterfrom
develop
Jul 26, 2026
Merged

Release#515
mbret merged 24 commits into
masterfrom
develop

Conversation

@mbret

Copy link
Copy Markdown
Owner

No description provided.

mbretand others added 23 commits July 20, 2026 13:52
* feat: upgrade @prose-reader/* to 1.332.0
Bump every @prose-reader/* dependency from 1.324.0 to 1.332.0 across web,
api and archive-metadata, and migrate the web reader to the breaking API
changes shipped in that range.
Reader lifecycle (prose #229/#230/#231):
- createReader() now takes the manifest (and optional cfi) at construction
and mount(containerElement) is a one-shot DOM attachment; reader.load()
is gone. useCreateReader now creates + mounts + destroys the reader in a
single effect (destroy() is the true inverse of create + mount, so the
effect is strict-mode safe), and useLoadReader is removed.
- The restored reading location (cfi) is read through a live ref so later
progress writes flowing back into the book query never destroy/recreate
the reader.
- reader.state$ ("idle"|"ready") is replaced by reader.mounted$; the book
loading overlay now keys off !mounted.
Pin react/react-dom to 19.1.8 in the web app only:
- react-dom 19.2 dev builds recursively read component props and access
$$typeof on every object, throwing SecurityError on our gapi cross-origin
iframe (react/react#34840). The uncaught
throw aborts passive-effect mounts (dead reader quick-menu) and corrupts
the work loop (crash on back navigation). Prod is unaffected; dev is
unusable.
- 19.1.x predates the offending logger and the web app uses no 19.2-only
APIs. admin/landing stay on 19.2.x (@Mantine 9.4 needs useEffectEvent),
so the pin is web-only, held together by resolve.dedupe in vite.config.
- Documented in AGENTS.md with removal steps for when the react bug is fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(reader): read book once via enabled gate instead of useLiveRef
The enabled predicate already stops observing after the first result, so
progress-sync writes to the same book document never change the value.
Drop the redundant useLiveRef, read bookOnce directly, and name the
enabled predicate to state the intent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: unpin apps/web react and bump react to 19.2.7 project-wide
apps/web was pinned to react 19.1.8 to dodge a dev-only React 19.2 logger
crash with the gapi iframe. The pin diverged web from the rest of the repo
and left @types on 19.2.x, creating a runtime/types skew. Align everything
to the latest 19.2.7 (react/react-dom) and 19.2.17 (@types/react) across
web, admin and landing.
Remove the now-unneeded scaffolding: the vite resolve.dedupe entry and the
AGENTS.md "React version pin" section. The dev-only reader crash returns at
parity with develop/master; production is unaffected (logger is dev-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…oS (#507)
Updates @nestjs/platform-express 11.1.27 -> 11.1.28, which pulls multer
2.1.1 -> 2.2.0 (multer is consumed only by platform-express). Resolves the
two multer denial-of-service advisories on the API file-upload path:
- GHSA-72gw-mp4g-v24j (high): DoS via deeply nested field names
- GHSA-3p4h-7m6x-2hcm (moderate): DoS via incomplete cleanup of aborted uploads
Claude-Session: https://claude.ai/code/session_01WKoPAepDc2xhSFFDhHTHe5
Co-authored-by: Claude <noreply@anthropic.com>
…n frames
React 19.2's dev-only performance track deep-walks changed props and
throws a SecurityError when it reaches the reader iframe's cross-origin
window, crashing the commit (react/react#34840). Guard the prop
walkers so unreadable values log as [inaccessible] instead of throwing.
Fixed upstream by react/react#35679 (19.3 canary only); remove with
`pnpm patch-remove react-dom@19.2.8` once on a release containing it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ernal peer
Docker Desktop writes both IPv4 and IPv6 host-gateway entries, so nginx
expands host.docker.internal into two peers; the IPv6 one is unreachable
from the IPv4-only compose network and its failure accounting could mark
the whole implicit upstream down (no live upstreams -> 502 bursts during
replication). An explicit upstream with max_fails=0 lets a failed connect
fall through to the reachable address instead of disabling the group.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel's lockfile heuristic picks pnpm 9, which can't read patchedDependencies
from pnpm-workspace.yaml and fails with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
The bootstrapped pnpm 11 defers to the packageManager pin in package.json.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel prepends its detected pnpm 9 to PATH, shadowing the npm-installed
pnpm 11, so the install still hit ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pnpm-workspace.yaml references patches/react-dom@19.2.8.patch, so the
frozen install in the base stage fails with ENOENT without it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
engines.node overrides the dashboard Node version; >=24 resolves to the
latest 24.x on Vercel while staying satisfied by the node 25 used in CI,
docker and local shells.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main.ts value-imports json/urlencoded from express, which pnpm's
isolated node_modules can't resolve through @nestjs/platform-express;
dist/main crashed with MODULE_NOT_FOUND on a clean install.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
useBook is a live rxdb-backed query and the book finished dialog keeps
an always-on observer of the same key, so the disabled observer in
useCreateReader still received every progress-sync cache update and the
bookOnce effect dependency remounted the reader on each write.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…514)
The back arrow fell through to home whenever a screen had performed a
replace-only navigation, such as switching tab on the book optimize
screen. react-router rebuilds the whole history state object on replace,
so the `__obokuCanGoBack` flag was dropped, and TrackHistoryCanGoBack
only re-stamped it on pathname changes.
react-router already tracks the entry position under `history.state.idx`,
preserved across replace, which removes the need for a custom flag, the
`__obokuFallbackBack` marker and the tracker component.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Extracts createBookQueryOptions alongside the existing
createLinkQueryOptions pattern and rebuilds the reader's frozen book
snapshot on top of a reusable hook that resolves a query's first result
and ignores later cache updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hanism
Splits the one-shot idea into an observable flavour of
queryClient.ensureQueryData (cache read, else first emission seeding the
empty cache, never touching the tanstack fetch machinery) and a thin
snapshot hook on top. Drops the inert observer, so consumers no longer
re-render on post-capture cache writes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the effect+state capture with a vanilla useQuery over a
prefix-namespaced key whose queryFn is ensureQueryData$: staleTime
Infinity freezes the snapshot, gcTime 0 scopes it to the consumers'
lifetime and concurrent consumers dedupe into a single resolution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercelBot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
obokuReadyReadyPreview, CommentJul 26, 2026 12:49pm
oboku-landingReadyReadyPreview, CommentJul 26, 2026 12:49pm

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6c4259fa2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadapps/web/src/queries/useEnsureQueryData$.ts
The private useQuery dropped the source query's networkMode, so offline
the default online mode paused resolution entirely — blocking even the
warm-cache read and leaving locally available books on the loading
screen. Resolution is cache-first, so always is the correct default;
network-bound sources can pass online explicitly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mbret
mbret merged commit 481cdbc into masterJul 26, 2026
5 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@mbret
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Release by mbret · Pull Request #515 · mbret/oboku · GitHub
Skip to content

Release - #515

Merged
mbret merged 24 commits into
masterfrom
develop
Jul 26, 2026
Merged

Release#515
mbret merged 24 commits into
masterfrom
develop

Conversation

@mbret

Copy link
Copy Markdown
Owner

No description provided.

mbretand others added 23 commits July 20, 2026 13:52
* feat: upgrade @prose-reader/* to 1.332.0
Bump every @prose-reader/* dependency from 1.324.0 to 1.332.0 across web,
api and archive-metadata, and migrate the web reader to the breaking API
changes shipped in that range.
Reader lifecycle (prose #229/#230/#231):
- createReader() now takes the manifest (and optional cfi) at construction
and mount(containerElement) is a one-shot DOM attachment; reader.load()
is gone. useCreateReader now creates + mounts + destroys the reader in a
single effect (destroy() is the true inverse of create + mount, so the
effect is strict-mode safe), and useLoadReader is removed.
- The restored reading location (cfi) is read through a live ref so later
progress writes flowing back into the book query never destroy/recreate
the reader.
- reader.state$ ("idle"|"ready") is replaced by reader.mounted$; the book
loading overlay now keys off !mounted.
Pin react/react-dom to 19.1.8 in the web app only:
- react-dom 19.2 dev builds recursively read component props and access
$$typeof on every object, throwing SecurityError on our gapi cross-origin
iframe (react/react#34840). The uncaught
throw aborts passive-effect mounts (dead reader quick-menu) and corrupts
the work loop (crash on back navigation). Prod is unaffected; dev is
unusable.
- 19.1.x predates the offending logger and the web app uses no 19.2-only
APIs. admin/landing stay on 19.2.x (@Mantine 9.4 needs useEffectEvent),
so the pin is web-only, held together by resolve.dedupe in vite.config.
- Documented in AGENTS.md with removal steps for when the react bug is fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(reader): read book once via enabled gate instead of useLiveRef
The enabled predicate already stops observing after the first result, so
progress-sync writes to the same book document never change the value.
Drop the redundant useLiveRef, read bookOnce directly, and name the
enabled predicate to state the intent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: unpin apps/web react and bump react to 19.2.7 project-wide
apps/web was pinned to react 19.1.8 to dodge a dev-only React 19.2 logger
crash with the gapi iframe. The pin diverged web from the rest of the repo
and left @types on 19.2.x, creating a runtime/types skew. Align everything
to the latest 19.2.7 (react/react-dom) and 19.2.17 (@types/react) across
web, admin and landing.
Remove the now-unneeded scaffolding: the vite resolve.dedupe entry and the
AGENTS.md "React version pin" section. The dev-only reader crash returns at
parity with develop/master; production is unaffected (logger is dev-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…oS (#507)
Updates @nestjs/platform-express 11.1.27 -> 11.1.28, which pulls multer
2.1.1 -> 2.2.0 (multer is consumed only by platform-express). Resolves the
two multer denial-of-service advisories on the API file-upload path:
- GHSA-72gw-mp4g-v24j (high): DoS via deeply nested field names
- GHSA-3p4h-7m6x-2hcm (moderate): DoS via incomplete cleanup of aborted uploads
Claude-Session: https://claude.ai/code/session_01WKoPAepDc2xhSFFDhHTHe5
Co-authored-by: Claude <noreply@anthropic.com>
…n frames
React 19.2's dev-only performance track deep-walks changed props and
throws a SecurityError when it reaches the reader iframe's cross-origin
window, crashing the commit (react/react#34840). Guard the prop
walkers so unreadable values log as [inaccessible] instead of throwing.
Fixed upstream by react/react#35679 (19.3 canary only); remove with
`pnpm patch-remove react-dom@19.2.8` once on a release containing it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ernal peer
Docker Desktop writes both IPv4 and IPv6 host-gateway entries, so nginx
expands host.docker.internal into two peers; the IPv6 one is unreachable
from the IPv4-only compose network and its failure accounting could mark
the whole implicit upstream down (no live upstreams -> 502 bursts during
replication). An explicit upstream with max_fails=0 lets a failed connect
fall through to the reachable address instead of disabling the group.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel's lockfile heuristic picks pnpm 9, which can't read patchedDependencies
from pnpm-workspace.yaml and fails with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
The bootstrapped pnpm 11 defers to the packageManager pin in package.json.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel prepends its detected pnpm 9 to PATH, shadowing the npm-installed
pnpm 11, so the install still hit ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pnpm-workspace.yaml references patches/react-dom@19.2.8.patch, so the
frozen install in the base stage fails with ENOENT without it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
engines.node overrides the dashboard Node version; >=24 resolves to the
latest 24.x on Vercel while staying satisfied by the node 25 used in CI,
docker and local shells.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main.ts value-imports json/urlencoded from express, which pnpm's
isolated node_modules can't resolve through @nestjs/platform-express;
dist/main crashed with MODULE_NOT_FOUND on a clean install.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
useBook is a live rxdb-backed query and the book finished dialog keeps
an always-on observer of the same key, so the disabled observer in
useCreateReader still received every progress-sync cache update and the
bookOnce effect dependency remounted the reader on each write.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…514)
The back arrow fell through to home whenever a screen had performed a
replace-only navigation, such as switching tab on the book optimize
screen. react-router rebuilds the whole history state object on replace,
so the `__obokuCanGoBack` flag was dropped, and TrackHistoryCanGoBack
only re-stamped it on pathname changes.
react-router already tracks the entry position under `history.state.idx`,
preserved across replace, which removes the need for a custom flag, the
`__obokuFallbackBack` marker and the tracker component.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Extracts createBookQueryOptions alongside the existing
createLinkQueryOptions pattern and rebuilds the reader's frozen book
snapshot on top of a reusable hook that resolves a query's first result
and ignores later cache updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hanism
Splits the one-shot idea into an observable flavour of
queryClient.ensureQueryData (cache read, else first emission seeding the
empty cache, never touching the tanstack fetch machinery) and a thin
snapshot hook on top. Drops the inert observer, so consumers no longer
re-render on post-capture cache writes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the effect+state capture with a vanilla useQuery over a
prefix-namespaced key whose queryFn is ensureQueryData$: staleTime
Infinity freezes the snapshot, gcTime 0 scopes it to the consumers'
lifetime and concurrent consumers dedupe into a single resolution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercelBot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
obokuReadyReadyPreview, CommentJul 26, 2026 12:49pm
oboku-landingReadyReadyPreview, CommentJul 26, 2026 12:49pm

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6c4259fa2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadapps/web/src/queries/useEnsureQueryData$.ts
The private useQuery dropped the source query's networkMode, so offline
the default online mode paused resolution entirely — blocking even the
warm-cache read and leaving locally available books on the loading
screen. Resolution is cache-first, so always is the correct default;
network-bound sources can pass online explicitly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mbret
mbret merged commit 481cdbc into masterJul 26, 2026
5 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@mbret
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Release by mbret · Pull Request #515 · mbret/oboku · GitHub
Skip to content

Release - #515

Merged
mbret merged 24 commits into
masterfrom
develop
Jul 26, 2026
Merged

Release#515
mbret merged 24 commits into
masterfrom
develop

Conversation

@mbret

Copy link
Copy Markdown
Owner

No description provided.

mbretand others added 23 commits July 20, 2026 13:52
* feat: upgrade @prose-reader/* to 1.332.0
Bump every @prose-reader/* dependency from 1.324.0 to 1.332.0 across web,
api and archive-metadata, and migrate the web reader to the breaking API
changes shipped in that range.
Reader lifecycle (prose #229/#230/#231):
- createReader() now takes the manifest (and optional cfi) at construction
and mount(containerElement) is a one-shot DOM attachment; reader.load()
is gone. useCreateReader now creates + mounts + destroys the reader in a
single effect (destroy() is the true inverse of create + mount, so the
effect is strict-mode safe), and useLoadReader is removed.
- The restored reading location (cfi) is read through a live ref so later
progress writes flowing back into the book query never destroy/recreate
the reader.
- reader.state$ ("idle"|"ready") is replaced by reader.mounted$; the book
loading overlay now keys off !mounted.
Pin react/react-dom to 19.1.8 in the web app only:
- react-dom 19.2 dev builds recursively read component props and access
$$typeof on every object, throwing SecurityError on our gapi cross-origin
iframe (react/react#34840). The uncaught
throw aborts passive-effect mounts (dead reader quick-menu) and corrupts
the work loop (crash on back navigation). Prod is unaffected; dev is
unusable.
- 19.1.x predates the offending logger and the web app uses no 19.2-only
APIs. admin/landing stay on 19.2.x (@Mantine 9.4 needs useEffectEvent),
so the pin is web-only, held together by resolve.dedupe in vite.config.
- Documented in AGENTS.md with removal steps for when the react bug is fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(reader): read book once via enabled gate instead of useLiveRef
The enabled predicate already stops observing after the first result, so
progress-sync writes to the same book document never change the value.
Drop the redundant useLiveRef, read bookOnce directly, and name the
enabled predicate to state the intent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: unpin apps/web react and bump react to 19.2.7 project-wide
apps/web was pinned to react 19.1.8 to dodge a dev-only React 19.2 logger
crash with the gapi iframe. The pin diverged web from the rest of the repo
and left @types on 19.2.x, creating a runtime/types skew. Align everything
to the latest 19.2.7 (react/react-dom) and 19.2.17 (@types/react) across
web, admin and landing.
Remove the now-unneeded scaffolding: the vite resolve.dedupe entry and the
AGENTS.md "React version pin" section. The dev-only reader crash returns at
parity with develop/master; production is unaffected (logger is dev-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…oS (#507)
Updates @nestjs/platform-express 11.1.27 -> 11.1.28, which pulls multer
2.1.1 -> 2.2.0 (multer is consumed only by platform-express). Resolves the
two multer denial-of-service advisories on the API file-upload path:
- GHSA-72gw-mp4g-v24j (high): DoS via deeply nested field names
- GHSA-3p4h-7m6x-2hcm (moderate): DoS via incomplete cleanup of aborted uploads
Claude-Session: https://claude.ai/code/session_01WKoPAepDc2xhSFFDhHTHe5
Co-authored-by: Claude <noreply@anthropic.com>
…n frames
React 19.2's dev-only performance track deep-walks changed props and
throws a SecurityError when it reaches the reader iframe's cross-origin
window, crashing the commit (react/react#34840). Guard the prop
walkers so unreadable values log as [inaccessible] instead of throwing.
Fixed upstream by react/react#35679 (19.3 canary only); remove with
`pnpm patch-remove react-dom@19.2.8` once on a release containing it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ernal peer
Docker Desktop writes both IPv4 and IPv6 host-gateway entries, so nginx
expands host.docker.internal into two peers; the IPv6 one is unreachable
from the IPv4-only compose network and its failure accounting could mark
the whole implicit upstream down (no live upstreams -> 502 bursts during
replication). An explicit upstream with max_fails=0 lets a failed connect
fall through to the reachable address instead of disabling the group.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel's lockfile heuristic picks pnpm 9, which can't read patchedDependencies
from pnpm-workspace.yaml and fails with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
The bootstrapped pnpm 11 defers to the packageManager pin in package.json.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vercel prepends its detected pnpm 9 to PATH, shadowing the npm-installed
pnpm 11, so the install still hit ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pnpm-workspace.yaml references patches/react-dom@19.2.8.patch, so the
frozen install in the base stage fails with ENOENT without it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
engines.node overrides the dashboard Node version; >=24 resolves to the
latest 24.x on Vercel while staying satisfied by the node 25 used in CI,
docker and local shells.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main.ts value-imports json/urlencoded from express, which pnpm's
isolated node_modules can't resolve through @nestjs/platform-express;
dist/main crashed with MODULE_NOT_FOUND on a clean install.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
useBook is a live rxdb-backed query and the book finished dialog keeps
an always-on observer of the same key, so the disabled observer in
useCreateReader still received every progress-sync cache update and the
bookOnce effect dependency remounted the reader on each write.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…514)
The back arrow fell through to home whenever a screen had performed a
replace-only navigation, such as switching tab on the book optimize
screen. react-router rebuilds the whole history state object on replace,
so the `__obokuCanGoBack` flag was dropped, and TrackHistoryCanGoBack
only re-stamped it on pathname changes.
react-router already tracks the entry position under `history.state.idx`,
preserved across replace, which removes the need for a custom flag, the
`__obokuFallbackBack` marker and the tracker component.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Extracts createBookQueryOptions alongside the existing
createLinkQueryOptions pattern and rebuilds the reader's frozen book
snapshot on top of a reusable hook that resolves a query's first result
and ignores later cache updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hanism
Splits the one-shot idea into an observable flavour of
queryClient.ensureQueryData (cache read, else first emission seeding the
empty cache, never touching the tanstack fetch machinery) and a thin
snapshot hook on top. Drops the inert observer, so consumers no longer
re-render on post-capture cache writes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the effect+state capture with a vanilla useQuery over a
prefix-namespaced key whose queryFn is ensureQueryData$: staleTime
Infinity freezes the snapshot, gcTime 0 scopes it to the consumers'
lifetime and concurrent consumers dedupe into a single resolution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercelBot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
obokuReadyReadyPreview, CommentJul 26, 2026 12:49pm
oboku-landingReadyReadyPreview, CommentJul 26, 2026 12:49pm

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6c4259fa2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadapps/web/src/queries/useEnsureQueryData$.ts
The private useQuery dropped the source query's networkMode, so offline
the default online mode paused resolution entirely — blocking even the
warm-cache read and leaving locally available books on the loading
screen. Resolution is cache-first, so always is the correct default;
network-bound sources can pass online explicitly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mbret
mbret merged commit 481cdbc into masterJul 26, 2026
5 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@mbret