Parent: #140 Tracked by: #134, #163, #164
Symbols (sub-slice 1 of the URL cluster)
| Symbol | Home (packages/runtime/src/jsc/URL.zig @ 7ed99c02) | Bun C++ (upstream/src/jsc/bindings/BunString.cpp) |
|---|---|---|
URL__fromString | :33 — (*String) ?*URL | :677 — parse, null when invalid, new WTF::URL |
URL__deinit | :43 — (*URL) void | :692 — delete url |
URL__href | :35 — (*URL) String | :697 — url->string() (with fragment) |
URL__protocol | :34 | :687 — scheme, no colon |
URL__username / URL__password | :36 / :37 | :702 / :707 |
URL__search | :38 | :712 — query WITH leading ? |
URL__host | :39 | :724 — host WITHOUT port (not JS host) |
URL__hostname | :40 | :736 — host WITH port (not JS hostname) |
URL__port | :41 — (*URL) u32 | :741 — maxInt(u32) when unset, else ≤ u16 |
URL__pathname | :45 | :752 |
URL__hash | :30 | :661 — fragment WITH leading # |
URL__fragmentIdentifier | :31 | :669 — fragment WITHOUT # |
The zig-js engine ships a real WHATWG-style URL implementation (urlParse with base/relative resolution, path/query/fragment state machine, UrlParts, live URLSearchParams linkage), so this slice is thin ABI plumbing over existing engine semantics — the same pattern as #307. Remaining URL-cluster symbols (URL__fromJS, URL__getHref*, URL__getFileURLString, URL__pathFromFileURL, BunString__toURL/toJSDOMURL, DOMURL__*, URL__originLength) are follow-up sub-slices.
Acceptance criteria
URL__fromStringparses the BunString through the engine'surlParse(no base) and returns a native parsed-URL record, null for invalid input; the record owns its component bytes independently of any realm/arena.URL__deinitfrees the record; null tolerated; component getters tolerate null by returning Dead (Bun UB on null deref → zig-js safe sentinel);port→maxInt(u32).- Component getters match Bun/WTF semantics exactly: protocol without colon, search with leading
?(present-but-empty query keeps?— deliberately NOT the JSsearchgetter, which the WHATWG DOM spec maps to""), host without port, hostname with port, port as u32 ormaxInt(u32), hash with#only for non-empty fragments, fragmentIdentifier without. - Returned
Strings are owned BunStrings (wtf_string_impltag) with lifetimes independent of the record, matching WTF refcount semantics. - Engine parse layer (
urlParse/urlSerialize/urlNormalizePort/urlParsePathQueryFrag/urlEncodeNormalizePath/urlParseRelative/urlPercentEncode/wtf8ToUtf8Bytes/urlOrigin) refactored from*Interpreterto allocator-first with zero behavior change — the functions used the interpreter only forself.arena; all existing call sites (URL builtin, fetch Request/Response/redirect) passself.arena. - Compiled runtime fixture covers: parse→getters roundtrip on a full special URL (userinfo, port, path, query, fragment) with JS
new URLparity where semantics overlap, default-port elision, non-special opaque-path URL, present-but-empty query/fragment, invalid input → null, null tolerance, record-lifetime independence after a second parse, UTF-16 BunString input. Fixture symbol count 294 → 307. - Home inventory
304/431, Bun inventory297/421;bun-private-abi-audit+c-api-auditgreen; fullzig build testzero new failures (worktree baseline-vs-candidate against the same pinned base).
Design constraints
*URLis a NATIVE heap record (c_allocator, magic-validated likePrivateStringBuilder), NOT a JS object — Bun'sWTF::URL*has no realm, andURL__fromStringcarries no global. The JS-facing casts (URL__fromJS, DOMURL) are the follow-up sub-slice.- No punycode/IDNA work is pulled in: whatever the engine's parser does with non-ASCII hosts today is the pinned behavior (documented in the fixture).
URL__originLengthstays deferred: Home itself declares it un-wrapped (needsbun.strings.firstNonASCII); assess separately.