refactor(packages): extract hashline-core package

Move the hash-anchored edit core (hash computation, validation, edit operations, text normalization, chunk formatter, diff utilities, and a runtime-aware xxHash32 binding) into a new @oh-my-opencode/hashline-core workspace package.

The src/tools/hashline-edit/ surface becomes a set of thin re-export shims, so existing import paths in the plugin keep working while the pure logic lives behind a stable package boundary that has no opencode runtime dependencies.

Tests: bun test packages/hashline-core src/tools/hashline-edit
This commit is contained in:
YeonGyu-Kim
2026-05-21 16:02:09 +09:00
parent f29411a689
commit 4ea76365cd
42 changed files with 2561 additions and 1260 deletions
@@ -0,0 +1,62 @@
import { describe, expect, it } from "bun:test"
import { normalizeHashlineEdits, type RawHashlineEdit } from "./normalize-edits"
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
describe("normalizeHashlineEdits", () => {
it("maps replace with pos to replace", () => {
//#given
const input: RawHashlineEdit[] = [{ op: "replace", pos: "2#VK", lines: "updated" }]
//#when
const result = normalizeHashlineEdits(input)
//#then
expect(result).toEqual([{ op: "replace", pos: "2#VK", lines: "updated" }])
})
it("maps replace with pos and end to replace", () => {
//#given
const input: RawHashlineEdit[] = [{ op: "replace", pos: "2#VK", end: "4#MB", lines: ["a", "b"] }]
//#when
const result = normalizeHashlineEdits(input)
//#then
expect(result).toEqual([{ op: "replace", pos: "2#VK", end: "4#MB", lines: ["a", "b"] }])
})
it("maps anchored append and prepend preserving op", () => {
//#given
const input: RawHashlineEdit[] = [
{ op: "append", pos: "2#VK", lines: ["after"] },
{ op: "prepend", pos: "4#MB", lines: ["before"] },
]
//#when
const result = normalizeHashlineEdits(input)
//#then
expect(result).toEqual([{ op: "append", pos: "2#VK", lines: ["after"] }, { op: "prepend", pos: "4#MB", lines: ["before"] }])
})
it("prefers pos over end for prepend anchors", () => {
//#given
const input: RawHashlineEdit[] = [{ op: "prepend", pos: "3#AA", end: "7#BB", lines: ["before"] }]
//#when
const result = normalizeHashlineEdits(input)
//#then
expect(result).toEqual([{ op: "prepend", pos: "3#AA", lines: ["before"] }])
})
it("rejects legacy payload without op", () => {
//#given
const input = unsafeTestValue<Parameters<
typeof normalizeHashlineEdits
>[0]>([{ type: "set_line", line: "2#VK", text: "updated" }])
//#when / #then
expect(() => normalizeHashlineEdits(input)).toThrow(/legacy format was removed/i)
})
})