this repo has no description
at main 339 lines 13 kB view raw view rendered
1# js_top_worker Architecture 2 3This document describes the current architecture of js_top_worker and the planned changes. 4 5## Overview 6 7js_top_worker is an OCaml toplevel (REPL) designed to run in a Web Worker or remote process. It enables interactive OCaml execution in browsers for: 8 9- Jupyter-style notebooks 10- Interactive documentation 11- Educational tools (lecture slides, tutorials) 12- Library documentation with live examples 13 14## System Architecture 15 16``` 17┌─────────────────────────────────────────────────────────────────┐ 18│ Browser │ 19│ ┌──────────────────┐ ┌──────────────────────────────┐ │ 20│ │ Frontend │ │ Web Worker │ │ 21│ │ │ │ │ │ 22│ │ ┌────────────┐ │ RPC │ ┌────────────────────────┐ │ │ 23│ │ │ Client │◄─┼────────►│ │ Server │ │ │ 24│ │ │ (Lwt/Fut) │ │ JSON │ │ (worker.ml) │ │ │ 25│ │ └────────────┘ │ │ └──────────┬─────────────┘ │ │ 26│ │ │ │ │ │ │ 27│ │ │ │ ┌──────────▼─────────────┐ │ │ 28│ │ │ │ │ Implementation │ │ │ 29│ │ │ │ │ (impl.ml) │ │ │ 30│ │ │ │ │ - Execute phrases │ │ │ 31│ │ │ │ │ - Type checking │ │ │ 32│ │ │ │ │ - Code completion │ │ │ 33│ │ │ │ └──────────┬─────────────┘ │ │ 34│ │ │ │ │ │ │ 35│ │ │ │ ┌──────────▼─────────────┐ │ │ 36│ │ │ │ │ js_of_ocaml-toplevel │ │ │ 37│ │ │ │ │ + Merlin │ │ │ 38│ │ │ │ └────────────────────────┘ │ │ 39│ └──────────────────┘ └──────────────────────────────┘ │ 40└─────────────────────────────────────────────────────────────────┘ 41``` 42 43## Package Structure 44 45| Package | Purpose | Key Files | 46|---------|---------|-----------| 47| `js_top_worker` | Core toplevel implementation | `lib/impl.ml`, `lib/ocamltop.ml` | 48| `js_top_worker-web` | Web Worker implementation | `lib/worker.ml`, `lib/findlibish.ml` | 49| `js_top_worker-rpc` | RPC type definitions | `idl/toplevel_api.ml` | 50| `js_top_worker-client` | Lwt-based client | `idl/js_top_worker_client.ml` | 51| `js_top_worker-client_fut` | Fut-based client | `idl/js_top_worker_client_fut.ml` | 52| `js_top_worker-unix` | Unix socket backend (testing) | - | 53| `js_top_worker-bin` | CLI tools (`jtw`) | `bin/jtw.ml` | 54 55## Current Communication Layer 56 57### RPC Protocol 58 59Uses [ocaml-rpc](https://github.com/mirage/ocaml-rpc) with JSON-RPC 2.0: 60 61``` 62Client Server (Worker) 63 │ │ 64 │ ──── JSON-RPC request ────────► │ 65 │ {method: "exec", │ 66 │ params: ["let x = 1"], │ 67 │ id: 1} │ 68 │ │ 69 │ ◄─── JSON-RPC response ──────── │ 70 │ {result: {...}, │ 71 │ id: 1} │ 72 │ │ 73``` 74 75### RPC Operations 76 77| Method | Parameters | Returns | Description | 78|--------|------------|---------|-------------| 79| `init` | `init_config` | `unit` | Initialize toplevel | 80| `setup` | `unit` | `exec_result` | Start toplevel | 81| `exec` | `string` | `exec_result` | Execute OCaml phrase | 82| `typecheck` | `string` | `exec_result` | Type check without execution | 83| `complete_prefix` | `id, deps, source, position` | `completions` | Autocomplete | 84| `query_errors` | `id, deps, source` | `error list` | Get compilation errors | 85| `type_enclosing` | `id, deps, source, position` | `typed_enclosings` | Type at position | 86 87### Type Definitions 88 89Key types from `idl/toplevel_api.ml`: 90 91```ocaml 92type exec_result = { 93 stdout : string option; 94 stderr : string option; 95 sharp_ppf : string option; (* # directive output *) 96 caml_ppf : string option; (* Regular output *) 97 highlight : highlight option; (* Error location *) 98 mime_vals : mime_val list; (* Rich output *) 99} 100 101type mime_val = { 102 mime_type : string; (* e.g., "text/html" *) 103 encoding : encoding; (* Noencoding | Base64 *) 104 data : string; 105} 106 107type init_config = { 108 findlib_requires : string list; (* Packages to preload *) 109 stdlib_dcs : string option; (* Dynamic CMIs URL *) 110 execute : bool; (* Allow execution? *) 111} 112``` 113 114## Core Implementation 115 116### Module Structure (`lib/impl.ml`) 117 118```ocaml 119module type S = sig 120 type findlib_t 121 val capture : (unit -> 'a) -> unit -> captured * 'a 122 val sync_get : string -> string option 123 val async_get : string -> (string, [`Msg of string]) result Lwt.t 124 val import_scripts : string list -> unit 125 val get_stdlib_dcs : string -> dynamic_cmis list 126 val findlib_init : string -> findlib_t Lwt.t 127 val require : bool -> findlib_t -> string list -> dynamic_cmis list 128 val path : string 129end 130 131module Make (S : S) : sig 132 val init : init_config -> unit Lwt.t 133 val setup : unit -> exec_result Lwt.t 134 val exec : string -> exec_result Lwt.t 135 val typecheck : string -> exec_result Lwt.t 136 (* ... *) 137end 138``` 139 140### Execution Flow 141 142``` 143exec(phrase) 144145146capture stdout/stderr 147148149parse phrase (Ocamltop.parse_toplevel) 150151152execute (Toploop.execute_phrase) 153154155collect MIME outputs 156157158return exec_result 159``` 160 161### Cell Dependency System 162 163Cells can depend on previous cells via module wrapping: 164 165```ocaml 166(* Cell "c1" defines: *) 167let x = 1 168 169(* Internally becomes module Cell__c1 *) 170 171(* Cell "c2" with deps=["c1"]: *) 172let y = x + 1 173 174(* Prepended with: open Cell__c1 *) 175``` 176 177The `mangle_toplevel` function handles this transformation. 178 179## Library Loading 180 181### findlibish.ml 182 183Custom findlib-like implementation for WebWorker context: 184 185``` 186 ┌─────────────────┐ 187 │ findlib_index.json │ (list of META URLs) 188 └────────┬────────┘ 189190 ┌──────────────┼──────────────┐ 191 ▼ ▼ ▼ 192 ┌─────────┐ ┌─────────┐ ┌─────────┐ 193 │ META │ │ META │ │ META │ 194 │ (pkg A) │ │ (pkg B) │ │ (pkg C) │ 195 └────┬────┘ └────┬────┘ └────┬────┘ 196 │ │ │ 197 ▼ ▼ ▼ 198 ┌─────────────────────────────────────┐ 199 │ Dependency Resolution │ 200 └─────────────────┬───────────────────┘ 201202 ┌────────────┼────────────┐ 203 ▼ ▼ ▼ 204 ┌──────────┐ ┌──────────┐ ┌──────────┐ 205 │ .cma.js │ │ .cma.js │ │ .cma.js │ 206 │ (import) │ │ (import) │ │ (import) │ 207 └──────────┘ └──────────┘ └──────────┘ 208``` 209 210### Package Loading Process 211 2121. Fetch `findlib_index.json` (list of META file URLs) 2132. Parse each META file with `Fl_metascanner` 2143. Build dependency graph 2154. On `#require`: 216 - Resolve dependencies 217 - Fetch `dynamic_cmis.json` for each package 218 - Load `.cma.js` via `import_scripts` 219 220### Preloaded Packages 221 222These are compiled into the worker and not loaded dynamically: 223 224- `compiler-libs.common`, `compiler-libs.toplevel` 225- `merlin-lib.*` 226- `js_of_ocaml-compiler`, `js_of_ocaml-toplevel` 227- `findlib`, `findlib.top` 228 229## Merlin Integration 230 231Code intelligence features use Merlin: 232 233| Feature | Merlin Query | Implementation | 234|---------|--------------|----------------| 235| Completion | `Query_protocol.Complete_prefix` | `complete_prefix` | 236| Type info | `Query_protocol.Type_enclosing` | `type_enclosing` | 237| Errors | `Query_protocol.Errors` | `query_errors` | 238 239Queries run through `Mpipeline` with source "mangled" to include cell dependencies. 240 241## Planned Architecture Changes 242 243### Phase 1: Communication Redesign 244 245Replace JSON-RPC with CBOR-based bidirectional channel: 246 247``` 248Current: Planned: 249┌─────────┐ JSON-RPC ┌─────────┐ CBOR 250│ Client │◄──────────────► │ Client │◄──────────────► 251│ │ request/response │ │ bidirectional 252└─────────┘ └─────────┘ 253 254 Message types: 255 - Request/Response (like RPC) 256 - Push (server → client) 257 - Widget events (bidirectional) 258``` 259 260### Phase 2: Environment Isolation 261 262Multiple isolated execution contexts: 263 264``` 265┌──────────────────────────────────────────┐ 266│ Web Worker │ 267│ │ 268│ ┌─────────────┐ ┌─────────────┐ │ 269│ │ Env "a" │ │ Env "b" │ │ 270│ │ │ │ │ │ 271│ │ Cell 1 │ │ Cell 1 │ │ 272│ │ Cell 2 │ │ Cell 2 │ │ 273│ │ (isolated) │ │ (isolated) │ │ 274│ └─────────────┘ └─────────────┘ │ 275│ │ 276│ Shared: stdlib, preloaded packages │ 277└──────────────────────────────────────────┘ 278``` 279 280### Phase 3: Rich Output & Widgets 281 282MIME-typed output with bidirectional widget communication: 283 284```ocaml 285(* User code *) 286let chart = Chart.bar [1; 2; 3; 4] in 287Display.show chart 288 289(* Generates *) 290{ 291 mime_type = "application/vnd.widget+json"; 292 data = {widget_id = "w1"; state = ...} 293} 294 295(* Frontend renders widget, sends events back *) 296Widget_event {widget_id = "w1"; event = Click {x; y}} 297``` 298 299## File Reference 300 301### Core Files 302 303| File | Lines | Purpose | 304|------|-------|---------| 305| `lib/impl.ml` | 985 | Main implementation (execute, typecheck, etc.) | 306| `lib/worker.ml` | 100 | WebWorker server setup | 307| `lib/findlibish.ml` | 221 | Package loading | 308| `idl/toplevel_api.ml` | 315 | RPC type definitions | 309| `idl/js_top_worker_client.ml` | 126 | Lwt client | 310 311### Build Outputs 312 313| File | Description | 314|------|-------------| 315| `worker.bc.js` | Compiled Web Worker | 316| `*.cma.js` | JavaScript-compiled OCaml libraries | 317| `dynamic_cmis.json` | CMI metadata for each package | 318 319## Dependencies 320 321### Runtime 322 323- `js_of_ocaml` >= 3.11.0 324- `js_of_ocaml-toplevel` 325- `js_of_ocaml-compiler` 326- `rpclib`, `rpclib-lwt` 327- `merlin-lib` 328- `compiler-libs` 329- `brr` >= 0.0.4 330 331### Planned Additions 332 333- `cbort` - CBOR codec (tangled.org) 334- `zarith_stubs_js` - JS stubs for zarith 335- `bytesrw` - Streaming I/O 336 337--- 338 339*Last updated: 2026-01-20*