diff --git a/src/agents/pi-embedded-runner/stream-resolution.test.ts b/src/agents/pi-embedded-runner/stream-resolution.test.ts index 6d5ad204dc4..607c01de742 100644 --- a/src/agents/pi-embedded-runner/stream-resolution.test.ts +++ b/src/agents/pi-embedded-runner/stream-resolution.test.ts @@ -149,12 +149,14 @@ describe("resolveEmbeddedAgentStreamFn", () => { it("routes PI native OpenAI-compatible provider streams through boundary-aware transports", async () => { const nativeStreamFn = getApiProvider("openai-completions")?.streamSimple; - expect(nativeStreamFn).toBeDefined(); + if (!nativeStreamFn) { + throw new Error("expected native OpenAI-compatible stream function"); + } const innerStreamFn = vi.fn(async (_model, _context, options) => options); overrideBoundaryAwareStreamFnOnce(innerStreamFn as never); const streamFn = resolveEmbeddedAgentStreamFn({ - currentStreamFn: nativeStreamFn as StreamFn, + currentStreamFn: nativeStreamFn, shouldUseWebSocketTransport: false, sessionId: "session-1", model: { diff --git a/src/oc-path/tests/parse.test.ts b/src/oc-path/tests/parse.test.ts index 10196c3d6e5..0fa4f9754ba 100644 --- a/src/oc-path/tests/parse.test.ts +++ b/src/oc-path/tests/parse.test.ts @@ -1,8 +1,8 @@ -import { describe, expect, it } from 'vitest'; -import { parseMd } from '../parse.js'; +import { describe, expect, it } from "vitest"; +import { parseMd } from "../parse.js"; -describe('parseMd — frontmatter', () => { - it('parses simple frontmatter', () => { +describe("parseMd — frontmatter", () => { + it("parses simple frontmatter", () => { const raw = `--- name: github description: gh CLI for issues, PRs, runs @@ -13,20 +13,20 @@ Body text. const { ast, diagnostics } = parseMd(raw); expect(diagnostics).toEqual([]); expect(ast.frontmatter).toEqual([ - { key: 'name', value: 'github', line: 2 }, - { key: 'description', value: 'gh CLI for issues, PRs, runs', line: 3 }, + { key: "name", value: "github", line: 2 }, + { key: "description", value: "gh CLI for issues, PRs, runs", line: 3 }, ]); }); - it('handles no frontmatter', () => { + it("handles no frontmatter", () => { const raw = `## First section\n\nContent.\n`; const { ast } = parseMd(raw); expect(ast.frontmatter).toEqual([]); - expect(ast.preamble).toBe(''); + expect(ast.preamble).toBe(""); expect(ast.blocks.length).toBe(1); }); - it('emits diagnostic for unclosed frontmatter', () => { + it("emits diagnostic for unclosed frontmatter", () => { const raw = `--- name: github description: never closes @@ -35,24 +35,24 @@ Body. `; const { diagnostics } = parseMd(raw); expect(diagnostics).toContainEqual( - expect.objectContaining({ code: 'OC_FRONTMATTER_UNCLOSED' }), + expect.objectContaining({ code: "OC_FRONTMATTER_UNCLOSED" }), ); }); - it('strips quotes from values', () => { + it("strips quotes from values", () => { const raw = `--- title: "Hello world" hint: 'quoted' --- `; const { ast } = parseMd(raw); - expect(ast.frontmatter[0]?.value).toBe('Hello world'); - expect(ast.frontmatter[1]?.value).toBe('quoted'); + expect(ast.frontmatter[0]?.value).toBe("Hello world"); + expect(ast.frontmatter[1]?.value).toBe("quoted"); }); }); -describe('parseMd — H2 blocks', () => { - it('splits sections', () => { +describe("parseMd — H2 blocks", () => { + it("splits sections", () => { const raw = `Preamble text. ## First @@ -64,14 +64,14 @@ Body of first. Body of second. `; const { ast } = parseMd(raw); - expect(ast.preamble.trim()).toBe('Preamble text.'); + expect(ast.preamble.trim()).toBe("Preamble text."); expect(ast.blocks.length).toBe(2); - expect(ast.blocks[0]?.heading).toBe('First'); - expect(ast.blocks[0]?.slug).toBe('first'); - expect(ast.blocks[1]?.heading).toBe('Second'); + expect(ast.blocks[0]?.heading).toBe("First"); + expect(ast.blocks[0]?.slug).toBe("first"); + expect(ast.blocks[1]?.heading).toBe("Second"); }); - it('preserves line numbers (1-based)', () => { + it("preserves line numbers (1-based)", () => { const raw = `Line 1 ## Heading at line 2 Line 3 @@ -80,7 +80,7 @@ Line 3 expect(ast.blocks[0]?.line).toBe(2); }); - it('does NOT split on `## ` inside fenced code blocks', () => { + it("does NOT split on `## ` inside fenced code blocks", () => { const raw = `## Real section \`\`\`md @@ -91,12 +91,12 @@ content ## Another section `; const { ast } = parseMd(raw); - expect(ast.blocks.map((b) => b.heading)).toEqual(['Real section', 'Another section']); + expect(ast.blocks.map((b) => b.heading)).toEqual(["Real section", "Another section"]); }); }); -describe('parseMd — items', () => { - it('extracts plain bullet items', () => { +describe("parseMd — items", () => { + it("extracts plain bullet items", () => { const raw = `## Boundaries - never write to /etc @@ -104,23 +104,23 @@ describe('parseMd — items', () => { `; const { ast } = parseMd(raw); expect(ast.blocks[0]?.items.length).toBe(2); - expect(ast.blocks[0]?.items[0]?.text).toBe('never write to /etc'); + expect(ast.blocks[0]?.items[0]?.text).toBe("never write to /etc"); expect(ast.blocks[0]?.items[0]?.kv).toBeUndefined(); }); - it('extracts kv items', () => { + it("extracts kv items", () => { const raw = `## Tools - gh: GitHub CLI - curl: HTTP client `; const { ast } = parseMd(raw); - expect(ast.blocks[0]?.items[0]?.kv).toEqual({ key: 'gh', value: 'GitHub CLI' }); - expect(ast.blocks[0]?.items[0]?.slug).toBe('gh'); - expect(ast.blocks[0]?.items[1]?.kv).toEqual({ key: 'curl', value: 'HTTP client' }); + expect(ast.blocks[0]?.items[0]?.kv).toEqual({ key: "gh", value: "GitHub CLI" }); + expect(ast.blocks[0]?.items[0]?.slug).toBe("gh"); + expect(ast.blocks[0]?.items[1]?.kv).toEqual({ key: "curl", value: "HTTP client" }); }); - it('does NOT extract bullets inside fenced code', () => { + it("does NOT extract bullets inside fenced code", () => { const raw = `## Section \`\`\` @@ -131,12 +131,12 @@ describe('parseMd — items', () => { `; const { ast } = parseMd(raw); expect(ast.blocks[0]?.items.length).toBe(1); - expect(ast.blocks[0]?.items[0]?.text).toBe('real bullet'); + expect(ast.blocks[0]?.items[0]?.text).toBe("real bullet"); }); }); -describe('parseMd — tables', () => { - it('extracts a simple table', () => { +describe("parseMd — tables", () => { + it("extracts a simple table", () => { const raw = `## Tool Guidance | tool | guidance | @@ -146,15 +146,17 @@ describe('parseMd — tables', () => { `; const { ast } = parseMd(raw); const table = ast.blocks[0]?.tables[0]; - expect(table).toBeDefined(); - expect(table?.headers).toEqual(['tool', 'guidance']); - expect(table?.rows.length).toBe(2); - expect(table?.rows[0]).toEqual(['gh', 'use for GitHub']); + if (!table) { + throw new Error("expected parsed markdown table"); + } + expect(table.headers).toEqual(["tool", "guidance"]); + expect(table.rows.length).toBe(2); + expect(table.rows[0]).toEqual(["gh", "use for GitHub"]); }); }); -describe('parseMd — code blocks', () => { - it('extracts a fenced code block', () => { +describe("parseMd — code blocks", () => { + it("extracts a fenced code block", () => { const raw = `## Examples \`\`\`ts @@ -163,12 +165,12 @@ const x = 1; `; const { ast } = parseMd(raw); expect(ast.blocks[0]?.codeBlocks[0]).toMatchObject({ - lang: 'ts', - text: 'const x = 1;', + lang: "ts", + text: "const x = 1;", }); }); - it('handles unlanguaged fences', () => { + it("handles unlanguaged fences", () => { const raw = `## Block \`\`\` @@ -180,24 +182,24 @@ plain text }); }); -describe('parseMd — byte-fidelity', () => { - it('preserves raw on the AST', () => { +describe("parseMd — byte-fidelity", () => { + it("preserves raw on the AST", () => { const raw = `---\nname: x\n---\n\n## Sec\n\n- a\n- b\n`; const { ast } = parseMd(raw); expect(ast.raw).toBe(raw); }); - it('preserves BOM in raw but ignores it for parsing', () => { - const raw = '## Heading\n'; + it("preserves BOM in raw but ignores it for parsing", () => { + const raw = "## Heading\n"; const { ast } = parseMd(raw); expect(ast.raw).toBe(raw); - expect(ast.blocks[0]?.heading).toBe('Heading'); + expect(ast.blocks[0]?.heading).toBe("Heading"); }); - it('handles CRLF line endings', () => { - const raw = '## Heading\r\n\r\n- item\r\n'; + it("handles CRLF line endings", () => { + const raw = "## Heading\r\n\r\n- item\r\n"; const { ast } = parseMd(raw); - expect(ast.blocks[0]?.heading).toBe('Heading'); - expect(ast.blocks[0]?.items[0]?.text).toBe('item'); + expect(ast.blocks[0]?.heading).toBe("Heading"); + expect(ast.blocks[0]?.items[0]?.text).toBe("item"); }); }); diff --git a/src/oc-path/tests/universal.test.ts b/src/oc-path/tests/universal.test.ts index 89a3bc7cff8..54b9bdf5cab 100644 --- a/src/oc-path/tests/universal.test.ts +++ b/src/oc-path/tests/universal.test.ts @@ -5,228 +5,220 @@ * dispatches via `ast.kind` and coerces value strings based on AST * shape at the path location. */ -import { describe, expect, it } from 'vitest'; -import { emitJsonc } from '../jsonc/emit.js'; -import { parseJsonc } from '../jsonc/parse.js'; -import { emitJsonl } from '../jsonl/emit.js'; -import { parseJsonl } from '../jsonl/parse.js'; -import { emitMd } from '../emit.js'; -import { parseMd } from '../parse.js'; -import { parseOcPath } from '../oc-path.js'; -import { - detectInsertion, - resolveOcPath, - setOcPath, -} from '../universal.js'; +import { describe, expect, it } from "vitest"; +import { emitMd } from "../emit.js"; +import { emitJsonc } from "../jsonc/emit.js"; +import { parseJsonc } from "../jsonc/parse.js"; +import { emitJsonl } from "../jsonl/emit.js"; +import { parseJsonl } from "../jsonl/parse.js"; +import { parseOcPath } from "../oc-path.js"; +import { parseMd } from "../parse.js"; +import { detectInsertion, resolveOcPath, setOcPath } from "../universal.js"; // ---------- detectInsertion ------------------------------------------------ -describe('detectInsertion', () => { - it('returns null for plain paths', () => { - expect(detectInsertion(parseOcPath('oc://X.md/section/item/field'))).toBeNull(); +describe("detectInsertion", () => { + it("returns null for plain paths", () => { + expect(detectInsertion(parseOcPath("oc://X.md/section/item/field"))).toBeNull(); }); - it('detects bare `+` end-insertion at section', () => { - const info = detectInsertion(parseOcPath('oc://X.md/tools/+')); - expect(info?.marker).toBe('+'); - expect(info?.parentPath.section).toBe('tools'); + it("detects bare `+` end-insertion at section", () => { + const info = detectInsertion(parseOcPath("oc://X.md/tools/+")); + expect(info?.marker).toBe("+"); + expect(info?.parentPath.section).toBe("tools"); expect(info?.parentPath.item).toBeUndefined(); }); - it('detects `+key` keyed insertion', () => { - const info = detectInsertion(parseOcPath('oc://config/plugins/+gitlab')); - expect(info?.marker).toEqual({ kind: 'keyed', key: 'gitlab' }); + it("detects `+key` keyed insertion", () => { + const info = detectInsertion(parseOcPath("oc://config/plugins/+gitlab")); + expect(info?.marker).toEqual({ kind: "keyed", key: "gitlab" }); }); - it('detects `+nnn` indexed insertion', () => { - const info = detectInsertion(parseOcPath('oc://config/items/+2')); - expect(info?.marker).toEqual({ kind: 'indexed', index: 2 }); + it("detects `+nnn` indexed insertion", () => { + const info = detectInsertion(parseOcPath("oc://config/items/+2")); + expect(info?.marker).toEqual({ kind: "indexed", index: 2 }); }); - it('detects file-root insertion', () => { - const info = detectInsertion(parseOcPath('oc://session.jsonl/+')); - expect(info?.marker).toBe('+'); + it("detects file-root insertion", () => { + const info = detectInsertion(parseOcPath("oc://session.jsonl/+")); + expect(info?.marker).toBe("+"); expect(info?.parentPath.section).toBeUndefined(); }); }); // ---------- resolveOcPath — universal across kinds ------------------------- -describe('resolveOcPath — md AST', () => { - const md = parseMd( - '---\nname: github\n---\n\n## Boundaries\n\n- enabled: true\n', - ).ast; +describe("resolveOcPath — md AST", () => { + const md = parseMd("---\nname: github\n---\n\n## Boundaries\n\n- enabled: true\n").ast; - it('returns leaf with valueText for frontmatter entry', () => { - const m = resolveOcPath(md, parseOcPath('oc://X.md/[frontmatter]/name')); - expect(m).toMatchObject({ kind: 'leaf', valueText: 'github', leafType: 'string' }); + it("returns leaf with valueText for frontmatter entry", () => { + const m = resolveOcPath(md, parseOcPath("oc://X.md/[frontmatter]/name")); + expect(m).toMatchObject({ kind: "leaf", valueText: "github", leafType: "string" }); }); - it('returns leaf for item-field', () => { - const m = resolveOcPath( - md, - parseOcPath('oc://X.md/boundaries/enabled/enabled'), - ); - expect(m).toMatchObject({ kind: 'leaf', valueText: 'true', leafType: 'string' }); + it("returns leaf for item-field", () => { + const m = resolveOcPath(md, parseOcPath("oc://X.md/boundaries/enabled/enabled")); + expect(m).toMatchObject({ kind: "leaf", valueText: "true", leafType: "string" }); }); - it('returns node for block', () => { - const m = resolveOcPath(md, parseOcPath('oc://X.md/boundaries')); - expect(m).toMatchObject({ kind: 'node', descriptor: 'md-block' }); + it("returns node for block", () => { + const m = resolveOcPath(md, parseOcPath("oc://X.md/boundaries")); + expect(m).toMatchObject({ kind: "node", descriptor: "md-block" }); }); - it('returns root for file-only path', () => { - const m = resolveOcPath(md, parseOcPath('oc://X.md')); - expect(m?.kind).toBe('root'); + it("returns root for file-only path", () => { + const m = resolveOcPath(md, parseOcPath("oc://X.md")); + expect(m?.kind).toBe("root"); }); - it('returns null for unresolved', () => { - expect(resolveOcPath(md, parseOcPath('oc://X.md/missing'))).toBeNull(); + it("returns null for unresolved", () => { + expect(resolveOcPath(md, parseOcPath("oc://X.md/missing"))).toBeNull(); }); }); -describe('resolveOcPath — jsonc AST', () => { +describe("resolveOcPath — jsonc AST", () => { const ast = parseJsonc('{ "k": 42, "s": "x", "b": true, "n": null, "arr": [1,2,3] }').ast; - it('returns leaf:number for numeric value', () => { - const m = resolveOcPath(ast, parseOcPath('oc://config/k')); - expect(m).toMatchObject({ kind: 'leaf', valueText: '42', leafType: 'number' }); + it("returns leaf:number for numeric value", () => { + const m = resolveOcPath(ast, parseOcPath("oc://config/k")); + expect(m).toMatchObject({ kind: "leaf", valueText: "42", leafType: "number" }); }); - it('returns leaf:string for string value', () => { - const m = resolveOcPath(ast, parseOcPath('oc://config/s')); - expect(m).toMatchObject({ kind: 'leaf', valueText: 'x', leafType: 'string' }); + it("returns leaf:string for string value", () => { + const m = resolveOcPath(ast, parseOcPath("oc://config/s")); + expect(m).toMatchObject({ kind: "leaf", valueText: "x", leafType: "string" }); }); - it('returns leaf:boolean for bool value', () => { - const m = resolveOcPath(ast, parseOcPath('oc://config/b')); - expect(m).toMatchObject({ kind: 'leaf', valueText: 'true', leafType: 'boolean' }); + it("returns leaf:boolean for bool value", () => { + const m = resolveOcPath(ast, parseOcPath("oc://config/b")); + expect(m).toMatchObject({ kind: "leaf", valueText: "true", leafType: "boolean" }); }); - it('returns leaf:null for null value', () => { - const m = resolveOcPath(ast, parseOcPath('oc://config/n')); - expect(m).toMatchObject({ kind: 'leaf', valueText: 'null', leafType: 'null' }); + it("returns leaf:null for null value", () => { + const m = resolveOcPath(ast, parseOcPath("oc://config/n")); + expect(m).toMatchObject({ kind: "leaf", valueText: "null", leafType: "null" }); }); - it('returns node:jsonc-array for array value', () => { - const m = resolveOcPath(ast, parseOcPath('oc://config/arr')); - expect(m).toMatchObject({ kind: 'node', descriptor: 'jsonc-array' }); + it("returns node:jsonc-array for array value", () => { + const m = resolveOcPath(ast, parseOcPath("oc://config/arr")); + expect(m).toMatchObject({ kind: "node", descriptor: "jsonc-array" }); }); - it('returns leaf at array index', () => { - const m = resolveOcPath(ast, parseOcPath('oc://config/arr.1')); - expect(m).toMatchObject({ kind: 'leaf', valueText: '2', leafType: 'number' }); + it("returns leaf at array index", () => { + const m = resolveOcPath(ast, parseOcPath("oc://config/arr.1")); + expect(m).toMatchObject({ kind: "leaf", valueText: "2", leafType: "number" }); }); }); -describe('resolveOcPath — jsonl AST', () => { +describe("resolveOcPath — jsonl AST", () => { const ast = parseJsonl('{"event":"start","n":1}\n{"event":"step","n":2}\n').ast; - it('returns node:jsonl-line for line address', () => { - const m = resolveOcPath(ast, parseOcPath('oc://log/L1')); - expect(m).toMatchObject({ kind: 'node', descriptor: 'jsonl-line' }); + it("returns node:jsonl-line for line address", () => { + const m = resolveOcPath(ast, parseOcPath("oc://log/L1")); + expect(m).toMatchObject({ kind: "node", descriptor: "jsonl-line" }); }); - it('returns leaf for field on line', () => { - const m = resolveOcPath(ast, parseOcPath('oc://log/L2/event')); - expect(m).toMatchObject({ kind: 'leaf', valueText: 'step', leafType: 'string' }); + it("returns leaf for field on line", () => { + const m = resolveOcPath(ast, parseOcPath("oc://log/L2/event")); + expect(m).toMatchObject({ kind: "leaf", valueText: "step", leafType: "string" }); }); - it('returns leaf:number for $last/n', () => { - const m = resolveOcPath(ast, parseOcPath('oc://log/$last/n')); - expect(m).toMatchObject({ kind: 'leaf', valueText: '2', leafType: 'number' }); + it("returns leaf:number for $last/n", () => { + const m = resolveOcPath(ast, parseOcPath("oc://log/$last/n")); + expect(m).toMatchObject({ kind: "leaf", valueText: "2", leafType: "number" }); }); }); -describe('resolveOcPath — insertion-point detection', () => { - it('returns insertion-point for md section append', () => { - const md = parseMd('## Tools\n').ast; - const m = resolveOcPath(md, parseOcPath('oc://X.md/tools/+')); - expect(m).toMatchObject({ kind: 'insertion-point', container: 'md-section' }); +describe("resolveOcPath — insertion-point detection", () => { + it("returns insertion-point for md section append", () => { + const md = parseMd("## Tools\n").ast; + const m = resolveOcPath(md, parseOcPath("oc://X.md/tools/+")); + expect(m).toMatchObject({ kind: "insertion-point", container: "md-section" }); }); - it('returns insertion-point for md file-level', () => { - const md = parseMd('## Tools\n').ast; - const m = resolveOcPath(md, parseOcPath('oc://X.md/+')); - expect(m).toMatchObject({ kind: 'insertion-point', container: 'md-file' }); + it("returns insertion-point for md file-level", () => { + const md = parseMd("## Tools\n").ast; + const m = resolveOcPath(md, parseOcPath("oc://X.md/+")); + expect(m).toMatchObject({ kind: "insertion-point", container: "md-file" }); }); - it('returns insertion-point for md frontmatter +key', () => { - const md = parseMd('---\nname: x\n---\n').ast; - const m = resolveOcPath( - md, - parseOcPath('oc://X.md/[frontmatter]/+description'), - ); - expect(m).toMatchObject({ kind: 'insertion-point', container: 'md-frontmatter' }); + it("returns insertion-point for md frontmatter +key", () => { + const md = parseMd("---\nname: x\n---\n").ast; + const m = resolveOcPath(md, parseOcPath("oc://X.md/[frontmatter]/+description")); + expect(m).toMatchObject({ kind: "insertion-point", container: "md-frontmatter" }); }); - it('returns insertion-point for jsonc array +', () => { + it("returns insertion-point for jsonc array +", () => { const ast = parseJsonc('{ "items": [1,2,3] }').ast; - const m = resolveOcPath(ast, parseOcPath('oc://config/items/+')); - expect(m).toMatchObject({ kind: 'insertion-point', container: 'jsonc-array' }); + const m = resolveOcPath(ast, parseOcPath("oc://config/items/+")); + expect(m).toMatchObject({ kind: "insertion-point", container: "jsonc-array" }); }); - it('returns insertion-point for jsonc object +key', () => { + it("returns insertion-point for jsonc object +key", () => { const ast = parseJsonc('{ "plugins": {} }').ast; - const m = resolveOcPath(ast, parseOcPath('oc://config/plugins/+gitlab')); - expect(m).toMatchObject({ kind: 'insertion-point', container: 'jsonc-object' }); + const m = resolveOcPath(ast, parseOcPath("oc://config/plugins/+gitlab")); + expect(m).toMatchObject({ kind: "insertion-point", container: "jsonc-object" }); }); - it('returns insertion-point for jsonl file-root +', () => { - const ast = parseJsonl('').ast; - const m = resolveOcPath(ast, parseOcPath('oc://log/+')); - expect(m).toMatchObject({ kind: 'insertion-point', container: 'jsonl-file' }); + it("returns insertion-point for jsonl file-root +", () => { + const ast = parseJsonl("").ast; + const m = resolveOcPath(ast, parseOcPath("oc://log/+")); + expect(m).toMatchObject({ kind: "insertion-point", container: "jsonl-file" }); }); - it('returns null when insertion target is not a container', () => { + it("returns null when insertion target is not a container", () => { const ast = parseJsonc('{ "k": 42 }').ast; - const m = resolveOcPath(ast, parseOcPath('oc://config/k/+')); + const m = resolveOcPath(ast, parseOcPath("oc://config/k/+")); expect(m).toBeNull(); }); }); // ---------- setOcPath — leaf assignment ------------------------------------ -describe('setOcPath — md leaf', () => { - it('replaces frontmatter value', () => { - const md = parseMd('---\nname: old\n---\n').ast; - const r = setOcPath(md, parseOcPath('oc://X.md/[frontmatter]/name'), 'new'); +describe("setOcPath — md leaf", () => { + it("replaces frontmatter value", () => { + const md = parseMd("---\nname: old\n---\n").ast; + const r = setOcPath(md, parseOcPath("oc://X.md/[frontmatter]/name"), "new"); expect(r.ok).toBe(true); - if (r.ok) {expect(r.ast.kind === 'md' && r.ast.frontmatter[0]?.value).toBe('new');} + if (r.ok) { + expect(r.ast.kind === "md" && r.ast.frontmatter[0]?.value).toBe("new"); + } }); - it('replaces item kv value', () => { - const md = parseMd('## Boundaries\n\n- timeout: 5\n').ast; - const r = setOcPath(md, parseOcPath('oc://X.md/boundaries/timeout/timeout'), '60'); + it("replaces item kv value", () => { + const md = parseMd("## Boundaries\n\n- timeout: 5\n").ast; + const r = setOcPath(md, parseOcPath("oc://X.md/boundaries/timeout/timeout"), "60"); expect(r.ok).toBe(true); if (r.ok) { const out = emitMd(r.ast as Parameters[0]); - expect(out).toContain('- timeout: 60'); + expect(out).toContain("- timeout: 60"); } }); - it('returns unresolved for missing path', () => { - const md = parseMd('').ast; - const r = setOcPath(md, parseOcPath('oc://X.md/missing/x/x'), 'v'); + it("returns unresolved for missing path", () => { + const md = parseMd("").ast; + const r = setOcPath(md, parseOcPath("oc://X.md/missing/x/x"), "v"); expect(r.ok).toBe(false); - if (!r.ok) {expect(r.reason).toBe('unresolved');} + if (!r.ok) { + expect(r.reason).toBe("unresolved"); + } }); }); -describe('setOcPath — jsonc leaf with coercion', () => { - it('replaces string leaf with string value', () => { +describe("setOcPath — jsonc leaf with coercion", () => { + it("replaces string leaf with string value", () => { const ast = parseJsonc('{ "k": "old" }').ast; - const r = setOcPath(ast, parseOcPath('oc://config/k'), 'new'); + const r = setOcPath(ast, parseOcPath("oc://config/k"), "new"); expect(r.ok).toBe(true); if (r.ok) { const ast2 = r.ast as Parameters[0]; - expect(JSON.parse(emitJsonc(ast2))).toEqual({ k: 'new' }); + expect(JSON.parse(emitJsonc(ast2))).toEqual({ k: "new" }); } }); - it('coerces value to number when leaf was number', () => { + it("coerces value to number when leaf was number", () => { const ast = parseJsonc('{ "k": 1 }').ast; - const r = setOcPath(ast, parseOcPath('oc://config/k'), '42'); + const r = setOcPath(ast, parseOcPath("oc://config/k"), "42"); expect(r.ok).toBe(true); if (r.ok) { const ast2 = r.ast as Parameters[0]; @@ -236,7 +228,7 @@ describe('setOcPath — jsonc leaf with coercion', () => { it('coerces "true"/"false" when leaf was boolean', () => { const ast = parseJsonc('{ "k": true }').ast; - const r = setOcPath(ast, parseOcPath('oc://config/k'), 'false'); + const r = setOcPath(ast, parseOcPath("oc://config/k"), "false"); expect(r.ok).toBe(true); if (r.ok) { const ast2 = r.ast as Parameters[0]; @@ -244,101 +236,109 @@ describe('setOcPath — jsonc leaf with coercion', () => { } }); - it('rejects non-numeric string for number leaf', () => { + it("rejects non-numeric string for number leaf", () => { const ast = parseJsonc('{ "k": 1 }').ast; - const r = setOcPath(ast, parseOcPath('oc://config/k'), 'not-a-number'); + const r = setOcPath(ast, parseOcPath("oc://config/k"), "not-a-number"); expect(r.ok).toBe(false); - if (!r.ok) {expect(r.reason).toBe('parse-error');} + if (!r.ok) { + expect(r.reason).toBe("parse-error"); + } }); - it('rejects non-bool string for boolean leaf', () => { + it("rejects non-bool string for boolean leaf", () => { const ast = parseJsonc('{ "k": true }').ast; - const r = setOcPath(ast, parseOcPath('oc://config/k'), 'maybe'); + const r = setOcPath(ast, parseOcPath("oc://config/k"), "maybe"); expect(r.ok).toBe(false); - if (!r.ok) {expect(r.reason).toBe('parse-error');} + if (!r.ok) { + expect(r.reason).toBe("parse-error"); + } }); }); -describe('setOcPath — jsonl leaf', () => { - it('replaces field on a value line with coercion', () => { +describe("setOcPath — jsonl leaf", () => { + it("replaces field on a value line with coercion", () => { const ast = parseJsonl('{"event":"start","n":1}\n').ast; - const r = setOcPath(ast, parseOcPath('oc://log/L1/n'), '42'); + const r = setOcPath(ast, parseOcPath("oc://log/L1/n"), "42"); expect(r.ok).toBe(true); if (r.ok) { const out = emitJsonl(r.ast as Parameters[0]); - expect(JSON.parse(out.split('\n')[0])).toEqual({ event: 'start', n: 42 }); + expect(JSON.parse(out.split("\n")[0])).toEqual({ event: "start", n: 42 }); } }); - it('replaces whole line via JSON value', () => { + it("replaces whole line via JSON value", () => { const ast = parseJsonl('{"event":"start"}\n').ast; - const r = setOcPath(ast, parseOcPath('oc://log/L1'), '{"event":"replaced"}'); + const r = setOcPath(ast, parseOcPath("oc://log/L1"), '{"event":"replaced"}'); expect(r.ok).toBe(true); if (r.ok) { const out = emitJsonl(r.ast as Parameters[0]); - expect(JSON.parse(out.split('\n')[0])).toEqual({ event: 'replaced' }); + expect(JSON.parse(out.split("\n")[0])).toEqual({ event: "replaced" }); } }); - it('rejects malformed JSON for whole-line replacement', () => { + it("rejects malformed JSON for whole-line replacement", () => { const ast = parseJsonl('{"event":"start"}\n').ast; - const r = setOcPath(ast, parseOcPath('oc://log/L1'), 'not json'); + const r = setOcPath(ast, parseOcPath("oc://log/L1"), "not json"); expect(r.ok).toBe(false); - if (!r.ok) {expect(r.reason).toBe('parse-error');} + if (!r.ok) { + expect(r.reason).toBe("parse-error"); + } }); }); // ---------- setOcPath — insertion ------------------------------------------ -describe('setOcPath — md insertion', () => { - it('appends item to section with `+`', () => { - const md = parseMd('## Tools\n\n- gh: GitHub CLI\n').ast; - const r = setOcPath(md, parseOcPath('oc://X.md/tools/+'), 'docker: container CLI'); +describe("setOcPath — md insertion", () => { + it("appends item to section with `+`", () => { + const md = parseMd("## Tools\n\n- gh: GitHub CLI\n").ast; + const r = setOcPath(md, parseOcPath("oc://X.md/tools/+"), "docker: container CLI"); expect(r.ok).toBe(true); if (r.ok) { const out = emitMd(r.ast as Parameters[0]); - expect(out).toContain('- gh: GitHub CLI'); - expect(out).toContain('- docker: container CLI'); + expect(out).toContain("- gh: GitHub CLI"); + expect(out).toContain("- docker: container CLI"); } }); - it('appends new section at file root with `+`', () => { - const md = parseMd('## Existing\n').ast; - const r = setOcPath(md, parseOcPath('oc://X.md/+'), 'New Section'); + it("appends new section at file root with `+`", () => { + const md = parseMd("## Existing\n").ast; + const r = setOcPath(md, parseOcPath("oc://X.md/+"), "New Section"); expect(r.ok).toBe(true); if (r.ok) { const out = emitMd(r.ast as Parameters[0]); - expect(out).toContain('## Existing'); - expect(out).toContain('## New Section'); + expect(out).toContain("## Existing"); + expect(out).toContain("## New Section"); } }); - it('adds new frontmatter key with +key', () => { - const md = parseMd('---\nname: x\n---\n').ast; + it("adds new frontmatter key with +key", () => { + const md = parseMd("---\nname: x\n---\n").ast; const r = setOcPath( md, - parseOcPath('oc://X.md/[frontmatter]/+description'), - 'a new description', + parseOcPath("oc://X.md/[frontmatter]/+description"), + "a new description", ); expect(r.ok).toBe(true); if (r.ok) { const out = emitMd(r.ast as Parameters[0]); - expect(out).toContain('description: a new description'); + expect(out).toContain("description: a new description"); } }); - it('rejects duplicate frontmatter key on insertion', () => { - const md = parseMd('---\nname: x\n---\n').ast; - const r = setOcPath(md, parseOcPath('oc://X.md/[frontmatter]/+name'), 'y'); + it("rejects duplicate frontmatter key on insertion", () => { + const md = parseMd("---\nname: x\n---\n").ast; + const r = setOcPath(md, parseOcPath("oc://X.md/[frontmatter]/+name"), "y"); expect(r.ok).toBe(false); - if (!r.ok) {expect(r.reason).toBe('type-mismatch');} + if (!r.ok) { + expect(r.reason).toBe("type-mismatch"); + } }); }); -describe('setOcPath — jsonc insertion', () => { - it('appends to array with `+`', () => { +describe("setOcPath — jsonc insertion", () => { + it("appends to array with `+`", () => { const ast = parseJsonc('{ "items": [1, 2] }').ast; - const r = setOcPath(ast, parseOcPath('oc://config/items/+'), '3'); + const r = setOcPath(ast, parseOcPath("oc://config/items/+"), "3"); expect(r.ok).toBe(true); if (r.ok) { const ast2 = r.ast as Parameters[0]; @@ -346,9 +346,9 @@ describe('setOcPath — jsonc insertion', () => { } }); - it('inserts at index with `+nnn`', () => { + it("inserts at index with `+nnn`", () => { const ast = parseJsonc('{ "items": [1, 3] }').ast; - const r = setOcPath(ast, parseOcPath('oc://config/items/+1'), '2'); + const r = setOcPath(ast, parseOcPath("oc://config/items/+1"), "2"); expect(r.ok).toBe(true); if (r.ok) { const ast2 = r.ast as Parameters[0]; @@ -356,120 +356,117 @@ describe('setOcPath — jsonc insertion', () => { } }); - it('adds object key with `+key`', () => { + it("adds object key with `+key`", () => { const ast = parseJsonc('{ "plugins": { "github": "tok" } }').ast; - const r = setOcPath( - ast, - parseOcPath('oc://config/plugins/+gitlab'), - '"new-tok"', - ); + const r = setOcPath(ast, parseOcPath("oc://config/plugins/+gitlab"), '"new-tok"'); expect(r.ok).toBe(true); if (r.ok) { const ast2 = r.ast as Parameters[0]; expect(JSON.parse(emitJsonc(ast2))).toEqual({ - plugins: { github: 'tok', gitlab: 'new-tok' }, + plugins: { github: "tok", gitlab: "new-tok" }, }); } }); - it('rejects duplicate object key', () => { + it("rejects duplicate object key", () => { const ast = parseJsonc('{ "plugins": { "github": "x" } }').ast; - const r = setOcPath(ast, parseOcPath('oc://config/plugins/+github'), '"y"'); + const r = setOcPath(ast, parseOcPath("oc://config/plugins/+github"), '"y"'); expect(r.ok).toBe(false); - if (!r.ok) {expect(r.reason).toBe('unresolved');} + if (!r.ok) { + expect(r.reason).toBe("unresolved"); + } }); - it('rejects +key on array', () => { + it("rejects +key on array", () => { const ast = parseJsonc('{ "items": [1, 2] }').ast; - const r = setOcPath(ast, parseOcPath('oc://config/items/+abc'), '3'); + const r = setOcPath(ast, parseOcPath("oc://config/items/+abc"), "3"); expect(r.ok).toBe(false); - if (!r.ok) {expect(r.reason).toBe('type-mismatch');} + if (!r.ok) { + expect(r.reason).toBe("type-mismatch"); + } }); - it('inserts complex object via JSON value', () => { + it("inserts complex object via JSON value", () => { const ast = parseJsonc('{ "plugins": {} }').ast; const r = setOcPath( ast, - parseOcPath('oc://config/plugins/+gitlab'), + parseOcPath("oc://config/plugins/+gitlab"), '{"token":"xyz","enabled":true}', ); expect(r.ok).toBe(true); if (r.ok) { const ast2 = r.ast as Parameters[0]; expect(JSON.parse(emitJsonc(ast2))).toEqual({ - plugins: { gitlab: { token: 'xyz', enabled: true } }, + plugins: { gitlab: { token: "xyz", enabled: true } }, }); } }); }); -describe('setOcPath — jsonl insertion (session append)', () => { - it('appends a JSON line with `+`', () => { +describe("setOcPath — jsonl insertion (session append)", () => { + it("appends a JSON line with `+`", () => { const ast = parseJsonl('{"event":"start"}\n').ast; - const r = setOcPath( - ast, - parseOcPath('oc://log/+'), - '{"event":"step","n":1}', - ); + const r = setOcPath(ast, parseOcPath("oc://log/+"), '{"event":"step","n":1}'); expect(r.ok).toBe(true); if (r.ok) { const out = emitJsonl(r.ast as Parameters[0]); - const lines = out.split('\n').filter((l) => l.length > 0); + const lines = out.split("\n").filter((l) => l.length > 0); expect(lines).toHaveLength(2); - expect(JSON.parse(lines[1])).toEqual({ event: 'step', n: 1 }); + expect(JSON.parse(lines[1])).toEqual({ event: "step", n: 1 }); } }); - it('rejects malformed JSON value', () => { - const ast = parseJsonl('').ast; - const r = setOcPath(ast, parseOcPath('oc://log/+'), 'not json'); + it("rejects malformed JSON value", () => { + const ast = parseJsonl("").ast; + const r = setOcPath(ast, parseOcPath("oc://log/+"), "not json"); expect(r.ok).toBe(false); - if (!r.ok) {expect(r.reason).toBe('parse-error');} + if (!r.ok) { + expect(r.reason).toBe("parse-error"); + } }); - it('rejects non-root insertion target', () => { + it("rejects non-root insertion target", () => { const ast = parseJsonl('{"a":1}\n').ast; - const r = setOcPath(ast, parseOcPath('oc://log/L1/+'), '{}'); + const r = setOcPath(ast, parseOcPath("oc://log/L1/+"), "{}"); expect(r.ok).toBe(false); }); }); // ---------- Cross-cutting properties --------------------------------------- -describe('setOcPath — cross-cutting properties', () => { - it('is non-mutating across all kinds', () => { - const md = parseMd('---\nname: x\n---\n').ast; +describe("setOcPath — cross-cutting properties", () => { + it("is non-mutating across all kinds", () => { + const md = parseMd("---\nname: x\n---\n").ast; const before = JSON.stringify(md); - setOcPath(md, parseOcPath('oc://X.md/[frontmatter]/name'), 'new'); + setOcPath(md, parseOcPath("oc://X.md/[frontmatter]/name"), "new"); expect(JSON.stringify(md)).toBe(before); const jsonc = parseJsonc('{ "k": 1 }').ast; const before2 = JSON.stringify(jsonc); - setOcPath(jsonc, parseOcPath('oc://config/k'), '99'); + setOcPath(jsonc, parseOcPath("oc://config/k"), "99"); expect(JSON.stringify(jsonc)).toBe(before2); const jsonl = parseJsonl('{"a":1}\n').ast; const before3 = JSON.stringify(jsonl); - setOcPath(jsonl, parseOcPath('oc://log/L1/a'), '99'); + setOcPath(jsonl, parseOcPath("oc://log/L1/a"), "99"); expect(JSON.stringify(jsonl)).toBe(before3); }); - it('returns ok-tagged result with new ast on success', () => { - const md = parseMd('---\nname: x\n---\n').ast; - const r = setOcPath(md, parseOcPath('oc://X.md/[frontmatter]/name'), 'y'); + it("returns ok-tagged result with new ast on success", () => { + const md = parseMd("---\nname: x\n---\n").ast; + const r = setOcPath(md, parseOcPath("oc://X.md/[frontmatter]/name"), "y"); expect(r.ok).toBe(true); if (r.ok) { - expect(r.ast.kind).toBe('md'); + expect(r.ast.kind).toBe("md"); } }); - it('returns failure-tagged result with reason on unresolved', () => { - const ast = parseJsonc('{}').ast; - const r = setOcPath(ast, parseOcPath('oc://config/missing'), 'v'); + it("returns failure-tagged result with reason on unresolved", () => { + const ast = parseJsonc("{}").ast; + const r = setOcPath(ast, parseOcPath("oc://config/missing"), "v"); expect(r.ok).toBe(false); if (!r.ok) { - expect(r.reason).toBeDefined(); - expect(typeof r.reason).toBe('string'); + expect(r.reason).toBe("unresolved"); } }); });