Skip to content

T-820W-yaml-splice-wasm-ts-binding

Status: closed/done · Impact: high · Complexity: large

Expose the pure yaml-splice Rust crate to TypeScript over WebAssembly, per D-W4SM-yaml-splice-wasm-typescript-binding, so the SDLC frontmatter writers (and later markdown vault tooling) get byte-identical edit_yaml semantics from one implementation. A spike proved this is low-difficulty; this task lands it in the tree: a wrapper crate, a typed TS package, a shared conformance corpus, and a drift gate. Unblocks the whole-block writer migrations in M-2VYE-yaml-splice-extraction-and-writer-migrations.

LocationRole today
packages/rust/yaml-splice/the pure Rust crate — edit_yaml, FieldEdit, EditOutcome, typed EditError. Compiles to wasm32-unknown-unknown unchanged. No TS surface.
packages/rust/yaml-splice/tests/conformance.rsthe 37-case §8 corpus, inline in Rust — not reachable from another language
packages/ts/TS workspace packages (intersect, agent-plugin, easy-git) consumed via workspace:*; no yaml-splice package
apps/sdlc/lib/model/ops/_update.ts_update.ts-class whole-block writer that reserializes via dumpFrontmatter — the eventual consumer, cannot call Rust today

Three new pieces plus a gate: packages/rust/yaml-splice-wasm (a cdylib wasm-bindgen wrapper), packages/ts/yaml-splice (@sksizer/yaml-splice — committed wasm artifact + typed façade), a language-neutral JSON conformance corpus both Rust and TS run, and a CI conformance gate that runs the corpus against the committed artifact. import { editYaml } from '@sksizer/yaml-splice' returns a plain { bytes, changed } or throws a typed EditError, byte-identical to the Rust crate.

  1. Wrapper crate. Create packages/rust/yaml-splice-wasm (crate-type = ["cdylib"], yaml-splice = { workspace = true }, pinned wasm-bindgen). Expose editYaml(source: &[u8], edits_json: &str) -> Result<EditResult, JsError>; parse the edits JSON into Vec<FieldEdit> through the crate’s From<&serde_json::Value> value bridge; map EditError to a thrown Error carrying name (variant) + span. (Reference the proven spike wrapper — ~50 LoC.)
  2. Build pipeline. A committed build script drives cargo build --release --target wasm32-unknown-unknown → the pinned wasm-bindgen CLI (nodejs target) → wasm-opt -Oz, emitting the glue + .wasm into packages/ts/yaml-splice. Pin the wasm-bindgen crate and CLI to the same version.
  3. TS package. packages/ts/yaml-splice (@sksizer/yaml-splice, workspace:*) with a hand-written index.ts façade: typed FieldEdit union / EditOutcome / EditError; read {bytes, changed} into a plain object and free() the bindgen handle; stringify edits internally. nodejs (sync) default entry.
  4. Shared corpus. Move the §8 fixtures to {source, edits, expected_bytes | expected_error} JSON under a shared location; point yaml-splice’s conformance.rs at it and add a TS test that runs the same fixtures against the wasm façade.
  5. Conformance gate. A CI check that runs the shared corpus against the committed artifact (bun test packages/ts/yaml-splice) and confirms the wrapper compiles to wasm32 — behavioral, not a cross-OS byte-diff (wasm runs identically everywhere but does not build byte-identically across hosts).
LocationKindChange
packages/rust/yaml-splice-wasm/Cargo.tomlnewcdylib; deps yaml-splice, pinned wasm-bindgen, serde_json
packages/rust/yaml-splice-wasm/src/lib.rsnewthe wasm-bindgen editYaml wrapper + error mapping
packages/rust/yaml-splice-wasm/build.shnewcargo → wasm-bindgen (pinned) → wasm-opt into the TS package
packages/ts/yaml-splice/package.jsonnew@sksizer/yaml-splice, bun/exports, nodejs entry
packages/ts/yaml-splice/index.tsnewtyped façade over the raw bindgen
packages/ts/yaml-splice/wasm/newcommitted generated glue + .wasm
packages/ts/yaml-splice/index.test.tsnewTS run of the shared conformance corpus
packages/rust/yaml-splice/tests/fixtures/conformance.jsonnewlanguage-neutral corpus
packages/rust/yaml-splice/tests/conformance.rsmodifyload the shared JSON fixtures
Cargo.tomlmodify(auto via packages/rust/* glob; add wasm-bindgen workspace dep if needed)
.github/workflows/newdrift gate: rebuild the artifact and diff
  • AC-1: packages/rust/yaml-splice-wasm builds to wasm32-unknown-unknown, and the pinned wasm-bindgen + wasm-opt pipeline regenerates the committed packages/ts/yaml-splice/wasm/ artifact deterministically.
  • AC-2: import { editYaml } from '@sksizer/yaml-splice' in a bun/node test does a value change that preserves quoting byte-for-byte, and a semantic no-op that returns byte-identical input with changed === false.
  • AC-3: an EditError from the Rust core surfaces as a typed TS error (name = variant, span); the façade exposes a typed FieldEdit union and never leaks the raw bindgen class (handle freed).
  • AC-4: the conformance corpus is language-neutral JSON; cargo test -p yaml-splice and the TS package test both load and pass the same fixtures (byte + changed + error-code parity).
  • AC-5: a CI conformance gate runs the shared §8 corpus against the committed wasm artifact and confirms the wrapper source compiles to wasm32 — behavioral, not a cross-OS byte-diff (wasm runs identically everywhere but does not build byte-identically across hosts).
  • AC-6: yaml-splice’s own three-dependency purity is unchanged — wasm-bindgen lives only in yaml-splice-wasm, not the core crate.
  • A time-boxed WASM spike (2026-07-21) proved the binding low-difficulty: compiles to wasm32 unchanged, ~50-line wrapper, both round-trips pass, ~271 KB optimized. This task productionizes that spike per D-W4SM-yaml-splice-wasm-typescript-binding.

← Back to Tasks