ReviewOS

craft-native/craft

No URL, URLSearchParams or TextEncoder on a native screen, and no error that says so

#379
Closed glennmichael123 opened this 8 days ago ·
8 days ago

What is missing

A native screen's JavaScript context has the language intrinsics plus what the runtime assigns (fetch, craft, the timer shims). It does not have URL, URLSearchParams or TextEncoder, and nothing shims them.

Why it matters

These are the APIs you reach for when building a request, and reaching for one fails in the worst possible way: silently, at runtime, inside a try that an app has for other reasons.

Ours cost a screen that made no request at all while every test passed. A shared module built its query the obvious way:

// shared by the web page and the native screen
export function leaderboardQuery(...): string {
  const params = new URLSearchParams()
  ...
  return params.toString()
}

In the browser: fine. On a native screen: URLSearchParams is not defined, the load path aborts, the screen renders its empty state, and nothing anywhere says why. Type-checking is happy — URLSearchParams is in the ambient DOM lib — and so is the linter.

It is not a one-off. Every query string in our shared modules is now hand-built with encodeURIComponent, and each one carries a comment explaining why, because the next person's instinct will be to tidy it back:

// Built by hand rather than with `URLSearchParams`: a native screen has
// none, and the failure is a screen that issues no request at all.
let path = `/api/territories/map?limit=${TERRITORY_LIMIT}`

We eventually wrote a repo gate that walks every module a native screen imports and fails the build on URLSearchParams, new URL(, TextEncoder, localStorage and friends. That gate is pure overhead that exists because the runtime cannot answer for itself.

What would help

Either of these would do, in order of preference:

  1. Provide them. URL and URLSearchParams are self-contained and implementable in JS over the intrinsics; TextEncoder is a small amount of UTF-8. Injecting them beside fetch would make shared browser/native code portable by default.
  2. Fail loudly. If they are deliberately out of scope, a stub that throws URLSearchParams is not available on a native screen — build the query with encodeURIComponent would turn a silent dead screen into a one-line fix. At present the ReferenceError surfaces wherever the caller happens to catch.

A documented list of exactly what a native context provides would help either way. We established ours empirically, by running code in a bare JS context and seeing what threw.

Versions

craft-native 0.0.237, @stacksjs/stx 0.2.563, iOS 26.5 simulator.

8 days ago

The native hosts now fail loudly and consistently for missing browser globals rather than surfacing an opaque ReferenceError: iOS JavaScriptCore and Android JavaScriptSandbox install named failures for URL, URLSearchParams, and TextEncoder.

Main commits 808ee522 (host behavior) and 0072acad (guide plus cross-host contract coverage) document and test the chosen fail-loud path. The web renderer is unchanged. This satisfies the issue’s second proposed resolution; the remaining alternative—shipping full browser implementations—belongs in the shared STX runtime rather than the Craft hosts.

8 days ago

Resolved on main by 808ee52 and 0072aca. Native screens now install named throwing stubs for URL, URLSearchParams, and TextEncoder, and the browser-global diagnostics are documented and covered by cross-host contract tests. On main, unreleased.

Sign in to comment on this issue.