Skip to content

feat(wasm-workbook): expose removeName on the JsWorkbook binding - #976

Merged
hhimanshu merged 1 commit into
mainfrom
feat/973-wasm-remove-name
Sep 1, 2026
Merged

feat(wasm-workbook): expose removeName on the JsWorkbook binding#976
hhimanshu merged 1 commit into
mainfrom
feat/973-wasm-remove-name

Conversation

@hhimanshu

@hhimanshu hhimanshu commented Sep 1, 2026

Copy link
Copy Markdown
Member

Workbook::remove_name (crates/workbook/src/mutate.rs) is public and has always worked. The wasm binding in crates/wasm-workbook/src/lib.rs exposed defineName and redefineName but not removeName — a JS consumer could define and redefine a named range but never remove one. Deleting a named range from a JS-facing document model left the engine still resolving the old name: =SUM(Total) kept returning the stale value after the range was deleted, only becoming #NAME? after a full rebuild forced a resync. A wrong value, silently, with no error.

What changed

JsWorkbook::remove_name, exposed as removeName(name), delegating straight to Workbook::remove_name.

Return-type note: defineName/redefineName return Result<(), JsError> because their Rust counterparts can fail (bad ref, dangling sheet, name collision, cap exceeded). remove_name returns Option<NamedRange> in Rust and cannot fail — removing an unknown name is an intentional no-op — so removeName returns nothing rather than a Result with a permanently-empty error arm, matching clear()'s existing silent-no-op convention instead. The generated .d.ts still types it removeName(name: string): void, the identical shape defineName/redefineName get (they're also void in TS despite being fallible — wasm-bindgen throws on Err rather than surfacing it in the return type).

This is a deliberate, not accidental, choice: if remove_name ever grows a failure mode, changing removeName's signature to return something is an ABI break for any caller that isn't already wrapping it in a try/catch. Flagging it here so it's a documented tradeoff rather than a surprise later.

Also updated the one line in crates/wasm-workbook/README.jsr.md that listed the named-range trio, to include removeName. The non-JSR crates/wasm-workbook/README.md documents defineName but never documented redefineName either — that's a pre-existing gap, not something this PR introduces, so it's left alone.

Test

crates/wasm-workbook/tests/wasm_surface.rs (new), gated #![cfg(target_arch = "wasm32")] and run via wasm-pack test --node crates/wasm-workbook, mirroring the existing crates/wasm/tests/wasm_surface.rs pattern (needed because a JsWorkbook method touching JsValue/JsError aborts the process when called natively outside a real wasm runtime — confirmed while writing this, see report). It defines a name, uses it in =SUM(Total), asserts it resolves to 30, calls removeName through the actual binding, recalcs, and asserts the formula now resolves to #NAME?.

  • Before: the test file fails to compile against the pre-fix binding (E0599: no method named 'remove_name' found for struct 'JsWorkbook') — a stronger signal than a runtime assertion failure, and independently reproduced during review.
  • After: passes — test remove_name_through_binding_invalidates_dependent_formula ... ok.

Also checked: the same asymmetry elsewhere

Enumerated every public mutator on Workbook (workbook.rs, mutate.rs, recalc.rs) against the wasm binding surface (crates/wasm-workbook/src/lib.rs). Bound: new, from_jsonfromJSON, to_jsontoJSON, add_sheetaddSheet, setset, set (date)→setDate, clearclear, define_namedefineName, redefine_nameredefineName, remove_nameremoveName (this PR), define_tabledefineTable, redefine_tableredefineTable, recalcrecalc.

Missing from the binding (not fixed here — listing only, per scope):

  • remove_table (mutate.rs) — the exact same trio gap this PR fixes, one level over: defineTable/redefineTable are bound, removeTable is not.
  • insert_sheet, remove_sheet, rename_sheet, move_sheet (workbook.rs) — only add_sheet of the five sheet-lifecycle mutators is bound.
  • recalc_incremental (recalc.rs) — only the full recalc is bound; incremental recalc (given an edited-cell list) has no JS-facing entry point.

Not counted as missing bindings (excluded deliberately): sheets_mut/names_mut/tables_mut are raw &mut Vec<T> escape hatches that bypass the validation (define/remove methods enforce caps, dangling-ref checks, collision checks) — binding them directly would be a regression, not a fix. drop_derived_state only invalidates the cache, no document content changes. trace_cell and seed_spill_sensitive_built_index take &self, not mutators despite living in mutate.rs/recalc.rs.

This PR adds only removeName, per the issue's scope.

Breaking change?

No — purely additive. removeName is a new method; nothing existing changes shape.

Verify

  • cargo build --locked --workspace --exclude truecalc-python — clean. (truecalc-python fails to link locally on this machine for an unrelated pre-existing reason — missing system libpython3.9 — not something this change touches; CI's cargo clippy --workspace -- -D warnings step builds the full workspace including it.)
  • cargo test --workspace --exclude truecalc-python — 4127 passed (112 suites).
  • cargo clippy --workspace --exclude truecalc-python -- -D warnings — no issues.
  • wasm-pack build crates/wasm-workbook --target web — succeeds; generated .d.ts shows removeName(name: string): void alongside defineName/redefineName.
  • wasm-pack test --node crates/wasm-workbook — new test passes.

closes #973


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

`Workbook::remove_name` has existed in Rust since named-range CRUD
landed, but the wasm binding only exposed `defineName`/`redefineName` —
a JS consumer could define and redefine a name but never remove one.
Deleting a named range from a JS-facing document model left the engine
still resolving the old name: a stale value with no error, until a
full rebuild forced a resync.

`removeName(name)` delegates to `Workbook::remove_name`, which returns
`Option<NamedRange>` and never fails (removing an unknown name is an
intentional no-op), so the binding returns nothing rather than forcing
an artificial `Result<(), JsError>` with a dead error arm — matching
`clear()`'s existing silent-no-op convention rather than
`defineName`/`redefineName`'s (which really can fail). The generated
`.d.ts` still types it `removeName(name: string): void`, identical in
shape to its two siblings.

Adds `crates/wasm-workbook/tests/wasm_surface.rs`, gated to `wasm32`
and run via `wasm-pack test --node`, mirroring `crates/wasm/tests/wasm_surface.rs`.
It defines a name, uses it in a formula, removes it through the actual
binding, recalcs, and asserts the formula now resolves to `#NAME?`
instead of the stale value. Confirmed the test does not compile
against the pre-fix binding (no such method) and passes after.

closes #973

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HAt4Keq1m4fEyHPmPjHSJ7
@hhimanshu hhimanshu self-assigned this Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Test Coverage by Category

Category Unit Tests Google Sheets Conformance Property Cases Total
Array 42 552/552 ✓ 1,000 (2×500) 1,594
Database 35 182/182 ✓ 3,500 (7×500) 3,717
Date 373 418/418 ✓ 2,500 (5×500) 3,291
Engineering 245 886/888 ⚠ 5,500 (11×500) 6,633
Filter 11 81/81 ✓ 4,500 (9×500) 4,592
Financial 149 1,208/1,208 ✓ 2,000 (4×500) 3,357
Info 0 256/256 ✓ 4,500 (9×500) 4,756
Logical 121 267/267 ✓ 3,500 (7×500) 3,888
Lookup 69 393/393 ✓ 1,000 (2×500) 1,462
Math 545 2,006/2,006 ✓ 8,000 (16×500) 10,551
Operator 87 251/251 ✓ 7,500 (15×500) 7,838
Parser 83 93/93 ✓ 4,000 (8×500) 4,176
Query 37 37
Statistical 529 3,191/3,191 ✓ 5,000 (10×500) 8,720
Text 327 803/804 ⚠ 4,000 (8×500) 5,131
Timezone 47 47
Volatile 0 3,500 (7×500) 3,500
Web 29 59/59 ✓ 6,000 (12×500) 6,088
Total 3,062 10,646/10,649 66,000 (132×500) ~79,711

✓ = 100% passing · ⚠ = known deviation · The ~79,711 total counts formula evaluations (each conformance row and each property case = 1). GitHub Checks reports 4,119 Rust test functions: 3,062 unit + 159 property functions (shown as cases above) + 898 conformance/integration.

@hhimanshu
hhimanshu merged commit 19c572f into main Sep 1, 2026
9 checks passed
@hhimanshu
hhimanshu deleted the feat/973-wasm-remove-name branch September 1, 2026 07:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wasm binding exposes defineName and redefineName but not removeName — the Rust method exists

1 participant