@@ -1398,6 +1398,192 @@ url: https://example.com
13981398 "parseFrontmatter: content must be a string, got boolean" ,
13991399 )
14001400 } )
1401+
1402+ describe ( "edge cases with malformed YAML" , ( ) => {
1403+ it ( "should return empty fields for frontmatter with only whitespace between delimiters" , ( ) => {
1404+ const content = `---
1405+
1406+
1407+ ---
1408+ # Content`
1409+ const result = parseFrontmatter ( content )
1410+ expect ( result . found ) . toBe ( true )
1411+ expect ( result . fields ) . toEqual ( { } )
1412+ expect ( result . endIndex ) . toBeGreaterThan ( 0 )
1413+ } )
1414+
1415+ it ( "should handle values containing unbalanced double quotes" , ( ) => {
1416+ const content = `---
1417+ name: "Unbalanced quote value
1418+ version: 1.0
1419+ ---
1420+ # Content`
1421+ const result = parseFrontmatter ( content )
1422+ expect ( result . found ) . toBe ( true )
1423+ // The unbalanced quote is kept as-is since it doesn't match the balanced quote pattern
1424+ expect ( result . fields . name ) . toBe ( '"Unbalanced quote value' )
1425+ expect ( result . fields . version ) . toBe ( "1.0" )
1426+ } )
1427+
1428+ it ( "should handle values containing unbalanced single quotes" , ( ) => {
1429+ const content = `---
1430+ name: 'Unbalanced single quote
1431+ version: 1.0
1432+ ---
1433+ # Content`
1434+ const result = parseFrontmatter ( content )
1435+ expect ( result . found ) . toBe ( true )
1436+ // The unbalanced quote is kept as-is
1437+ expect ( result . fields . name ) . toBe ( "'Unbalanced single quote" )
1438+ expect ( result . fields . version ) . toBe ( "1.0" )
1439+ } )
1440+
1441+ it ( "should handle values with mismatched quote types" , ( ) => {
1442+ const content = `---
1443+ name: "Mismatched quotes'
1444+ version: 1.0
1445+ ---
1446+ # Content`
1447+ const result = parseFrontmatter ( content )
1448+ expect ( result . found ) . toBe ( true )
1449+ // Mismatched quotes are not stripped
1450+ expect ( result . fields . name ) . toBe ( "\"Mismatched quotes'" )
1451+ expect ( result . fields . version ) . toBe ( "1.0" )
1452+ } )
1453+
1454+ it ( "should return unclosed when closing --- is on same line as content" , ( ) => {
1455+ const content = `---
1456+ version: 1.0
1457+ requires: opencode---
1458+ # Content`
1459+ const result = parseFrontmatter ( content )
1460+ // The closing --- must be on its own line (preceded by \n)
1461+ // "opencode---" does not match "\n---" pattern
1462+ expect ( result . found ) . toBe ( false )
1463+ expect ( result . reason ) . toBe ( "unclosed" )
1464+ } )
1465+
1466+ it ( "should handle closing --- immediately after newline with no space" , ( ) => {
1467+ const content = `---
1468+ version: 1.0
1469+ ---`
1470+ const result = parseFrontmatter ( content )
1471+ expect ( result . found ) . toBe ( true )
1472+ expect ( result . fields . version ) . toBe ( "1.0" )
1473+ } )
1474+
1475+ it ( "should use the last value when duplicate keys exist" , ( ) => {
1476+ const content = `---
1477+ version: 1.0
1478+ requires: opencode
1479+ version: 2.0
1480+ ---
1481+ # Content`
1482+ const result = parseFrontmatter ( content )
1483+ expect ( result . found ) . toBe ( true )
1484+ // The parser iterates through lines and overwrites, so last value wins
1485+ expect ( result . fields . version ) . toBe ( "2.0" )
1486+ expect ( result . fields . requires ) . toBe ( "opencode" )
1487+ } )
1488+
1489+ it ( "should handle multiple duplicate keys with last value winning" , ( ) => {
1490+ const content = `---
1491+ key: first
1492+ key: second
1493+ key: third
1494+ ---
1495+ # Content`
1496+ const result = parseFrontmatter ( content )
1497+ expect ( result . found ) . toBe ( true )
1498+ expect ( result . fields . key ) . toBe ( "third" )
1499+ } )
1500+
1501+ it ( "should preserve leading whitespace in values" , ( ) => {
1502+ const content = `---
1503+ name: leading spaces
1504+ version: 1.0
1505+ ---
1506+ # Content`
1507+ const result = parseFrontmatter ( content )
1508+ expect ( result . found ) . toBe ( true )
1509+ // trim() is called on the value, so leading spaces are removed
1510+ expect ( result . fields . name ) . toBe ( "leading spaces" )
1511+ } )
1512+
1513+ it ( "should preserve trailing whitespace in values (trimmed)" , ( ) => {
1514+ const content = `---
1515+ name: trailing spaces
1516+ version: 1.0
1517+ ---
1518+ # Content`
1519+ const result = parseFrontmatter ( content )
1520+ expect ( result . found ) . toBe ( true )
1521+ // trim() is called on the value, so trailing spaces are removed
1522+ expect ( result . fields . name ) . toBe ( "trailing spaces" )
1523+ } )
1524+
1525+ it ( "should handle values with both leading and trailing whitespace" , ( ) => {
1526+ const content = `---
1527+ name: surrounded by spaces
1528+ version: 1.0
1529+ ---
1530+ # Content`
1531+ const result = parseFrontmatter ( content )
1532+ expect ( result . found ) . toBe ( true )
1533+ // Both leading and trailing spaces are trimmed
1534+ expect ( result . fields . name ) . toBe ( "surrounded by spaces" )
1535+ } )
1536+
1537+ it ( "should handle whitespace-only values as empty strings" , ( ) => {
1538+ const content = `---
1539+ name:
1540+ version: 1.0
1541+ ---
1542+ # Content`
1543+ const result = parseFrontmatter ( content )
1544+ expect ( result . found ) . toBe ( true )
1545+ // Whitespace-only value becomes empty string after trim
1546+ expect ( result . fields . name ) . toBe ( "" )
1547+ expect ( result . fields . version ) . toBe ( "1.0" )
1548+ } )
1549+
1550+ it ( "should handle quoted values with internal whitespace preserved" , ( ) => {
1551+ const content = `---
1552+ name: " internal spaces "
1553+ version: 1.0
1554+ ---
1555+ # Content`
1556+ const result = parseFrontmatter ( content )
1557+ expect ( result . found ) . toBe ( true )
1558+ // Quotes are stripped, but internal whitespace is preserved
1559+ expect ( result . fields . name ) . toBe ( " internal spaces " )
1560+ } )
1561+
1562+ it ( "should handle empty value after colon" , ( ) => {
1563+ const content = `---
1564+ name:
1565+ version: 1.0
1566+ ---
1567+ # Content`
1568+ const result = parseFrontmatter ( content )
1569+ expect ( result . found ) . toBe ( true )
1570+ expect ( result . fields . name ) . toBe ( "" )
1571+ expect ( result . fields . version ) . toBe ( "1.0" )
1572+ } )
1573+
1574+ it ( "should handle keys with leading/trailing whitespace" , ( ) => {
1575+ const content = `---
1576+ name : value
1577+ version: 1.0
1578+ ---
1579+ # Content`
1580+ const result = parseFrontmatter ( content )
1581+ expect ( result . found ) . toBe ( true )
1582+ // Key whitespace is trimmed
1583+ expect ( result . fields . name ) . toBe ( "value" )
1584+ expect ( result . fields . version ) . toBe ( "1.0" )
1585+ } )
1586+ } )
14011587 } )
14021588
14031589 describe ( "validateAgentContent" , ( ) => {
0 commit comments