feat(sdk): can() returns CanResult; T4 dispatch-boundary tests (#1426)

* feat(sdk): can() returns CanResult; T4 dispatch-boundary tests

* fix(sdk): 8 code-review correctness fixes

- setGsapScript: remove element when newScript="" (fixes undo/redo duplicate-script bug)
- parseDeclarations: track quotes so ; inside CSS values (data URIs) doesn't split
- handleRemoveGsapKeyframe: guard against duplicate-percentage ambiguity (return EMPTY)
- resolveKeyframe: return kfs so callers can check uniqueness
- handleSetClassStyle: emit op:"add" (not "replace") when no prior <style> element
- FsAdapter listVersions: Number(f.split("_")[0]) — was NaN due to underscore in key
- FsAdapter doWrite: split try/catch so appendVersion failure doesn't fire error handlers
- FileAdapter playground: add content:"" field to satisfy PersistVersionEntry contract

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(sdk): export CanResult from package root so callers can switch on result.code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
This commit is contained in:
Vance Ingalls
2026-06-15 02:02:37 -07:00
committed by GitHub
co-authored by Claude Sonnet 4.6 Miguel Ángel
parent 0a30011abd
commit 5ecaac1fcb
12 changed files with 410 additions and 113 deletions
+15 -18
View File
@@ -35,18 +35,18 @@ function getStyleText(parsed: ReturnType<typeof parseMutable>): string {
// ─── validateOp ───────────────────────────────────────────────────────────────
describe("validateOp setClassStyle", () => {
it("returns true (always valid — creates <style> if absent)", () => {
it("returns ok:true (always valid — creates <style> if absent)", () => {
expect(
validateOp(fresh(), { type: "setClassStyle", selector: ".box", styles: { opacity: "1" } }),
validateOp(fresh(), { type: "setClassStyle", selector: ".box", styles: { opacity: "1" } }).ok,
).toBe(true);
});
it("returns true even when no <style> element present", () => {
it("returns ok:true even when no <style> element present", () => {
const noStyle = parseMutable(
`<div data-hf-id="hf-stage" data-hf-root><div data-hf-id="hf-box"></div></div>`,
);
expect(
validateOp(noStyle, { type: "setClassStyle", selector: ".box", styles: { opacity: "1" } }),
validateOp(noStyle, { type: "setClassStyle", selector: ".box", styles: { opacity: "1" } }).ok,
).toBe(true);
});
});
@@ -54,6 +54,15 @@ describe("validateOp setClassStyle", () => {
// ─── setClassStyle: update existing rule ──────────────────────────────────────
describe("setClassStyle — update existing rule", () => {
function applyBoxOpacity1() {
const result = applyOp(fresh(), {
type: "setClassStyle",
selector: ".box",
styles: { opacity: "1" },
});
return String(result.forward[0]?.value ?? "");
}
it("adds a new property to an existing rule", () => {
const parsed = fresh();
const result = applyOp(parsed, {
@@ -69,13 +78,7 @@ describe("setClassStyle — update existing rule", () => {
});
it("overwrites an existing property value", () => {
const parsed = fresh();
const result = applyOp(parsed, {
type: "setClassStyle",
selector: ".box",
styles: { opacity: "1" },
});
const newCss = String(result.forward[0]?.value ?? "");
const newCss = applyBoxOpacity1();
expect(newCss).toContain("opacity: 1");
expect(newCss).not.toContain("opacity: 0");
expect(newCss).toContain("translateX(-50px)");
@@ -94,13 +97,7 @@ describe("setClassStyle — update existing rule", () => {
});
it("leaves other rules untouched", () => {
const parsed = fresh();
const result = applyOp(parsed, {
type: "setClassStyle",
selector: ".box",
styles: { opacity: "1" },
});
const newCss = String(result.forward[0]?.value ?? "");
const newCss = applyBoxOpacity1();
expect(newCss).toContain(".title");
expect(newCss).toContain("color: #fff");
});