@fossil-lang/wasm 0.3.0-alpha.2 → 0.3.0-alpha.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,24 @@
6
6
  *
7
7
  * `Copy + Clone + Eq + Hash` so it can be a `HashMap` key on the Rust
8
8
  * side; `#[wasm_bindgen]` so JS can pass it back across the boundary.
9
+ *
10
+ * # Every exported method takes `&FileHandle`, and it has to
11
+ *
12
+ * wasm-bindgen CONSUMES an exported struct passed by value: the generated glue
13
+ * calls `__destroy_into_raw()` on the JS wrapper, which nulls its pointer. A
14
+ * handle passed by value is therefore good for exactly one call, and the second
15
+ * throws *"null pointer passed to rust"* — from inside a method that has nothing
16
+ * wrong with it, naming no handle and no file.
17
+ *
18
+ * `Copy` does not save it. The derive is a Rust-side property; nothing about it
19
+ * reaches the JS boundary, where this is a class holding a pointer like any
20
+ * other. The three methods a host calls repeatedly with one handle
21
+ * (`update_file`, `close_file`, `diagnostics_for`) take `&FileHandle` so
22
+ * wasm-bindgen borrows instead, and the handle stays live across the whole
23
+ * lifetime `open_file`'s doc comment promises.
24
+ *
25
+ * This was reachable from the FIRST loop an editor runs: `updateFile(h, text)` on
26
+ * every check, with the handle `openFile` returned once.
9
27
  */
10
28
  export class FileHandle {
11
29
  static __wrap(ptr) {
@@ -28,143 +46,238 @@ export class FileHandle {
28
46
  if (Symbol.dispose) FileHandle.prototype[Symbol.dispose] = FileHandle.prototype.free;
29
47
 
30
48
  /**
31
- * JS-facing handle for the Fossil compiler running inside a WASM module.
49
+ * The JS-facing workspace — `FossilWorkspace` on the JS side, and a
50
+ * `RefCell` around the Rust one.
51
+ *
52
+ * # Why the interior `RefCell`, and it is not a style choice
53
+ *
54
+ * `wasm-bindgen` wraps every exported struct in its own `WasmRefCell`. A
55
+ * method taking `&mut self` borrows that cell EXCLUSIVELY for the call, and a
56
+ * failed borrow does not return — it panics, with
57
+ * *"recursive use of an object detected which would lead to unsafe aliasing in
58
+ * rust"*. On `wasm32-unknown-unknown` a panic is an abort: the trap unwinds
59
+ * nothing, so the borrow flag it was holding is never cleared, and **every
60
+ * later call on that object fails the same way for the life of the module.**
61
+ * One bad call poisons the workspace permanently.
62
+ *
63
+ * That is a real defect and it was reachable from correct host code: an
64
+ * editor reproduced it by typing sixteen characters faster than its
65
+ * debounce, and had to carry a `busy` flag and a 120 ms coalesce to stay
66
+ * out of it. A mitigation a host has to remember is not a fix — and an editor
67
+ * is precisely the workload that forgets.
32
68
  *
33
- * One instance owns one [`WasmDb`] (Salsa store + injected [`WasmSystem`]
34
- * + `Arc<OutputDescriptorKind>`). Hosts construct a single playground per
35
- * browser tab / Node process and reuse it across all method calls to
36
- * amortise the Salsa interning + memoisation overhead.
69
+ * So no exported method takes `&mut self`. Every one takes `&self`, which
70
+ * wasm-bindgen borrows SHARED — shared borrows nest, so re-entry cannot fail
71
+ * there — and the mutation goes through this `RefCell` with
72
+ * `try_borrow_mut`. Genuine re-entry (a JS callback that calls back in while a
73
+ * call is live) now returns a catchable `Error` naming what happened, and
74
+ * **the workspace is still usable afterwards**, because a returned `Err` drops
75
+ * its guard where a panic did not.
76
+ *
77
+ * The Rust-side [`FossilWorkspace`] keeps its `&mut self` signatures
78
+ * untouched: the native tests drive it directly. Only the JS boundary changed.
37
79
  */
38
- export class FossilPlayground {
80
+ export class FossilWorkspace {
39
81
  __destroy_into_raw() {
40
82
  const ptr = this.__wbg_ptr;
41
83
  this.__wbg_ptr = 0;
42
- FossilPlaygroundFinalization.unregister(this);
84
+ FossilWorkspaceFinalization.unregister(this);
43
85
  return ptr;
44
86
  }
45
87
  free() {
46
88
  const ptr = this.__destroy_into_raw();
47
- wasm.__wbg_fossilplayground_free(ptr, 0);
89
+ wasm.__wbg_fossilworkspace_free(ptr, 0);
48
90
  }
49
91
  /**
50
- * Run `parse → def_map → typecheck_mapping` across every open file and
51
- * return a flat JS array of `{ uri, range, severity, message }` rows
52
- * keyed by file URI. The LSP Worker (07-03) republishes these grouped
53
- * by URI as `textDocument/publishDiagnostics` notifications.
92
+ * Every open **program**'s diagnostics as a flat JS array of
93
+ * [`CheckRow`]s keyed by file URI — the diagnostics panel's view. It is
94
+ * NOT the LSP wire's shape; see [`CheckRow`].
95
+ *
96
+ * A buffer the installed provider catalogue claims — a `.shex` being
97
+ * edited, a `.csv`, a `.parquet` — is an INPUT and is not parsed as fossil.
98
+ * [`fossil_ide::diagnostics()`] is where that is said and measured, for both
99
+ * hosts.
54
100
  *
55
101
  * `range` is the UTF-16 LSP range (via `fossil_ide::LineIndex` — the
56
- * rust-analyzer model from 06-05). `severity` is the LSP integer
57
- * constant (1 = error, 2 = warning, 3 = info). `message` carries any
58
- * `suggestion_source` as a `\nhelp: ...` suffix (mirroring `fossil-lsp`
59
- * `to_lsp_diagnostic` in 06-09).
102
+ * rust-analyzer model). `severity` is the LSP integer constant
103
+ * (1 = error, 2 = warning, 3 = info). `message` is the problem rendered
104
+ * and nothing else; `help` is its own field.
60
105
  *
61
106
  * # Errors
62
107
  *
63
- * Returns a JS error only if the result fails to serialize to `JsValue`.
108
+ * Returns a JS error if the workspace is busy, or if the result fails to
109
+ * serialize to `JsValue`.
64
110
  * @returns {any}
65
111
  */
66
112
  check() {
67
- const ret = wasm.fossilplayground_check(this.__wbg_ptr);
113
+ const ret = wasm.fossilworkspace_check(this.__wbg_ptr);
68
114
  if (ret[2]) {
69
115
  throw takeFromExternrefTable0(ret[1]);
70
116
  }
71
117
  return takeFromExternrefTable0(ret[0]);
72
118
  }
73
119
  /**
74
- * Return the stdlib classification manifest as a JS array of
75
- * `{ name, wasm_class }` objects (STDL-07).
120
+ * Close a file in the workspace. Idempotent in spirit but strict in
121
+ * signal: closing an unknown / already-closed handle is an error so
122
+ * JS-side bugs surface loudly (mirrors `ty_wasm`).
76
123
  *
77
- * `wasm_class` is the string `"pure_sql"` or `"native_udf_only"`. The
78
- * playground reads this once at startup to render `native_udf_only`
79
- * functions as disabled with a "native-only — unavailable in the browser"
80
- * tooltip (SC#1 playground half). `DuckDB`-WASM cannot register the Rust
81
- * UDFs those functions need (Pitfall 3), so the classification is the
82
- * authority on what is runnable in-browser.
124
+ * # Errors
83
125
  *
84
- * This is pure read-only data projected from the `&'static`-ready
85
- * `fossil_registry::FunctionRegistry` — no `DuckDB`, no native UDF code,
86
- * WASM-clean.
126
+ * Returns a JS error if `handle` was never opened or was already closed,
127
+ * or if the workspace is busy.
128
+ * @param {FileHandle} handle
129
+ */
130
+ close_file(handle) {
131
+ _assertClass(handle, FileHandle);
132
+ const ret = wasm.fossilworkspace_close_file(this.__wbg_ptr, handle.__wbg_ptr);
133
+ if (ret[1]) {
134
+ throw takeFromExternrefTable0(ret[0]);
135
+ }
136
+ }
137
+ /**
138
+ * The completion candidates at a position: `{ label, kind, detail, insert }` rows,
139
+ * already narrowed by the receiver — `str.` offers string members and no
140
+ * reader, a property key position offers the target shape's predicates and
141
+ * no catalogue row.
142
+ *
143
+ * `kind` is the LSP `CompletionItemKind` **by name** (`"function"`,
144
+ * `"field"`). See [`ide::CompletionRow`] for why a number does not cross
145
+ * this boundary.
87
146
  *
88
147
  * # Errors
89
148
  *
90
- * Returns a JS error only if the manifest fails to serialize to `JsValue`.
149
+ * Returns a JS error if the workspace is busy, or if the result fails to
150
+ * serialize to `JsValue`. An unknown handle is an empty array.
151
+ * @param {FileHandle} handle
152
+ * @param {number} line
153
+ * @param {number} character
91
154
  * @returns {any}
92
155
  */
93
- classification() {
94
- const ret = wasm.fossilplayground_classification(this.__wbg_ptr);
156
+ completions(handle, line, character) {
157
+ _assertClass(handle, FileHandle);
158
+ const ret = wasm.fossilworkspace_completions(this.__wbg_ptr, handle.__wbg_ptr, line, character);
95
159
  if (ret[2]) {
96
160
  throw takeFromExternrefTable0(ret[1]);
97
161
  }
98
162
  return takeFromExternrefTable0(ret[0]);
99
163
  }
100
164
  /**
101
- * Close a file in the workspace. Idempotent in spirit but strict in
102
- * signal: closing an unknown / already-closed handle is an error so
103
- * JS-side bugs surface loudly (mirrors `ty_wasm`).
165
+ * Per-file diagnostic drain: `check()` returns the workspace-wide flat
166
+ * array, this returns one file's rows, so a host can refresh one buffer's
167
+ * panel without partitioning the workspace array on the JS side. It is NOT
168
+ * the LSP wire's shape — that is [`fossil_ide::lsp_diagnostics`].
104
169
  *
105
170
  * # Errors
106
171
  *
107
- * Returns a JS error if `handle` was never opened or was already closed.
172
+ * Returns a JS error if `handle` is unknown, if the workspace is busy, or
173
+ * if serialization fails.
108
174
  * @param {FileHandle} handle
175
+ * @returns {any}
109
176
  */
110
- close_file(handle) {
177
+ diagnostics_for(handle) {
111
178
  _assertClass(handle, FileHandle);
112
- var ptr0 = handle.__destroy_into_raw();
113
- const ret = wasm.fossilplayground_close_file(this.__wbg_ptr, ptr0);
114
- if (ret[1]) {
115
- throw takeFromExternrefTable0(ret[0]);
179
+ const ret = wasm.fossilworkspace_diagnostics_for(this.__wbg_ptr, handle.__wbg_ptr);
180
+ if (ret[2]) {
181
+ throw takeFromExternrefTable0(ret[1]);
116
182
  }
183
+ return takeFromExternrefTable0(ret[0]);
117
184
  }
118
185
  /**
119
- * Per-file diagnostic drain — the B3 follow-up accessor the LSP Worker
120
- * (07-03) consumes for its per-file `publishDiagnostics` notifications.
121
- * `check()` returns the workspace-wide flat array; `diagnostics_for`
122
- * returns just one file's rows so 07-03 can dispatch one notification
123
- * per affected URI without partitioning the workspace array on the JS
124
- * side.
186
+ * Where the name under the cursor is defined: `{ uri, range }` rows, empty
187
+ * when nothing there has a definition.
188
+ *
189
+ * `uri` is the key the host opened the buffer under, verbatim — and two of
190
+ * the three positions goto-def recognises resolve into the shape document,
191
+ * so a host with one pane has to read it before moving a cursor.
125
192
  *
126
193
  * # Errors
127
194
  *
128
- * Returns a JS error if `handle` is unknown, or if serialization fails.
195
+ * Returns a JS error if the workspace is busy, or if the result fails to
196
+ * serialize to `JsValue`.
129
197
  * @param {FileHandle} handle
198
+ * @param {number} line
199
+ * @param {number} character
130
200
  * @returns {any}
131
201
  */
132
- diagnostics_for(handle) {
202
+ gotoDefinition(handle, line, character) {
133
203
  _assertClass(handle, FileHandle);
134
- var ptr0 = handle.__destroy_into_raw();
135
- const ret = wasm.fossilplayground_diagnostics_for(this.__wbg_ptr, ptr0);
204
+ const ret = wasm.fossilworkspace_gotoDefinition(this.__wbg_ptr, handle.__wbg_ptr, line, character);
136
205
  if (ret[2]) {
137
206
  throw takeFromExternrefTable0(ret[1]);
138
207
  }
139
208
  return takeFromExternrefTable0(ret[0]);
140
209
  }
141
210
  /**
142
- * Construct a new playground.
211
+ * What is under the cursor: `{ markdown, range }`, or `null` when the
212
+ * cursor is on whitespace, on an expression the checker inferred no type
213
+ * for, or outside any mapping.
143
214
  *
144
- * Installs `console_error_panic_hook` (idempotent — RESEARCH.md
145
- * Don't-Hand-Roll #8) so any panic inside compiler-core surfaces as a
146
- * `console.error` stack trace in the host (browser `DevTools` or Node).
215
+ * `line` / `character` are LSP — zero-based, and `character` in UTF-16
216
+ * code units, which is what a JS host counts in anyway. The `range` comes
217
+ * back in the same units.
218
+ *
219
+ * # Errors
220
+ *
221
+ * Returns a JS error if the workspace is busy, or if the result fails to
222
+ * serialize to `JsValue`. An unknown handle is `null`, not an error —
223
+ * see [`FossilWorkspace::hover_row`].
224
+ * @param {FileHandle} handle
225
+ * @param {number} line
226
+ * @param {number} character
227
+ * @returns {any}
228
+ */
229
+ hover(handle, line, character) {
230
+ _assertClass(handle, FileHandle);
231
+ const ret = wasm.fossilworkspace_hover(this.__wbg_ptr, handle.__wbg_ptr, line, character);
232
+ if (ret[2]) {
233
+ throw takeFromExternrefTable0(ret[1]);
234
+ }
235
+ return takeFromExternrefTable0(ret[0]);
236
+ }
237
+ /**
238
+ * The documents the file at `handle` names that nothing has registered:
239
+ * `{ key, locator }` rows, the locator expanded through the connection map.
240
+ *
241
+ * # Errors
242
+ *
243
+ * Returns a JS error if `handle` is unknown, if the workspace is busy, or
244
+ * if serialization fails.
245
+ * @param {FileHandle} handle
246
+ * @returns {any}
247
+ */
248
+ missingDocuments(handle) {
249
+ _assertClass(handle, FileHandle);
250
+ const ret = wasm.fossilworkspace_missingDocuments(this.__wbg_ptr, handle.__wbg_ptr);
251
+ if (ret[2]) {
252
+ throw takeFromExternrefTable0(ret[1]);
253
+ }
254
+ return takeFromExternrefTable0(ret[0]);
255
+ }
256
+ /**
257
+ * Construct a new workspace.
258
+ *
259
+ * Installs `console_error_panic_hook` (idempotent) so any panic inside
260
+ * compiler-core surfaces as a `console.error` stack trace in the host
261
+ * (browser `DevTools` or Node).
147
262
  */
148
263
  constructor() {
149
- const ret = wasm.fossilplayground_new();
264
+ const ret = wasm.fossilworkspace_new();
150
265
  this.__wbg_ptr = ret;
151
- FossilPlaygroundFinalization.register(this, this.__wbg_ptr, this);
266
+ FossilWorkspaceFinalization.register(this, this.__wbg_ptr, this);
152
267
  return this;
153
268
  }
154
269
  /**
155
270
  * Open a file in the workspace. Returns a [`FileHandle`] the JS side
156
- * keys subsequent `update_file` / `close_file` / `compile_file` calls
157
- * on. `path` is the URI / virtual path the diagnostics carry back to
158
- * the LSP client.
271
+ * keys subsequent `update_file` / `close_file` calls on. `path` is the
272
+ * URI / virtual path the diagnostics carry back to the LSP client.
159
273
  *
160
274
  * Mirrors `ty_wasm::Workspace::open_file` (Astral). Interns a fresh
161
275
  * [`fossil_base::SourceFile`] under the current Salsa revision.
162
276
  *
163
277
  * # Errors
164
278
  *
165
- * Returns a JS error only if the internal counter is exhausted
166
- * (`u32::MAX` files — would require a runaway loop in JS, not a normal
167
- * failure mode).
279
+ * Returns a JS error if the workspace is already inside another call —
280
+ * see the type-level note on [`WasmWorkspace`].
168
281
  * @param {string} path
169
282
  * @param {string} contents
170
283
  * @returns {FileHandle}
@@ -174,91 +287,116 @@ export class FossilPlayground {
174
287
  const len0 = WASM_VECTOR_LEN;
175
288
  const ptr1 = passStringToWasm0(contents, wasm.__wbindgen_malloc_command_export, wasm.__wbindgen_realloc_command_export);
176
289
  const len1 = WASM_VECTOR_LEN;
177
- const ret = wasm.fossilplayground_open_file(this.__wbg_ptr, ptr0, len0, ptr1, len1);
290
+ const ret = wasm.fossilworkspace_open_file(this.__wbg_ptr, ptr0, len0, ptr1, len1);
178
291
  if (ret[2]) {
179
292
  throw takeFromExternrefTable0(ret[1]);
180
293
  }
181
294
  return FileHandle.__wrap(ret[0]);
182
295
  }
296
+ /**
297
+ * Register a fetched document's `text` under the `key` `missingDocuments`
298
+ * reported.
299
+ *
300
+ * # Errors
301
+ *
302
+ * Returns a JS error if the workspace is busy.
303
+ * @param {string} key
304
+ * @param {string} text
305
+ */
306
+ registerDocument(key, text) {
307
+ const ptr0 = passStringToWasm0(key, wasm.__wbindgen_malloc_command_export, wasm.__wbindgen_realloc_command_export);
308
+ const len0 = WASM_VECTOR_LEN;
309
+ const ptr1 = passStringToWasm0(text, wasm.__wbindgen_malloc_command_export, wasm.__wbindgen_realloc_command_export);
310
+ const len1 = WASM_VECTOR_LEN;
311
+ const ret = wasm.fossilworkspace_registerDocument(this.__wbg_ptr, ptr0, len0, ptr1, len1);
312
+ if (ret[1]) {
313
+ throw takeFromExternrefTable0(ret[0]);
314
+ }
315
+ }
183
316
  /**
184
317
  * Register an [`fossil_descriptors_input::InferredDescriptor`] for a
185
- * source binding name BEFORE invoking [`Self::compile`] /
186
- * [`Self::compile_file`]. The Rust compiler reads from this registration
187
- * during forward type propagation (Phase 3 CORE-05 rewired in plan
188
- * 13-02).
189
- *
190
- * `descriptor_json` is the JSON serialisation of `InferredDescriptor`;
191
- * the canonical shape is exposed in `packages/wasm/src/index.ts` as
192
- * `InferredDescriptorJson`:
193
- *
194
- * ```json
195
- * {
196
- * "source_name": "users",
197
- * "columns": [
198
- * { "name": "id", "primitive": "Integer" },
199
- * { "name": "name", "primitive": "String" }
200
- * ],
201
- * "content_hash": ""
202
- * }
203
- * ```
204
- *
205
- * Called by the browser-side playground orchestration AFTER running
206
- * DuckDB-WASM `DESCRIBE read_csv_auto('<resolved-url>')` and BEFORE
207
- * invoking `compile()` / `compile_file()`. Keyed by source-binding
208
- * name (`"users"` for `users := io.csv("...")`), NOT by URL.
209
- *
210
- * Idempotent: re-registering with the same `source_name` OVERWRITES the
211
- * previous entry — intentional, since the host may re-introspect when
212
- * file content changes.
318
+ * source binding name BEFORE invoking `check`. The Rust compiler reads
319
+ * from this registration during forward type propagation — the browser has
320
+ * no filesystem to introspect a CSV from, so the column types must arrive
321
+ * from the host.
322
+ *
323
+ * Registering the same URI twice REPLACES the previous descriptor
324
+ * — intentional, since the host re-introspects when the source changes.
213
325
  *
214
326
  * # Errors
215
327
  *
216
328
  * - Malformed JSON / missing required fields → JS `Error` with the
217
329
  * underlying `serde_json` message.
218
- *
219
- * Implementation: thin shim over the pure-Rust
220
- * [`Self::register_inferred_descriptor_native`] helper.
330
+ * - The workspace is busy.
221
331
  * @param {string} descriptor_json
222
332
  */
223
333
  registerInferredDescriptor(descriptor_json) {
224
334
  const ptr0 = passStringToWasm0(descriptor_json, wasm.__wbindgen_malloc_command_export, wasm.__wbindgen_realloc_command_export);
225
335
  const len0 = WASM_VECTOR_LEN;
226
- const ret = wasm.fossilplayground_registerInferredDescriptor(this.__wbg_ptr, ptr0, len0);
336
+ const ret = wasm.fossilworkspace_registerInferredDescriptor(this.__wbg_ptr, ptr0, len0);
227
337
  if (ret[1]) {
228
338
  throw takeFromExternrefTable0(ret[0]);
229
339
  }
230
340
  }
231
341
  /**
232
- * Install a user-supplied `ShEx` schema as the active output descriptor.
233
- * On parse failure the previously-installed descriptor is RETAINED (no
234
- * half-applied state — a broken schema must never wedge the editor;
235
- * same contract as `fossil-lsp`'s `load_sibling_shex` in 06-09).
236
- *
237
- * The schema is parsed via
238
- * [`fossil_descriptors_output::ShExDescriptor::from_reader`] (no
239
- * network access — Pitfall 1) and wrapped in
240
- * [`OutputDescriptorKind::ShEx`]. The resulting `Arc` is swapped into
241
- * the [`WasmDb`]'s descriptor slot atomically; future `fossil-ide`
242
- * feature calls (hover, completion) reading
243
- * [`HirDb::output_descriptor_kind`] see the new schema.
342
+ * Every classified span of the file: `{ range, kind, modifiers }` rows in
343
+ * source order, `kind` and `modifiers` by legend NAME. An editor lays these
344
+ * over its lexical highlighting — see [`ide::SemanticTokenRow`].
244
345
  *
245
346
  * # Errors
246
347
  *
247
- * Returns a JS error if the text is not a parseable `ShEx` schema.
248
- * @param {string} text
348
+ * Returns a JS error if the workspace is busy, or if the result fails to
349
+ * serialize to `JsValue`. An unknown handle is an empty array.
350
+ * @param {FileHandle} handle
351
+ * @returns {any}
249
352
  */
250
- set_target_shex(text) {
251
- const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc_command_export, wasm.__wbindgen_realloc_command_export);
252
- const len0 = WASM_VECTOR_LEN;
253
- const ret = wasm.fossilplayground_set_target_shex(this.__wbg_ptr, ptr0, len0);
353
+ semanticTokens(handle) {
354
+ _assertClass(handle, FileHandle);
355
+ const ret = wasm.fossilworkspace_semanticTokens(this.__wbg_ptr, handle.__wbg_ptr);
356
+ if (ret[2]) {
357
+ throw takeFromExternrefTable0(ret[1]);
358
+ }
359
+ return takeFromExternrefTable0(ret[0]);
360
+ }
361
+ /**
362
+ * Replace the connection map `@name/…` expands against, as
363
+ * `Host.connections()` answers it.
364
+ *
365
+ * # Errors
366
+ *
367
+ * Returns a JS error if `connections` is not a string-to-string record, or
368
+ * if the workspace is busy.
369
+ * @param {any} connections
370
+ */
371
+ setConnections(connections) {
372
+ const ret = wasm.fossilworkspace_setConnections(this.__wbg_ptr, connections);
254
373
  if (ret[1]) {
255
374
  throw takeFromExternrefTable0(ret[0]);
256
375
  }
257
376
  }
377
+ /**
378
+ * The data sources the file at `handle` reads — see
379
+ * [`fossil_lineage::program_sources`].
380
+ *
381
+ * # Errors
382
+ *
383
+ * Returns a JS error if `handle` is unknown, if the workspace is busy, or
384
+ * if serialization fails.
385
+ * @param {FileHandle} handle
386
+ * @returns {any}
387
+ */
388
+ sources(handle) {
389
+ _assertClass(handle, FileHandle);
390
+ const ret = wasm.fossilworkspace_sources(this.__wbg_ptr, handle.__wbg_ptr);
391
+ if (ret[2]) {
392
+ throw takeFromExternrefTable0(ret[1]);
393
+ }
394
+ return takeFromExternrefTable0(ret[0]);
395
+ }
258
396
  /**
259
397
  * Apply an edit to an open file. Mutates the SAME `SourceFile` via the
260
398
  * Salsa [`salsa::Setter`] (`set_text`) — this BUMPS THE REVISION, the
261
- * real cancellation trigger (ADR-0022): any in-flight analysis from the
399
+ * real cancellation trigger: any in-flight analysis from the
262
400
  * previous keystroke observes the new revision at its next cooperative
263
401
  * checkpoint. NO new `SourceFile` is interned — the Salsa input
264
402
  * identity stays stable across the file's lifetime, so memoised
@@ -267,42 +405,59 @@ export class FossilPlayground {
267
405
  *
268
406
  * # Errors
269
407
  *
270
- * Returns a JS error if `handle` was never opened or was already closed.
408
+ * Returns a JS error if `handle` was never opened or was already closed,
409
+ * or if the workspace is already inside another call — see the type-level
410
+ * note on [`WasmWorkspace`]. **Neither leaves the workspace unusable**,
411
+ * which is the whole reason this method takes `&self`.
271
412
  * @param {FileHandle} handle
272
413
  * @param {string} contents
273
414
  */
274
415
  update_file(handle, contents) {
275
416
  _assertClass(handle, FileHandle);
276
- var ptr0 = handle.__destroy_into_raw();
277
- const ptr1 = passStringToWasm0(contents, wasm.__wbindgen_malloc_command_export, wasm.__wbindgen_realloc_command_export);
278
- const len1 = WASM_VECTOR_LEN;
279
- const ret = wasm.fossilplayground_update_file(this.__wbg_ptr, ptr0, ptr1, len1);
417
+ const ptr0 = passStringToWasm0(contents, wasm.__wbindgen_malloc_command_export, wasm.__wbindgen_realloc_command_export);
418
+ const len0 = WASM_VECTOR_LEN;
419
+ const ret = wasm.fossilworkspace_update_file(this.__wbg_ptr, handle.__wbg_ptr, ptr0, len0);
280
420
  if (ret[1]) {
281
421
  throw takeFromExternrefTable0(ret[0]);
282
422
  }
283
423
  }
284
424
  }
285
- if (Symbol.dispose) FossilPlayground.prototype[Symbol.dispose] = FossilPlayground.prototype.free;
425
+ if (Symbol.dispose) FossilWorkspace.prototype[Symbol.dispose] = FossilWorkspace.prototype.free;
286
426
 
287
427
  /**
288
- * `JS`-facing re-export of the Phase-6 06-07 semantic-token legend.
289
- *
290
- * Returns `{ tokenTypes: string[], tokenModifiers: string[] }` — the exact
291
- * LSP `SemanticTokensLegend` shape. `CodeMirror` (plan 08-08 +
292
- * `packages/codemirror-fossil/src/tags.ts`) uses this to translate
293
- * LSP `semanticTokens/full` response indices into highlight tag names.
294
- *
295
- * Delegates to [`fossil_ide::semantic_legend`] (the 06-07 authoritative
296
- * definition) — NO duplicate legend definition lives here.
428
+ * The providers this host installs (`io.csv`, `io.rdf`, `io.shex`, …) as a JS
429
+ * array of `{ name, extensions, kind }`. Pure projection of the provider
430
+ * registry — no parsing, no db. `kind` is read off each row's capabilities, so
431
+ * the two shape-document rows come back as `Schema`.
297
432
  *
298
433
  * # Errors
434
+ * Returns a JS error only if the result fails to serialize to `JsValue`.
435
+ * @returns {any}
436
+ */
437
+ export function providers() {
438
+ const ret = wasm.providers();
439
+ if (ret[2]) {
440
+ throw takeFromExternrefTable0(ret[1]);
441
+ }
442
+ return takeFromExternrefTable0(ret[0]);
443
+ }
444
+
445
+ /**
446
+ * Parse `program` and return its external references — every data URI +
447
+ * `schema =` argument, each tagged with its `@conn` alias (`null` for a direct
448
+ * path) and role (`Data` / `Schema`) — as a JS array of
449
+ * `{ connection, path, role }`. Parse-only; the host resolves `@conn` →
450
+ * `{base}/path` itself (its data-plane job, kept out of fossil's semantics).
299
451
  *
300
- * Returns a JS error only if `serde_wasm_bindgen` fails (impossible in
301
- * practice for the legend shape).
452
+ * # Errors
453
+ * Returns a JS error only if the result fails to serialize to `JsValue`.
454
+ * @param {string} program
302
455
  * @returns {any}
303
456
  */
304
- export function semantic_legend() {
305
- const ret = wasm.semantic_legend();
457
+ export function refs(program) {
458
+ const ptr0 = passStringToWasm0(program, wasm.__wbindgen_malloc_command_export, wasm.__wbindgen_realloc_command_export);
459
+ const len0 = WASM_VECTOR_LEN;
460
+ const ret = wasm.refs(ptr0, len0);
306
461
  if (ret[2]) {
307
462
  throw takeFromExternrefTable0(ret[1]);
308
463
  }
@@ -310,28 +465,37 @@ export function semantic_legend() {
310
465
  }
311
466
 
312
467
  /**
313
- * Worker entrypoint. Idempotent in spirit — calling twice replaces the
314
- * previous `onmessage` handler (the leaked Closure from the first call
315
- * stays alive but is no longer referenced by the global scope).
468
+ * The name of every [`TokenRow::kind`], indexed BY that kind.
469
+ *
470
+ * `token_kinds()[row.kind]` is the variant name — `"Comment"`, `"KwFrom"`,
471
+ * `"String"`. A host maps names to highlight categories and never writes a
472
+ * discriminant down, which is the only version of this contract that survives
473
+ * a reorder of the lexer.
316
474
  *
317
- * Must be called from a `DedicatedWorker` global scope; returns a JS error
318
- * otherwise (e.g. if called from the main thread).
475
+ * Semantic tokens make the same move one step further: `semanticTokens()`
476
+ * sends the legend's names themselves, so no index crosses at all.
477
+ *
478
+ * A kind past the end of the array is a variant appended by a newer compiler
479
+ * than the host was built against. That is BACKWARDS-COMPATIBLE by
480
+ * construction: the host sees `undefined` and styles the span as plain text.
319
481
  *
320
482
  * # Errors
321
483
  *
322
- * Returns a JS error when not running inside a `DedicatedWorker` scope.
484
+ * Returns a JS error only if `serde_wasm_bindgen` fails to serialise a
485
+ * `Vec<&str>` (impossible in practice).
486
+ * @returns {any}
323
487
  */
324
- export function start_lsp_worker() {
325
- const ret = wasm.start_lsp_worker();
326
- if (ret[1]) {
327
- throw takeFromExternrefTable0(ret[0]);
488
+ export function tokenKinds() {
489
+ const ret = wasm.tokenKinds();
490
+ if (ret[2]) {
491
+ throw takeFromExternrefTable0(ret[1]);
328
492
  }
493
+ return takeFromExternrefTable0(ret[0]);
329
494
  }
330
495
 
331
496
  /**
332
497
  * `JS`-facing tokenize. Returns the same row stream as [`tokenize_native`],
333
- * serialised via `serde_wasm_bindgen`. Consumed by
334
- * `@fossil-lang/codemirror-fossil`'s `StreamParser` (plan 08-08).
498
+ * serialised via `serde_wasm_bindgen`.
335
499
  *
336
500
  * # Errors
337
501
  *
@@ -358,19 +522,6 @@ function __wbg_get_imports() {
358
522
  const ret = Error(getStringFromWasm0(arg0, arg1));
359
523
  return ret;
360
524
  },
361
- __wbg_String_8564e559799eccda: function(arg0, arg1) {
362
- const ret = String(arg1);
363
- const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc_command_export, wasm.__wbindgen_realloc_command_export);
364
- const len1 = WASM_VECTOR_LEN;
365
- getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
366
- getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
367
- },
368
- __wbg___wbindgen_bigint_get_as_i64_3af6d4ca77193a4b: function(arg0, arg1) {
369
- const v = arg1;
370
- const ret = typeof(v) === 'bigint' ? v : undefined;
371
- getDataViewMemory0().setBigInt64(arg0 + 8 * 1, isLikeNone(ret) ? BigInt(0) : ret, true);
372
- getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);
373
- },
374
525
  __wbg___wbindgen_boolean_get_c3dd5c39f1b5a12b: function(arg0) {
375
526
  const v = arg0;
376
527
  const ret = typeof(v) === 'boolean' ? v : undefined;
@@ -383,14 +534,6 @@ function __wbg_get_imports() {
383
534
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
384
535
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
385
536
  },
386
- __wbg___wbindgen_in_2617fa76397620d3: function(arg0, arg1) {
387
- const ret = arg0 in arg1;
388
- return ret;
389
- },
390
- __wbg___wbindgen_is_bigint_d6a8167cac401b95: function(arg0) {
391
- const ret = typeof(arg0) === 'bigint';
392
- return ret;
393
- },
394
537
  __wbg___wbindgen_is_function_2f0fd7ceb86e64c5: function(arg0) {
395
538
  const ret = typeof(arg0) === 'function';
396
539
  return ret;
@@ -408,10 +551,6 @@ function __wbg_get_imports() {
408
551
  const ret = arg0 === undefined;
409
552
  return ret;
410
553
  },
411
- __wbg___wbindgen_jsval_eq_403eaa3610500a25: function(arg0, arg1) {
412
- const ret = arg0 === arg1;
413
- return ret;
414
- },
415
554
  __wbg___wbindgen_jsval_loose_eq_1978f1e77b4bce62: function(arg0, arg1) {
416
555
  const ret = arg0 == arg1;
417
556
  return ret;
@@ -437,10 +576,6 @@ function __wbg_get_imports() {
437
576
  const ret = arg0.call(arg1);
438
577
  return ret;
439
578
  }, arguments); },
440
- __wbg_data_4a14fad4c5f216c4: function(arg0) {
441
- const ret = arg0.data;
442
- return ret;
443
- },
444
579
  __wbg_done_b1afd6201ac045e0: function(arg0) {
445
580
  const ret = arg0.done;
446
581
  return ret;
@@ -460,6 +595,13 @@ function __wbg_get_imports() {
460
595
  wasm.__wbindgen_free_command_export(deferred0_0, deferred0_1, 1);
461
596
  }
462
597
  },
598
+ __wbg_getRandomValues_3f44b700395062e5: function() { return handleError(function (arg0, arg1) {
599
+ globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1));
600
+ }, arguments); },
601
+ __wbg_get_41476db20fef99a8: function() { return handleError(function (arg0, arg1) {
602
+ const ret = Reflect.get(arg0, arg1);
603
+ return ret;
604
+ }, arguments); },
463
605
  __wbg_get_652f640b3b0b6e3e: function(arg0, arg1) {
464
606
  const ret = arg0[arg1 >>> 0];
465
607
  return ret;
@@ -472,10 +614,6 @@ function __wbg_get_imports() {
472
614
  const ret = arg0[arg1 >>> 0];
473
615
  return ret;
474
616
  },
475
- __wbg_get_with_ref_key_6412cf3094599694: function(arg0, arg1) {
476
- const ret = arg0[arg1];
477
- return ret;
478
- },
479
617
  __wbg_instanceof_ArrayBuffer_eab9f28fbec23477: function(arg0) {
480
618
  let result;
481
619
  try {
@@ -486,20 +624,10 @@ function __wbg_get_imports() {
486
624
  const ret = result;
487
625
  return ret;
488
626
  },
489
- __wbg_instanceof_DedicatedWorkerGlobalScope_32441a4d2ca5f2c4: function(arg0) {
627
+ __wbg_instanceof_Error_5e21755e9d9cbee5: function(arg0) {
490
628
  let result;
491
629
  try {
492
- result = arg0 instanceof DedicatedWorkerGlobalScope;
493
- } catch (_) {
494
- result = false;
495
- }
496
- const ret = result;
497
- return ret;
498
- },
499
- __wbg_instanceof_Map_10d4edf60fcf9327: function(arg0) {
500
- let result;
501
- try {
502
- result = arg0 instanceof Map;
630
+ result = arg0 instanceof Error;
503
631
  } catch (_) {
504
632
  result = false;
505
633
  }
@@ -516,14 +644,6 @@ function __wbg_get_imports() {
516
644
  const ret = result;
517
645
  return ret;
518
646
  },
519
- __wbg_isArray_c6c6ef8308995bcf: function(arg0) {
520
- const ret = Array.isArray(arg0);
521
- return ret;
522
- },
523
- __wbg_isSafeInteger_3c56c421a5b4cce4: function(arg0) {
524
- const ret = Number.isSafeInteger(arg0);
525
- return ret;
526
- },
527
647
  __wbg_iterator_9d68985a1d096fc2: function() {
528
648
  const ret = Symbol.iterator;
529
649
  return ret;
@@ -536,6 +656,14 @@ function __wbg_get_imports() {
536
656
  const ret = arg0.length;
537
657
  return ret;
538
658
  },
659
+ __wbg_message_d5628ca19de920d3: function(arg0) {
660
+ const ret = arg0.message;
661
+ return ret;
662
+ },
663
+ __wbg_name_bf92195f4668ab6e: function(arg0) {
664
+ const ret = arg0.name;
665
+ return ret;
666
+ },
539
667
  __wbg_new_227d7c05414eb861: function() {
540
668
  const ret = new Error();
541
669
  return ret;
@@ -548,14 +676,14 @@ function __wbg_get_imports() {
548
676
  const ret = new Array();
549
677
  return ret;
550
678
  },
551
- __wbg_new_46ae4e4ff2a07a64: function() {
552
- const ret = new Map();
553
- return ret;
554
- },
555
679
  __wbg_new_8454eee672b2ba6e: function(arg0) {
556
680
  const ret = new Uint8Array(arg0);
557
681
  return ret;
558
682
  },
683
+ __wbg_new_c9ea13ea803a692e: function(arg0, arg1) {
684
+ const ret = new Error(getStringFromWasm0(arg0, arg1));
685
+ return ret;
686
+ },
559
687
  __wbg_next_261c3c48c6e309a5: function(arg0) {
560
688
  const ret = arg0.next;
561
689
  return ret;
@@ -564,24 +692,25 @@ function __wbg_get_imports() {
564
692
  const ret = arg0.next();
565
693
  return ret;
566
694
  }, arguments); },
567
- __wbg_postMessage_ead2ef5ee8c7a94e: function() { return handleError(function (arg0, arg1) {
568
- arg0.postMessage(arg1);
695
+ __wbg_parse_342d5616e14beccc: function() { return handleError(function (arg0, arg1) {
696
+ const ret = JSON.parse(getStringFromWasm0(arg0, arg1));
697
+ return ret;
569
698
  }, arguments); },
570
699
  __wbg_prototypesetcall_fd4050e806e1d519: function(arg0, arg1, arg2) {
571
700
  Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2);
572
701
  },
702
+ __wbg_set_5337f8ac82364a3f: function() { return handleError(function (arg0, arg1, arg2) {
703
+ const ret = Reflect.set(arg0, arg1, arg2);
704
+ return ret;
705
+ }, arguments); },
573
706
  __wbg_set_6be42768c690e380: function(arg0, arg1, arg2) {
574
707
  arg0[arg1] = arg2;
575
708
  },
576
- __wbg_set_82f7a370f604db70: function(arg0, arg1, arg2) {
577
- const ret = arg0.set(arg1, arg2);
578
- return ret;
579
- },
580
709
  __wbg_set_f614f6a0608d1d1d: function(arg0, arg1, arg2) {
581
710
  arg0[arg1 >>> 0] = arg2;
582
711
  },
583
- __wbg_set_onmessage_2686976b9bf47e87: function(arg0, arg1) {
584
- arg0.onmessage = arg1;
712
+ __wbg_set_name_76644e7964825fa3: function(arg0, arg1, arg2) {
713
+ arg0.name = getStringFromWasm0(arg1, arg2);
585
714
  },
586
715
  __wbg_stack_3b0d974bbf31e44f: function(arg0, arg1) {
587
716
  const ret = arg1.stack;
@@ -590,51 +719,20 @@ function __wbg_get_imports() {
590
719
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
591
720
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
592
721
  },
593
- __wbg_static_accessor_GLOBAL_THIS_1c7f1bd6c6941fdb: function() {
594
- const ret = typeof globalThis === 'undefined' ? null : globalThis;
595
- return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
596
- },
597
- __wbg_static_accessor_GLOBAL_e039bc914f83e74e: function() {
598
- const ret = typeof global === 'undefined' ? null : global;
599
- return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
600
- },
601
- __wbg_static_accessor_SELF_8bf8c48c28420ad5: function() {
602
- const ret = typeof self === 'undefined' ? null : self;
603
- return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
604
- },
605
- __wbg_static_accessor_WINDOW_6aeee9b51652ee0f: function() {
606
- const ret = typeof window === 'undefined' ? null : window;
607
- return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
608
- },
609
722
  __wbg_value_f852716acdeb3e82: function(arg0) {
610
723
  const ret = arg0.value;
611
724
  return ret;
612
725
  },
613
- __wbindgen_cast_0000000000000001: function(arg0, arg1) {
614
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 9, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
615
- const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h4393dedb0066ad40);
616
- return ret;
617
- },
618
- __wbindgen_cast_0000000000000002: function(arg0) {
726
+ __wbindgen_cast_0000000000000001: function(arg0) {
619
727
  // Cast intrinsic for `F64 -> Externref`.
620
728
  const ret = arg0;
621
729
  return ret;
622
730
  },
623
- __wbindgen_cast_0000000000000003: function(arg0) {
624
- // Cast intrinsic for `I64 -> Externref`.
625
- const ret = arg0;
626
- return ret;
627
- },
628
- __wbindgen_cast_0000000000000004: function(arg0, arg1) {
731
+ __wbindgen_cast_0000000000000002: function(arg0, arg1) {
629
732
  // Cast intrinsic for `Ref(String) -> Externref`.
630
733
  const ret = getStringFromWasm0(arg0, arg1);
631
734
  return ret;
632
735
  },
633
- __wbindgen_cast_0000000000000005: function(arg0) {
634
- // Cast intrinsic for `U64 -> Externref`.
635
- const ret = BigInt.asUintN(64, arg0);
636
- return ret;
637
- },
638
736
  __wbindgen_init_externref_table: function() {
639
737
  const table = wasm.__wbindgen_externrefs;
640
738
  const offset = table.grow(4);
@@ -651,16 +749,12 @@ function __wbg_get_imports() {
651
749
  };
652
750
  }
653
751
 
654
- function wasm_bindgen__convert__closures_____invoke__h4393dedb0066ad40(arg0, arg1, arg2) {
655
- wasm.wasm_bindgen__convert__closures_____invoke__h4393dedb0066ad40(arg0, arg1, arg2);
656
- }
657
-
658
752
  const FileHandleFinalization = (typeof FinalizationRegistry === 'undefined')
659
753
  ? { register: () => {}, unregister: () => {} }
660
754
  : new FinalizationRegistry(ptr => wasm.__wbg_filehandle_free(ptr, 1));
661
- const FossilPlaygroundFinalization = (typeof FinalizationRegistry === 'undefined')
755
+ const FossilWorkspaceFinalization = (typeof FinalizationRegistry === 'undefined')
662
756
  ? { register: () => {}, unregister: () => {} }
663
- : new FinalizationRegistry(ptr => wasm.__wbg_fossilplayground_free(ptr, 1));
757
+ : new FinalizationRegistry(ptr => wasm.__wbg_fossilworkspace_free(ptr, 1));
664
758
 
665
759
  function addToExternrefTable0(obj) {
666
760
  const idx = wasm.__externref_table_alloc_command_export();
@@ -674,10 +768,6 @@ function _assertClass(instance, klass) {
674
768
  }
675
769
  }
676
770
 
677
- const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined')
678
- ? { register: () => {}, unregister: () => {} }
679
- : new FinalizationRegistry(state => wasm.__wbindgen_destroy_closure_command_export(state.a, state.b));
680
-
681
771
  function debugString(val) {
682
772
  // primitive types
683
773
  const type = typeof val;
@@ -781,34 +871,6 @@ function isLikeNone(x) {
781
871
  return x === undefined || x === null;
782
872
  }
783
873
 
784
- function makeMutClosure(arg0, arg1, f) {
785
- const state = { a: arg0, b: arg1, cnt: 1 };
786
- const real = (...args) => {
787
-
788
- // First up with a closure we increment the internal reference
789
- // count. This ensures that the Rust closure environment won't
790
- // be deallocated while we're invoking it.
791
- state.cnt++;
792
- const a = state.a;
793
- state.a = 0;
794
- try {
795
- return f(a, state.b, ...args);
796
- } finally {
797
- state.a = a;
798
- real._wbg_cb_unref();
799
- }
800
- };
801
- real._wbg_cb_unref = () => {
802
- if (--state.cnt === 0) {
803
- wasm.__wbindgen_destroy_closure_command_export(state.a, state.b);
804
- state.a = 0;
805
- CLOSURE_DTORS.unregister(state);
806
- }
807
- };
808
- CLOSURE_DTORS.register(real, state, state);
809
- return real;
810
- }
811
-
812
874
  function passStringToWasm0(arg, malloc, realloc) {
813
875
  if (realloc === undefined) {
814
876
  const buf = cachedTextEncoder.encode(arg);