// parse-external-html.transform.ts
import { sanitize_external } from "../../../safety/sanitize-html.utils.js";
import { HsonNode } from "../../../core/types.js";
import { _throw_transform_err } from "../utils/sys-utils/throw-transform-err.utils.js";
import { normalize_html_source_attributes } from "../utils/html-preflights/ordinary-attribute-transit.js";
import {
decode_hson_metadata_transit,
encode_hson_metadata_transit,
} from "../utils/html-preflights/hson-metadata-transit.js";
import { parse_html } from "./parse-html.js";
const XML_SHAPED_ARRAY_WRAPPER = /<\/?_hson_(?:arr|ii)(?=[\s/>])/i;
/**
* Parse untrusted HTML into a sanitized `HsonNode` tree.
*
* Pipeline:
* 1. Apply the source-aware attribute pass while duplicate and reserved-name
* identity is still observable.
* 2. Sanitize the normalized HTML via `sanitize_external` (DOMPurify-based).
* 3. If sanitization removes all content (only forbidden tags/attrs),
* throw with a clear error message.
* 4. Pass the sanitized HTML into `parse_html` to build the Hson tree.
*
* This function is the safe HTML entry-point: all external/untrusted
* HTML should go through this path rather than `parse_html` directly.
*
* @param raw - Untrusted HTML string to sanitize and parse.
* @returns A rooted `HsonNode` tree representing the sanitized markup.
* @see sanitize_external
* @see parse_html
*/
export function parse_external_html(raw: string): HsonNode {
const sourceAwareHtml = normalize_html_source_attributes(raw);
const xmlShaped = XML_SHAPED_ARRAY_WRAPPER.test(sourceAwareHtml);
const sanitizerInput = xmlShaped
? encode_hson_metadata_transit(
normalize_html_source_attributes(sourceAwareHtml, { encodeTransit: true }),
)
: sourceAwareHtml;
const sanitized = sanitize_external(sanitizerInput, { xmlShaped });
const safeHtml = xmlShaped
? decode_hson_metadata_transit(sanitized)
: sanitized;
// if sanitizer nuked everything, fail with a clearer reason
if (!safeHtml.trim()) {
_throw_transform_err(
"parse_external_html(): all content removed by sanitizer (forbidden tags/attrs only).",
"parse_external_html",
raw.slice(0, 200)
);
}
return parse_html(safeHtml);
}