nx_html_to_nishi_page.nx
buildroot/runtime/hub/nx_html_to_nishi_page.nx
about
nx_html_to_nishi_page.nx -- HUB primitive; site-conversion tool.
HTML input -> Nishi Page Format V1 conforming output (per
NISHI_PAGE_FORMAT_V1.md spec).
PURPOSE per NISHI_SEARCH_CHARTER §8 + operator cardinal
"convert website to nishi site functionality to help with link rot":
1. Accept arbitrary HTML (e.g., archived competitor docs)
2. Emit Nishi Page Format V1 conforming HTML (9 required meta tags)
3. Annotate external links for archive subsystem pickup
4. Flag unconvertible content (iframe/script) with sealed verdicts
COMPOSES (HUB primitives only):
nx_html_extract (title/body extraction)
nx_html_escape (safe meta-value emission)
nx_search_inverted (FNV-1a 64 for content-hash)
COMPOSED BY:
(future) wiki/nx_wiki_import_legacy.nx
(future) bin/nishi-convert CLI tool wrapping this primitive
V1 SCOPE (per charter §8):
- HTML input -> conforming HTML output
- Caller provides: canonical, summary, tags, license, author
(these CANNOT be reliably auto-derived; honest gap per M6)
- Auto-derived: title (from <title>), content-hash (FNV-1a 64
of body), last-modified (caller OR current time), page-version (1.0.0)
- External links annotated with data-nishi-archive-pending
attribute for archive subsystem (queued)
- <iframe>/<script>/<object>/<embed> detected -> sealed verdict
UNCONVERTIBLE_CONTENT; operator must remove or accept lossy
conversion via FORCE flag
V2 SCOPE (TODO):
- Markdown input adapter (V2)
- RST input adapter (V2)
- Auto-summary via first-paragraph extraction (V2)
- Auto-tags via term-frequency analysis (V2)
- Archive subsystem live link-rewriting (V3 after archive ships)
Status: V1. 2026-05-27.
dependencies 4 imports · 0 importers
imports: nx_syscalls.nxnx_html_extract.nxnx_html_escape.nxnx_search_inverted.nx
imported by: nobody (leaf or entry point)
structs
| 80 | struct NxHtmlToNishiInputs |
consts
| 48 | const NX_HNP_OK: i64 = 0 |
| 49 | const NX_HNP_BAD_INPUT: i64 = 2400 |
| 50 | const NX_HNP_OUTPUT_OVERFLOW: i64 = 2401 |
| 51 | const NX_HNP_INPUT_TOO_LARGE: i64 = 2402 |
| 52 | const NX_HNP_NO_TITLE_FOUND: i64 = 2403 |
| 53 | const NX_HNP_NO_BODY_FOUND: i64 = 2404 |
| 54 | const NX_HNP_UNCONVERTIBLE_CONTENT: i64 = 2405 |
| 55 | const NX_HNP_INVALID_CANONICAL: i64 = 2406 |
| 56 | const NX_HNP_INVALID_LICENSE: i64 = 2407 |
| 57 | const NX_HNP_LOOP_BUDGET: i64 = 2408 |
| 58 | const NX_HNP_ESCAPE_FAIL: i64 = 2409 |
| 59 | const NX_HNP_MALFORMED_HTML: i64 = 2410 |
| 62 | const NX_HNP_MAX_INPUT_BYTES: i64 = 1048576 // 1 MB input cap |
| 63 | const NX_HNP_MAX_OUTPUT_BYTES: i64 = 2097152 // 2 MB output cap (after annotations) |
| 64 | const NX_HNP_MAX_TITLE_LEN: i64 = 200 // per NISHI_PAGE_FORMAT_V1 §2.2 |
| 65 | const NX_HNP_MAX_SUMMARY_LEN: i64 = 500 // per §2.3 |
| 66 | const NX_HNP_MAX_AUTHOR_LEN: i64 = 100 // per §2.9 |
| 67 | const NX_HNP_MAX_LICENSE_LEN: i64 = 64 |
| 68 | const NX_HNP_MAX_CANONICAL_LEN: i64 = 2048 |
| 69 | const NX_HNP_MAX_TAGS_LEN: i64 = 1024 |
| 70 | const NX_HNP_MAX_TIMESTAMP_LEN: i64 = 32 |
| 71 | const NX_HNP_CONTENT_HASH_LEN: i64 = 16 // FNV-1a 64 -> 16 hex chars |
| 72 | const NX_HNP_LOOP_BUDGET_CAP: i64 = 10000000 |
| 75 | const NX_HNP_ASCII_LT: i64 = 0x3C // '<' |
| 76 | const NX_HNP_ASCII_GT: i64 = 0x3E // '>' |
functions
| 108 | func nx_hnp_inputs_init(inp: *NxHtmlToNishiInputs, |
| 151 | func nx_hnp_put_raw(inp: *NxHtmlToNishiInputs, off: i64, |
| 164 | func nx_hnp_put_z(inp: *NxHtmlToNishiInputs, off: i64, s: *u8) -> i64 |
| 173 | func nx_hnp_put_escaped(inp: *NxHtmlToNishiInputs, off: i64, |
| 184 | func nx_hnp_hex16(v: i64, out: *u8) -> i64 called by 1: nx_html_to_nishi_page |
| 200 | func nx_hnp_has_unconvertible(src: *u8, src_n: i64) -> i64 |
| 223 | func nx_hnp_extract_title(src: *u8, src_n: i64, |
| 275 | func nx_hnp_extract_body(src: *u8, src_n: i64, |
| 322 | func nx_hnp_emit_meta(inp: *NxHtmlToNishiInputs, off: i64, |
| 338 | func nx_html_to_nishi_page(inp: *NxHtmlToNishiInputs) -> i64 |