Commit Graph
6384 Commits
Author SHA1 Message Date
Francis BouvierandClaude Fable 5 1d263aabbd webapi: keep explicit null as AbortSignal abort reason
Fixes 1 failing test in WPT /dom/abort/event.any.html (and its .worker
variant): "AbortController abort(null) should set signal.reason".

abort() reason parameters were typed ?js.Value.Global, and the JS->Zig
conversion collapses an explicit JS null into a missing argument, so
controller.abort(null) fell back to the default "AbortError"
DOMException. Per the DOM spec, the abort reason defaults to a new
AbortError only when the reason is not given (undefined); an explicit
null must be stored as-is.

The parameters are now ?js.Value, which preserves the missing vs
explicit-null distinction, and the new AbortSignal.reasonFromJs helper
only falls back to the default for a missing or undefined reason.

Coverage: /dom/abort/event.any.worker.html 15/16 -> 16/16,
/dom/abort/event.any.html 15/16 -> 16/16.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:47:42 +02:00
Francis BouvierandClaude Fable 5 7d23b62573 webapi: fix AbortSignal dependent-signal abort semantics
Fixes 3 failing tests in WPT /dom/abort/abort-signal-any.any.html (and
its .worker variant):

- "Dependent signals are aborted correctly for reentrant aborts": when a
  source signal aborted a second source from within an abort listener,
  the shared dependent signal dispatched its abort event twice. Per the
  DOM spec, only dependent signals that are not already aborted are
  appended to the list of signals to dispatch. Dependend.markAborted now
  reports whether the dependent was newly marked, and abort() only
  dispatches for those.

- "Dependent signals should use the same DOMException instance" (already
  aborted source / source aborted later): Reason.dom held a DOMException
  by value, so every .reason access wrapped a fresh JS object and the
  identity check source.reason === dependent.reason failed. Reason.dom
  is now a *DOMException allocated once at abort time; the JS bridge
  identity-maps the pointer so the source and all dependents expose the
  very same DOMException instance.

Coverage: /dom/abort/abort-signal-any.any.worker.html 11/14 -> 14/14,
/dom/abort/abort-signal-any.any.html 11/14 -> 14/14.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:42:51 +02:00
Karl Seguin 15744bca37 ooops: remove stray debug print 2026-07-14 15:15:10 +08:00
Karl Seguin 93e86cbe6a Merge pull request #2936 from lightpanda-io/silence-expected-test-errors
test: Filter out expected error messages from tests
2026-07-14 15:10:31 +08:00
Karl Seguin 3b266359d2 Merge pull request #2937 from lightpanda-io/support-large-content-length
http: u32 -> usize for [very] large content length
2026-07-14 15:10:21 +08:00
Karl Seguin 0f589567f9 update for new v8 global management 2026-07-14 14:21:44 +08:00
Karl Seguin 8ad4bd9015 webapi: Add EventSource
The EventSource API only requires a simple line-based parser. It's pretty
straightforward. It's RC'd by v8 and itself, but also by scheduled tasks (e.g.
to reconnect).

The bigger change is to the HttpClient to support streaming requests. This
changes to things:
1 - A transfer doesn't only deliver() once
2 - A transfer can be in the multi while delivering

Neither of these are surprising, but they both add complexity.
2026-07-14 14:19:27 +08:00
Karl Seguin a0549b072d http: u32 -> usize for [very] large content length
There's 1 WPT test that returns a 8GB value content-length (testing failures)
and the u32 limit fails to parse it and makes it as though no content-length
header exists.
2026-07-14 14:09:01 +08:00
Karl Seguin 46e90a984f zig fmt 2026-07-14 11:55:59 +08:00
Karl Seguin 10e91d0503 test: Filter out expected error messages from tests
Please enter the commit message for your changes. Lines starting
2026-07-14 11:50:22 +08:00
Karl Seguin 1ac4435660 extend test buffer size 2026-07-14 11:45:17 +08:00
Karl Seguin f0784de015 add http metrics 2026-07-14 11:32:52 +08:00
Karl Seguin 4d81e48e44 ops: Add prometheus metrics
Adds a /metrics endpoint in serve mode. Can be disabled via --disable-metrics.

In fetch, dumps the metrics if --metrics is specified (defaults to false).

None of the metrics being collected are on a hot path, so they're just always
collected using atomic operations (i.e. no `if (!enabled) return;`). Because
the operations are cheap and infrequent enough not to matter.

The one place I want to add more metrics is in HttpClient (bytes, status,
counts, ...) but after the pending HttpClient-related PR is merged.

./lightpanda fetch --log-level fatal --metrics "https://lightpanda.io/"

```
build_info{version="1.0.0-dev.7837+70493ce35"} 1
cdp_connections_total 0
cdp_connection_limit_total 0
cdp_active_connections 0
cdp_commands_total 0
cdp_unknown_commands_total 0
js_heap_limits_total 0
script_errors_total 0
arena_hit_total{size="tiny"} 908
arena_hit_total{size="small"} 35
arena_hit_total{size="medium"} 0
arena_hit_total{size="large"} 27
arena_miss_total{size="tiny"} 193
arena_miss_total{size="small"} 31
arena_miss_total{size="medium"} 5
arena_miss_total{size="large"} 23
navigate_total{type="page"} 1
navigate_total{type="iframe"} 0
navigate_total{type="popup"} 0
js_heap_size_bytes_bucket{le="4194304"} 0
js_heap_size_bytes_bucket{le="8388608"} 0
js_heap_size_bytes_bucket{le="16777216"} 1
js_heap_size_bytes_bucket{le="33554432"} 1
js_heap_size_bytes_bucket{le="67108864"} 1
js_heap_size_bytes_bucket{le="134217728"} 1
js_heap_size_bytes_bucket{le="268435456"} 1
js_heap_size_bytes_bucket{le="536870912"} 1
js_heap_size_bytes_bucket{le="+Inf"} 1
js_heap_size_bytes_sum 11223040
js_heap_size_bytes_count 1
```
2026-07-14 09:17:14 +08:00
Karl Seguin 56c5701b7b Merge pull request #2925 from lightpanda-io/default-max-response-size
http: Change default max http size to 1GB (from unlimited)
2026-07-14 09:08:50 +08:00
Karl Seguin bd6706b2b4 Merge pull request #2923 from lightpanda-io/domrect
webapi: Improve DOMRect
2026-07-14 07:59:06 +08:00
Karl Seguin 8ccb9b2948 Merge pull request #2922 from lightpanda-io/custom-element-extend-html-interfaces
webapi: Allow CustomElements to extend any HTMLElement interfaces.
2026-07-14 07:58:45 +08:00
Karl Seguin c5f26cd7a0 Merge pull request #2924 from lightpanda-io/xhr-tweaks
webapi, minor: XHR Response-Type and invalid JSON parsing tweaks
2026-07-14 07:58:18 +08:00
Karl Seguin 2d325ce8b3 Merge pull request #2926 from lightpanda-io/set-outerHTML-document-protection
minor, webapi: Prevent outerHTML of document's element's from being w…
2026-07-14 07:57:37 +08:00
Karl Seguin 70278c0091 'GB' -> 'GiB' 2026-07-14 07:57:11 +08:00
Adrià Arrufat 830d90adc8 Merge pull request #2915 from lightpanda-io/agent-streaming
feat(agent): add assistant text streaming
2026-07-13 19:19:58 +02:00
Karl Seguin 1ae105f08f Merge pull request #2933 from lightpanda-io/fix-custom-element-recursion
fix: Custom-element constructor parser endless recursion
2026-07-13 23:40:36 +08:00
Karl Seguin ca1e80a397 fix: Custom-element constructor parser endless recursion
In a custom element, when this.innerHTML = '....' is called, we need to be
careful to prevent endless recursion. The html5ever callback used to determine
the context element should not invoke the custom-element constructor, else we'll
enter an endless loop.

This also fixes an ungating problem added with the new HttpClient when a
waitForImport can block forever.

Both issues were see on a WooCommerce site - though the HttpClient is only
due to an earlier HttpClient refactor.
2026-07-13 23:23:00 +08:00
Adrià Arrufat 8c575e2dc8 Merge branch 'main' into agent-streaming 2026-07-13 14:38:38 +02:00
Karl Seguin 9f47b28666 webapi: ResizeObserver.observer box option default
This should default to "content-box". Not that the value is important, but
without the default, we fail if an empty object is passed.
2026-07-13 20:30:43 +08:00
Karl Seguin 43b55b8013 http: Change default max http size to 1GB (from unlimited) 2026-07-13 18:55:05 +08:00
Karl Seguin 24d1051b1a Merge pull request #2919 from lightpanda-io/mcp-multi-session
mcp: add HTTP transport and multi-session support
2026-07-13 18:53:51 +08:00
Karl Seguin 6484161e2b Merge pull request #2928 from lightpanda-io/Dont-Log-JsException
ops: Reduce logging of JsException on event dispatch
2026-07-13 18:13:50 +08:00
Karl Seguin 77ec7d83e9 Merge pull request #2927 from lightpanda-io/Dont-Log-TryCatchRethrow
ops: Don't log TryCatchRethrow errors
2026-07-13 18:13:40 +08:00
Karl Seguin fae6389bca Merge pull request #2889 from lightpanda-io/httpclient-pipeline-and-transfer-queue
refactor: HttpClient
2026-07-13 17:02:57 +08:00
Adrià Arrufat 2c4803b1cf mcp: ensure clean http server shutdown
Track active connections and shut down sockets during deinit to unblock
pending reads. Drain the job queue properly before stopping the worker.
Also fix `--http-port` references to `--port` in docs and errors.
2026-07-13 10:07:37 +02:00
Adrià ArrufatandKarl Seguin 6039bc82b8 docs: correct --host description
Co-authored-by: Karl Seguin <karlseguin@users.noreply.github.com>
2026-07-13 09:28:25 +02:00
Karl Seguin df0473420c ops: Reduce logging of JsException on event dispatch
Event dispatch using a try/catch (tryCallWithThis) so that the EventManager can
decide how to handle the error. Previously, using the `callWithThis` would
result in always logging the error. Now, the EventManager can skip logging
JsExceptions.
2026-07-13 13:20:01 +08:00
Karl Seguin f4da23da2e ops: Don't log TryCatchRethrow errors
error.TryCatchRethrow is used as a control flow (sorry). It signals that an
error has already been throw in v8 and that the method should exit (ultimately,
it ends up being the return value passed to our v8 bridge, which knows to ignore
it). We shouldn't log this as a WARN, it's completely normal that it happens AND
it really contains no meaningful information as-is.
2026-07-13 12:59:29 +08:00
Karl Seguin e4387bf7fa minor, webapi: Prevent outerHTML of document's element's from being written
Captured by WPT test: /domparsing/outerhtml-01.html
2026-07-13 12:52:38 +08:00
Karl Seguin f7f8869ed3 webapi, minor: XHR Response-Type and invalid JSON parsing tweaks
Add an explicit 'default' ResponseType, which mostly behaves like `text`, but
can be used do discriminate between an explicit text and the default value.

Also, on JSON parse error, return null, not an exception.
2026-07-13 12:28:59 +08:00
Karl Seguin 7b8ea39164 webapi: Improve DOMRect
We had a basic DORect implementation (which probably covered more cases). This
commit extends the implementation to be more complete:

- Add DOMRectReadOnly (which DOMRect inherits from)
- Add StructuredClone support to DOMRect and DOMRectReadOnly
- Make DOMRect and DOMRectReadOnly usable from Worker

DOMRect can be on the hot path with IntersectionObserver, so we maintain a
value-based data object (DOMRect.Data) for internal use (things returned to js
always have to be heap allocated (and always were, we're just more explicit
about this part now)).
2026-07-13 12:03:52 +08:00
Karl Seguin 5e2615850d webapi: Allow CustomElements to extend any HTMLElement interfaces.
Pervious, extension was limited to HtmlElement. CustomElements can now extend
any html element interface.

The implementation isn't great. This is a special case, and rather than coming
up with some abstract way to define this in bridge, we make the Snapshot
aware of this specific case directly.
2026-07-13 11:13:49 +08:00
Karl Seguin 0ff08420c8 Merge pull request #2917 from lightpanda-io/svg
webapi: Add various SVG types
2026-07-13 07:49:18 +08:00
Karl Seguin f444f2cf6d Merge pull request #2898 from lightpanda-io/js_global_rework
refactor: Rework how v8::Globals are managed
2026-07-13 07:20:37 +08:00
Adrià Arrufat bdb122cdce Merge branch 'main' into agent-streaming 2026-07-12 22:56:54 +02:00
Adrià Arrufat 46be87f2ca test(agent): cover resolveStream precedence and stream round-trip
Verify the stream default is on, that a remembered value wins, and that
the field parses from (and is absent-safe in) .lp-agent.zon.
2026-07-12 22:13:49 +02:00
Adrià Arrufat fa2dc02721 fix(agent): reset stream_active at turn start
`stream_active` relied on every runTools exit path balancing pause with
`endStreamedText`. If one is ever missed, a stale-true flag skips the
spinner pause on the next turn, interleaving frames with streamed text.
Reset it alongside `streamed_text` so the invariant is local.
2026-07-12 22:10:29 +02:00
Adrià Arrufat 2e8e0d1a6f docs(agent): note why the streamed_text guard is turn-wide safe
Document the invariant that lets a single per-turn flag suppress the
buffered reprint without risking a dropped final answer.
2026-07-12 22:10:11 +02:00
Adrià Arrufat a64ced0ccc fix(agent): stream assistant text only in the REPL
Streaming defaulted on for every mode, so one-shot `--task` and script
runs interleaved intermediate assistant commentary into stdout, which
wrappers treat as the answer. Gate the stream hook on `Terminal.isRepl`
so non-interactive modes keep the buffered final answer.
2026-07-12 22:09:48 +02:00
Adrià Arrufat 5abae593d8 fix(agent): keep streaming off for suppressed turns
Streaming wrote assistant deltas straight to stdout regardless of
`suppress_answer`, so `--save`/capture turns (which set it to hide the
answer) leaked model text past the `runTurn` guard. Pass a null stream
hook when the turn's answer is suppressed.
2026-07-12 22:09:25 +02:00
Adrià Arrufat f66f0c191d mcp: add HTTP transport and multi-session support
Introduces an HTTP transport option to serve multipleagents from a
single process. Each connection is routed to its own isolated
browsing session using the `Mcp-Session-Id` header.

Also adds new session management tools (`session_new`, `session_list`,
`session_close`) and refactors the MCP server to support multiple V8
isolates with parking.
2026-07-12 15:27:20 +02:00
Karl Seguin 2649b4c6cf Merge pull request #2841 from lightpanda-io/indexeddb
webapi: IndexedDB base
2026-07-12 07:31:37 +08:00
Karl Seguin 1967be7e54 webapi: Add various SVG types
Rather than having a generic SVG type (which gets us past parsing, but even the
slightest JS usage is likely to fail), this adds concrete types for a number of
known SVG types. A lot of these are empty (but even that's enough to pass
something like `instanceof SVGAElement`), but a handful of the more important
accessors and methods are implemented (e.g. I ran into a site that made
extensive use of SVGSVGElement.getElementById).
2026-07-11 18:31:09 +08:00
Karl Seguin ca927adff2 Merge pull request #2916 from lightpanda-io/attribute-frame
fix: When mutating attributes, use element's owner frame
2026-07-11 09:29:58 +08:00
Karl Seguin d43d11a641 Merge pull request #2912 from lightpanda-io/agent-repl-fixes
agent: multi-line slash commands and --save .js extension
2026-07-11 07:26:29 +08:00