fix(git-sync): forward gzip encoding (#4) + catch read-advertise reject after hijack (#5); fix Export 500 on pages with inline comments

- Review #4: forward HTTP_CONTENT_ENCODING to git http-backend so it inflates
  gzip'd RPC bodies — a non-trivial `git pull` no longer fails with
  `fatal: expected 'packfile'`. (git-http-backend + git-http.service)
- Review #5: the read-advertisement branch runs under the space lock AFTER
  reply.hijack(); a reject there (e.g. Redis down) previously left the socket
  open forever, hanging every clone/fetch. Mirror the push branch: catch, 500 if
  unwritten, always end the socket. (git-http.service)
- GS-EXPORT-500 (QA): a page with an inline comment mark returned HTTP 500 on
  Export/copy-as-markdown. The Comment mark's renderHTML took the imperative
  document.createElement branch server-side (the DOM shim used by generateHTML
  defines window/document), returning a live node with no content hole that
  crashed prosemirror-model's DOMSerializer under happy-dom. Gate the imperative
  branch on a real browser (navigator.userAgent contains 'Mozilla'); the server
  now uses the static DOMOutputSpec form. Verified: export 200 (was 500).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
claude-stand
2026-07-02 18:16:59 +03:00
parent c838fdeebe
commit f9cd3e6318
3 changed files with 61 additions and 4 deletions
+21 -1
View File
@@ -172,7 +172,27 @@ export const Comment = Mark.create<ICommentOptions, ICommentStorage>({
const commentId = HTMLAttributes?.["data-comment-id"] || null;
const resolved = HTMLAttributes?.["data-resolved"] || false;
if (typeof window === "undefined" || typeof document === "undefined") {
// Prefer the static array (DOMOutputSpec) form whenever we are NOT in a real
// interactive browser. Guarding only on `document`/`window` is insufficient
// on the server: `generateHTML()` runs under a DOM shim (happy-dom/jsdom) that
// DEFINES both globals, so the imperative `document.createElement` branch ran
// server-side and its live node + addEventListener crashed the shim's
// DOMSerializer ("Cannot read properties of undefined (reading 'length')"),
// turning every Export / copy-as-markdown of a page with an inline comment
// into an HTTP 500 (QA GS-EXPORT-500). A real browser has a non-empty
// navigator.userAgent; the SSR shims do not — route the server to the safe
// static form while keeping the clickable node in the actual editor.
const isInteractiveBrowser =
typeof window !== "undefined" &&
typeof document !== "undefined" &&
typeof navigator !== "undefined" &&
typeof navigator.userAgent === "string" &&
// Real browsers ALL carry "Mozilla" in the UA string (historical); the
// server-side DOM shim used by generateHTML() does not (e.g. "Node.js/22").
// This keeps the interactive click node ONLY in a true browser and routes
// the server to the crash-free static form.
navigator.userAgent.includes("Mozilla");
if (!isInteractiveBrowser) {
return [
"span",
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {