code wiki / wiki / nx_wiki_search_wiring.nx

nx_wiki_search_wiring.nx source

↩ module page · 471 lines · 20931 B

1// nx_wiki_search_wiring.nx -- WIKI-SPECIFIC THIN WIRING for search. 2// 3// All search logic lives in HUB primitives; this file is glue: 4// 1. Extract query string from URL (after '?') 5// 2. Run the hub flow with wiki-specific theme 6// 3. Resolve each result rowid via wiki doc store 7// 4. HTTP-wrap with /wiki/search-style 200 response 8// 9// Per operator cardinal 2026-05-27: "make sure you make the onsite and 10// offsite search reusable and not just tied to the wiki" -- this file 11// is structurally enforced THIN per NISHI_SMALL_SHARP_COMPOSABLE_STANDARD 12// §4.1 M3 + the hub<->site separation in nx_search_handler_flow. 13// 14// COMPOSES (only hub + wiki primitives): 15// hub/nx_search_handler_flow (5-step pipeline) 16// hub/nx_search_render_html (theme + inputs structs) 17// hub/nx_search_query_parser (NxSearchQuery accessors) 18// nx_search_onsite_engine (NxSearchResults accessors) 19// nx_search_inverted (NxInvIndex) 20// wiki/nx_wiki_search_render (theme constants) 21// 22// COMPOSED BY: 23// wiki/nx_wiki_routes (dispatches /wiki/search here) 24 25import "nx_syscalls.nx" 26import "hub/nx_search_handler_flow.nx" 27import "hub/nx_search_render_html.nx" 28import "hub/nx_search_query_parser.nx" 29import "nx_search_onsite_engine.nx" 30import "nx_search_inverted.nx" 31import "wiki/nx_wiki_search_render.nx" 32import "wiki/nx_wiki_index_builder.nx" 33import "wiki/nx_wiki_bm25_search.nx" 34 35// ===== Sealed verdict surface (codes 2200-2209) ================================================= 36const NX_WSW_OK: i64 = 0 37const NX_WSW_BAD_INPUT: i64 = 2200 38const NX_WSW_QUERY_TOO_LONG: i64 = 2201 39const NX_WSW_RESOLVER_OVERFLOW: i64 = 2202 40const NX_WSW_FLOW_FAIL: i64 = 2203 41 42// ===== Named sizing constants (M7) ================================================= 43const NX_WSW_MAX_URL_LEN: i64 = 4096 44const NX_WSW_RESOLVER_BUF_CAP: i64 = 16384 // 16 KB scratch for resolved doc data 45const NX_WSW_PLACEHOLDER_TITLE_CAP: i64 = 64 46const NX_WSW_PLACEHOLDER_URL_CAP: i64 = 128 47const NX_WSW_PLACEHOLDER_BODY_CAP: i64 = 512 48const NX_WSW_RANK_CAP: i64 = 256 // BM25 ranked-result scratch cap 49const NX_WSW_RENDER_CAP: i64 = 200 // hub render hard cap (NX_SRH_MAX_RESULTS_RENDER) 50 51// ===== V1 resolver (placeholder; HONEST gap-marker) ================================================= 52// 53// V1 SCOPE per NISHI_CODE_HYGIENE M6 (anti-pretend-stub): 54// The wiki doc store + on-disk doc loader is queued via 55// nx_wiki_index_builder (charter §6 module 6). Until that ships: 56// 57// This resolver fills the inputs with EXPLICITLY-LABELED placeholder 58// strings -- title="(Doc loader pending)", url="/wiki/", body="..." 59// The rendered response is well-formed HTML showing the SEARCH FLOW 60// is wired end-to-end + the gap is visible to the operator (not 61// hidden behind a 500 or a 501). 62// 63// When nx_wiki_index_builder ships, this function is REPLACED with 64// a real walker that opens the doc-store kv (per nx_kv_store) and 65// reads title/url/body from disk. 66 67func nx_wsw_resolve_docs_placeholder(flow: *NxSearchFlow, 68 scratch_buf: *u8, scratch_cap: i64) -> i64 { 69 if flow.valid != 1 { return 0 - NX_WSW_BAD_INPUT } 70 if (scratch_buf as i64) == 0 { return 0 - NX_WSW_BAD_INPUT } 71 if scratch_cap < NX_WSW_RESOLVER_BUF_CAP { return 0 - NX_WSW_RESOLVER_OVERFLOW } 72 73 let r: *NxSearchResults = nx_search_flow_results(flow) 74 if (r as i64) == 0 { return 0 - NX_WSW_BAD_INPUT } 75 76 let title_lit: *u8 = "(Doc loader pending)" as *u8 77 let title_lit_n: i64 = 20 78 let url_prefix: *u8 = "/wiki/doc-" as *u8 79 let url_prefix_n: i64 = 10 80 let body_lit: *u8 = "Search arc wired end-to-end via hub/nx_search_handler_flow. Doc loader (nx_wiki_index_builder) queued per NISHI_SEARCH_CHARTER section 6 module 6 -- when that ships, this resolver is replaced with real wiki doc-store reads. The 501 has been closed; the 200 honestly shows the gap." as *u8 81 let body_lit_n: i64 = 297 82 83 var i: i64 = 0 84 var off: i64 = 0 85 while i < r.count { 86 if i >= flow.docs_cap { return 0 - NX_WSW_RESOLVER_OVERFLOW } 87 // Title (shared literal) 88 flow.title_ptrs[i] = title_lit as i64 89 flow.title_lens[i] = title_lit_n 90 // URL: "/wiki/doc-<rowid>" 91 if off + url_prefix_n + 16 > scratch_cap { return 0 - NX_WSW_RESOLVER_OVERFLOW } 92 let url_start: i64 = off 93 var k: i64 = 0 94 while k < url_prefix_n { scratch_buf[off] = url_prefix[k]; off = off + 1; k = k + 1 } 95 let rowid: i64 = nx_search_results_rowid_at(r, i) 96 let rowid_n: i64 = nx_shf_int_to_dec(rowid, (scratch_buf as i64 + off) as *u8, 16) 97 off = off + rowid_n 98 flow.url_ptrs[i] = (scratch_buf as i64 + url_start) 99 flow.url_lens[i] = off - url_start 100 // Body (shared literal) 101 flow.body_ptrs[i] = body_lit as i64 102 flow.body_lens[i] = body_lit_n 103 i = i + 1 104 } 105 return NX_WSW_OK 106} 107 108// ===== V1 REAL resolver -- walks NxWikiDocStore (closes placeholder gap) ================================================= 109// 110// Replaces nx_wsw_resolve_docs_placeholder when caller passes a 111// populated NxWikiDocStore (typically from nx_wiki_index_builder). 112// 113// For each result rowid, looks up (title, url, body) from the store 114// and stamps into flow.inputs parallel arrays. Per Cardinal #25 115// (Build Intelligence, Never Strip Features) -- this is the real fn; 116// placeholder remains for tests / pre-builder scenarios. 117 118func nx_wsw_resolve_docs_from_store(flow: *NxSearchFlow, 119 store: *NxWikiDocStore) -> i64 { 120 if flow.valid != 1 { return 0 - NX_WSW_BAD_INPUT } 121 if store.valid != 1 { return 0 - NX_WSW_BAD_INPUT } 122 123 let r: *NxSearchResults = nx_search_flow_results(flow) 124 if (r as i64) == 0 { return 0 - NX_WSW_BAD_INPUT } 125 126 var i: i64 = 0 127 while i < r.count { 128 if i >= flow.docs_cap { return 0 - NX_WSW_RESOLVER_OVERFLOW } 129 130 let rowid: i64 = nx_search_results_rowid_at(r, i) 131 if rowid < 0 { return 0 - NX_WSW_BAD_INPUT } 132 133 let t_ptr: *i64 = (sys_mmap(8)) as *i64 134 let t_n: *i64 = (sys_mmap(8)) as *i64 135 let u_ptr: *i64 = (sys_mmap(8)) as *i64 136 let u_n: *i64 = (sys_mmap(8)) as *i64 137 let b_ptr: *i64 = (sys_mmap(8)) as *i64 138 let b_n: *i64 = (sys_mmap(8)) as *i64 139 t_ptr[0] = 0; t_n[0] = 0; u_ptr[0] = 0; u_n[0] = 0; b_ptr[0] = 0; b_n[0] = 0 140 141 let rc_l: i64 = nx_wiki_doc_store_lookup(store, rowid, 142 t_ptr, t_n, u_ptr, u_n, b_ptr, b_n) 143 if rc_l != NX_WIB_OK { 144 // Defensive at boundary (Cardinal 12): doc store missing 145 // a rowid the index reported is INDEX/STORE drift; render 146 // as "(missing)" rather than abort. 147 flow.title_ptrs[i] = 0 148 flow.title_lens[i] = 0 149 flow.url_ptrs[i] = 0 150 flow.url_lens[i] = 0 151 flow.body_ptrs[i] = 0 152 flow.body_lens[i] = 0 153 } 154 if rc_l == NX_WIB_OK { 155 flow.title_ptrs[i] = t_ptr[0] 156 flow.title_lens[i] = t_n[0] 157 flow.url_ptrs[i] = u_ptr[0] 158 flow.url_lens[i] = u_n[0] 159 flow.body_ptrs[i] = b_ptr[0] 160 flow.body_lens[i] = b_n[0] 161 } 162 i = i + 1 163 } 164 return NX_WSW_OK 165} 166 167// ===== Extract query string from URL (find '?', return offset + len) ================================================= 168 169func nx_wsw_extract_query_string(url: *u8, url_n: i64, 170 out_off: *i64, out_len: *i64) -> i64 { 171 if (out_off as i64) == 0 { return 0 - NX_WSW_BAD_INPUT } 172 if (out_len as i64) == 0 { return 0 - NX_WSW_BAD_INPUT } 173 if url_n < 0 { return 0 - NX_WSW_BAD_INPUT } 174 if url_n > NX_WSW_MAX_URL_LEN { return 0 - NX_WSW_QUERY_TOO_LONG } 175 out_off[0] = 0 176 out_len[0] = 0 177 var i: i64 = 0 178 while i < url_n { 179 if url[i] == (0x3F as u8) { // '?' 180 out_off[0] = i + 1 181 out_len[0] = url_n - i - 1 182 return NX_WSW_OK 183 } 184 i = i + 1 185 } 186 // No '?': empty query string, well-defined. 187 return NX_WSW_OK 188} 189 190// ===== Top-level handler V2: REAL doc-store resolver ================================================= 191// 192// Called from nx_wiki_route_dispatch when nx_wiki_index_builder has 193// been run at startup + a populated NxWikiDocStore is available. 194// Returns real search results backed by the live wiki doc store. 195 196func nx_wiki_search_handle_v2(flow: *NxSearchFlow, 197 idx: *NxInvIndex, 198 store: *NxWikiDocStore, 199 url: *u8, url_n: i64, 200 resp_buf: *u8, resp_cap: i64, 201 out_resp_n: *i64) -> i64 { 202 if flow.valid != 1 { return 0 - NX_WSW_BAD_INPUT } 203 if (resp_buf as i64) == 0 { return 0 - NX_WSW_BAD_INPUT } 204 205 // Extract query string. 206 let q_off: *i64 = (sys_mmap(8)) as *i64 207 let q_len: *i64 = (sys_mmap(8)) as *i64 208 q_off[0] = 0; q_len[0] = 0 209 let rc_xq: i64 = nx_wsw_extract_query_string(url, url_n, q_off, q_len) 210 if rc_xq != NX_WSW_OK { return rc_xq } 211 let q_ptr: *u8 = (url as i64 + q_off[0]) as *u8 212 let q_n: i64 = q_len[0] 213 214 // Steps 1-2. 215 let rc_p: i64 = nx_search_flow_parse(flow, q_ptr, q_n) 216 if rc_p != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 217 if (idx as i64) != 0 { 218 let rc_e: i64 = nx_search_flow_execute_onsite(flow, idx) 219 if rc_e != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 220 } 221 if (idx as i64) == 0 { 222 flow.results.count = 0 223 flow.results.elapsed_us = 0 224 } 225 226 // Step 3: REAL resolver if store available; else placeholder. 227 if (store as i64) != 0 { 228 if store.valid == 1 { 229 let rc_res: i64 = nx_wsw_resolve_docs_from_store(flow, store) 230 if rc_res != NX_WSW_OK { return rc_res } 231 } 232 } 233 if (store as i64) == 0 { 234 let scratch: *u8 = (sys_mmap(NX_WSW_RESOLVER_BUF_CAP)) as *u8 235 let rc_res: i64 = nx_wsw_resolve_docs_placeholder(flow, scratch, NX_WSW_RESOLVER_BUF_CAP) 236 if rc_res != NX_WSW_OK { return rc_res } 237 } 238 let rc_fin: i64 = nx_search_flow_finalize_inputs(flow) 239 if rc_fin != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 240 241 // Step 4: render. 242 let theme: *NxSearchRenderTheme = (sys_mmap(128)) as *NxSearchRenderTheme 243 let rc_t: i64 = nx_wiki_search_theme_init(theme) 244 if rc_t != NX_SRH_OK { return 0 - NX_WSW_FLOW_FAIL } 245 let body_n: *i64 = (sys_mmap(8)) as *i64 246 body_n[0] = 0 247 let rc_r: i64 = nx_search_flow_render(flow, theme, body_n) 248 if rc_r != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 249 250 // Step 5: HTTP wrap. 251 let rc_w: i64 = nx_search_flow_wrap_http(flow, body_n[0], resp_buf, resp_cap, out_resp_n) 252 if rc_w != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 253 254 return NX_WSW_OK 255} 256 257// ===== Top-level handler V1: PLACEHOLDER resolver (kept for compat) ================================================= 258// 259// Called from nx_wiki_route_dispatch when no doc store has been built. 260// Now thin wrapper: dispatches to V2 with store=0. 261 262func nx_wiki_search_handle(flow: *NxSearchFlow, 263 idx: *NxInvIndex, 264 url: *u8, url_n: i64, 265 resp_buf: *u8, resp_cap: i64, 266 out_resp_n: *i64) -> i64 { 267 return nx_wiki_search_handle_v2(flow, idx, 0 as *NxWikiDocStore, 268 url, url_n, resp_buf, resp_cap, out_resp_n) 269} 270 271// ===== V3 handler: BM25-RANKED + ARCHIVE-UNIFIED search ================================================= 272// 273// THE wiki R3 route. Upgrades ranking from the onsite engine's presence/AND 274// (count-of-matched-terms) to real Okapi BM25 (term-frequency saturated, 275// length-normalized, IDF-weighted) AND unifies the no-link-rot archive: content 276// that lives only in the content-addressed archive (seg_store prefix 277// knowledge/store/wikiarchive-) is folded in via its term index and ranked in 278// the SAME corpus. Sorted most-relevant-first. 279// 280// Pipeline: 281// 1. parse query string -> NxSearchQuery (hub primitive, unchanged) 282// 2. lift the parsed terms into the (**u8, *i64) shape nx_wiki_bm25_rank wants 283// 3. nx_wiki_bm25_rank over [doc store] ++ [archive blobs that hit a term] 284// 4. stamp the ranked rowids/scores into flow.results; resolve each ranked row 285// into flow.inputs (DOC rows from the doc store; ARCHIVE rows from the 286// ranked blob body + a synthesized "(archived)" title/url) 287// 5. render (existing wiki theme) + HTTP-wrap (existing hub steps) 288// 289// archive_prefix may be 0 (doc-store-only ranking, still BM25). When the live 290// wiki passes WAR_PREFIX (knowledge/store/wikiarchive-), the archive is searched. 291 292func nx_wiki_search_handle_bm25(flow: *NxSearchFlow, 293 store: *NxWikiDocStore, 294 archive_prefix: *u8, 295 url: *u8, url_n: i64, 296 resp_buf: *u8, resp_cap: i64, 297 out_resp_n: *i64) -> i64 { 298 if flow.valid != 1 { return 0 - NX_WSW_BAD_INPUT } 299 if (resp_buf as i64) == 0 { return 0 - NX_WSW_BAD_INPUT } 300 if (store as i64) == 0 { return 0 - NX_WSW_BAD_INPUT } 301 if store.valid != 1 { return 0 - NX_WSW_BAD_INPUT } 302 303 // Step 1: extract + parse the query string. 304 let q_off: *i64 = (sys_mmap(8)) as *i64 305 let q_len: *i64 = (sys_mmap(8)) as *i64 306 q_off[0] = 0; q_len[0] = 0 307 let rc_xq: i64 = nx_wsw_extract_query_string(url, url_n, q_off, q_len) 308 if rc_xq != NX_WSW_OK { return rc_xq } 309 let q_ptr: *u8 = (url as i64 + q_off[0]) as *u8 310 let q_n: i64 = q_len[0] 311 312 let rc_p: i64 = nx_search_flow_parse(flow, q_ptr, q_n) 313 if rc_p != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 314 315 // Step 2: lift parsed terms into BM25 term arrays. 316 let nterms: i64 = nx_search_query_count(flow.q) 317 let qterms: *i64 = (sys_mmap(8 * 64)) as *i64 318 let qlens: *i64 = (sys_mmap(8 * 64)) as *i64 319 var nt: i64 = 0 320 var ti: i64 = 0 321 while ti < nterms { 322 if nt < 32 { 323 let tp: *i64 = (sys_mmap(8)) as *i64 324 let tl: *i64 = (sys_mmap(8)) as *i64 325 tp[0] = 0; tl[0] = 0 326 let rc_t: i64 = nx_search_query_term_at(flow.q, ti, tp, tl) 327 if rc_t == NX_SQP_OK { 328 if tl[0] > 0 { 329 qterms[nt] = tp[0] 330 qlens[nt] = tl[0] 331 nt = nt + 1 332 } 333 } 334 } 335 ti = ti + 1 336 } 337 338 // Step 3: BM25 rank over doc store + archive. 339 let ranked: *NxWikiRanked = (sys_mmap(128)) as *NxWikiRanked 340 let rc_ri: i64 = nx_wiki_ranked_init(ranked, NX_WSW_RANK_CAP) 341 if rc_ri != 0 { return 0 - NX_WSW_FLOW_FAIL } 342 let max_results: i64 = nx_search_query_max_results(flow.q) 343 let rc_rank: i64 = nx_wiki_bm25_rank(store, archive_prefix, 344 qterms as **u8, qlens, nt, 345 ranked, max_results) 346 if rc_rank != 0 { return 0 - NX_WSW_FLOW_FAIL } 347 348 // Step 4: stamp ranked results + resolve each row into render inputs. 349 var n: i64 = nx_wiki_ranked_count(ranked) 350 if n > flow.docs_cap { n = flow.docs_cap } 351 if n > NX_WSW_RENDER_CAP { n = NX_WSW_RENDER_CAP } // hub render cap (200) 352 flow.results.count = n 353 flow.results.n_terms = nt 354 flow.results.elapsed_us = 0 355 356 let arch_title: *u8 = "(archived page)" as *u8 357 let arch_title_n: i64 = 15 358 let arch_url: *u8 = "/wiki/archive/" as *u8 359 let arch_url_n: i64 = 14 360 361 var i: i64 = 0 362 while i < n { 363 flow.results.rowids[i] = nx_wiki_ranked_rowid_at(ranked, i) 364 flow.results.scores[i] = nx_wiki_ranked_score_at(ranked, i) 365 let kind: i64 = nx_wiki_ranked_kind_at(ranked, i) 366 if kind == NX_WBS_SRC_DOC { 367 // resolve title/url/body from the live doc store by rowid 368 let t_ptr: *i64 = (sys_mmap(8)) as *i64 369 let t_n: *i64 = (sys_mmap(8)) as *i64 370 let u_ptr: *i64 = (sys_mmap(8)) as *i64 371 let u_n: *i64 = (sys_mmap(8)) as *i64 372 let b_ptr: *i64 = (sys_mmap(8)) as *i64 373 let b_n: *i64 = (sys_mmap(8)) as *i64 374 t_ptr[0] = 0; t_n[0] = 0; u_ptr[0] = 0; u_n[0] = 0; b_ptr[0] = 0; b_n[0] = 0 375 let rc_l: i64 = nx_wiki_doc_store_lookup(store, flow.results.rowids[i], 376 t_ptr, t_n, u_ptr, u_n, b_ptr, b_n) 377 if rc_l == NX_WIB_OK { 378 flow.title_ptrs[i] = t_ptr[0]; flow.title_lens[i] = t_n[0] 379 flow.url_ptrs[i] = u_ptr[0]; flow.url_lens[i] = u_n[0] 380 flow.body_ptrs[i] = b_ptr[0]; flow.body_lens[i] = b_n[0] 381 } 382 if rc_l != NX_WIB_OK { 383 flow.title_ptrs[i] = 0; flow.title_lens[i] = 0 384 flow.url_ptrs[i] = 0; flow.url_lens[i] = 0 385 flow.body_ptrs[i] = 0; flow.body_lens[i] = 0 386 } 387 } 388 if kind == NX_WBS_SRC_ARCHIVE { 389 // archive row: body is the ranked blob body; title/url synthesized 390 // (the no-rot archive is content-addressed; archive router serves it) 391 flow.title_ptrs[i] = arch_title as i64; flow.title_lens[i] = arch_title_n 392 flow.url_ptrs[i] = arch_url as i64; flow.url_lens[i] = arch_url_n 393 flow.body_ptrs[i] = ranked.body_ptrs[i]; flow.body_lens[i] = ranked.body_lens[i] 394 } 395 i = i + 1 396 } 397 398 let rc_fin: i64 = nx_search_flow_finalize_inputs(flow) 399 if rc_fin != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 400 401 // Step 5: render (wiki theme) + HTTP wrap. 402 let theme: *NxSearchRenderTheme = (sys_mmap(128)) as *NxSearchRenderTheme 403 let rc_t: i64 = nx_wiki_search_theme_init(theme) 404 if rc_t != NX_SRH_OK { return 0 - NX_WSW_FLOW_FAIL } 405 let body_n: *i64 = (sys_mmap(8)) as *i64 406 body_n[0] = 0 407 let rc_r: i64 = nx_search_flow_render(flow, theme, body_n) 408 if rc_r != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 409 let rc_w: i64 = nx_search_flow_wrap_http(flow, body_n[0], resp_buf, resp_cap, out_resp_n) 410 if rc_w != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 411 412 return NX_WSW_OK 413} 414 415// ===== LEGACY V1 (internal; unused after V2 unification) ================================================= 416 417func nx_wiki_search_handle_legacy(flow: *NxSearchFlow, 418 idx: *NxInvIndex, 419 url: *u8, url_n: i64, 420 resp_buf: *u8, resp_cap: i64, 421 out_resp_n: *i64) -> i64 { 422 if flow.valid != 1 { return 0 - NX_WSW_BAD_INPUT } 423 if (resp_buf as i64) == 0 { return 0 - NX_WSW_BAD_INPUT } 424 if (out_resp_n as i64) == 0 { return 0 - NX_WSW_BAD_INPUT } 425 426 // Extract query string from URL. 427 let q_off: *i64 = (sys_mmap(8)) as *i64 428 let q_len: *i64 = (sys_mmap(8)) as *i64 429 q_off[0] = 0; q_len[0] = 0 430 let rc_xq: i64 = nx_wsw_extract_query_string(url, url_n, q_off, q_len) 431 if rc_xq != NX_WSW_OK { return rc_xq } 432 let q_ptr: *u8 = (url as i64 + q_off[0]) as *u8 433 let q_n: i64 = q_len[0] 434 435 // Step 1: parse. 436 let rc_p: i64 = nx_search_flow_parse(flow, q_ptr, q_n) 437 if rc_p != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 438 439 // Step 2: execute onsite engine (only if idx is provided; if no 440 // index built yet, results stay at 0 -- empty-state renders). 441 if (idx as i64) != 0 { 442 let rc_e: i64 = nx_search_flow_execute_onsite(flow, idx) 443 if rc_e != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 444 } 445 if (idx as i64) == 0 { 446 flow.results.count = 0 447 flow.results.elapsed_us = 0 448 } 449 450 // Step 3: resolve docs (V1 placeholder; replaced when index_builder ships). 451 let scratch: *u8 = (sys_mmap(NX_WSW_RESOLVER_BUF_CAP)) as *u8 452 let rc_res: i64 = nx_wsw_resolve_docs_placeholder(flow, scratch, NX_WSW_RESOLVER_BUF_CAP) 453 if rc_res != NX_WSW_OK { return rc_res } 454 let rc_fin: i64 = nx_search_flow_finalize_inputs(flow) 455 if rc_fin != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 456 457 // Step 4: render HTML with wiki theme. 458 let theme: *NxSearchRenderTheme = (sys_mmap(128)) as *NxSearchRenderTheme 459 let rc_t: i64 = nx_wiki_search_theme_init(theme) 460 if rc_t != NX_SRH_OK { return 0 - NX_WSW_FLOW_FAIL } 461 let body_n: *i64 = (sys_mmap(8)) as *i64 462 body_n[0] = 0 463 let rc_r: i64 = nx_search_flow_render(flow, theme, body_n) 464 if rc_r != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 465 466 // Step 5: HTTP wrap. 467 let rc_w: i64 = nx_search_flow_wrap_http(flow, body_n[0], resp_buf, resp_cap, out_resp_n) 468 if rc_w != NX_SHF_OK { return 0 - NX_WSW_FLOW_FAIL } 469 470 return NX_WSW_OK 471}