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 // <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 <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 <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}