site-to-api-runtime
v0.1.4
Published
Small runtime for clients generated by site-to-api: cookie jar, retries, session hooks and HTML extraction. Runs on Node, Bun, browsers and edge workers.
Maintainers
Readme
site-to-api-runtime
The small runtime that clients generated by site-to-api gen import: a cookie jar, fetch with
sessions and retries, and HTML or text extraction. It uses only web-standard APIs (fetch,
Request, Headers, URL), so it runs on Node 22.12+, Bun and edge workers. Its only
dependencies are zod (as zod/mini) and, outside browsers, htmlparser2 with domhandler,
css-select and domutils.
You rarely call it directly: createSiteClient and the per-host factories in a generated client
wire it up. This page lists everything it exports; how to use a generated client in an app is in
using the client.
npm install site-to-api-runtime zodBuilds
- The default build parses HTML with the platform's
DOMParserwhen there is one and with htmlparser2 otherwise. - The
browserbuild, which bundlers pick through thebrowserexport condition, parses HTML withDOMParseronly, so no HTML parser is bundled.:contains("text")selectors, whichbuildwrites for label rows,:scope + trsibling selectors andtable > trrows (the parser adds a<tbody>the page did not write; it is unwrapped) read the same on both, and the tests run every HTML case on both. One difference remains: misnested markup (a<div>inside a<p>) is repaired by the browser's HTML5 parser and kept as written by htmlparser2, so a selector through such nesting can match on one and not the other.
Cookies
new CookieJar(options?) stores cookies from responses and produces the Cookie header for
requests, following RFC 6265 (domain and path matching, expiry, Secure, host-only cookies).
| Member | What it does |
| --- | --- |
| setFromResponse(url, headers) | Stores every Set-Cookie of a response |
| setCookieString(url, header) | Stores one Set-Cookie value; false when rejected |
| set(name, value, options) | Stores a cookie you know (domain, path, expires, …) |
| cookies(url) / cookieHeader(url) | The cookies a request to url sends, as objects or a header |
| all(), delete(name, domain?), clear() | Inspect or remove cookies |
| toJSON() / CookieJar.fromJSON(data) | Save the jar (a file, a database, a worker's storage) and restore it |
options.now replaces the clock, for tests. A saved jar holds real session cookies: never commit
it.
Fetch
createFetch(options?) returns a fetch that adds the session's credentials and the jar's
cookies, stores the cookies of every response (redirects included), and retries.
| Option | Default |
| --- | --- |
| jar | the session's jar, or a new one |
| session | cookieSession(jar) |
| retry | 3 attempts on 423, 429, 502, 503, 504 with backoff and Retry-After; idempotent methods only unless unsafe; false sends once |
| fetch | globalThis.fetch |
| defaultHeaders | none; added to requests that do not set them |
| redirects | jar follows redirects itself so cookies of each hop are kept; native leaves them to the platform (browsers) |
| credentialHeaders | header names dropped, like authorization and cookie, when a redirect leaves the origin |
| onRequest, onResponse | hooks for logging |
Sessions
A Session is { jar?, prepare(request), onAuthFailure(response, request) }.
cookieSession(jar): sends the jar's cookies; the default.tokenSession({ getToken, header?, scheme?, refresh?, refreshOn?, jar? }): sends a token (Authorization: Bearer …by default) and callsrefreshonce on 401.browserSession({ jar, bootstrap, refreshOn? }):bootstraploads the site in a real browser (for example Playwright) and returns its cookies; they are reused for plain HTTP and fetched again on 401 or 403. Returns the session withrefresh().combineSessions(...sessions): several at once (browser cookies plus a token); they must share one jar.
Errors
SiteApiError (status, statusText, url, method, body) is a failed HTTP call;
isSiteApiError(value) tells one apart from other errors.
Extraction
extract(text, spec): records from an HTML or text response, following anExtractSpecthatbuildinferred and verified against the captured samples. HTML specs select records and fields with CSS selectors; text specs split delimited rows or match a regular expression with named groups. Values are coerced to the field type; a missing required field isnull.extractValue(html, { selector, attr, pattern }): one value, such as a CSRF token in a meta tag.- Types:
ExtractSpec,HtmlExtractSpec,TextExtractSpec,ExtractField,FieldType,ExtractedRecord,HtmlValueLocation, andHtmlEngine, the interface both HTML parsers implement.
Both throw a TypeError for an invalid selector or pattern. Generated clients wrap them in typed
parsers (extractDealsFromGetDeals(text)) that validate the records with Zod.
Used by generated code
requestValidationInput(data, mixedCaseHeaders?): turns hey-api's resolved request options into a plain object a Zod schema can validate (itsheadersis aHeadersinstance).graphqlQuerySerializer,fetchWithoutFragment,withoutFragment: send GraphQL operations and dispatcher functions, whose spec paths carry a#namethat must not reach the site.
