code wiki / wiki / nx_wiki_doc_handler.nx

nx_wiki_doc_handler.nx source

↩ module page · 611 lines · 29524 B

1// nx_wiki_doc_handler.nx -- /wiki/<doc-name> HTTP route handler. 2// Renders one doc as V1 page format conformant HTML via the 3// page-format-trinity primitives. 4// 5// COMPOSES: 6// wiki/nx_wiki_index_builder (NxWikiDocStore + find_by_url + lookup) 7// wiki/nx_wiki_doc_render (NxWikiDocCtx + set_meta + write_header_v1) 8// hub/nx_nishi_page_emit (transitive via write_header_v1) 9// nx_http_server (for response wrap) 10// markdown_block (transitive via nx_wiki_doc_render) 11// 12// COMPOSED BY: 13// wiki/nx_wiki_routes (dispatch /wiki/<name>) 14// 15// V1 SCOPE: 16// - URL -> rowid via NxWikiDocStore.find_by_url (linear scan) 17// - Render: ctx_init + set_meta (default tags/license/author) + 18// write_header_v1 + markdown body emit + write_footer 19// - HTTP/1.1 200 + HTML on hit; 404 on miss 20// 21// V2 SCOPE (TODO): 22// - Per-doc metadata from doc-store (currently uses sensible defaults) 23// - ETag + If-None-Match (304 Not Modified) 24// - Conditional GET via nishi-content-hash 25// - Per-doc tag/license/author from indexed nishi-* meta tags 26// 27// Status: V1. 2026-05-27. 28 29import "nx_syscalls.nx" 30import "nx_http_server.nx" 31import "wiki/nx_wiki_index_builder.nx" 32import "wiki/nx_wiki_doc_render.nx" 33import "wiki/nx_artifact_store.nx" 34import "hub/nx_pipeline_banner.nx" 35import "nx_html_extract.nx" 36// wiki R4 navigation organs wired into the served page (auto-TOC + heading 37// anchor ids; "what links here" backlinks panel). Imported bare to match the 38// R4 organs' own internal import convention -- the resolver canonicalizes the 39// path so this dedupes with the wiki/-prefixed nx_wiki_index_builder above (no 40// rc6 double-import). nx_wiki_backlinks pulls the shared forward-link scanner. 41import "nx_wiki_toc.nx" 42import "nx_wiki_backlinks.nx" 43 44// ===== Sealed verdict surface (codes 2700-2709) ================================================= 45const NX_WDH_OK: i64 = 0 46const NX_WDH_BAD_INPUT: i64 = 2700 47const NX_WDH_NOT_FOUND: i64 = 2701 48const NX_WDH_RENDER_FAILED: i64 = 2702 49const NX_WDH_RESP_OVERFLOW: i64 = 2703 50 51// ===== Named constants (M7) ================================================= 52const NX_WDH_RESP_HDR_CAP: i64 = 512 53const NX_WDH_RENDER_CAP: i64 = 1048576 // 1 MB rendered HTML 54const NX_WDH_SCRATCH_CAP: i64 = 65536 // 64 KB scratch 55const NX_WDH_DOC_NAMES_FALLBACK: i64 = 64 56// [[wikilink]] doc-names set sized for the full doc-store cap (1000 docs * 64-byte 57// slots) so EVERY real page's slug is resolvable -- the resolver's known-page set 58// is derived from the store here (bug #2 fix), not a trimmed ctx_init arg. 59const NX_WDH_DOC_NAMES_CAP: i64 = 64000 // 1000 slots * 64 bytes 60// wiki R4 nav-wiring buffers (M7). The body markdown now renders into a temp 61// buffer (NX_WDH_MD_CAP) so the auto-TOC can be prepended and heading anchor 62// ids injected before the article HTML reaches the response. The injected copy 63// can grow (each <hN> gains ` id="<slug>"`), so its buffer is larger than the 64// raw markdown output. The backlinks panel + TOC each get their own scratch. 65const NX_WDH_MD_CAP: i64 = 1048576 // 1 MB markdown-HTML temp 66const NX_WDH_INJECT_CAP: i64 = 1310720 // 1.25 MB id-injected copy (headroom for id= attrs) 67const NX_WDH_TOC_CAP: i64 = 131072 // 128 KB TOC nav 68const NX_WDH_BACKLINKS_CAP: i64 = 262144 // 256 KB backlinks aside 69 70// V1 default metadata constants (applied when doc store doesn't carry 71// per-doc nishi-* tags yet; V2 reads from the indexed source's <meta>). 72const NX_WDH_DEFAULT_TAGS: *u8 = "wiki,nishi,charter" as *u8 73const NX_WDH_DEFAULT_TAGS_N: i64 = 19 74const NX_WDH_DEFAULT_LICENSE: *u8 = "nishi-substrate-public" as *u8 75const NX_WDH_DEFAULT_LICENSE_N: i64 = 22 76const NX_WDH_DEFAULT_AUTHOR: *u8 = "elderwesto" as *u8 77const NX_WDH_DEFAULT_AUTHOR_N: i64 = 10 78const NX_WDH_DEFAULT_LAST_MOD: *u8 = "2026-05-27T00:00:00Z" as *u8 79const NX_WDH_DEFAULT_LAST_MOD_N: i64 = 20 80const NX_WDH_CANONICAL_PREFIX: *u8 = "https://nishifamily.com" as *u8 81const NX_WDH_CANONICAL_PREFIX_N: i64 = 23 82 83// ===== Build canonical URL: "https://nishifamily.com" + url_path ================================================= 84// 85// append a NUL-terminated source (passed as a *u8 ARGUMENT, never a direct 86// const-index) into dst at off; returns the new off. THIS is the landmine-safe 87// way to emit a literal prefix: binding the const to a pointer parameter loads 88// it into a register and walks it, vs `CONST_NAME[i]` which the sovereign 89// compiler mis-resolves (it read 23 garbage bytes for the canonical prefix -- 90// the `������.../wiki/...` corruption seen in the served nishi-canonical meta). 91func nx_wdh_cat(dst: *u8, off: i64, s: *u8) -> i64 { 92 var i: i64 = 0 93 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 94 return off + i 95} 96 97// Append a counted byte run into the render ctx at ctx.out_off, bounded by the 98// real output capacity (M5) with a loop cap sized to the 1 MB render buffer 99// (M3) -- NOT the 32-byte-class NX_WIKI_DOC_MAX_PARSE_ITER guard in the render 100// module's small-write helper, which would falsely trip on a >100 KB rendered 101// body. Returns NX_WDH_OK or -NX_WDH_RESP_OVERFLOW. Used for the markdown body, 102// the TOC nav, and the backlinks aside (all of which can be large). 103func nx_wdh_emit_run(ctx: *NxWikiDocCtx, src: *u8, n: i64) -> i64 { 104 if n < 0 { return 0 - NX_WDH_BAD_INPUT } 105 if ctx.out_off + n > ctx.out_cap { return 0 - NX_WDH_RESP_OVERFLOW } 106 var i: i64 = 0 107 while i < n { 108 if i >= NX_WDH_RENDER_CAP { return 0 - NX_WDH_RESP_OVERFLOW } 109 ctx.out_buf[ctx.out_off + i] = src[i] 110 i = i + 1 111 } 112 ctx.out_off = ctx.out_off + n 113 return NX_WDH_OK 114} 115 116// ===== populate the [[wikilink]] doc-names set FROM THE STORE ================= 117// 118// Bug #2 fix (the "always broken" wikilink): nx_wiki_doc_handle used to call 119// nx_wiki_doc_ctx_init with doc_names_count=0 (the doc-names arg was trimmed by 120// a 16-arg IR cap), so the resolver's known-page set was EMPTY and every 121// [[name]] resolved to a broken-span even when the page existed. We dodge the 122// arg cap by DERIVING the names from the NxWikiDocStore the handler already 123// holds -- after ctx_init -- rather than threading a separate trimmed arg. 124// 125// Each store URL (e.g. "/wiki/realpage") has its optional "/wiki/" prefix 126// stripped to the bare slug ("realpage") -- the form a [[realpage]] token names. 127// Names are packed into names_buf as 64-byte slots: byte0 = name_len (1..63), 128// bytes 1..len = the slug. Returns the count of populated entries (>=0). 129// Slugs longer than 63 bytes, or once the buffer's slot capacity is reached, 130// are skipped (graceful: they simply won't resolve, rendering as broken-spans). 131func nx_wdh_populate_doc_names(store: *NxWikiDocStore, 132 names_buf: *u8, names_cap: i64) -> i64 { 133 if (store as i64) == 0 { return 0 } 134 if store.valid != 1 { return 0 } 135 let slots: i64 = names_cap / 64 136 let dc: i64 = nx_wiki_doc_store_count(store) 137 let tp: *i64 = sys_mmap(8) as *i64 138 let tn: *i64 = sys_mmap(8) as *i64 139 let up: *i64 = sys_mmap(8) as *i64 140 let un: *i64 = sys_mmap(8) as *i64 141 let bp: *i64 = sys_mmap(8) as *i64 142 let bn: *i64 = sys_mmap(8) as *i64 143 var cnt: i64 = 0 144 var r: i64 = 0 145 while r < dc { 146 if r >= 1000 { r = dc } 147 if r < dc { 148 if cnt < slots { 149 let rc_lk: i64 = nx_wiki_doc_store_lookup(store, r, tp, tn, up, un, bp, bn) 150 if rc_lk == NX_WIB_OK { 151 var sp: *u8 = up[0] as *u8 152 var sn: i64 = un[0] 153 // strip a leading "/wiki/" so the stored URL becomes a bare slug. 154 if sn >= 6 { 155 if sp[0] == (0x2F as u8) { 156 if sp[1] == (0x77 as u8) { 157 if sp[2] == (0x69 as u8) { 158 if sp[3] == (0x6B as u8) { 159 if sp[4] == (0x69 as u8) { 160 if sp[5] == (0x2F as u8) { 161 sp = (sp as i64 + 6) as *u8 162 sn = sn - 6 163 } 164 } 165 } 166 } 167 } 168 } 169 } 170 if sn > 0 { 171 if sn <= 63 { 172 let slot_off: i64 = cnt * 64 173 names_buf[slot_off] = (sn & 0xff) as u8 174 var k: i64 = 0 175 while k < sn { names_buf[slot_off + 1 + k] = sp[k]; k = k + 1 } 176 cnt = cnt + 1 177 } 178 } 179 } 180 } 181 } 182 r = r + 1 183 } 184 return cnt 185} 186 187func nx_wdh_build_canonical(out: *u8, cap: i64, 188 url_path: *u8, url_path_n: i64) -> i64 { 189 let need: i64 = NX_WDH_CANONICAL_PREFIX_N + url_path_n 190 if need > cap { return 0 - NX_WDH_RESP_OVERFLOW } 191 // emit the prefix via the cat helper (NOT NX_WDH_CANONICAL_PREFIX[i]). 192 let pfx: *u8 = NX_WDH_CANONICAL_PREFIX 193 var o: i64 = nx_wdh_cat(out, 0, pfx) 194 var i: i64 = 0 195 while i < url_path_n { 196 out[o + i] = url_path[i] 197 i = i + 1 198 } 199 return o + url_path_n 200} 201 202// ===== Build summary: first 200 bytes of body or "(no summary)" ================================================= 203 204func nx_wdh_build_summary(body: *u8, body_n: i64, 205 out: *u8, cap: i64) -> i64 { 206 if body_n < 1 { 207 let fallback: *u8 = "(no summary)" as *u8 208 let fallback_n: i64 = 12 209 if fallback_n > cap { return 0 - NX_WDH_RESP_OVERFLOW } 210 var i: i64 = 0 211 while i < fallback_n { out[i] = fallback[i]; i = i + 1 } 212 return fallback_n 213 } 214 var take: i64 = body_n 215 if take > 200 { take = 200 } 216 if take > cap { take = cap } 217 // Strip leading '#' heading line (markdown title) to make summary 218 // descriptive vs duplicating the title. 219 var start: i64 = 0 220 if body[0] == (0x23 as u8) { // '#' 221 // Skip to end of first line. 222 var i: i64 = 0 223 while i < body_n { 224 if body[i] == (0x0A as u8) { start = i + 1; i = body_n + 1 } 225 if i <= body_n { if i < body_n { i = i + 1 } } 226 } 227 } 228 if start >= body_n { start = 0 } 229 var actual: i64 = body_n - start 230 if actual > take { actual = take } 231 // Replace newlines with spaces for summary readability (single line). 232 var j: i64 = 0 233 while j < actual { 234 let b: i64 = body[start + j] as i64 235 if b == 0x0A { out[j] = 0x20 as u8 } else { out[j] = body[start + j] } 236 j = j + 1 237 } 238 if actual < 1 { actual = 1; out[0] = 0x20 as u8 } 239 return actual 240} 241 242// ===== V2.0 P-4: URL -> artifact-path translation ================================================= 243// 244// The wiki doc-store is keyed by URL (/wiki/<slug>); the artifact store 245// is keyed by filesystem path (/mnt/c/.../FILE.md). For V2.0 the seed 246// lists are parallel + handcrafted, so we map URL -> path in code. 247// V2.5 swaps for an embedded map in NxWikiDocStore. 248// 249// Returns 1 if URL has a known mapping (out_path/out_n filled); 0 otherwise. 250 251func nx_wdh_url_to_artifact_path(url: *u8, url_n: i64, 252 out_path: *i64, out_n: *i64) -> i64 { 253 if (out_path as i64) == 0 { return 0 } 254 if (out_n as i64) == 0 { return 0 } 255 out_path[0] = 0; out_n[0] = 0 256 257 // Match URL exactly + return hardcoded artifact path. Order: longest first. 258 if url_n == 27 { 259 if nx_he_match_ci(url, 0, url_n, "/wiki/nishi-search-charter" as *u8, 26) == 1 { 260 out_path[0] = ("/mnt/c/Users/elder/nishi-silicon/NISHI_SEARCH_CHARTER.md" as *u8) as i64 261 out_n[0] = 55 262 return 1 263 } 264 } 265 if url_n == 26 { 266 if nx_he_match_ci(url, 0, url_n, "/wiki/nishi-page-format-v1" as *u8, 26) == 1 { 267 out_path[0] = ("/mnt/c/Users/elder/nishi-silicon/NISHI_PAGE_FORMAT_V1.md" as *u8) as i64 268 out_n[0] = 55 269 return 1 270 } 271 } 272 if url_n == 33 { 273 if nx_he_match_ci(url, 0, url_n, "/wiki/nishi-code-hygiene-standard" as *u8, 33) == 1 { 274 out_path[0] = ("/mnt/c/Users/elder/nishi-silicon/NISHI_CODE_HYGIENE_STANDARD.md" as *u8) as i64 275 out_n[0] = 62 276 return 1 277 } 278 } 279 if url_n == 43 { 280 if nx_he_match_ci(url, 0, url_n, "/wiki/nishi-small-sharp-composable-standard" as *u8, 43) == 1 { 281 out_path[0] = ("/mnt/c/Users/elder/nishi-silicon/NISHI_SMALL_SHARP_COMPOSABLE_STANDARD.md" as *u8) as i64 282 out_n[0] = 72 283 return 1 284 } 285 } 286 if url_n == 31 { 287 if nx_he_match_ci(url, 0, url_n, "/wiki/nishilang-superiority-bar" as *u8, 31) == 1 { 288 out_path[0] = ("/mnt/c/Users/elder/nishi-silicon/NISHILANG_SUPERIORITY_BAR.md" as *u8) as i64 289 out_n[0] = 60 290 return 1 291 } 292 } 293 if url_n == 24 { 294 if nx_he_match_ci(url, 0, url_n, "/wiki/nishi-wiki-charter" as *u8, 24) == 1 { 295 out_path[0] = ("/mnt/c/Users/elder/nishi-silicon/NISHI_WIKI_CHARTER.md" as *u8) as i64 296 out_n[0] = 53 297 return 1 298 } 299 } 300 if url_n == 39 { 301 if nx_he_match_ci(url, 0, url_n, "/wiki/nishi-archive-integration-charter" as *u8, 39) == 1 { 302 out_path[0] = ("/mnt/c/Users/elder/nishi-silicon/NISHI_ARCHIVE_INTEGRATION_CHARTER.md" as *u8) as i64 303 out_n[0] = 68 304 return 1 305 } 306 } 307 if url_n == 32 { 308 if nx_he_match_ci(url, 0, url_n, "/wiki/nishi-product-ops-platform" as *u8, 32) == 1 { 309 out_path[0] = ("/mnt/c/Users/elder/nishi-silicon/NISHI_PRODUCT_OPS_PLATFORM.md" as *u8) as i64 310 out_n[0] = 61 311 return 1 312 } 313 } 314 if url_n == 30 { 315 if nx_he_match_ci(url, 0, url_n, "/wiki/interoperability-charter" as *u8, 30) == 1 { 316 out_path[0] = ("/mnt/c/Users/elder/nishi-silicon/INTEROPERABILITY_CHARTER.md" as *u8) as i64 317 out_n[0] = 59 318 return 1 319 } 320 } 321 if url_n == 22 { 322 if nx_he_match_ci(url, 0, url_n, "/wiki/workload-targets" as *u8, 22) == 1 { 323 out_path[0] = ("/mnt/c/Users/elder/nishi-silicon/WORKLOAD_TARGETS.md" as *u8) as i64 324 out_n[0] = 51 325 return 1 326 } 327 if nx_he_match_ci(url, 0, url_n, "/wiki/zero-to-advanced" as *u8, 22) == 1 { 328 out_path[0] = ("/mnt/c/Users/elder/nishi-silicon/ZERO_TO_ADVANCED.md" as *u8) as i64 329 out_n[0] = 51 330 return 1 331 } 332 } 333 return 0 334} 335 336// ===== Top-level handler ================================================= 337// 338// Looks up doc by URL path; renders with V1 page format chrome + (V2.0) 339// pipeline-stage banner if artifact_store provided + URL has mapping. 340// 341// CITE-PREFIX-INJECTABLE handler (the real render body). cite_prefix is the 342// content-addressed archive store prefix the [[cite:<cid>]] post-pass consults 343// for the archive lookup + license gate. Production passes WAR_PREFIX (via the 344// wrapper above); a gate passes a fresh per-run prefix so it can prove the 345// SERVED (post-markdown) cite card is LIVE + the liar-kill holds without 346// touching the production archive. Everything else is identical to the 347// production path. 348func nx_wiki_doc_handle_cp(store: *NxWikiDocStore, 349 artifact_store: *NxArtifactStore, 350 url_path: *u8, url_path_n: i64, 351 resp_buf: *u8, resp_cap: i64, 352 out_resp_n: *i64, cite_prefix: *u8) -> i64 { 353 if (store as i64) == 0 { return 0 - NX_WDH_NOT_FOUND } 354 if store.valid != 1 { return 0 - NX_WDH_NOT_FOUND } 355 if (url_path as i64) == 0 { return 0 - NX_WDH_BAD_INPUT } 356 if (resp_buf as i64) == 0 { return 0 - NX_WDH_BAD_INPUT } 357 if (out_resp_n as i64) == 0 { return 0 - NX_WDH_BAD_INPUT } 358 if (cite_prefix as i64) == 0 { return 0 - NX_WDH_BAD_INPUT } 359 out_resp_n[0] = 0 360 361 // Find doc by URL. 362 let rowid: i64 = nx_wiki_doc_store_find_by_url(store, url_path, url_path_n) 363 if rowid < 0 { return 0 - NX_WDH_NOT_FOUND } 364 365 // Look up (title, url, body). 366 let t_ptr: *i64 = (sys_mmap(8)) as *i64 367 let t_n: *i64 = (sys_mmap(8)) as *i64 368 let u_ptr: *i64 = (sys_mmap(8)) as *i64 369 let u_n: *i64 = (sys_mmap(8)) as *i64 370 let b_ptr: *i64 = (sys_mmap(8)) as *i64 371 let b_n: *i64 = (sys_mmap(8)) as *i64 372 t_ptr[0] = 0; t_n[0] = 0; u_ptr[0] = 0; u_n[0] = 0; b_ptr[0] = 0; b_n[0] = 0 373 let rc_l: i64 = nx_wiki_doc_store_lookup(store, rowid, 374 t_ptr, t_n, u_ptr, u_n, b_ptr, b_n) 375 if rc_l != NX_WIB_OK { return 0 - NX_WDH_NOT_FOUND } 376 377 // Allocate render scratch + output buffer. 378 let render_buf: *u8 = sys_mmap(NX_WDH_RENDER_CAP) 379 let scratch: *u8 = sys_mmap(NX_WDH_SCRATCH_CAP) 380 let doc_names: *u8 = sys_mmap(NX_WDH_DOC_NAMES_CAP) 381 382 // Bug #2 fix: populate the [[wikilink]] known-page set FROM THE STORE (derive 383 // the slugs the handler already holds), then pass the REAL count into ctx_init 384 // -- so [[name]] resolves LIVE when name is a real page. This sidesteps the 385 // 16-arg IR cap that previously forced doc_names_count=0 (empty set -> every 386 // link broken). 387 let dn_count: i64 = nx_wdh_populate_doc_names(store, doc_names, NX_WDH_DOC_NAMES_CAP) 388 389 // Build ctx. 390 let ctx: *NxWikiDocCtx = (sys_mmap(512)) as *NxWikiDocCtx 391 let rc_ci: i64 = nx_wiki_doc_ctx_init(ctx, 392 render_buf, NX_WDH_RENDER_CAP, 393 (b_ptr[0]) as *u8, b_n[0], 394 doc_names, NX_WDH_DOC_NAMES_CAP, dn_count, 395 scratch, NX_WDH_SCRATCH_CAP) 396 if rc_ci != NX_WIKI_DOC_OK { return 0 - NX_WDH_RENDER_FAILED } 397 398 // Build per-doc metadata for V1 page format conformance. 399 let canonical_buf: *u8 = sys_mmap(NX_WDH_RESP_HDR_CAP) 400 let canonical_n: i64 = nx_wdh_build_canonical(canonical_buf, NX_WDH_RESP_HDR_CAP, 401 url_path, url_path_n) 402 if canonical_n < 0 { return 0 - NX_WDH_RENDER_FAILED } 403 404 let summary_buf: *u8 = sys_mmap(512) 405 let summary_n: i64 = nx_wdh_build_summary((b_ptr[0]) as *u8, b_n[0], 406 summary_buf, 500) 407 if summary_n < 0 { return 0 - NX_WDH_RENDER_FAILED } 408 409 let rc_sm: i64 = nx_wiki_doc_ctx_set_meta(ctx, 410 canonical_buf, canonical_n, 411 summary_buf, summary_n, 412 NX_WDH_DEFAULT_TAGS, NX_WDH_DEFAULT_TAGS_N, 413 NX_WDH_DEFAULT_LICENSE, NX_WDH_DEFAULT_LICENSE_N, 414 NX_WDH_DEFAULT_AUTHOR, NX_WDH_DEFAULT_AUTHOR_N, 415 NX_WDH_DEFAULT_LAST_MOD, NX_WDH_DEFAULT_LAST_MOD_N) 416 if rc_sm != NX_WIKI_DOC_OK { return 0 - NX_WDH_RENDER_FAILED } 417 418 // V1 header (composes hub/nx_nishi_page_emit). 419 let rc_h: i64 = nx_wiki_doc_write_header_v1(ctx, (t_ptr[0]) as *u8, t_n[0]) 420 if rc_h != NX_WIKI_DOC_OK { return 0 - NX_WDH_RENDER_FAILED } 421 422 // V2.0 P-4: pipeline-stage banner (composes hub/nx_pipeline_banner). 423 // Silent no-op if artifact_store=0 OR URL has no path mapping; per 424 // Cardinal 14 graceful degradation -- doc still renders. 425 let art_path_ptr: *i64 = (sys_mmap(8)) as *i64 426 let art_path_n: *i64 = (sys_mmap(8)) as *i64 427 art_path_ptr[0] = 0; art_path_n[0] = 0 428 if nx_wdh_url_to_artifact_path(url_path, url_path_n, art_path_ptr, art_path_n) == 1 { 429 if (artifact_store as i64) != 0 { 430 let banner_off: *i64 = (sys_mmap(8)) as *i64 431 banner_off[0] = ctx.out_off 432 let rc_b: i64 = nx_pipeline_banner_render(ctx.out_buf, ctx.out_cap, 433 ctx.out_off, 434 artifact_store, 435 art_path_ptr[0] as *u8, 436 art_path_n[0], 437 banner_off) 438 if rc_b == NX_PBAN_OK { ctx.out_off = banner_off[0] } 439 // On banner render error: silent skip (per Cardinal 14) 440 } 441 } 442 443 // NO pre-markdown cite pass (the "escaped cite card" fix, same shape as the 444 // escaped-wikilink fix): expanding [[cite:<cid>]] into <details ...> card HTML 445 // BEFORE markdown made markdown_inline html-escape it -> the served bytes were 446 // &lt;details... (dead escaped text), NOT a real card. We instead leave the 447 // literal [[cite:<cid>]] token in the body (markdown leaves '['/'']' alone and 448 // "[[cite:...]]" is not markdown syntax) and expand it in the POST-markdown 449 // cite pass below, emitting the card RAW. The wikilink post-pass copies any 450 // [[cite:...]] through verbatim, so the two passes coexist. 451 452 // Render markdown body (composes wikilink preprocess + markdown_block). 453 let used_out: *i64 = (sys_mmap(8)) as *i64 454 used_out[0] = 0 455 let rc_pre: i64 = nx_wiki_doc_preprocess_wikilinks(ctx, used_out) 456 if rc_pre != NX_WIKI_DOC_OK { return 0 - NX_WDH_RENDER_FAILED } 457 458 // ----- wiki R4: auto-TOC + heading anchor ids wired into the served 459 // <article>. The body markdown renders into a TEMP buffer (md_tmp) rather 460 // than straight into the response, so we can (a) prepend the table of 461 // contents built from the SAME markdown source the renderer reads -- so the 462 // "#section-a" TOC links and the id="section-a" stamped on the <h2> share 463 // one definition of a heading -- and (b) inject id="<slug>" onto every <hN> 464 // open tag. ctx.src_buf here is the cite-expanded body; ATX headings pass 465 // through the cite + wikilink passes untouched, so TOC and rendered <hN> 466 // agree. Graceful (Cardinal 14): if a step overflows we fall back to the 467 // plain markdown HTML so the page still serves. 468 let md_tmp: *u8 = sys_mmap(NX_WDH_MD_CAP) 469 let md_rc: i64 = markdown_block(ctx.scratch_buf, used_out[0], md_tmp, NX_WDH_MD_CAP) 470 if md_rc < 0 { return 0 - NX_WDH_RENDER_FAILED } 471 472 // TOC built from the markdown source (ATX heading lines). 473 let toc_buf: *u8 = sys_mmap(NX_WDH_TOC_CAP) 474 let toc_used: *i64 = (sys_mmap(8)) as *i64 475 toc_used[0] = 0 476 let rc_toc: i64 = nx_wiki_toc_build(ctx.src_buf, ctx.src_len, 477 toc_buf, NX_WDH_TOC_CAP, toc_used) 478 479 // id-injected copy of the rendered body HTML. 480 let inj_buf: *u8 = sys_mmap(NX_WDH_INJECT_CAP) 481 let inj_used: *i64 = (sys_mmap(8)) as *i64 482 inj_used[0] = 0 483 let rc_inj: i64 = nx_wiki_toc_inject_ids(md_tmp, md_rc, 484 inj_buf, NX_WDH_INJECT_CAP, inj_used) 485 486 // Write the TOC nav (when the doc has headings + build succeeded), then the 487 // body HTML (id-injected when injection succeeded, else the raw markdown). 488 if rc_toc == NX_WTOC_OK { 489 if toc_used[0] > 0 { 490 let rc_wt: i64 = nx_wdh_emit_run(ctx, toc_buf, toc_used[0]) 491 if rc_wt != NX_WDH_OK { return 0 - NX_WDH_RENDER_FAILED } 492 } 493 } 494 var body_src: *u8 = md_tmp 495 var body_n2: i64 = md_rc 496 if rc_inj == NX_WTOC_OK { body_src = inj_buf; body_n2 = inj_used[0] } 497 498 // Bug #1 fix: the POST-markdown [[wikilink]] pass. markdown_block left every 499 // literal [[name]] in the rendered HTML untouched ('['/'']' are not markdown 500 // syntax and html_escape leaves them alone), so we resolve them HERE, AFTER 501 // markdown, emitting each as a LIVE <a href="/wiki/<slug>">name</a> (resolved 502 // against the store-derived doc-names set) or a <span class="wikilink-broken"> 503 // (missing) -- written RAW so the anchor is NOT re-escaped into &lt;a. The 504 // wikilink pass copies any [[cite:...]] token through VERBATIM (it is not a 505 // cross-reference), so the cite post-pass below still sees them. Graceful 506 // (Cardinal 14): on overflow we fall back to the pre-pass body so the page 507 // still serves. 508 let wl_buf: *u8 = sys_mmap(NX_WDH_INJECT_CAP) 509 let wl_used: *i64 = (sys_mmap(8)) as *i64 510 wl_used[0] = 0 511 let rc_wl: i64 = nx_wiki_doc_postpass_wikilinks(ctx, body_src, body_n2, 512 wl_buf, NX_WDH_INJECT_CAP, wl_used) 513 if rc_wl == NX_WIKI_DOC_OK { body_src = wl_buf; body_n2 = wl_used[0] } 514 515 // Cite-card fix: the POST-markdown [[cite:<cid>]] pass. The literal 516 // [[cite:<cid>]] token was left in the body (NOT expanded pre-markdown), so it 517 // survived markdown unescaped; we expand it HERE, AFTER markdown, into the 518 // license-aware supporting-source card emitted RAW (never re-escaped, so the 519 // served bytes are a LIVE <details class="nx-cite"> not &lt;details). cite_one 520 // does the REAL archive lookup + license gate + THE LIAR-KILL: a hostable 521 // (PD/CC) source's bytes are inlined (escaped inside the card by cite_one); a 522 // proprietary/unknown source gets a link + license-note only, its bytes NEVER 523 // inlined. Graceful (Cardinal 14): on overflow we fall back to the prior body. 524 let ct_buf: *u8 = sys_mmap(NX_WDH_INJECT_CAP) 525 let ct_used: *i64 = (sys_mmap(8)) as *i64 526 ct_used[0] = 0 527 let rc_ct: i64 = nx_wiki_doc_postpass_cite(ctx, cite_prefix, body_src, body_n2, 528 ct_buf, NX_WDH_INJECT_CAP, ct_used) 529 if rc_ct == NX_WIKI_DOC_OK { body_src = ct_buf; body_n2 = ct_used[0] } 530 531 let rc_wb: i64 = nx_wdh_emit_run(ctx, body_src, body_n2) 532 if rc_wb != NX_WDH_OK { return 0 - NX_WDH_RENDER_FAILED } 533 534 // ----- wiki R4: "what links here" backlinks panel appended after the 535 // article body. Computed from ground truth (the actual page bodies in the 536 // doc store) via nx_wiki_backlinks_panel; the target slug is this page's URL 537 // (the panel strips the optional "/wiki/" prefix on both sides when 538 // matching). Graceful: a panel-render error simply omits the aside. 539 let bl_buf: *u8 = sys_mmap(NX_WDH_BACKLINKS_CAP) 540 let bl_used: *i64 = (sys_mmap(8)) as *i64 541 bl_used[0] = 0 542 let rc_bl: i64 = nx_wiki_backlinks_panel(store, url_path, url_path_n, 543 bl_buf, NX_WDH_BACKLINKS_CAP, bl_used) 544 if rc_bl == NX_WBL_OK { 545 if bl_used[0] > 0 { 546 let rc_wbl: i64 = nx_wdh_emit_run(ctx, bl_buf, bl_used[0]) 547 if rc_wbl != NX_WDH_OK { return 0 - NX_WDH_RENDER_FAILED } 548 } 549 } 550 551 // Footer (closes <article>/<main> opened by write_header_v1). 552 let rc_f: i64 = nx_wiki_doc_write_footer(ctx) 553 if rc_f != NX_WIKI_DOC_OK { return 0 - NX_WDH_RENDER_FAILED } 554 555 let body_len: i64 = ctx.out_off 556 557 // HTTP wrap: HTTP/1.1 200 OK + Content-Type + Content-Length + \r\n\r\n + body 558 let hdr_prefix: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " as *u8 559 let hdr_prefix_n: i64 = 71 560 let crlf2: *u8 = "\r\n\r\n" as *u8 561 let crlf2_n: i64 = 4 562 563 // Decimal body length. 564 let len_buf: *u8 = sys_mmap(32) 565 var bl: i64 = body_len 566 var ld: i64 = 0 567 if bl == 0 { len_buf[0] = 0x30 as u8; ld = 1 } 568 if bl > 0 { 569 let tmp: *u8 = sys_mmap(32) 570 var k: i64 = 0 571 while bl > 0 { tmp[k] = (0x30 + (bl % 10)) as u8; bl = bl / 10; k = k + 1 } 572 var j: i64 = k - 1 573 while j >= 0 { len_buf[ld] = tmp[j]; ld = ld + 1; j = j - 1 } 574 } 575 576 let need: i64 = hdr_prefix_n + ld + crlf2_n + body_len 577 if need > resp_cap { return 0 - NX_WDH_RESP_OVERFLOW } 578 var off: i64 = 0 579 var ii: i64 = 0 580 while ii < hdr_prefix_n { resp_buf[off + ii] = hdr_prefix[ii]; ii = ii + 1 } 581 off = off + hdr_prefix_n 582 ii = 0 583 while ii < ld { resp_buf[off + ii] = len_buf[ii]; ii = ii + 1 } 584 off = off + ld 585 ii = 0 586 while ii < crlf2_n { resp_buf[off + ii] = crlf2[ii]; ii = ii + 1 } 587 off = off + crlf2_n 588 ii = 0 589 while ii < body_len { resp_buf[off + ii] = ctx.out_buf[ii]; ii = ii + 1 } 590 off = off + body_len 591 592 out_resp_n[0] = off 593 return NX_WDH_OK 594} 595 596// PRODUCTION ENTRY (stable API contract -- the exact function the multi-vhost 597// daemon + nx_wiki_routes dispatch to): renders against the PRODUCTION cite 598// archive prefix WAR_PREFIX. Thin wrapper over nx_wiki_doc_handle_cp so the 599// served code path is IDENTICAL; the only difference is the cite-archive prefix. 600// (Additive per Cardinal 19: callers of the 7-arg contract are untouched; the 601// _cp variant adds the injectable cite prefix a gate needs to drive the SERVED 602// path against a fresh, isolated, idempotent per-run archive -- never the 603// production store.) Defined AFTER _cp so the call is a backward reference. 604func nx_wiki_doc_handle(store: *NxWikiDocStore, 605 artifact_store: *NxArtifactStore, 606 url_path: *u8, url_path_n: i64, 607 resp_buf: *u8, resp_cap: i64, 608 out_resp_n: *i64) -> i64 { 609 return nx_wiki_doc_handle_cp(store, artifact_store, url_path, url_path_n, 610 resp_buf, resp_cap, out_resp_n, WAR_PREFIX) 611}