debug: enhance publish pipeline logging
Browse filesLog live Hocuspocus document state, XmlFragment content,
full JSON structure, CSS loading, and meta to diagnose
incomplete published articles.
Made-with: Cursor
- backend/src/publisher/index.ts +13 -6
- backend/src/server.ts +11 -5
backend/src/publisher/index.ts
CHANGED
|
@@ -60,24 +60,30 @@ function extractFromYDoc(ydoc: Y.Doc): {
|
|
| 60 |
frontmatter[key] = value;
|
| 61 |
});
|
| 62 |
|
| 63 |
-
// Debug: list all shared types in the Y.Doc
|
| 64 |
const sharedTypes: string[] = [];
|
| 65 |
ydoc.share.forEach((_value, key) => sharedTypes.push(key));
|
| 66 |
console.log("[publish] Y.Doc shared types:", sharedTypes);
|
| 67 |
console.log("[publish] frontmatter:", JSON.stringify(frontmatter));
|
| 68 |
|
| 69 |
-
// Check the XmlFragment directly
|
| 70 |
const fragment = ydoc.getXmlFragment("default");
|
| 71 |
console.log("[publish] XmlFragment 'default' length:", fragment.length);
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
-
// Convert Y.XmlFragment -> ProseMirror JSON using TipTap extensions
|
| 74 |
const extensions = getServerExtensions();
|
| 75 |
const json = TiptapTransformer
|
| 76 |
.extensions(extensions)
|
| 77 |
.fromYdoc(ydoc, "default");
|
| 78 |
|
| 79 |
console.log("[publish] TiptapTransformer result type:", json?.type);
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
if (json && typeof json === "object" && json.type === "doc") {
|
| 83 |
return { json, frontmatter };
|
|
@@ -126,11 +132,12 @@ export async function publishDocument(docName: string, token?: string): Promise<
|
|
| 126 |
date: (frontmatter.date as string) || new Date().toISOString(),
|
| 127 |
};
|
| 128 |
|
| 129 |
-
// Load CSS
|
| 130 |
const css = loadCSS();
|
|
|
|
|
|
|
| 131 |
|
| 132 |
-
// Generate HTML
|
| 133 |
const html = renderArticleHTML(json, meta, css.tokens, css.article);
|
|
|
|
| 134 |
|
| 135 |
// Generate PDF + thumbnail
|
| 136 |
let pdf: Buffer | null = null;
|
|
|
|
| 60 |
frontmatter[key] = value;
|
| 61 |
});
|
| 62 |
|
|
|
|
| 63 |
const sharedTypes: string[] = [];
|
| 64 |
ydoc.share.forEach((_value, key) => sharedTypes.push(key));
|
| 65 |
console.log("[publish] Y.Doc shared types:", sharedTypes);
|
| 66 |
console.log("[publish] frontmatter:", JSON.stringify(frontmatter));
|
| 67 |
|
|
|
|
| 68 |
const fragment = ydoc.getXmlFragment("default");
|
| 69 |
console.log("[publish] XmlFragment 'default' length:", fragment.length);
|
| 70 |
+
if (fragment.length > 0) {
|
| 71 |
+
console.log("[publish] XmlFragment first child tag:", fragment.toArray()[0]?.constructor?.name);
|
| 72 |
+
}
|
| 73 |
|
|
|
|
| 74 |
const extensions = getServerExtensions();
|
| 75 |
const json = TiptapTransformer
|
| 76 |
.extensions(extensions)
|
| 77 |
.fromYdoc(ydoc, "default");
|
| 78 |
|
| 79 |
console.log("[publish] TiptapTransformer result type:", json?.type);
|
| 80 |
+
const contentArr = (json as any)?.content;
|
| 81 |
+
console.log("[publish] TiptapTransformer content count:", Array.isArray(contentArr) ? contentArr.length : "N/A");
|
| 82 |
+
if (Array.isArray(contentArr) && contentArr.length > 0) {
|
| 83 |
+
const types = contentArr.slice(0, 10).map((n: any) => n.type);
|
| 84 |
+
console.log("[publish] First 10 node types:", types);
|
| 85 |
+
}
|
| 86 |
+
console.log("[publish] Full JSON (truncated):", JSON.stringify(json).slice(0, 2000));
|
| 87 |
|
| 88 |
if (json && typeof json === "object" && json.type === "doc") {
|
| 89 |
return { json, frontmatter };
|
|
|
|
| 132 |
date: (frontmatter.date as string) || new Date().toISOString(),
|
| 133 |
};
|
| 134 |
|
|
|
|
| 135 |
const css = loadCSS();
|
| 136 |
+
console.log("[publish] CSS tokens length:", css.tokens.length, "article length:", css.article.length);
|
| 137 |
+
console.log("[publish] Meta:", JSON.stringify(meta));
|
| 138 |
|
|
|
|
| 139 |
const html = renderArticleHTML(json, meta, css.tokens, css.article);
|
| 140 |
+
console.log("[publish] Generated HTML length:", html.length);
|
| 141 |
|
| 142 |
// Generate PDF + thumbnail
|
| 143 |
let pdf: Buffer | null = null;
|
backend/src/server.ts
CHANGED
|
@@ -181,13 +181,19 @@ app.post("/api/publish", async (req, res) => {
|
|
| 181 |
}
|
| 182 |
|
| 183 |
try {
|
| 184 |
-
// Flush live Hocuspocus document to disk so the publisher can read it
|
| 185 |
const conn = await hocuspocus.openDirectConnection(DEFAULT_DOC_NAME);
|
| 186 |
if (conn.document) {
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
}
|
| 192 |
await conn.disconnect();
|
| 193 |
|
|
|
|
| 181 |
}
|
| 182 |
|
| 183 |
try {
|
|
|
|
| 184 |
const conn = await hocuspocus.openDirectConnection(DEFAULT_DOC_NAME);
|
| 185 |
if (conn.document) {
|
| 186 |
+
const liveTypes: string[] = [];
|
| 187 |
+
conn.document.share.forEach((_v, k) => liveTypes.push(k));
|
| 188 |
+
console.log("[publish] Live Hocuspocus doc shared types:", liveTypes);
|
| 189 |
+
const liveFragment = conn.document.getXmlFragment("default");
|
| 190 |
+
console.log("[publish] Live XmlFragment length:", liveFragment.length);
|
| 191 |
+
|
| 192 |
+
const update = Y.encodeStateAsUpdate(conn.document);
|
| 193 |
+
console.log("[publish] Encoded state size:", update.byteLength, "bytes");
|
| 194 |
+
writeFileSync(docPath(DEFAULT_DOC_NAME), Buffer.from(update));
|
| 195 |
+
} else {
|
| 196 |
+
console.warn("[publish] openDirectConnection returned no document");
|
| 197 |
}
|
| 198 |
await conn.disconnect();
|
| 199 |
|