···11+# tessera-geotessera-jsoo Design
22+33+## Goal
44+55+A thin js_of_ocaml wrapper that provides a browser-compatible synchronous HTTP fetch for `tessera-geotessera`, enabling tile fetching in the OCaml notebook's web worker context.
66+77+## Architecture
88+99+The core `tessera-geotessera` library is already parameterised over `fetch:(string -> string)`. This package provides:
1010+1111+1. A synchronous XHR-based `fetch` function (works in web workers)
1212+2. A convenience `fetch_mosaic` that wires the XHR fetch into `Geotessera.fetch_mosaic_sync`
1313+1414+Everything else (`bbox`, `tile_coord`, `dequantize`, etc.) comes from the core `Geotessera` module.
1515+1616+## API
1717+1818+```ocaml
1919+val fetch : string -> string
2020+(** Synchronous HTTP GET via XMLHttpRequest. Raises on failure. *)
2121+2222+val fetch_mosaic : ?base_url:string -> ?version:string -> year:int ->
2323+ Geotessera.bbox -> Linalg.mat * int * int
2424+(** Fetch and assemble tiles for a bounding box using browser XHR.
2525+ Convenience wrapper around Geotessera.fetch_mosaic_sync. *)
2626+```
2727+2828+## Package Structure
2929+3030+```
3131+tessera-geotessera-jsoo/
3232+ dune-project
3333+ tessera-geotessera-jsoo.opam
3434+ lib/
3535+ dune
3636+ geotessera_jsoo.ml
3737+ geotessera_jsoo.mli
3838+ test/
3939+ dune
4040+ test_browser.ml # compiled to JS
4141+ test_browser.html # loads the compiled JS
4242+```
4343+4444+## Implementation
4545+4646+The `fetch` function uses `js_of_ocaml`'s `XmlHttpRequest` API:
4747+- Create XHR, open synchronous GET, send
4848+- Check status = 200, extract response as arraybuffer
4949+- Convert arraybuffer to OCaml string via `Typed_array.String.of_arrayBuffer`
5050+- Raise `Failure` on non-200 status
5151+5252+Follows the existing pattern from `js_top_worker/lib/jslib.ml` `sync_get`.
5353+5454+## Dependencies
5555+5656+- `tessera-geotessera` (core library)
5757+- `js_of_ocaml` (XHR bindings)
5858+- `js_of_ocaml-ppx` (preprocessor)
5959+6060+No Lwt dependency needed for the synchronous approach.
6161+6262+## Testing
6363+6464+Playwright-based browser testing:
6565+6666+1. **Test executable** — OCaml program compiled to JS via `(modes js)` that:
6767+ - Calls `Geotessera_jsoo.fetch` on a known geotessera URL
6868+ - Calls `Geotessera_jsoo.fetch_mosaic` on a small bbox
6969+ - Writes results (shape, values, pass/fail) to the DOM
7070+7171+2. **HTML harness** — minimal page that loads the compiled JS
7272+7373+3. **Playwright test** — navigates to page served locally, waits for results, asserts expected values
7474+7575+4. **Network** — hits dl2.geotessera.org directly (CORS enabled, no auth)
7676+7777+## Design Decisions
7878+7979+- **Synchronous XHR over async**: The notebook toplevel runs in a web worker where sync XHR is permitted. Avoids Lwt dependency and matches the existing `fetch_mosaic_sync` API. An async variant can be added later.
8080+- **Raises on failure**: `fetch_mosaic_sync` expects `string -> string` (not option/result), so the fetch function raises on HTTP errors.
8181+- **No re-export of Geotessera types**: Users `#require` both `tessera-geotessera` and `tessera-geotessera-jsoo`. The jsoo package only adds browser-specific functionality.