code wiki / _hdl_build / nx_js_headless_render_gate.nx

nx_js_headless_render_gate.nx source

↩ module page · 75 lines · 5092 B

1// nx_js_headless_render_gate.nx -- proves rung 5 (headless hydration): the ASYNC rungs (event loop / 2// promise / fetch) INTEGRATE with the DOM-render pipeline (js_render_page). A page whose inline script 3// mutates the DOM from a setTimeout / Promise.then / fetch(data:).then callback -> the mutation appears in 4// the SERIALIZED output (the drain runs the async work before serialize). This is the SPA-hydration path. 5// license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_js_eval.nx" 8 9func r_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 10func r_clen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 11func r_contains(buf: *u8, blen: i64, needle: *u8) -> i64 { 12 let nl: i64 = r_clen(needle) 13 if nl == 0 { return 1 } 14 var i: i64 = 0 15 while (i + nl) <= blen { 16 var j: i64 = 0; var ok: i64 = 1 17 while j < nl { if (buf[i + j] & 0xff) != (needle[j] & 0xff) { ok = 0; j = nl } else { j = j + 1 } } 18 if ok == 1 { return 1 } 19 i = i + 1 20 } 21 return 0 22} 23func chk(cond: i64, name: *u8, pp: *i64, tp: *i64) -> i64 { 24 tp[0] = tp[0] + 1; r_p(" "); r_p(name) 25 if cond == 1 { pp[0] = pp[0] + 1; r_p(": ok\n\x00" as *u8) } else { r_p(": FAIL\n\x00" as *u8) } 26 return 0 27} 28 29func main() -> i64 { 30 r_p("=== nx_js_headless_render_gate: async DOM hydration through js_render_page ===\n\x00" as *u8) 31 let pp: *i64 = sys_mmap(8) as *i64; pp[0] = 0 32 let tp: *i64 = sys_mmap(8) as *i64; tp[0] = 0 33 let out: *u8 = sys_mmap(262144) 34 35 // H1: setTimeout hydration -- DOM write from a macrotask lands in the serialized output. 36 let p1: *u8 = "<html><body><div id=\"app\">LOADING</div><script>setTimeout(function(){document.getElementById('app').innerHTML='<p>HYDRATED</p>';},0);</script></body></html>\x00" as *u8 37 let n1: i64 = js_render_page(p1, r_clen(p1), out, 262144) 38 chk(r_contains(out, n1, "HYDRATED\x00" as *u8), "H1 setTimeout hydrates DOM\x00" as *u8, pp, tp) 39 chk(r_contains(out, n1, "LOADING\x00" as *u8) == 0, "H1b old content replaced\x00" as *u8, pp, tp) 40 41 // H2: Promise.then hydration -- DOM write from a microtask. 42 let p2: *u8 = "<html><body><span id=\"s\">x</span><script>Promise.resolve().then(function(){document.getElementById('s').textContent='PROMISED';});</script></body></html>\x00" as *u8 43 let n2: i64 = js_render_page(p2, r_clen(p2), out, 262144) 44 chk(r_contains(out, n2, "PROMISED\x00" as *u8), "H2 Promise.then hydrates\x00" as *u8, pp, tp) 45 46 // H3: THE SPA PATTERN -- fetch(data:).then(r=>r.text()).then(t=> DOM=t). 47 let p3: *u8 = "<html><body><div id=\"app\">L</div><script>fetch('data:,DATAHYDRATED').then(function(r){return r.text();}).then(function(t){document.getElementById('app').textContent=t;});</script></body></html>\x00" as *u8 48 let n3: i64 = js_render_page(p3, r_clen(p3), out, 262144) 49 chk(r_contains(out, n3, "DATAHYDRATED\x00" as *u8), "H3 fetch->text->DOM (SPA)\x00" as *u8, pp, tp) 50 51 // H4: NESTED async -- fetch -> then -> setTimeout -> DOM (proves micro+macro interleave through render). 52 let p4: *u8 = "<html><body><div id=\"app\">L</div><script>fetch('data:,NEST').then(function(r){return r.text();}).then(function(t){setTimeout(function(){document.getElementById('app').textContent=t;},0);});</script></body></html>\x00" as *u8 53 let n4: i64 = js_render_page(p4, r_clen(p4), out, 262144) 54 chk(r_contains(out, n4, "NEST\x00" as *u8), "H4 fetch->then->setTimeout->DOM\x00" as *u8, pp, tp) 55 56 // H5: control -- a script-less page round-trips unchanged. 57 let p5: *u8 = "<html><body><h1>Plain</h1></body></html>\x00" as *u8 58 let n5: i64 = js_render_page(p5, r_clen(p5), out, 262144) 59 chk(r_contains(out, n5, "<h1>Plain</h1>\x00" as *u8), "H5 no-script round-trip\x00" as *u8, pp, tp) 60 61 // H6: LIAR-KILL -- a REJECTED fetch's .then chain is skipped, so the DOM is NOT mutated (LOADING stays). 62 // Proves the drain doesn't spuriously mutate; hydration happens ONLY when the async actually resolves. 63 let p6: *u8 = "<html><body><div id=\"app\">LOADING</div><script>fetch('http://api/x').then(function(r){return r.text();}).then(function(t){document.getElementById('app').textContent=t;});</script></body></html>\x00" as *u8 64 let n6: i64 = js_render_page(p6, r_clen(p6), out, 262144) 65 chk(r_contains(out, n6, "LOADING\x00" as *u8), "H6 liar-kill (rejected fetch leaves DOM)\x00" as *u8, pp, tp) 66 67 r_p("=== \x00" as *u8) 68 if pp[0] == tp[0] { r_p("GREEN\x00" as *u8) } else { r_p("RED\x00" as *u8) } 69 r_p(" (\x00" as *u8) 70 var m: i64 = pp[0]; let tb: *u8 = sys_mmap(8); var k: i64 = 0; if m==0 { tb[0]=48; k=1 } else { while m>0 { tb[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } } var j: i64=k-1; while j>=0 { sys_write(1,((tb as i64)+j) as *u8,1); j=j-1 } 71 r_p("/\x00" as *u8); m = tp[0]; k=0; if m==0 { tb[0]=48; k=1 } else { while m>0 { tb[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } } j=k-1; while j>=0 { sys_write(1,((tb as i64)+j) as *u8,1); j=j-1 } 72 r_p(") ===\n\x00" as *u8) 73 if pp[0] == tp[0] { return 0 } 74 return 1 75}