Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"version": 1,
"isRoot": true,
"tools": {
"fallout.cli": {
"version": "11.0.18",
"fallout.globaltool": {
"version": "10.4.0",
"commands": [
"fallout"
]
Expand Down
80 changes: 80 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# AngleSharp.Wasm

## Purpose

AngleSharp.Wasm extends AngleSharp with WebAssembly execution and a DOM/JavaScript-facing WebAssembly surface. The repository produces the `AngleSharp.Wasm` NuGet package.

## Repository Layout

- `src/AngleSharp.Wasm/`: library implementation.
- `src/AngleSharp.Wasm/Dom/`: DOM-annotated WebAssembly bridge types exposed to scripting integrations.
- `src/AngleSharp.Wasm.Tests/`: NUnit tests for configuration, runtime behavior, and the DOM bridge.
- `src/AngleSharp.Wasm.Docs/`: TypeScript documentation site configuration and entry point.
- `docs/`: Markdown documentation, including the API, examples, and WebAssembly specification coverage.
- `nuke/`: NUKE build project and build configuration.
- `build.sh`, `build.ps1`, `build.cmd`: platform-specific build entry points.

## Development Environment

- The solution is `src/AngleSharp.Wasm.sln`.
- The library and test projects target `net8.0` and `net10.0`.
- The library references AngleSharp 1.x and Wasmtime 44.0.0.
- The default runtime is `WasmtimeWasmRuntime`, registered through `Configuration.WithWasm()`.
- `global.json`, when present, controls the SDK version used by the build wrapper.

## Common Commands

Run from the repository root:

```sh
./build.sh
```

The build wrapper restores local .NET tools and delegates to NUKE. On Windows, use `build.ps1` or `build.cmd`.

For focused local checks, use the .NET CLI against the solution or test project:

```sh
dotnet build src/AngleSharp.Wasm.sln
dotnet test src/AngleSharp.Wasm.Tests/AngleSharp.Wasm.Tests.csproj
```

When changing the DOM bridge or runtime behavior, run the relevant NUnit tests and then the full test project for both target frameworks when practical.

## Architecture and Conventions

- Keep runtime abstractions in `src/AngleSharp.Wasm/` independent of the concrete Wasmtime implementation where possible.
- `IWasmRuntime`, `IWasmCompiledModule`, `IWasmInstance`, and `IWasmRuntimeFactory` define the runtime boundary.
- Host imports are supplied through `IWasmImportProvider`, `WasmImportFunction`, and the `WithWasmImports(...)` configuration extensions.
- The default runtime is Wasmtime-specific; do not make Wasmtime types part of the public abstraction unless the feature requires it.
- DOM-facing members use AngleSharp DOM annotations such as `[DomName]`, `[DomExposed]`, `[DomAccessor]`, and `[DomNoInterfaceObject]`.
- Preserve the existing synchronous bridge style unless deliberately adding a Promise/streaming API.
- Use existing AngleSharp and Wasmtime APIs before introducing new abstractions.
- Keep changes focused and preserve public APIs unless the task explicitly requires a breaking change.
- Use ASCII for new source and documentation unless the surrounding file already requires another character set.

## Current DOM/WebAssembly Surface

The package exposes a minimal `window.WebAssembly` bridge through the types in `src/AngleSharp.Wasm/Dom/`:

- `WebAssembly.compile(byte[])` creates a `WasmJsModule`.
- `WebAssembly.instantiate(WasmJsModule)` creates a `WasmJsInstance`.
- Module metadata includes imports, exports, and custom sections.
- Instance exports support name lookup and function invocation through `WasmJsExportedFunction`.
- Non-function exports are currently descriptor objects rather than full `Memory`, `Table`, `Global`, `Tag`, or `Exception` projections.

The documented gaps include `WebAssembly.validate(...)`, streaming APIs, compile options, full non-function export objects, and dedicated WebAssembly error constructors. Consult `docs/general/02-Spec-Coverage.md` before extending the DOM surface.

## Testing Guidance

Add or update focused NUnit tests in `src/AngleSharp.Wasm.Tests/` for every behavior change. DOM bridge tests should configure a browsing context with `Configuration.Default.WithWasm()`. Runtime tests should cover both successful behavior and invalid input or disposal paths where applicable.

For new WebAssembly binary fixtures, keep the fixture small and document its module shape near the byte array. Prefer testing the public bridge and runtime contracts rather than implementation details of Wasmtime.

## Documentation

Update the relevant Markdown documentation and the specification coverage matrix when adding or changing public WebAssembly functionality. Keep API names aligned with the WebAssembly JavaScript API where the package intentionally provides a compatible projection, and document any synchronous or .NET-specific adaptation.

## Change Hygiene

Do not commit generated output from `bin/`, `obj/`, or NUKE build folders unless the repository explicitly requires it. Do not alter unrelated user changes. Do not commit changes unless explicitly requested.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# 1.1.0

Released on Friday, August 21 2026.

- Updated with compile options support (#13)
- Added `WebAssembly.CompileError`, `WebAssembly.LinkError`, and `WebAssembly.RuntimeError` projections (#14)
- Added `WebAssembly.compileStreaming` and `WebAssembly.instantiateStreaming` methods (#12)
- Added `WebAssembly.validate` method (#11)
- Added `WebAssembly.Exception` object projection (#10)
- Added `WebAssembly.Tag` object projection (#9)
- Added `WebAssembly.Global` object projection (#8)
- Added `WebAssembly.Table` object projection (#7)
- Added `WebAssembly.Memory` object projection (#6)

# 1.0.0

Released on Friday, July 31 2026.
Expand Down
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

The guidance is shared with every AI agent working here, so it lives in AGENTS.md and is
imported below. Record new guidance there rather than in this file.

@AGENTS.md
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,22 @@ The current package targets `net8.0` and `net10.0`.
- `exports()`
- `imports()`
- `customSections(name)`
- Module validation via `validate(bytes)`
- Response-based compilation and instantiation via `compileStreaming(response)` and `instantiateStreaming(response)`
- Compile option routing for `builtins` and `importedStringConstants`
- Instance export access
- `exports` lookup by export name
- export key enumeration
- Export invocation helpers
- `instance.Invoke(...)`
- `WasmJsExportedFunction.Invoke(...)`
- WebAssembly object projections
- memory `buffer`, `read`, `write`, and `grow`
- table `length`, `get`, `set`, and `grow`
- global `value` and `valueOf`
- tag `type().parameters`
- exception `is(tag)` and `getArg(tag, index)`
- `CompileError`, `LinkError`, and `RuntimeError`
- Multi-target support for `net8.0` and `net10.0`

## Current Scope and Limitations
Expand All @@ -102,9 +112,10 @@ AngleSharp.Wasm currently provides a practical subset of the WebAssembly JS API.

- Bridge methods are synchronous from the caller perspective.
- Promise-based namespace operations are not currently exposed.
- `validate(...)` and streaming APIs are not yet implemented.
- Compile options such as builtins / imported string constants are not yet implemented.
- Non-function exports are currently represented as descriptors (`name`, `kind`) rather than full `Memory` / `Table` / `Global` / `Tag` objects.
- Streaming APIs synchronously buffer AngleSharp response streams because module metadata requires the complete binary.
- Compile options are validated and forwarded to capable runtimes. Wasmtime 44 does not expose JavaScript string builtins or imported string constants, so requesting either option with the default backend throws `NotSupportedException`.
- Exported memories, tables, globals, and tags have object projections; other non-function exports remain descriptors (`name`, `kind`).
- Host-created tags and exceptions are supported. Wasmtime 44 does not expose native Tag or Exception handles or enable exception modules through its .NET API, so runtime-thrown Wasm exceptions are not yet projected.

See [Spec Coverage Matrix](docs/general/02-Spec-Coverage.md) for a section-by-section status overview.

Expand Down
4 changes: 1 addition & 3 deletions build/_build.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Fallout.Common" Version="11.0.18" />
<!-- Transitive pin: Fallout.Common 11.0.18 still resolves 10.0.6, which carries advisories. -->
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.10" PrivateAssets="All" />
<PackageReference Include="Fallout.Common" Version="10.4.0" />
</ItemGroup>

</Project>
6 changes: 4 additions & 2 deletions docs/general/01-Basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ var config = Configuration.Default
AngleSharp.Wasm currently implements a pragmatic subset of the WebAssembly JS API.

- Synchronous bridge calls are used (`compile` / `instantiate` are not Promise-based APIs).
- `WebAssembly.validate(...)`, streaming APIs, and compile options are not implemented.
- Streaming APIs synchronously buffer AngleSharp response streams. Compile options are forwarded to capable runtimes, but Wasmtime 44 does not support JavaScript string builtins or imported string constants.
- `Instance.exports` function members are wrapper objects requiring `.invoke(...)`.
- Non-function exports are currently represented as descriptors (`name`, `kind`), not full `Memory` / `Table` / `Global` / `Tag` objects.
- Compile, link, and execution failures are projected as `CompileError`, `LinkError`, and `RuntimeError` objects.
- Exported memories, tables, globals, and tags have object projections; other non-function exports remain descriptors (`name`, `kind`).
- Host-created exceptions support tag identity and typed payload inspection. The default Wasmtime 44 backend does not expose native Tag or Exception handles, so runtime-thrown Wasm exceptions are not yet projected.
- `customSections(...)` returns payload bytes (`byte[]`) mapped from custom sections.
- Runtime invocation/import marshaling is currently focused on numeric value kinds (`i32`, `i64`, `f32`, `f64`).

Expand Down
33 changes: 21 additions & 12 deletions docs/general/02-Spec-Coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,46 @@ Status values:
| --- | --- | --- |
| WebAssembly namespace: `compile(bytes)` | Partial | Exposed as synchronous bridge method `WebAssembly.compile(byte[])`. |
| WebAssembly namespace: `instantiate(...)` | Partial | Exposed as synchronous bridge method `WebAssembly.instantiate(WasmJsModule)`. |
| WebAssembly namespace: `validate(bytes)` | Not yet | Not currently exposed. |
| WebAssembly namespace: streaming APIs | Not yet | No `instantiateStreaming` / `compileStreaming`. |
| WebAssembly namespace: compile options | Not yet | No `builtins` / `importedStringConstants` options support yet. |
| WebAssembly namespace: `validate(bytes)` | Implemented | Exposed as synchronous `WebAssembly.validate(byte[])`, using native Wasmtime validation or compile-and-dispose fallback for custom runtimes. |
| WebAssembly namespace: streaming APIs | Partial | `compileStreaming(IResponse)` and `instantiateStreaming(IResponse)` synchronously consume successful `application/wasm` AngleSharp responses. Response bodies are buffered for metadata extraction. |
| WebAssembly namespace: compile options | Partial | `WasmCompileOptions` validates and routes `builtins` and `importedStringConstants` through compile, byte instantiate, and streaming entry points. Wasmtime 44 cannot execute these options. |
| `Module.exports(module)` | Partial | Available as instance method `module.exports()` returning descriptor objects. |
| `Module.imports(module)` | Partial | Available as instance method `module.imports()` returning descriptor objects. |
| `Module.customSections(module, name)` | Partial | Available as instance method `module.customSections(name)` returning `byte[][]`. |
| `Instance.exports` | Partial | Available as `instance.exports`, with function wrappers and descriptor values for non-function exports. |
| `Instance.exports` | Partial | Available as `instance.exports`, with function, memory, table, global, and tag wrappers plus descriptor values for other export kinds. |
| Exported function invocation | Implemented | Supported via `instance.invoke(...)` and `WasmJsExportedFunction.invoke(...)`. |
| Host import functions | Implemented | Supported via `WithWasmImports(...)` and `WasmImportFunction`. |
| Memory object API (`Memory`) | Not yet | No full JS API `Memory` object projection yet. |
| Table object API (`Table`) | Not yet | No full JS API `Table` object projection yet. |
| Global object API (`Global`) | Not yet | No full JS API `Global` object projection yet. |
| Tag object API (`Tag`) | Not yet | No full JS API `Tag` object projection yet. |
| Exception object API (`Exception`) | Not yet | No full JS API exception projection yet. |
| Error constructors (`CompileError`, `LinkError`, `RuntimeError`) | Not yet | No dedicated namespace error constructor projection yet. |
| JS String builtins set | Not yet | No compile-option builtin-set wiring yet. |
| Memory object API (`Memory`) | Partial | Exported memories expose `buffer`, `read(offset, count)`, `write(offset, bytes)`, and `grow(delta)`. `buffer` is a current `byte[]` snapshot rather than a live JavaScript `ArrayBuffer`; memory construction and imports are not yet exposed. |
| Table object API (`Table`) | Partial | Exported `funcref` and `externref` tables expose `length`, `get(index)`, `set(index, value)`, and `grow(delta, value)`. Table construction and imports are not yet exposed. |
| Global object API (`Global`) | Partial | Exported numeric and reference globals expose a mutable or immutable `value` accessor and `valueOf()`. Global construction and imports are not yet exposed. |
| Tag object API (`Tag`) | Partial | Host-created and exported tags expose `type().parameters`, including re-exported imported tags. Wasmtime 44 does not expose native Tag handles or enable exception modules through its .NET API. |
| Exception object API (`Exception`) | Partial | Host-created exceptions validate typed payloads and expose `is(tag)` and `getArg(tag, index)`. Runtime-thrown Wasm exceptions cannot be projected through Wasmtime 44. |
| Error constructors (`CompileError`, `LinkError`, `RuntimeError`) | Implemented | Public DOM-named constructors are available, and compile, instantiate/link, and execution failures are translated at bridge boundaries. |
| JS String builtins set | Partial | The `js-string` builtin set is recognized and forwarded to capable custom runtimes; Wasmtime 44 has no corresponding API. |

## Runtime and Type Support

| Area | Status | Notes |
| --- | --- | --- |
| Runtime backend | Implemented | Uses Wasmtime through the default `WithWasm()` registration. |
| Invocation numeric value types | Implemented | `i32`, `i64`, `f32`, `f64` are supported for import/export invocation paths. |
| Extended/reference value kinds | Not yet | Rich reference-type projections are not yet exposed through JS API object wrappers. |
| Extended/reference value kinds | Partial | Table projections support `funcref` and `externref`; broader reference-type invocation marshaling is not yet exposed. |

## Test-Backed Behavior

The current implementation is validated by runtime and bridge tests in the repository, including:

- compile and instantiate flows
- module validation
- response-based compilation and instantiation
- compile option validation and runtime forwarding
- export invocation
- exported memory read, write, and growth
- exported table access, mutation, and growth
- exported global access and mutation
- exported and re-exported tag type metadata
- host-created exception payload validation and inspection
- compile, link, and runtime error translation
- import descriptor extraction
- export descriptor extraction
- custom section lookup behavior
Expand Down
57 changes: 56 additions & 1 deletion docs/tutorials/01-API.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,37 @@ When used with a scripting integration that discovers DOM attributes, the follow

Compiles bytes and returns a `WasmJsModule`.

An overload accepts `WasmCompileOptions`.

### `WebAssembly.validate(byte[] moduleBytes)`

Returns whether the bytes form a valid WebAssembly module without instantiating it.

### `WebAssembly.compileStreaming(IResponse source)`

Synchronously consumes a successful `application/wasm` response and returns a compiled `WasmJsModule`.

An overload accepts `WasmCompileOptions`.

### `WebAssembly.instantiateStreaming(IResponse source)`

Synchronously consumes a successful `application/wasm` response and returns a `WasmJsInstantiationResult` containing `module` and `instance`.

An overload accepts `WasmCompileOptions`.

### `WebAssembly.instantiate(WasmJsModule module)`

Instantiates a compiled module and returns a `WasmJsInstance`.

Byte-array overloads compile and instantiate in one operation and return `WasmJsInstantiationResult`; compile options can be supplied.

## `WasmCompileOptions`

- `builtins`: requested builtin sets; currently the standard `js-string` value is recognized.
- `importedStringConstants`: module namespace for imported string constants.

Options are forwarded to runtimes implementing `IWasmCompileOptionsCompiler`. Wasmtime 44 does not expose either feature and rejects non-empty options.

## `WasmJsModule`

### `exports()`
Expand Down Expand Up @@ -80,7 +107,11 @@ Invokes an exported function by name.
Returns an export entry by name:

- `WasmJsExportedFunction` for function exports
- `WasmJsExportValue` for non-function exports
- `WasmJsMemory` for memory exports
- `WasmJsTable` for table exports
- `WasmJsGlobal` for global exports
- `WasmJsTag` for tag exports
- `WasmJsExportValue` for other non-function exports
- `null` if no export is found

### `keys()`
Expand All @@ -93,6 +124,30 @@ Returns all export names.

Invokes the wrapped exported function.

## `WasmJsMemory`

Provides `buffer`, `read(offset, count)`, `write(offset, bytes)`, and `grow(delta)`.

## `WasmJsTable`

Provides `length`, `get(index)`, `set(index, value)`, and `grow(delta, value)`. Function references returned by `get(...)` expose `invoke(...)`.

## `WasmJsGlobal`

Provides a `value` accessor and `valueOf()`. Assigning to an immutable global throws `InvalidOperationException`.

## `WasmJsTag`

Can be constructed with an ordered array of WebAssembly value type names. Provides `type()`, whose descriptor exposes the tag's `parameters`.

## `WasmJsException`

Construct with a `WasmJsTag` and matching payload array. Provides `is(tag)` and `getArg(tag, index)` and can be thrown and caught as a .NET exception. Runtime-thrown Wasm exceptions are not available through the default Wasmtime 44 backend.

## Error Constructors

`WasmJsCompileError`, `WasmJsLinkError`, and `WasmJsRuntimeError` expose the standard `CompileError`, `LinkError`, and `RuntimeError` DOM names. Each can be constructed with an optional message. Bridge compilation, linking, and execution failures are translated automatically.

## `WasmJsExportValue`

Descriptor type for non-function exports.
Expand Down
Loading
Loading