@descent-vtt/spec-graph 0.11.1 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","sourceRoot":"","sources":["../src/select.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,UAAU,EAA0D,MAAM,YAAY,CAAC;AAChG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAqB,MAAM,qCAAqC,CAAC;AA8ClG,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,6DAA6D;IACpD,MAAM,CAAS;IAExB,YAAY,OAAe,EAAE,MAAc;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAE1C,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAEhF,MAAM,UAAU,GAA8C;IAC5D,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,UAAU;IACrB,GAAG,EAAE,UAAU;IACf,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,MAAM;IACb,IAAI,EAAE,IAAI;IACV,KAAK,EAAE,IAAI;IACX,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;CACV,CAAC;AAEF,2DAA2D;AAC3D,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;IACxC,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,MAAM,CAAC,SAAS,EAAE,CAAC;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;QAC/B,MAAM,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,MAAM;IACO,MAAM,CAAS;IACxB,QAAQ,GAAG,CAAC,CAAC;IAErB,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC7C,CAAC;IAED,SAAS;QACP,wEAAwE;QACxE,4EAA4E;QAC5E,8BAA8B;QAC9B,OAAO,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAW,CAAC;YAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IACnH,CAAC;IAED,gBAAgB;QACd,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,IAAI,GAAoB,IAAI,CAAC;QACjC,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACrE,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,IAAI,GAAI,IAAI,CAAC,CAAC,CAAY,CAAC,WAAW,EAAE,CAAC;YAC/C,IAAI,CAAC,CAAC,IAAI,IAAI,UAAU,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,UAAU,CAClB,sBAAsB,IAAI,CAAC,CAAC,CAAC,6CAA6C,EAC1E,IAAI,CAAC,QAAQ,CACd,CAAC;YACJ,CAAC;YACD,IAAI,GAAG,UAAU,CAAC,IAAI,CAAoB,CAAC;YAC3C,IAAI,CAAC,QAAQ,IAAK,IAAI,CAAC,CAAC,CAAY,CAAC,MAAM,CAAC;QAC9C,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC;YAC9C,MAAM,IAAI,UAAU,CAAC,6DAA6D,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrG,CAAC;QAED,MAAM,UAAU,GAAgB,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG;YAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;QAClF,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC9B,CAAC;IAEO,cAAc;QACpB,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnB,MAAM,QAAQ,GAAG,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACrF,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,UAAU,CAAC,sCAAsC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3F,MAAM,GAAG,GAAI,QAAQ,CAAC,CAAC,CAAY,CAAC,WAAW,EAAE,CAAC;QAClD,IAAI,CAAC,QAAQ,IAAK,QAAQ,CAAC,CAAC,CAAY,CAAC,MAAM,CAAC;QAEhD,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC;YACvC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnB,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QAChD,CAAC;QAED,MAAM,aAAa,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACtF,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,UAAU,CAClB,8DAA8D,GAAG,GAAG,EACpE,IAAI,CAAC,QAAQ,CACd,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAa,CAAC;QAC9C,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC;QAEjC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC;QACjC,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG;YAAE,MAAM,IAAI,UAAU,CAAC,qCAAqC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnH,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnB,2EAA2E;QAC3E,4EAA4E;QAC5E,yEAAyE;QACzE,yEAAyE;QACzE,4CAA4C;QAC5C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,IAAI,CAAC;gBACH,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,CAAC,KAAK,YAAY,UAAU,CAAC;oBAAE,MAAM,KAAK,CAAC;gBAChD,MAAM,IAAI,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7G,CAAC;QACH,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClC,CAAC;IAEO,UAAU;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YACnC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnB,IAAI,GAAG,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,KAAK,EAAE,CAAC;gBAClF,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI;oBAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;gBAC5D,mEAAmE;gBACnE,gEAAgE;gBAChE,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACxC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACrB,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,KAAK;gBAAE,MAAM,IAAI,UAAU,CAAC,2BAA2B,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3G,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnB,OAAO,GAAG,CAAC;QACb,CAAC;QACD,sEAAsE;QACtE,kEAAkE;QAClE,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjE,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAW,CAAC;QAC3C,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,SAAS;QACP,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9C,4EAA4E;QAC5E,sEAAsE;QACtE,2EAA2E;QAC3E,4EAA4E;QAC5E,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,gDAAgD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE7E,kEAAkE;QAClE,8CAA8C;QAC9C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,UAAU,GAAI,OAAO,CAAC,CAAC,CAAY,KAAK,GAAG,CAAC;YAClD,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAW,CAAC;YACrC,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,KAAK,UAAU,EAAE,CAAC;gBACtC,MAAM,IAAI,UAAU,CAAC,qEAAqE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7G,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAW,EAAE,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YACvE,IAAI,CAAC,QAAQ,IAAK,OAAO,CAAC,CAAC,CAAY,CAAC,MAAM,CAAC;YAC/C,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC;QAChF,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,UAAU,GAAI,QAAQ,CAAC,CAAC,CAAY,KAAK,IAAI,CAAC;YACpD,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAW,CAAC;YACtC,IAAI,CAAC,OAAO,KAAK,GAAG,CAAC,KAAK,UAAU,EAAE,CAAC;gBACrC,MAAM,IAAI,UAAU,CAAC,qEAAqE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7G,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAW,EAAE,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YACxE,IAAI,CAAC,QAAQ,IAAK,QAAQ,CAAC,CAAC,CAAY,CAAC,MAAM,CAAC;YAChD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC;QAC/E,CAAC;QAED,MAAM,IAAI,UAAU,CAClB,+EAA+E,EAC/E,IAAI,CAAC,QAAQ,CACd,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACK,UAAU,CAAC,GAAW,EAAE,MAAc;QAC5C,IAAI,GAAG,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAC7B,MAAM,KAAK,GAAe,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YAChC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAChC,IAAI,CAAE,UAAgC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtD,MAAM,IAAI,UAAU,CAAC,qBAAqB,IAAI,uBAAuB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YACxG,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,IAAgB,CAAC,CAAC;QAC/B,CAAC;QACD,wEAAwE;QACxE,oEAAoE;QACpE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,CAAC;CACF;AAED,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAEhF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,aAAa,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC5D,OAAO;IACP,MAAM;IACN,YAAY;IACZ,aAAa;IACb,UAAU;IACV,UAAU;IACV,MAAM;IACN,IAAI;IACJ,MAAM;IACN,MAAM;IACN,UAAU;IACV,MAAM;IACN,OAAO;IACP,aAAa;IACb,SAAS;IACT,OAAO;IACP,QAAQ;IACR,MAAM;IACN,OAAO;CACR,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,IAAc,EAAE,GAAW,EAAE,KAAiB;IACzE,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,IAAI;YACP,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,KAAK,MAAM;YACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,KAAK,OAAO;YACV,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtB,KAAK,MAAM;YACT,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QACxB,KAAK,MAAM;YACT,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,KAAK,UAAU;YACb,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7B,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,MAAM;gBACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,KAAK,OAAO;gBACV,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,KAAK,QAAQ;gBACX,OAAO,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACzD,KAAK,aAAa;gBAChB,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACrC,KAAK,OAAO;gBACV,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,EAAE,CAAC;YACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,8EAA8E;IAC9E,2EAA2E;IAC3E,8EAA8E;IAC9E,2BAA2B;IAC3B,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,aAAa,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACtG,MAAM,KAAK,GAAG,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7C,IAAI,KAAK;YAAE,OAAO,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAClD,OAAO,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,CAAC;IAED,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,aAAa;YAChB,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5B,KAAK,UAAU;YACb,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzB,KAAK,SAAS;YACZ,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3B,KAAK,MAAM;YACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,KAAK,UAAU;YACb,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChC,KAAK,YAAY;YACf,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACxD;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAc,EAAE,SAAoB,EAAE,KAAiB;IAC5E,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;IAE7C,IAAI,SAAS,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACrF,IAAI,SAAS,CAAC,QAAQ,KAAK,IAAI;QAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;IAEhG,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;QACzB,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QAChC,QAAQ,SAAS,CAAC,QAAQ,EAAE,CAAC;YAC3B,KAAK,GAAG;gBACN,OAAO,KAAK,KAAK,MAAM,CAAC;YAC1B,KAAK,IAAI;gBACP,OAAO,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAClC,KAAK,IAAI;gBACP,OAAO,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAChC,KAAK,IAAI;gBACP,OAAO,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAChC,KAAK,IAAI;gBACP,OAAO,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5C;gBACE,oEAAoE;gBACpE,kEAAkE;gBAClE,OAAO,KAAK,CAAC;QACjB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,MAAc;IACpC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,MAAM,CAAC,IAAwB;IACtC,OAAO,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC;AACtC,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,OAAO,CAAC,IAAc,EAAE,OAAoB,EAAE,KAAiB;IAC7E,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACtE,OAAO,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;AACxF,CAAC;AA4BD,6DAA6D;AAC7D,MAAM,UAAU,OAAO,CAAC,KAAgB,EAAE,IAAe,EAAE,OAAO,GAAmB,EAAE;IACrF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,mBAAmB,CAAC;IACnD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC;IAEvD,IAAI,QAAQ,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IACpF,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAY,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAE/B,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAa,CAAC;YAC7D,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,CAAC;gBACvE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC;oBAAE,SAAS;gBAC/C,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;gBACnE,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACd,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC/E,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK;oBAAE,MAAM;YAClC,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK;gBAAE,MAAM;QAClC,CAAC;QAED,QAAQ,GAAG,IAAI,CAAC;QAChB,0EAA0E;QAC1E,gBAAgB;QAChB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;IACnC,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAOD,+EAA+E;AAC/E,yEAAyE;AACzE,oCAAoC;AACpC,SAAS,MAAM,CAAC,KAAgB,EAAE,IAAc,EAAE,IAAc,EAAE,cAAuB;IACvF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;IAC/B,MAAM,GAAG,GAAgB,EAAE,CAAC;IAE5B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC9F,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,SAAS;gBAAE,SAAS;YAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YAChE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAClC,IAAI,IAAI;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,8EAA8E;IAC9E,8DAA8D;IAC9D,MAAM,OAAO,GACX,IAAI,CAAC,SAAS,KAAK,KAAK;QACtB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC;QAC7B,CAAC,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAE3C,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,SAAS;QACtE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5B,IAAI,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,KAAgB,EAAE,EAAU,EAAE,KAA0B;IAC7E,MAAM,KAAK,GAAG,IAAI,GAAG,EAA2B,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,GAAG,CAA0B,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAa,CAAC,EAAE,CAAC,CAAC;IAE7B,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAY,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAoB,CAAC;QACtD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;YAC5C,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE;gBAAE,SAAS;YACxD,MAAM,IAAI,GAAG,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC;YAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE,EAAE,CAAC;gBACrB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;gBACxC,SAAS;YACX,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC5B,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,KAAK,CAAC,KAAgB,EAAE,QAAgB,EAAE,OAAwB;IAChF,OAAO,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,KAAY;IACtC,IAAI,EAAE,GAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAc,CAAC,EAAE,CAAC;IACzC,MAAM,KAAK,GAAa,CAAC,EAAE,CAAC,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAClC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC","sourcesContent":["/**\n * The selector language.\n *\n * Rules and users ask the graph the same kinds of question, so they use the same\n * machinery. Every built-in diagnostic is a path query plus a message; anything\n * a rule can find, `spec-graph query` can find too, which means a team can\n * express a convention spec-graph never anticipated without writing a plugin.\n *\n * ```text\n * item[openness=open] -delegates-to-> document[phase=retired]\n * document[phase=active] -assumes,depends-on-> document[phase=retired]\n * document =supersedes=> document[phase=active]\n * item[state=obviated] <-assumes- *\n * *[path^=docs/adr] -blocked-by-> item[openness=open]\n * ```\n *\n * A query evaluates to **paths**, not endpoints. A rule that has the whole path\n * can say \"ADR-0004 line 31 delegates this to ADR-0002, which was retired in\n * March\" instead of naming two documents and leaving the reader to reconstruct\n * the relationship.\n */\n\nimport { receptivityOf } from './lifecycle.js';\nimport type { SpecGraph } from './graph.js';\nimport { EDGE_KINDS, type Edge, type EdgeKind, type NodeKind, type SpecNode } from './types.js';\nimport { compileRegex, RegexError, type RegexMatcher } from './vendor/spec-core/pattern/index.js';\n\n/* -------------------------------------------------------------------------- */\n/* Shapes */\n/* -------------------------------------------------------------------------- */\n\nexport type Operator = '=' | '!=' | '^=' | '$=' | '*=' | '~=' | 'exists';\n\nexport interface Predicate {\n readonly key: string;\n readonly operator: Operator;\n readonly value: string;\n}\n\nexport interface NodeMatcher {\n /** `null` matches both documents and items. */\n readonly kind: NodeKind | null;\n readonly predicates: readonly Predicate[];\n}\n\nexport interface StepSpec {\n readonly direction: 'out' | 'in';\n /** `null` matches any edge kind. */\n readonly kinds: readonly EdgeKind[] | null;\n /** Follow the relation transitively, taking the shortest path to each node. */\n readonly transitive: boolean;\n readonly node: NodeMatcher;\n}\n\nexport interface QuerySpec {\n readonly start: NodeMatcher;\n readonly steps: readonly StepSpec[];\n}\n\n/**\n * One path through the graph.\n *\n * `nodes` holds the start and the node each step matched; `edges` is the whole\n * path in order. A transitive step contributes one node and every edge it\n * walked, so the nodes it passed through are named only by those edges.\n */\nexport interface Match {\n readonly nodes: readonly SpecNode[];\n readonly edges: readonly Edge[];\n}\n\nexport class QueryError extends Error {\n /** Offset into the query string where the problem starts. */\n readonly offset: number;\n\n constructor(message: string, offset: number) {\n super(message);\n this.name = 'QueryError';\n this.offset = offset;\n }\n}\n\n/** Guards against a pathological query fanning out over a huge corpus. */\nexport const DEFAULT_MATCH_LIMIT = 10_000;\n\n/* -------------------------------------------------------------------------- */\n/* Parsing */\n/* -------------------------------------------------------------------------- */\n\nconst NODE_KINDS: Readonly<Record<string, NodeKind | null>> = {\n document: 'document',\n documents: 'document',\n doc: 'document',\n docs: 'document',\n item: 'item',\n items: 'item',\n node: null,\n nodes: null,\n any: null,\n '*': null,\n};\n\n/** Compiles a selector string into a {@link QuerySpec}. */\nexport function parseQuery(source: string): QuerySpec {\n const parser = new Parser(source);\n const start = parser.parseNodeMatcher();\n const steps: StepSpec[] = [];\n parser.skipSpace();\n while (!parser.atEnd()) {\n steps.push(parser.parseStep());\n parser.skipSpace();\n }\n return { start, steps };\n}\n\nclass Parser {\n private readonly source: string;\n private position = 0;\n\n constructor(source: string) {\n this.source = source;\n }\n\n atEnd(): boolean {\n return this.position >= this.source.length;\n }\n\n skipSpace(): void {\n // The bound decides nothing: past the end the character is `undefined`,\n // which `test` reads as the word \"undefined\" and rejects. It is kept so the\n // loop does not lean on that.\n while (this.position < this.source.length && /\\s/.test(this.source[this.position] as string)) this.position += 1;\n }\n\n parseNodeMatcher(): NodeMatcher {\n this.skipSpace();\n let kind: NodeKind | null = null;\n const word = /^[A-Za-z]+|^\\*/.exec(this.source.slice(this.position));\n if (word) {\n const text = (word[0] as string).toLowerCase();\n if (!(text in NODE_KINDS)) {\n throw new QueryError(\n `unknown node type \"${word[0]}\", expected one of: document, item, * (any)`,\n this.position,\n );\n }\n kind = NODE_KINDS[text] as NodeKind | null;\n this.position += (word[0] as string).length;\n } else if (this.source[this.position] !== '[') {\n throw new QueryError('expected a node type (document, item or *) or a [predicate]', this.position);\n }\n\n const predicates: Predicate[] = [];\n while (this.source[this.position] === '[') predicates.push(this.parsePredicate());\n return { kind, predicates };\n }\n\n private parsePredicate(): Predicate {\n this.position += 1;\n const keyMatch = /^[A-Za-z_][A-Za-z0-9_.:-]*/.exec(this.source.slice(this.position));\n if (!keyMatch) throw new QueryError('expected an attribute name after \"[\"', this.position);\n const key = (keyMatch[0] as string).toLowerCase();\n this.position += (keyMatch[0] as string).length;\n\n if (this.source[this.position] === ']') {\n this.position += 1;\n return { key, operator: 'exists', value: '' };\n }\n\n const operatorMatch = /^(!=|\\^=|\\$=|\\*=|~=|=)/.exec(this.source.slice(this.position));\n if (!operatorMatch) {\n throw new QueryError(\n `expected an operator (=, !=, ^=, $=, *=, ~=) or \"]\" after \"${key}\"`,\n this.position,\n );\n }\n const operator = operatorMatch[0] as Operator;\n this.position += operator.length;\n\n const valueStart = this.position;\n const value = this.parseValue();\n if (this.source[this.position] !== ']') throw new QueryError('expected \"]\" to close the predicate', this.position);\n this.position += 1;\n // A pattern is checked here rather than where it runs, because here is the\n // only place that knows where in the selector it was written. It used to be\n // compiled on first use and an unparseable one silently matched nothing,\n // which is the same silence a misspelled relation would have produced if\n // this grammar tolerated one. See ADR-0017.\n if (operator === '~=') {\n try {\n pattern(value);\n } catch (error) {\n if (!(error instanceof RegexError)) throw error;\n throw new QueryError(error.message, valueStart + error.offset + (quoted(this.source[valueStart]) ? 1 : 0));\n }\n }\n return { key, operator, value };\n }\n\n private parseValue(): string {\n const quote = this.source[this.position];\n if (quote === '\"' || quote === \"'\") {\n this.position += 1;\n let out = '';\n while (this.position < this.source.length && this.source[this.position] !== quote) {\n if (this.source[this.position] === '\\\\') this.position += 1;\n // `?? ''` meets only a backslash at the very end, and the value it\n // builds then is thrown away with the unterminated quote below.\n out += this.source[this.position] ?? '';\n this.position += 1;\n }\n if (this.source[this.position] !== quote) throw new QueryError('unterminated quoted value', this.position);\n this.position += 1;\n return out;\n }\n // The anchor decides nothing, since a starred class matches the empty\n // string where the search starts; it says where the value begins.\n const match = /^[^\\]\\s]*/.exec(this.source.slice(this.position));\n const value = (match?.[0] ?? '') as string;\n this.position += value.length;\n return value;\n }\n\n parseStep(): StepSpec {\n const rest = this.source.slice(this.position);\n // The forward form is lazy because its terminator is two characters (`->`),\n // which cannot be confused with the hyphen inside `delegates-to`. The\n // backward terminator is a single `-`, so it must be greedy and backtrack:\n // matching lazily would read `<-delegates-to-` as the relation `delegates`.\n const forward = /^(-|=)([A-Za-z,*-]*?)(->|=>)/.exec(rest);\n const backward = /^(<-|<=)([A-Za-z,*-]*)(-|=)(?=\\s|$|[A-Za-z*[])/.exec(rest);\n\n // Both are anchored where the step starts and open with different\n // characters, so at most one of them matches.\n if (forward) {\n const transitive = (forward[1] as string) === '=';\n const closing = forward[3] as string;\n if ((closing === '=>') !== transitive) {\n throw new QueryError('mismatched arrow: use -kind-> for one hop or =kind=> for transitive', this.position);\n }\n const kinds = this.parseKinds(forward[2] as string, this.position + 1);\n this.position += (forward[0] as string).length;\n return { direction: 'out', kinds, transitive, node: this.parseNodeMatcher() };\n }\n\n if (backward) {\n const transitive = (backward[1] as string) === '<=';\n const closing = backward[3] as string;\n if ((closing === '=') !== transitive) {\n throw new QueryError('mismatched arrow: use <-kind- for one hop or <=kind= for transitive', this.position);\n }\n const kinds = this.parseKinds(backward[2] as string, this.position + 2);\n this.position += (backward[0] as string).length;\n return { direction: 'in', kinds, transitive, node: this.parseNodeMatcher() };\n }\n\n throw new QueryError(\n 'expected a relation step such as -delegates-to->, <-assumes- or =supersedes=>',\n this.position,\n );\n }\n\n /**\n * Reads the relation list between a step's arrows.\n *\n * Nothing is trimmed, because the step patterns admit no whitespace inside\n * an arrow. An empty list is not a special case either: it splits into one\n * empty name, which is skipped, and no names at all means any relation.\n */\n private parseKinds(raw: string, offset: number): readonly EdgeKind[] | null {\n if (raw === '*') return null;\n const kinds: EdgeKind[] = [];\n for (const part of raw.split(',')) {\n const name = part.toLowerCase();\n if (name.length === 0) continue;\n if (!(EDGE_KINDS as readonly string[]).includes(name)) {\n throw new QueryError(`unknown relation \"${name}\", expected one of: ${EDGE_KINDS.join(', ')}`, offset);\n }\n kinds.push(name as EdgeKind);\n }\n // `null` rather than an empty list, which the graph would read the same\n // way: the spec says \"any relation\" in the form its type documents.\n return kinds.length > 0 ? kinds : null;\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Attributes */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Every attribute key a node can answer to, excluding the open `fm.` namespace.\n *\n * Selectors do not validate their keys - an unknown one simply matches nothing,\n * which is the forgiving reading a query typed at a prompt wants. A message\n * template is the opposite case: `{1.phse}` would render as an empty string in\n * a diagnostic somebody has to act on, silently and forever. So the list is\n * published here, next to the switch it mirrors, for the one caller that needs\n * to be strict. See ADR-0016.\n */\nexport const SELECTOR_KEYS: readonly string[] = Object.freeze([\n 'alias',\n 'body',\n 'conflicted',\n 'disposition',\n 'document',\n 'evidence',\n 'file',\n 'id',\n 'kind',\n 'line',\n 'openness',\n 'path',\n 'phase',\n 'receptivity',\n 'section',\n 'state',\n 'status',\n 'text',\n 'title',\n]);\n\n/**\n * The values a predicate key sees on a node.\n *\n * Multi-valued keys (`alias`, `section`) match when *any* value matches, which\n * is what makes `[section=Open Questions]` read the way people expect.\n */\nexport function attributesOf(node: SpecNode, key: string, graph?: SpecGraph): string[] {\n switch (key) {\n case 'id':\n return [node.id];\n case 'kind':\n return [node.kind];\n case 'title':\n return [node.title];\n case 'file':\n return [node.at.file];\n case 'line':\n return [String(node.at.span.start.line)];\n case 'document':\n return [node.kind === 'item' ? node.document : node.id];\n }\n\n if (node.kind === 'document') {\n switch (key) {\n case 'path':\n return [node.path];\n case 'phase':\n return [node.phase];\n case 'status':\n return node.rawStatus === null ? [] : [node.rawStatus];\n case 'receptivity':\n return [receptivityOf(node.phase)];\n case 'alias':\n return [...node.aliases];\n }\n if (key.startsWith('fm.')) {\n const value = node.frontMatter[key.slice(3)];\n if (value === undefined) return [];\n return typeof value === 'string' ? [value] : [...value];\n }\n return [];\n }\n\n // An item inherits its document's lifecycle. Without this, the two rules that\n // matter most could not be written as queries at all, because the question\n // they ask - \"is the document holding this obligation still open?\" - is about\n // the owner, not the item.\n if (key === 'phase' || key === 'receptivity' || key === 'status' || key === 'path' || key === 'alias') {\n const owner = graph?.owningDocument(node.id);\n if (owner) return attributesOf(owner, key, graph);\n return key === 'path' ? [node.at.file] : [];\n }\n\n switch (key) {\n case 'state':\n case 'disposition':\n return [node.disposition];\n case 'openness':\n return [node.openness];\n case 'section':\n return [...node.section];\n case 'text':\n return [node.text];\n case 'body':\n return [node.body];\n case 'evidence':\n return [node.evidence.source];\n case 'conflicted':\n return [node.conflicts.length > 0 ? 'true' : 'false'];\n default:\n return [];\n }\n}\n\nfunction testPredicate(node: SpecNode, predicate: Predicate, graph?: SpecGraph): boolean {\n const values = attributesOf(node, predicate.key, graph);\n const needle = predicate.value.toLowerCase();\n\n if (predicate.operator === 'exists') return values.some((value) => value.length > 0);\n if (predicate.operator === '!=') return !values.some((value) => value.toLowerCase() === needle);\n\n return values.some((raw) => {\n const value = raw.toLowerCase();\n switch (predicate.operator) {\n case '=':\n return value === needle;\n case '^=':\n return value.startsWith(needle);\n case '$=':\n return value.endsWith(needle);\n case '*=':\n return value.includes(needle);\n case '~=':\n return pattern(predicate.value).test(raw);\n default:\n // Unreachable: `exists` and `!=` returned above, and the type knows\n // no other operator. Narrowing does not reach into this callback.\n return false;\n }\n });\n}\n\nconst patterns = new Map<string, RegexMatcher>();\n\n/**\n * Compiles a `~=` pattern, once per distinct pattern.\n *\n * Cached because a rule runs its predicate against every node in the corpus,\n * and because compiling is the expensive half now that matching is linear.\n * The cache changes the cost and nothing else: a pattern compiled twice\n * matches what it matched once.\n */\nexport function pattern(source: string): RegexMatcher {\n const cached = patterns.get(source);\n if (cached) return cached;\n const compiled = compileRegex(source);\n patterns.set(source, compiled);\n return compiled;\n}\n\nfunction quoted(char: string | undefined): boolean {\n return char === '\"' || char === \"'\";\n}\n\n/** True when a node satisfies a matcher. */\nexport function matches(node: SpecNode, matcher: NodeMatcher, graph?: SpecGraph): boolean {\n if (matcher.kind !== null && node.kind !== matcher.kind) return false;\n return matcher.predicates.every((predicate) => testPredicate(node, predicate, graph));\n}\n\n/* -------------------------------------------------------------------------- */\n/* Execution */\n/* -------------------------------------------------------------------------- */\n\nexport interface ExecuteOptions {\n /**\n * Cap on the paths produced by each traversal step.\n *\n * It bounds *expansion*, not the initial selection: a query with no steps\n * returns every matching node. That asymmetry is deliberate. The limit exists\n * so a query across a dense graph cannot fan out unboundedly, and the rules\n * run through this engine - truncating the set of nodes a rule starts from\n * would silently drop findings, whereas truncating a runaway expansion only\n * ever drops paths that were already beyond reading.\n */\n readonly limit?: number | undefined;\n /**\n * Whether a step may stay inside one document.\n *\n * Off by default for reflexive edges: a document that links to itself is a\n * formatting quirk, not a relationship, and letting those through turns every\n * table of contents into a finding.\n */\n readonly allowReflexive?: boolean | undefined;\n}\n\n/** Runs a compiled query and returns every matching path. */\nexport function execute(graph: SpecGraph, spec: QuerySpec, options: ExecuteOptions = {}): Match[] {\n const limit = options.limit ?? DEFAULT_MATCH_LIMIT;\n const allowReflexive = options.allowReflexive ?? false;\n\n let frontier: Match[] = [];\n for (const node of graph.nodes.values()) {\n if (matches(node, spec.start, graph)) frontier.push({ nodes: [node], edges: [] });\n }\n\n for (const step of spec.steps) {\n const next: Match[] = [];\n const seen = new Set<string>();\n\n for (const match of frontier) {\n const tail = match.nodes[match.nodes.length - 1] as SpecNode;\n for (const { node, path } of expand(graph, tail, step, allowReflexive)) {\n if (!matches(node, step.node, graph)) continue;\n const key = `${match.nodes.map((n) => n.id).join('>')}>${node.id}`;\n if (seen.has(key)) continue;\n seen.add(key);\n next.push({ nodes: [...match.nodes, node], edges: [...match.edges, ...path] });\n if (next.length >= limit) break;\n }\n if (next.length >= limit) break;\n }\n\n frontier = next;\n // An empty frontier stays empty through every later step; this only stops\n // walking them.\n if (frontier.length === 0) break;\n }\n\n return frontier;\n}\n\ninterface Expansion {\n readonly node: SpecNode;\n readonly path: readonly Edge[];\n}\n\n// `buildGraph` drops every edge that names an unknown node, so the `if (node)`\n// guards below never skip anything in a graph it built. They stand for a\n// `SpecGraph` built some other way.\nfunction expand(graph: SpecGraph, from: SpecNode, step: StepSpec, allowReflexive: boolean): Expansion[] {\n const kinds = step.kinds ?? [];\n const out: Expansion[] = [];\n\n if (!step.transitive) {\n const edges = step.direction === 'out' ? graph.out(from.id, kinds) : graph.in(from.id, kinds);\n for (const edge of edges) {\n if (!allowReflexive && edge.reflexive) continue;\n const targetId = step.direction === 'out' ? edge.to : edge.from;\n const node = graph.node(targetId);\n if (node) out.push({ node, path: [edge] });\n }\n return out;\n }\n\n // Transitive steps take the shortest path to each reachable node, which keeps\n // the result deterministic and the reported evidence minimal.\n const reached =\n step.direction === 'out'\n ? graph.reach(from.id, kinds)\n : reachIncoming(graph, from.id, kinds);\n\n for (const [id, path] of reached) {\n if (!allowReflexive && path.every((edge) => edge.reflexive)) continue;\n const node = graph.node(id);\n if (node) out.push({ node, path });\n }\n return out;\n}\n\nfunction reachIncoming(graph: SpecGraph, id: string, kinds: readonly EdgeKind[]): Map<string, readonly Edge[]> {\n const paths = new Map<string, readonly Edge[]>();\n const pathTo = new Map<string, readonly Edge[]>([[id, []]]);\n const queue: string[] = [id];\n\n while (queue.length > 0) {\n const current = queue.shift() as string;\n const prefix = pathTo.get(current) as readonly Edge[];\n for (const edge of graph.in(current, kinds)) {\n if (pathTo.has(edge.from) && edge.from !== id) continue;\n const path = [...prefix, edge];\n if (edge.from === id) {\n if (!paths.has(id)) paths.set(id, path);\n continue;\n }\n pathTo.set(edge.from, path);\n paths.set(edge.from, path);\n queue.push(edge.from);\n }\n }\n return paths;\n}\n\n/** Parses and runs a selector in one call. */\nexport function query(graph: SpecGraph, selector: string, options?: ExecuteOptions): Match[] {\n return execute(graph, parseQuery(selector), options);\n}\n\n/**\n * Renders a match the way the selector reads, for `spec-graph query`.\n *\n * Every edge is written in the direction it was walked - `A -kind-> B`\n * forwards, `A <-kind- B` backwards - and names the node it arrives at, so a\n * transitive step shows each document it passed through. Reading the nodes off\n * `match.nodes` instead printed the end of a transitive path in its middle, and\n * a backward step as a relation pointing the other way.\n */\nexport function renderMatch(match: Match): string {\n let at = (match.nodes[0] as SpecNode).id;\n const parts: string[] = [at];\n for (const edge of match.edges) {\n const forwards = edge.from === at;\n at = forwards ? edge.to : edge.from;\n parts.push(forwards ? `-${edge.kind}->` : `<-${edge.kind}-`, at);\n }\n return parts.join(' ');\n}\n"]}
1
+ {"version":3,"file":"select.js","sourceRoot":"","sources":["../src/select.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,UAAU,EAA0D,MAAM,YAAY,CAAC;AAChG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAqB,MAAM,qCAAqC,CAAC;AA8ClG,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,6DAA6D;IACpD,MAAM,CAAS;IAExB,YAAY,OAAe,EAAE,MAAc;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAE1C,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAEhF;;;;;;;GAOG;AACH,MAAM,UAAU,GAAyC,IAAI,GAAG,CAA0B;IACxF,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,WAAW,EAAE,UAAU,CAAC;IACzB,CAAC,KAAK,EAAE,UAAU,CAAC;IACnB,CAAC,MAAM,EAAE,UAAU,CAAC;IACpB,CAAC,MAAM,EAAE,MAAM,CAAC;IAChB,CAAC,OAAO,EAAE,MAAM,CAAC;IACjB,CAAC,MAAM,EAAE,IAAI,CAAC;IACd,CAAC,OAAO,EAAE,IAAI,CAAC;IACf,CAAC,KAAK,EAAE,IAAI,CAAC;IACb,CAAC,GAAG,EAAE,IAAI,CAAC;CACZ,CAAC,CAAC;AAEH,2DAA2D;AAC3D,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;IACxC,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,MAAM,CAAC,SAAS,EAAE,CAAC;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;QAC/B,MAAM,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,MAAM;IACO,MAAM,CAAS;IACxB,QAAQ,GAAG,CAAC,CAAC;IAErB,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC7C,CAAC;IAED,SAAS;QACP,wEAAwE;QACxE,4EAA4E;QAC5E,8BAA8B;QAC9B,OAAO,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAW,CAAC;YAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IACnH,CAAC;IAED,gBAAgB;QACd,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,IAAI,GAAoB,IAAI,CAAC;QACjC,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACrE,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,IAAI,GAAI,IAAI,CAAC,CAAC,CAAY,CAAC,WAAW,EAAE,CAAC;YAC/C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,UAAU,CAClB,sBAAsB,IAAI,CAAC,CAAC,CAAC,6CAA6C,EAC1E,IAAI,CAAC,QAAQ,CACd,CAAC;YACJ,CAAC;YACD,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAoB,CAAC;YAC/C,IAAI,CAAC,QAAQ,IAAK,IAAI,CAAC,CAAC,CAAY,CAAC,MAAM,CAAC;QAC9C,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC;YAC9C,MAAM,IAAI,UAAU,CAAC,6DAA6D,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrG,CAAC;QAED,MAAM,UAAU,GAAgB,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG;YAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;QAClF,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC9B,CAAC;IAEO,cAAc;QACpB,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnB,MAAM,QAAQ,GAAG,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACrF,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,UAAU,CAAC,sCAAsC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3F,MAAM,GAAG,GAAI,QAAQ,CAAC,CAAC,CAAY,CAAC,WAAW,EAAE,CAAC;QAClD,IAAI,CAAC,QAAQ,IAAK,QAAQ,CAAC,CAAC,CAAY,CAAC,MAAM,CAAC;QAEhD,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC;YACvC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnB,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QAChD,CAAC;QAED,MAAM,aAAa,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACtF,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,UAAU,CAClB,8DAA8D,GAAG,GAAG,EACpE,IAAI,CAAC,QAAQ,CACd,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAa,CAAC;QAC9C,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC;QAEjC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC;QACjC,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG;YAAE,MAAM,IAAI,UAAU,CAAC,qCAAqC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnH,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnB,2EAA2E;QAC3E,4EAA4E;QAC5E,yEAAyE;QACzE,yEAAyE;QACzE,4CAA4C;QAC5C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,IAAI,CAAC;gBACH,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,CAAC,KAAK,YAAY,UAAU,CAAC;oBAAE,MAAM,KAAK,CAAC;gBAChD,MAAM,IAAI,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7G,CAAC;QACH,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClC,CAAC;IAEO,UAAU;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YACnC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnB,IAAI,GAAG,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,KAAK,EAAE,CAAC;gBAClF,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI;oBAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;gBAC5D,mEAAmE;gBACnE,gEAAgE;gBAChE,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACxC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACrB,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,KAAK;gBAAE,MAAM,IAAI,UAAU,CAAC,2BAA2B,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3G,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnB,OAAO,GAAG,CAAC;QACb,CAAC;QACD,sEAAsE;QACtE,kEAAkE;QAClE,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjE,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAW,CAAC;QAC3C,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,SAAS;QACP,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9C,4EAA4E;QAC5E,sEAAsE;QACtE,2EAA2E;QAC3E,4EAA4E;QAC5E,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,gDAAgD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE7E,kEAAkE;QAClE,8CAA8C;QAC9C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,UAAU,GAAI,OAAO,CAAC,CAAC,CAAY,KAAK,GAAG,CAAC;YAClD,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAW,CAAC;YACrC,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,KAAK,UAAU,EAAE,CAAC;gBACtC,MAAM,IAAI,UAAU,CAAC,qEAAqE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7G,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAW,EAAE,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YACvE,IAAI,CAAC,QAAQ,IAAK,OAAO,CAAC,CAAC,CAAY,CAAC,MAAM,CAAC;YAC/C,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC;QAChF,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,UAAU,GAAI,QAAQ,CAAC,CAAC,CAAY,KAAK,IAAI,CAAC;YACpD,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAW,CAAC;YACtC,IAAI,CAAC,OAAO,KAAK,GAAG,CAAC,KAAK,UAAU,EAAE,CAAC;gBACrC,MAAM,IAAI,UAAU,CAAC,qEAAqE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7G,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAW,EAAE,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YACxE,IAAI,CAAC,QAAQ,IAAK,QAAQ,CAAC,CAAC,CAAY,CAAC,MAAM,CAAC;YAChD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC;QAC/E,CAAC;QAED,MAAM,IAAI,UAAU,CAClB,+EAA+E,EAC/E,IAAI,CAAC,QAAQ,CACd,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACK,UAAU,CAAC,GAAW,EAAE,MAAc;QAC5C,IAAI,GAAG,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAC7B,MAAM,KAAK,GAAe,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YAChC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAChC,IAAI,CAAE,UAAgC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtD,MAAM,IAAI,UAAU,CAAC,qBAAqB,IAAI,uBAAuB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YACxG,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,IAAgB,CAAC,CAAC;QAC/B,CAAC;QACD,wEAAwE;QACxE,oEAAoE;QACpE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,CAAC;CACF;AAED,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAEhF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,aAAa,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC5D,OAAO;IACP,MAAM;IACN,YAAY;IACZ,aAAa;IACb,UAAU;IACV,UAAU;IACV,MAAM;IACN,IAAI;IACJ,MAAM;IACN,MAAM;IACN,UAAU;IACV,MAAM;IACN,OAAO;IACP,aAAa;IACb,SAAS;IACT,OAAO;IACP,QAAQ;IACR,MAAM;IACN,OAAO;CACR,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,IAAc,EAAE,GAAW,EAAE,KAAiB;IACzE,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,IAAI;YACP,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,KAAK,MAAM;YACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,KAAK,OAAO;YACV,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtB,KAAK,MAAM;YACT,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QACxB,KAAK,MAAM;YACT,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,KAAK,UAAU;YACb,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7B,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,MAAM;gBACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,KAAK,OAAO;gBACV,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,KAAK,QAAQ;gBACX,OAAO,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACzD,KAAK,aAAa;gBAChB,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACrC,KAAK,OAAO;gBACV,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,mEAAmE;YACnE,mCAAmC;YACnC,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC;gBAAE,OAAO,EAAE,CAAC;YACtD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAA+B,CAAC;YACnE,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,8EAA8E;IAC9E,2EAA2E;IAC3E,8EAA8E;IAC9E,2BAA2B;IAC3B,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,aAAa,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACtG,MAAM,KAAK,GAAG,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7C,IAAI,KAAK;YAAE,OAAO,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAClD,OAAO,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,CAAC;IAED,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,aAAa;YAChB,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5B,KAAK,UAAU;YACb,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzB,KAAK,SAAS;YACZ,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3B,KAAK,MAAM;YACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,KAAK,UAAU;YACb,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChC,KAAK,YAAY;YACf,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACxD;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAc,EAAE,SAAoB,EAAE,KAAiB;IAC5E,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;IAE7C,IAAI,SAAS,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACrF,IAAI,SAAS,CAAC,QAAQ,KAAK,IAAI;QAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;IAEhG,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;QACzB,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QAChC,QAAQ,SAAS,CAAC,QAAQ,EAAE,CAAC;YAC3B,KAAK,GAAG;gBACN,OAAO,KAAK,KAAK,MAAM,CAAC;YAC1B,KAAK,IAAI;gBACP,OAAO,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAClC,KAAK,IAAI;gBACP,OAAO,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAChC,KAAK,IAAI;gBACP,OAAO,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAChC,KAAK,IAAI;gBACP,OAAO,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5C;gBACE,oEAAoE;gBACpE,kEAAkE;gBAClE,OAAO,KAAK,CAAC;QACjB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,MAAc;IACpC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,MAAM,CAAC,IAAwB;IACtC,OAAO,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC;AACtC,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,OAAO,CAAC,IAAc,EAAE,OAAoB,EAAE,KAAiB;IAC7E,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACtE,OAAO,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;AACxF,CAAC;AA4BD,6DAA6D;AAC7D,MAAM,UAAU,OAAO,CAAC,KAAgB,EAAE,IAAe,EAAE,OAAO,GAAmB,EAAE;IACrF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,mBAAmB,CAAC;IACnD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC;IAEvD,IAAI,QAAQ,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IACpF,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAY,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAE/B,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAa,CAAC;YAC7D,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,CAAC;gBACvE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC;oBAAE,SAAS;gBAC/C,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;gBACnE,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACd,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC/E,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK;oBAAE,MAAM;YAClC,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK;gBAAE,MAAM;QAClC,CAAC;QAED,QAAQ,GAAG,IAAI,CAAC;QAChB,0EAA0E;QAC1E,gBAAgB;QAChB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;IACnC,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAOD,+EAA+E;AAC/E,yEAAyE;AACzE,oCAAoC;AACpC,SAAS,MAAM,CAAC,KAAgB,EAAE,IAAc,EAAE,IAAc,EAAE,cAAuB;IACvF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;IAC/B,MAAM,GAAG,GAAgB,EAAE,CAAC;IAE5B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC9F,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,SAAS;gBAAE,SAAS;YAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YAChE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAClC,IAAI,IAAI;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,8EAA8E;IAC9E,8DAA8D;IAC9D,MAAM,OAAO,GACX,IAAI,CAAC,SAAS,KAAK,KAAK;QACtB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC;QAC7B,CAAC,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAE3C,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,SAAS;QACtE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5B,IAAI,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,KAAgB,EAAE,EAAU,EAAE,KAA0B;IAC7E,MAAM,KAAK,GAAG,IAAI,GAAG,EAA2B,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,GAAG,CAA0B,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAa,CAAC,EAAE,CAAC,CAAC;IAE7B,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAY,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAoB,CAAC;QACtD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;YAC5C,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE;gBAAE,SAAS;YACxD,MAAM,IAAI,GAAG,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC;YAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE,EAAE,CAAC;gBACrB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;gBACxC,SAAS;YACX,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC5B,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,KAAK,CAAC,KAAgB,EAAE,QAAgB,EAAE,OAAwB;IAChF,OAAO,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,KAAY;IACtC,IAAI,EAAE,GAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAc,CAAC,EAAE,CAAC;IACzC,MAAM,KAAK,GAAa,CAAC,EAAE,CAAC,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAClC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC","sourcesContent":["/**\n * The selector language.\n *\n * Rules and users ask the graph the same kinds of question, so they use the same\n * machinery. Every built-in diagnostic is a path query plus a message; anything\n * a rule can find, `spec-graph query` can find too, which means a team can\n * express a convention spec-graph never anticipated without writing a plugin.\n *\n * ```text\n * item[openness=open] -delegates-to-> document[phase=retired]\n * document[phase=active] -assumes,depends-on-> document[phase=retired]\n * document =supersedes=> document[phase=active]\n * item[state=obviated] <-assumes- *\n * *[path^=docs/adr] -blocked-by-> item[openness=open]\n * ```\n *\n * A query evaluates to **paths**, not endpoints. A rule that has the whole path\n * can say \"ADR-0004 line 31 delegates this to ADR-0002, which was retired in\n * March\" instead of naming two documents and leaving the reader to reconstruct\n * the relationship.\n */\n\nimport { receptivityOf } from './lifecycle.js';\nimport type { SpecGraph } from './graph.js';\nimport { EDGE_KINDS, type Edge, type EdgeKind, type NodeKind, type SpecNode } from './types.js';\nimport { compileRegex, RegexError, type RegexMatcher } from './vendor/spec-core/pattern/index.js';\n\n/* -------------------------------------------------------------------------- */\n/* Shapes */\n/* -------------------------------------------------------------------------- */\n\nexport type Operator = '=' | '!=' | '^=' | '$=' | '*=' | '~=' | 'exists';\n\nexport interface Predicate {\n readonly key: string;\n readonly operator: Operator;\n readonly value: string;\n}\n\nexport interface NodeMatcher {\n /** `null` matches both documents and items. */\n readonly kind: NodeKind | null;\n readonly predicates: readonly Predicate[];\n}\n\nexport interface StepSpec {\n readonly direction: 'out' | 'in';\n /** `null` matches any edge kind. */\n readonly kinds: readonly EdgeKind[] | null;\n /** Follow the relation transitively, taking the shortest path to each node. */\n readonly transitive: boolean;\n readonly node: NodeMatcher;\n}\n\nexport interface QuerySpec {\n readonly start: NodeMatcher;\n readonly steps: readonly StepSpec[];\n}\n\n/**\n * One path through the graph.\n *\n * `nodes` holds the start and the node each step matched; `edges` is the whole\n * path in order. A transitive step contributes one node and every edge it\n * walked, so the nodes it passed through are named only by those edges.\n */\nexport interface Match {\n readonly nodes: readonly SpecNode[];\n readonly edges: readonly Edge[];\n}\n\nexport class QueryError extends Error {\n /** Offset into the query string where the problem starts. */\n readonly offset: number;\n\n constructor(message: string, offset: number) {\n super(message);\n this.name = 'QueryError';\n this.offset = offset;\n }\n}\n\n/** Guards against a pathological query fanning out over a huge corpus. */\nexport const DEFAULT_MATCH_LIMIT = 10_000;\n\n/* -------------------------------------------------------------------------- */\n/* Parsing */\n/* -------------------------------------------------------------------------- */\n\n/**\n * The words that name a node type, and `null` for the ones that name any.\n *\n * A map, because a selector is typed by a person and an object answers to\n * more names than it was given: `constructor` was a node type no node has, so\n * `constructor[status=draft]` matched nothing where `construct[status=draft]`\n * was refused.\n */\nconst NODE_KINDS: ReadonlyMap<string, NodeKind | null> = new Map<string, NodeKind | null>([\n ['document', 'document'],\n ['documents', 'document'],\n ['doc', 'document'],\n ['docs', 'document'],\n ['item', 'item'],\n ['items', 'item'],\n ['node', null],\n ['nodes', null],\n ['any', null],\n ['*', null],\n]);\n\n/** Compiles a selector string into a {@link QuerySpec}. */\nexport function parseQuery(source: string): QuerySpec {\n const parser = new Parser(source);\n const start = parser.parseNodeMatcher();\n const steps: StepSpec[] = [];\n parser.skipSpace();\n while (!parser.atEnd()) {\n steps.push(parser.parseStep());\n parser.skipSpace();\n }\n return { start, steps };\n}\n\nclass Parser {\n private readonly source: string;\n private position = 0;\n\n constructor(source: string) {\n this.source = source;\n }\n\n atEnd(): boolean {\n return this.position >= this.source.length;\n }\n\n skipSpace(): void {\n // The bound decides nothing: past the end the character is `undefined`,\n // which `test` reads as the word \"undefined\" and rejects. It is kept so the\n // loop does not lean on that.\n while (this.position < this.source.length && /\\s/.test(this.source[this.position] as string)) this.position += 1;\n }\n\n parseNodeMatcher(): NodeMatcher {\n this.skipSpace();\n let kind: NodeKind | null = null;\n const word = /^[A-Za-z]+|^\\*/.exec(this.source.slice(this.position));\n if (word) {\n const text = (word[0] as string).toLowerCase();\n if (!NODE_KINDS.has(text)) {\n throw new QueryError(\n `unknown node type \"${word[0]}\", expected one of: document, item, * (any)`,\n this.position,\n );\n }\n kind = NODE_KINDS.get(text) as NodeKind | null;\n this.position += (word[0] as string).length;\n } else if (this.source[this.position] !== '[') {\n throw new QueryError('expected a node type (document, item or *) or a [predicate]', this.position);\n }\n\n const predicates: Predicate[] = [];\n while (this.source[this.position] === '[') predicates.push(this.parsePredicate());\n return { kind, predicates };\n }\n\n private parsePredicate(): Predicate {\n this.position += 1;\n const keyMatch = /^[A-Za-z_][A-Za-z0-9_.:-]*/.exec(this.source.slice(this.position));\n if (!keyMatch) throw new QueryError('expected an attribute name after \"[\"', this.position);\n const key = (keyMatch[0] as string).toLowerCase();\n this.position += (keyMatch[0] as string).length;\n\n if (this.source[this.position] === ']') {\n this.position += 1;\n return { key, operator: 'exists', value: '' };\n }\n\n const operatorMatch = /^(!=|\\^=|\\$=|\\*=|~=|=)/.exec(this.source.slice(this.position));\n if (!operatorMatch) {\n throw new QueryError(\n `expected an operator (=, !=, ^=, $=, *=, ~=) or \"]\" after \"${key}\"`,\n this.position,\n );\n }\n const operator = operatorMatch[0] as Operator;\n this.position += operator.length;\n\n const valueStart = this.position;\n const value = this.parseValue();\n if (this.source[this.position] !== ']') throw new QueryError('expected \"]\" to close the predicate', this.position);\n this.position += 1;\n // A pattern is checked here rather than where it runs, because here is the\n // only place that knows where in the selector it was written. It used to be\n // compiled on first use and an unparseable one silently matched nothing,\n // which is the same silence a misspelled relation would have produced if\n // this grammar tolerated one. See ADR-0017.\n if (operator === '~=') {\n try {\n pattern(value);\n } catch (error) {\n if (!(error instanceof RegexError)) throw error;\n throw new QueryError(error.message, valueStart + error.offset + (quoted(this.source[valueStart]) ? 1 : 0));\n }\n }\n return { key, operator, value };\n }\n\n private parseValue(): string {\n const quote = this.source[this.position];\n if (quote === '\"' || quote === \"'\") {\n this.position += 1;\n let out = '';\n while (this.position < this.source.length && this.source[this.position] !== quote) {\n if (this.source[this.position] === '\\\\') this.position += 1;\n // `?? ''` meets only a backslash at the very end, and the value it\n // builds then is thrown away with the unterminated quote below.\n out += this.source[this.position] ?? '';\n this.position += 1;\n }\n if (this.source[this.position] !== quote) throw new QueryError('unterminated quoted value', this.position);\n this.position += 1;\n return out;\n }\n // The anchor decides nothing, since a starred class matches the empty\n // string where the search starts; it says where the value begins.\n const match = /^[^\\]\\s]*/.exec(this.source.slice(this.position));\n const value = (match?.[0] ?? '') as string;\n this.position += value.length;\n return value;\n }\n\n parseStep(): StepSpec {\n const rest = this.source.slice(this.position);\n // The forward form is lazy because its terminator is two characters (`->`),\n // which cannot be confused with the hyphen inside `delegates-to`. The\n // backward terminator is a single `-`, so it must be greedy and backtrack:\n // matching lazily would read `<-delegates-to-` as the relation `delegates`.\n const forward = /^(-|=)([A-Za-z,*-]*?)(->|=>)/.exec(rest);\n const backward = /^(<-|<=)([A-Za-z,*-]*)(-|=)(?=\\s|$|[A-Za-z*[])/.exec(rest);\n\n // Both are anchored where the step starts and open with different\n // characters, so at most one of them matches.\n if (forward) {\n const transitive = (forward[1] as string) === '=';\n const closing = forward[3] as string;\n if ((closing === '=>') !== transitive) {\n throw new QueryError('mismatched arrow: use -kind-> for one hop or =kind=> for transitive', this.position);\n }\n const kinds = this.parseKinds(forward[2] as string, this.position + 1);\n this.position += (forward[0] as string).length;\n return { direction: 'out', kinds, transitive, node: this.parseNodeMatcher() };\n }\n\n if (backward) {\n const transitive = (backward[1] as string) === '<=';\n const closing = backward[3] as string;\n if ((closing === '=') !== transitive) {\n throw new QueryError('mismatched arrow: use <-kind- for one hop or <=kind= for transitive', this.position);\n }\n const kinds = this.parseKinds(backward[2] as string, this.position + 2);\n this.position += (backward[0] as string).length;\n return { direction: 'in', kinds, transitive, node: this.parseNodeMatcher() };\n }\n\n throw new QueryError(\n 'expected a relation step such as -delegates-to->, <-assumes- or =supersedes=>',\n this.position,\n );\n }\n\n /**\n * Reads the relation list between a step's arrows.\n *\n * Nothing is trimmed, because the step patterns admit no whitespace inside\n * an arrow. An empty list is not a special case either: it splits into one\n * empty name, which is skipped, and no names at all means any relation.\n */\n private parseKinds(raw: string, offset: number): readonly EdgeKind[] | null {\n if (raw === '*') return null;\n const kinds: EdgeKind[] = [];\n for (const part of raw.split(',')) {\n const name = part.toLowerCase();\n if (name.length === 0) continue;\n if (!(EDGE_KINDS as readonly string[]).includes(name)) {\n throw new QueryError(`unknown relation \"${name}\", expected one of: ${EDGE_KINDS.join(', ')}`, offset);\n }\n kinds.push(name as EdgeKind);\n }\n // `null` rather than an empty list, which the graph would read the same\n // way: the spec says \"any relation\" in the form its type documents.\n return kinds.length > 0 ? kinds : null;\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Attributes */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Every attribute key a node can answer to, excluding the open `fm.` namespace.\n *\n * Selectors do not validate their keys - an unknown one simply matches nothing,\n * which is the forgiving reading a query typed at a prompt wants. A message\n * template is the opposite case: `{1.phse}` would render as an empty string in\n * a diagnostic somebody has to act on, silently and forever. So the list is\n * published here, next to the switch it mirrors, for the one caller that needs\n * to be strict. See ADR-0016.\n */\nexport const SELECTOR_KEYS: readonly string[] = Object.freeze([\n 'alias',\n 'body',\n 'conflicted',\n 'disposition',\n 'document',\n 'evidence',\n 'file',\n 'id',\n 'kind',\n 'line',\n 'openness',\n 'path',\n 'phase',\n 'receptivity',\n 'section',\n 'state',\n 'status',\n 'text',\n 'title',\n]);\n\n/**\n * The values a predicate key sees on a node.\n *\n * Multi-valued keys (`alias`, `section`) match when *any* value matches, which\n * is what makes `[section=Open Questions]` read the way people expect.\n */\nexport function attributesOf(node: SpecNode, key: string, graph?: SpecGraph): string[] {\n switch (key) {\n case 'id':\n return [node.id];\n case 'kind':\n return [node.kind];\n case 'title':\n return [node.title];\n case 'file':\n return [node.at.file];\n case 'line':\n return [String(node.at.span.start.line)];\n case 'document':\n return [node.kind === 'item' ? node.document : node.id];\n }\n\n if (node.kind === 'document') {\n switch (key) {\n case 'path':\n return [node.path];\n case 'phase':\n return [node.phase];\n case 'status':\n return node.rawStatus === null ? [] : [node.rawStatus];\n case 'receptivity':\n return [receptivityOf(node.phase)];\n case 'alias':\n return [...node.aliases];\n }\n if (key.startsWith('fm.')) {\n // Its own keys only: every object answers to `constructor`, and no\n // document's front matter said so.\n const name = key.slice(3);\n if (!Object.hasOwn(node.frontMatter, name)) return [];\n const value = node.frontMatter[name] as string | readonly string[];\n return typeof value === 'string' ? [value] : [...value];\n }\n return [];\n }\n\n // An item inherits its document's lifecycle. Without this, the two rules that\n // matter most could not be written as queries at all, because the question\n // they ask - \"is the document holding this obligation still open?\" - is about\n // the owner, not the item.\n if (key === 'phase' || key === 'receptivity' || key === 'status' || key === 'path' || key === 'alias') {\n const owner = graph?.owningDocument(node.id);\n if (owner) return attributesOf(owner, key, graph);\n return key === 'path' ? [node.at.file] : [];\n }\n\n switch (key) {\n case 'state':\n case 'disposition':\n return [node.disposition];\n case 'openness':\n return [node.openness];\n case 'section':\n return [...node.section];\n case 'text':\n return [node.text];\n case 'body':\n return [node.body];\n case 'evidence':\n return [node.evidence.source];\n case 'conflicted':\n return [node.conflicts.length > 0 ? 'true' : 'false'];\n default:\n return [];\n }\n}\n\nfunction testPredicate(node: SpecNode, predicate: Predicate, graph?: SpecGraph): boolean {\n const values = attributesOf(node, predicate.key, graph);\n const needle = predicate.value.toLowerCase();\n\n if (predicate.operator === 'exists') return values.some((value) => value.length > 0);\n if (predicate.operator === '!=') return !values.some((value) => value.toLowerCase() === needle);\n\n return values.some((raw) => {\n const value = raw.toLowerCase();\n switch (predicate.operator) {\n case '=':\n return value === needle;\n case '^=':\n return value.startsWith(needle);\n case '$=':\n return value.endsWith(needle);\n case '*=':\n return value.includes(needle);\n case '~=':\n return pattern(predicate.value).test(raw);\n default:\n // Unreachable: `exists` and `!=` returned above, and the type knows\n // no other operator. Narrowing does not reach into this callback.\n return false;\n }\n });\n}\n\nconst patterns = new Map<string, RegexMatcher>();\n\n/**\n * Compiles a `~=` pattern, once per distinct pattern.\n *\n * Cached because a rule runs its predicate against every node in the corpus,\n * and because compiling is the expensive half now that matching is linear.\n * The cache changes the cost and nothing else: a pattern compiled twice\n * matches what it matched once.\n */\nexport function pattern(source: string): RegexMatcher {\n const cached = patterns.get(source);\n if (cached) return cached;\n const compiled = compileRegex(source);\n patterns.set(source, compiled);\n return compiled;\n}\n\nfunction quoted(char: string | undefined): boolean {\n return char === '\"' || char === \"'\";\n}\n\n/** True when a node satisfies a matcher. */\nexport function matches(node: SpecNode, matcher: NodeMatcher, graph?: SpecGraph): boolean {\n if (matcher.kind !== null && node.kind !== matcher.kind) return false;\n return matcher.predicates.every((predicate) => testPredicate(node, predicate, graph));\n}\n\n/* -------------------------------------------------------------------------- */\n/* Execution */\n/* -------------------------------------------------------------------------- */\n\nexport interface ExecuteOptions {\n /**\n * Cap on the paths produced by each traversal step.\n *\n * It bounds *expansion*, not the initial selection: a query with no steps\n * returns every matching node. That asymmetry is deliberate. The limit exists\n * so a query across a dense graph cannot fan out unboundedly, and the rules\n * run through this engine - truncating the set of nodes a rule starts from\n * would silently drop findings, whereas truncating a runaway expansion only\n * ever drops paths that were already beyond reading.\n */\n readonly limit?: number | undefined;\n /**\n * Whether a step may stay inside one document.\n *\n * Off by default for reflexive edges: a document that links to itself is a\n * formatting quirk, not a relationship, and letting those through turns every\n * table of contents into a finding.\n */\n readonly allowReflexive?: boolean | undefined;\n}\n\n/** Runs a compiled query and returns every matching path. */\nexport function execute(graph: SpecGraph, spec: QuerySpec, options: ExecuteOptions = {}): Match[] {\n const limit = options.limit ?? DEFAULT_MATCH_LIMIT;\n const allowReflexive = options.allowReflexive ?? false;\n\n let frontier: Match[] = [];\n for (const node of graph.nodes.values()) {\n if (matches(node, spec.start, graph)) frontier.push({ nodes: [node], edges: [] });\n }\n\n for (const step of spec.steps) {\n const next: Match[] = [];\n const seen = new Set<string>();\n\n for (const match of frontier) {\n const tail = match.nodes[match.nodes.length - 1] as SpecNode;\n for (const { node, path } of expand(graph, tail, step, allowReflexive)) {\n if (!matches(node, step.node, graph)) continue;\n const key = `${match.nodes.map((n) => n.id).join('>')}>${node.id}`;\n if (seen.has(key)) continue;\n seen.add(key);\n next.push({ nodes: [...match.nodes, node], edges: [...match.edges, ...path] });\n if (next.length >= limit) break;\n }\n if (next.length >= limit) break;\n }\n\n frontier = next;\n // An empty frontier stays empty through every later step; this only stops\n // walking them.\n if (frontier.length === 0) break;\n }\n\n return frontier;\n}\n\ninterface Expansion {\n readonly node: SpecNode;\n readonly path: readonly Edge[];\n}\n\n// `buildGraph` drops every edge that names an unknown node, so the `if (node)`\n// guards below never skip anything in a graph it built. They stand for a\n// `SpecGraph` built some other way.\nfunction expand(graph: SpecGraph, from: SpecNode, step: StepSpec, allowReflexive: boolean): Expansion[] {\n const kinds = step.kinds ?? [];\n const out: Expansion[] = [];\n\n if (!step.transitive) {\n const edges = step.direction === 'out' ? graph.out(from.id, kinds) : graph.in(from.id, kinds);\n for (const edge of edges) {\n if (!allowReflexive && edge.reflexive) continue;\n const targetId = step.direction === 'out' ? edge.to : edge.from;\n const node = graph.node(targetId);\n if (node) out.push({ node, path: [edge] });\n }\n return out;\n }\n\n // Transitive steps take the shortest path to each reachable node, which keeps\n // the result deterministic and the reported evidence minimal.\n const reached =\n step.direction === 'out'\n ? graph.reach(from.id, kinds)\n : reachIncoming(graph, from.id, kinds);\n\n for (const [id, path] of reached) {\n if (!allowReflexive && path.every((edge) => edge.reflexive)) continue;\n const node = graph.node(id);\n if (node) out.push({ node, path });\n }\n return out;\n}\n\nfunction reachIncoming(graph: SpecGraph, id: string, kinds: readonly EdgeKind[]): Map<string, readonly Edge[]> {\n const paths = new Map<string, readonly Edge[]>();\n const pathTo = new Map<string, readonly Edge[]>([[id, []]]);\n const queue: string[] = [id];\n\n while (queue.length > 0) {\n const current = queue.shift() as string;\n const prefix = pathTo.get(current) as readonly Edge[];\n for (const edge of graph.in(current, kinds)) {\n if (pathTo.has(edge.from) && edge.from !== id) continue;\n const path = [...prefix, edge];\n if (edge.from === id) {\n if (!paths.has(id)) paths.set(id, path);\n continue;\n }\n pathTo.set(edge.from, path);\n paths.set(edge.from, path);\n queue.push(edge.from);\n }\n }\n return paths;\n}\n\n/** Parses and runs a selector in one call. */\nexport function query(graph: SpecGraph, selector: string, options?: ExecuteOptions): Match[] {\n return execute(graph, parseQuery(selector), options);\n}\n\n/**\n * Renders a match the way the selector reads, for `spec-graph query`.\n *\n * Every edge is written in the direction it was walked - `A -kind-> B`\n * forwards, `A <-kind- B` backwards - and names the node it arrives at, so a\n * transitive step shows each document it passed through. Reading the nodes off\n * `match.nodes` instead printed the end of a transitive path in its middle, and\n * a backward step as a relation pointing the other way.\n */\nexport function renderMatch(match: Match): string {\n let at = (match.nodes[0] as SpecNode).id;\n const parts: string[] = [at];\n for (const edge of match.edges) {\n const forwards = edge.from === at;\n at = forwards ? edge.to : edge.from;\n parts.push(forwards ? `-${edge.kind}->` : `<-${edge.kind}-`, at);\n }\n return parts.join(' ');\n}\n"]}
package/dist/state.js CHANGED
@@ -147,26 +147,32 @@ const MARKERS = {
147
147
  ],
148
148
  unresolved: ['unresolved', 'still open', 'open question', 'tbd', 'undecided', 'reopened'],
149
149
  };
150
- /** Words accepted in a `state=` attribute, including friendly synonyms. */
151
- const STATE_ALIASES = {
152
- open: 'unresolved',
153
- unresolved: 'unresolved',
154
- todo: 'unresolved',
155
- narrowed: 'narrowed',
156
- partial: 'narrowed',
157
- delegated: 'delegated',
158
- satisfied: 'satisfied',
159
- resolved: 'satisfied',
160
- done: 'satisfied',
161
- closed: 'satisfied',
162
- 'accepted-debt': 'accepted-debt',
163
- debt: 'accepted-debt',
164
- wontfix: 'accepted-debt',
165
- rejected: 'rejected',
166
- declined: 'rejected',
167
- obviated: 'obviated',
168
- moot: 'obviated',
169
- };
150
+ /**
151
+ * Words accepted in a `state=` attribute, including friendly synonyms.
152
+ *
153
+ * A map, because the word comes from a document and an object answers to more
154
+ * names than it was given: `state="constructor"` was a state, of no
155
+ * disposition, and the run that met it ended there.
156
+ */
157
+ const STATE_ALIASES = new Map([
158
+ ['open', 'unresolved'],
159
+ ['unresolved', 'unresolved'],
160
+ ['todo', 'unresolved'],
161
+ ['narrowed', 'narrowed'],
162
+ ['partial', 'narrowed'],
163
+ ['delegated', 'delegated'],
164
+ ['satisfied', 'satisfied'],
165
+ ['resolved', 'satisfied'],
166
+ ['done', 'satisfied'],
167
+ ['closed', 'satisfied'],
168
+ ['accepted-debt', 'accepted-debt'],
169
+ ['debt', 'accepted-debt'],
170
+ ['wontfix', 'accepted-debt'],
171
+ ['rejected', 'rejected'],
172
+ ['declined', 'rejected'],
173
+ ['obviated', 'obviated'],
174
+ ['moot', 'obviated'],
175
+ ]);
170
176
  const MARKER_PATTERN = buildMarkerPattern();
171
177
  const MARKER_LOOKUP = markerLookup();
172
178
  /** Collects every state signal on an item and decides between them. */
@@ -177,7 +183,7 @@ export function resolveItemState(input) {
177
183
  if (input.directive) {
178
184
  const declared = attr(input.directive, 'state');
179
185
  if (declared) {
180
- const mapped = STATE_ALIASES[declared.value.toLowerCase()];
186
+ const mapped = STATE_ALIASES.get(declared.value.toLowerCase());
181
187
  if (mapped) {
182
188
  signals.push({
183
189
  source: 'directive',
@@ -376,5 +382,5 @@ function escapeRegExp(value) {
376
382
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
377
383
  }
378
384
  /** Exposed for the query language, which lets users filter on these names. */
379
- export const KNOWN_STATE_WORDS = Object.keys(STATE_ALIASES);
385
+ export const KNOWN_STATE_WORDS = [...STATE_ALIASES.keys()];
380
386
  //# sourceMappingURL=state.js.map
package/dist/state.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,OAAO,EAAE,KAAK,EAAkB,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,WAAW,EAAqE,MAAM,YAAY,CAAC;AAE5G,oEAAoE;AACpE,MAAM,eAAe,GAAoD;IACvE,SAAS,EAAE,CAAC;IACZ,MAAM,EAAE,CAAC;IACT,aAAa,EAAE,CAAC;IAChB,QAAQ,EAAE,CAAC;IACX,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,CAAC;CACX,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,cAAc,GAA0C;IAC5D,GAAG,EAAE,YAAY;IACjB,CAAC,EAAE,WAAW;IACd,CAAC,EAAE,WAAW;IACd,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,YAAY;CAClB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,OAAO,GAAqD;IAChE,SAAS,EAAE;QACT,UAAU;QACV,UAAU;QACV,SAAS;QACT,SAAS;QACT,MAAM;QACN,UAAU;QACV,WAAW;QACX,QAAQ;QACR,OAAO;QACP,SAAS;QACT,WAAW;QACX,aAAa;QACb,QAAQ;QACR,WAAW;KACZ;IACD,QAAQ,EAAE;QACR,oBAAoB;QACpB,oBAAoB;QACpB,gBAAgB;QAChB,iBAAiB;QACjB,UAAU;QACV,aAAa;QACb,aAAa;QACb,eAAe;QACf,YAAY;QACZ,SAAS;QACT,aAAa;QACb,WAAW;QACX,OAAO;KACR;IACD,SAAS,EAAE;QACT,WAAW;QACX,cAAc;QACd,UAAU;QACV,YAAY;QACZ,YAAY;QACZ,YAAY;QACZ,eAAe;QACf,WAAW;QACX,aAAa;QACb,cAAc;QACd,cAAc;QACd,aAAa;QACb,cAAc;QACd,UAAU;KACX;IACD,eAAe,EAAE;QACf,eAAe;QACf,kBAAkB;QAClB,eAAe;QACf,kBAAkB;QAClB,aAAa;QACb,WAAW;QACX,gBAAgB;QAChB,WAAW;QACX,SAAS;QACT,WAAW;QACX,cAAc;QACd,WAAW;QACX,WAAW;QACX,gBAAgB;KACjB;IACD,QAAQ,EAAE;QACR,UAAU;QACV,UAAU;QACV,SAAS;QACT,WAAW;QACX,aAAa;QACb,WAAW;QACX,WAAW;QACX,UAAU;QACV,WAAW;KACZ;IACD,QAAQ,EAAE;QACR,sBAAsB;QACtB,oBAAoB;QACpB,kBAAkB;QAClB,qBAAqB;QACrB,iBAAiB;QACjB,UAAU;QACV,UAAU;QACV,gBAAgB;QAChB,MAAM;QACN,MAAM;QACN,KAAK;QACL,KAAK;KACN;IACD,UAAU,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC;CAC1F,CAAC;AAEF,2EAA2E;AAC3E,MAAM,aAAa,GAA0C;IAC3D,IAAI,EAAE,YAAY;IAClB,UAAU,EAAE,YAAY;IACxB,IAAI,EAAE,YAAY;IAClB,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,UAAU;IACnB,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,WAAW;IACrB,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,WAAW;IACnB,eAAe,EAAE,eAAe;IAChC,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,eAAe;IACxB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,UAAU;CACjB,CAAC;AAEF,MAAM,cAAc,GAAG,kBAAkB,EAAE,CAAC;AAC5C,MAAM,aAAa,GAAG,YAAY,EAAE,CAAC;AAsBrC,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IACpC,MAAM,OAAO,GAAkB,EAAE,CAAC;IAElC,MAAM,EAAE,GAAG,CAAC,KAAa,EAAE,GAAW,EAAa,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAErF,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAChD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;YAC3D,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,CAAC,IAAI,CAAC;oBACX,MAAM,EAAE,WAAW;oBACnB,WAAW,EAAE,MAAM;oBACnB,GAAG,EAAE,QAAQ,CAAC,KAAK;oBACnB,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC;iBACrC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,uCAAuC;IACvC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,QAAQ;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC;SACjE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,eAAe;YACvB,WAAW,EAAE,WAAW;YACxB,GAAG,EAAE,MAAM;YACX,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;SAC/D,CAAC,CAAC;IACL,CAAC;IAED,6EAA6E;IAC7E,6EAA6E;IAC7E,uDAAuD;IACvD,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC;gBACX,MAAM,EAAE,UAAU;gBAClB,WAAW,EAAE,MAAM;gBACnB,GAAG,EAAE,IAAI,IAAI,CAAC,QAAQ,GAAG;gBACzB,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;aACnD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,mBAAmB,EAAE,CAAC;QAC9B,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,SAAS;YACjB,WAAW,EAAE,YAAY;YACzB,uEAAuE;YACvE,sCAAsC;YACtC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE;YAClD,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC;SAC7B,CAAC,CAAC;IACL,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAgB;YAC5B,MAAM,EAAE,SAAS;YACjB,WAAW,EAAE,YAAY;YACzB,GAAG,EAAE,EAAE;YACP,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC;SAC7B,CAAC;QACF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC5F,CAAC;IAED,2EAA2E;IAC3E,2DAA2D;IAC3D,EAAE;IACF,2EAA2E;IAC3E,yEAAyE;IACzE,6EAA6E;IAC7E,2EAA2E;IAC3E,IAAI,MAAM,GAAG,OAAO,CAAC,CAAC,CAAgB,CAAC;IACvC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC/E,IAAI,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9F,MAAM,GAAG,MAAM,CAAC;QAClB,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACjD,qEAAqE;IACrE,wEAAwE;IACxE,6EAA6E;IAC7E,0CAA0C;IAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAC9B,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,QAAQ,CACxF,CAAC;IAEF,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AACpF,CAAC;AAaD,SAAS,kBAAkB;IACzB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;SACnC,IAAI,EAAE;QACP,oEAAoE;SACnE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;SACnC,GAAG,CAAC,YAAY,CAAC;QAClB,yEAAyE;QACzE,+DAA+D;SAC9D,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAEhF,OAAO,IAAI,MAAM;IACf,2EAA2E;IAC3E,kEAAkE;IAClE,MAAM,CAAC,GAAG,CAAA,yBAAyB;QACjC,2EAA2E;QAC3E,uEAAuE;QACvE,sDAAsD;QACtD,MAAM,CAAC,GAAG,CAAA,0CAA0C;QACpD,iFAAiF;QACjF,MAAM,CAAC,GAAG,CAAA,mBAAmB;QAC7B,MAAM,CAAC,GAAG,CAAA,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG;QACrC,MAAM,CAAC,GAAG,CAAA,mBAAmB;QAC7B,0DAA0D;QAC1D,MAAM,CAAC,GAAG,CAAA,kCAAkC;QAC5C,yEAAyE;QACzE,wDAAwD;QACxD,MAAM,CAAC,GAAG,CAAA,4BAA4B,EACxC,KAAK,CACN,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,GAAG,GAAgB,EAAE,CAAC;IAE5B,cAAc,CAAC,SAAS,GAAG,CAAC,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,QAAQ,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QAChG,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,CAAW,CAAC;QAC9B,MAAM,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3B,6EAA6E;QAC7E,yEAAyE;QACzE,wEAAwE;QACxE,kDAAkD;QAClD,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAE7B,MAAM,UAAU,GAAG,YAAY,KAAK,SAAS,IAAI,aAAa,KAAK,SAAS,CAAC;QAC7E,MAAM,UAAU,GAAG,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,SAAS,KAAK,GAAG,CAAC;QAC3G,MAAM,OAAO,GAAG,MAAM,KAAK,MAAM,CAAC,WAAW,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAExE,0EAA0E;QAC1E,0CAA0C;QAC1C,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,IAAI,CAAC,OAAO;YAAE,SAAS;QAErD,wEAAwE;QACxE,8DAA8D;QAC9D,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,WAAW;YAAE,SAAS;QAE3B,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAI,CAAC,CAAC,CAAC,CAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAChE,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAE1E,wEAAwE;QACxE,cAAc,CAAC,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IACnD,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,IAAY,EAAE,KAAa;IACnE,IAAI,OAAO,CAAC,SAAS,IAAI,KAAK;QAAE,OAAO,CAAC,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;IAC9D,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,wEAAwE;AACxE,SAAS,YAAY;IACnB,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC3C,KAAK,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAuC,EAAE,CAAC;QACnG,KAAK,MAAM,MAAM,IAAI,OAAO;YAAE,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,eAAe,CAAC,MAAc;IACrC,OAAO,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;AAC1E,CAAC;AAED,8FAA8F;AAC9F,SAAS,eAAe,CAAC,IAAY;IACnC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAChC,0EAA0E;IAC1E,uCAAuC;IACvC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,iBAAiB,GAAsB,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC","sourcesContent":["/**\n * Item state resolution.\n *\n * The premise of this module is that `- [ ]` and `- [x]` are the *least*\n * reliable statement a document makes about an obligation. Real specifications\n * record outcomes in prose, on a continuation line, days after the checkbox was\n * written and nobody went back to tick it:\n *\n * ```md\n * - [ ] Should the write path shard before the migration?\n * **Resolved (2026-03):** no - one node holds three years of growth.\n * ```\n *\n * A grep for `- [ ]` calls that an open question and re-opens a settled debate.\n * A grep for `- [x]` misses it entirely. spec-graph reads both signals, lets the\n * more specific one win, and *reports the disagreement* rather than hiding it,\n * because a checkbox that contradicts its own body is itself a defect worth\n * knowing about.\n */\n\nimport type { Directive } from './directives.js';\nimport { attr } from './directives.js';\nimport type { ListItem } from './markdown.js';\nimport { refOf, type LineIndex } from './source.js';\nimport { OPENNESS_OF, type Disposition, type Openness, type SourceRef, type StateSignal } from './types.js';\n\n/** Which signal wins when two disagree. Higher is more specific. */\nconst SIGNAL_PRIORITY: Readonly<Record<StateSignal['source'], number>> = {\n directive: 5,\n marker: 4,\n strikethrough: 3,\n checkbox: 2,\n section: 1,\n default: 0,\n};\n\n/**\n * Checkbox characters.\n *\n * `[ ]` and `[x]` are universal; the rest come from conventions that several\n * ecosystems share (`[~]` partial, `[-]` dropped, `[?]` question, `[/]` in\n * progress). Reading them costs nothing and refusing to would throw away the\n * one place a document states its nuance unambiguously.\n */\nconst CHECKBOX_STATE: Readonly<Record<string, Disposition>> = {\n ' ': 'unresolved',\n x: 'satisfied',\n X: 'satisfied',\n '+': 'satisfied',\n '~': 'narrowed',\n '/': 'narrowed',\n '-': 'rejected',\n '?': 'unresolved',\n '!': 'unresolved',\n '*': 'unresolved',\n};\n\n/**\n * Prose markers, by the state they announce.\n *\n * Matched only in the qualified forms real documents use - `**Resolved:**`,\n * `RESOLVED -`, `**Moot**` - never as bare words, so ordinary prose such as\n * \"we resolved to keep the queue\" does not close an item.\n */\nconst MARKERS: Readonly<Record<Disposition, readonly string[]>> = {\n satisfied: [\n 'resolved',\n 'answered',\n 'decided',\n 'settled',\n 'done',\n 'complete',\n 'completed',\n 'closed',\n 'fixed',\n 'shipped',\n 'addressed',\n 'implemented',\n 'solved',\n 'confirmed',\n ],\n narrowed: [\n 'partially resolved',\n 'partially answered',\n 'partially done',\n 'partly resolved',\n 'narrowed',\n 'narrowed to',\n 'scoped down',\n 'reduced scope',\n 'reduced to',\n 'partial',\n 'in progress',\n 'remaining',\n 'split',\n ],\n delegated: [\n 'delegated',\n 'delegated to',\n 'moved to',\n 'tracked in',\n 'tracked by',\n 'handed off',\n 'handed off to',\n 'handed to',\n 'deferred to',\n 'follow up in',\n 'follow-up in',\n 'followup in',\n 'continued in',\n 'owned by',\n ],\n 'accepted-debt': [\n 'accepted debt',\n 'accepted as debt',\n 'accepted risk',\n 'known limitation',\n 'known issue',\n 'known gap',\n 'technical debt',\n 'tech debt',\n 'wontfix',\n \"won't fix\",\n 'will not fix',\n 'by design',\n 'tolerated',\n 'living with it',\n ],\n rejected: [\n 'rejected',\n 'declined',\n 'dropped',\n 'not doing',\n 'will not do',\n 'abandoned',\n 'cancelled',\n 'canceled',\n 'withdrawn',\n ],\n obviated: [\n 'no longer applicable',\n 'no longer relevant',\n 'no longer needed',\n 'overtaken by events',\n 'premise invalid',\n 'obviated',\n 'obsolete',\n 'not applicable',\n 'moot',\n 'void',\n 'n/a',\n 'obe',\n ],\n unresolved: ['unresolved', 'still open', 'open question', 'tbd', 'undecided', 'reopened'],\n};\n\n/** Words accepted in a `state=` attribute, including friendly synonyms. */\nconst STATE_ALIASES: Readonly<Record<string, Disposition>> = {\n open: 'unresolved',\n unresolved: 'unresolved',\n todo: 'unresolved',\n narrowed: 'narrowed',\n partial: 'narrowed',\n delegated: 'delegated',\n satisfied: 'satisfied',\n resolved: 'satisfied',\n done: 'satisfied',\n closed: 'satisfied',\n 'accepted-debt': 'accepted-debt',\n debt: 'accepted-debt',\n wontfix: 'accepted-debt',\n rejected: 'rejected',\n declined: 'rejected',\n obviated: 'obviated',\n moot: 'obviated',\n};\n\nconst MARKER_PATTERN = buildMarkerPattern();\nconst MARKER_LOOKUP = markerLookup();\n\nexport interface StateInput {\n readonly file: string;\n readonly index: LineIndex;\n readonly item: ListItem;\n /** Heading path the item sits under. */\n readonly section: readonly string[];\n /** A `@spec-item` directive attached to the item, when there is one. */\n readonly directive: Directive | null;\n /** True when the item sits under a heading that declares obligations. */\n readonly inObligationSection: boolean;\n}\n\nexport interface ResolvedState {\n readonly disposition: Disposition;\n readonly openness: Openness;\n readonly evidence: StateSignal;\n /** Signals that disagree with `evidence` about how open the item is. */\n readonly conflicts: readonly StateSignal[];\n}\n\n/** Collects every state signal on an item and decides between them. */\nexport function resolveItemState(input: StateInput): ResolvedState {\n const { file, index, item } = input;\n const signals: StateSignal[] = [];\n\n const at = (start: number, end: number): SourceRef => refOf(file, index, start, end);\n\n if (input.directive) {\n const declared = attr(input.directive, 'state');\n if (declared) {\n const mapped = STATE_ALIASES[declared.value.toLowerCase()];\n if (mapped) {\n signals.push({\n source: 'directive',\n disposition: mapped,\n raw: declared.value,\n at: at(declared.start, declared.end),\n });\n }\n }\n }\n\n // Markers are searched in the masked body: a fenced example nested under an\n // item must never be able to close it.\n for (const found of findMarkers(item.maskedBody)) {\n signals.push({\n source: 'marker',\n disposition: found.disposition,\n raw: found.raw,\n at: at(item.textStart + found.start, item.textStart + found.end),\n });\n }\n\n const struck = strikethroughOf(item.firstLine);\n if (struck) {\n signals.push({\n source: 'strikethrough',\n disposition: 'satisfied',\n raw: struck,\n at: at(item.textStart, item.textStart + item.firstLine.length),\n });\n }\n\n // The scanner reads a checkbox only from the ten characters above, and gives\n // it a start exactly when it gives it a character: these guards narrow types\n // and decide nothing, so their mutants are equivalent.\n if (item.checkbox !== null && item.checkboxStart !== null) {\n const mapped = CHECKBOX_STATE[item.checkbox];\n if (mapped) {\n signals.push({\n source: 'checkbox',\n disposition: mapped,\n raw: `[${item.checkbox}]`,\n at: at(item.checkboxStart, item.checkboxStart + 3),\n });\n }\n }\n\n if (input.inObligationSection) {\n signals.push({\n source: 'section',\n disposition: 'unresolved',\n // An item in an obligation section is under a heading, so the fallback\n // narrows the type and is never read.\n raw: input.section[input.section.length - 1] ?? '',\n at: at(item.start, item.end),\n });\n }\n\n if (signals.length === 0) {\n const fallback: StateSignal = {\n source: 'default',\n disposition: 'unresolved',\n raw: '',\n at: at(item.start, item.end),\n };\n return { disposition: 'unresolved', openness: 'open', evidence: fallback, conflicts: [] };\n }\n\n // The most specific signal wins; ties go to the one written last, which is\n // the one a reader of the document sees as the final word.\n //\n // Signals are pushed most specific first and markers in document order, so\n // the winner only changes on a tie, where array order and document order\n // agree. Mutants of the comparisons are equivalent for that reason; they are\n // written out so that neither fact about the pushes above is load-bearing.\n let winner = signals[0] as StateSignal;\n for (const signal of signals) {\n const better = SIGNAL_PRIORITY[signal.source] - SIGNAL_PRIORITY[winner.source];\n if (better > 0 || (better === 0 && signal.at.span.start.offset > winner.at.span.start.offset)) {\n winner = signal;\n }\n }\n\n const openness = OPENNESS_OF[winner.disposition];\n // Only disagreement about *openness* is a conflict. A body that says\n // \"Resolved\" under a ticked checkbox agrees with it; one under an empty\n // checkbox does not, and that is worth a human looking at. The winner agrees\n // with itself, so it is never among them.\n const conflicts = signals.filter(\n (signal) => signal.source !== 'section' && OPENNESS_OF[signal.disposition] !== openness,\n );\n\n return { disposition: winner.disposition, openness, evidence: winner, conflicts };\n}\n\n/* -------------------------------------------------------------------------- */\n/* Marker matching */\n/* -------------------------------------------------------------------------- */\n\ninterface MarkerHit {\n readonly disposition: Disposition;\n readonly raw: string;\n readonly start: number;\n readonly end: number;\n}\n\nfunction buildMarkerPattern(): RegExp {\n const phrases = Object.values(MARKERS)\n .flat()\n // Longest first so `partially resolved` is not eaten by `resolved`.\n .sort((a, b) => b.length - a.length)\n .map(escapeRegExp)\n // Allow the writer to space, hyphenate or underscore a multi-word marker\n // whichever way, and to type its apostrophe straight or curly.\n .map((phrase) => phrase.replace(/\\\\?[ -]/g, '[ _\\\\-]').replace(/'/g, \"['’]\"));\n\n return new RegExp(\n // A marker only counts at the start of a statement: the start of the item,\n // a new line, after a sentence ends, or after an emphasis opener.\n String.raw`(?:^|\\n|[.?!]\\s+|[(\\[])` +\n // Skip block-quote and list markers, but only when the marker character is\n // followed by whitespace. Without that guard the `*` of `**Resolved**`\n // reads as a bullet and the emphasis wrapper is lost.\n String.raw`(?:[ \\t>•]|(?:[-*+]|\\d{1,9}[.)])[ \\t]+)*` +\n // Optional emphasis wrapper, remembered so `**Moot**` qualifies without a colon.\n String.raw`(\\*{1,2}|_{1,2})?` +\n String.raw`\\s*(${phrases.join('|')})` +\n String.raw`(\\*{1,2}|_{1,2})?` +\n // An optional dated parenthetical: `Resolved (2026-03):`.\n String.raw`\\s*(?:[(\\[][^)\\]\\n]{0,60}[)\\]])?` +\n // Optional, and validated in code below: an emphasised or shouted marker\n // stands on its own, a bare lower-case word never does.\n String.raw`\\s*([::]|[-–—]\\s|\\.|$|\\n)?`,\n 'gim',\n );\n}\n\nfunction findMarkers(body: string): MarkerHit[] {\n const out: MarkerHit[] = [];\n\n MARKER_PATTERN.lastIndex = 0;\n for (let m = MARKER_PATTERN.exec(body); m !== null; m = execPast(MARKER_PATTERN, body, m.index)) {\n const phrase = m[2] as string;\n const emphasisOpen = m[1];\n const emphasisClose = m[3];\n // A dash arrives with the space after it. An absent qualifier is none of the\n // four, so what stands in for it is equivalent as long as it is not one.\n // Only the dash alternative puts a dash in the qualifier at all, so the\n // anchor on the dash test decides nothing either.\n const qualifier = m[4] ?? '';\n\n const emphasised = emphasisOpen !== undefined && emphasisClose !== undefined;\n const punctuated = qualifier === ':' || qualifier === ':' || /^[-–—]/.test(qualifier) || qualifier === '.';\n const shouted = phrase === phrase.toUpperCase() && /[A-Z]/.test(phrase);\n\n // Without one of these three the match is ordinary prose. \"We resolved to\n // keep the queue\" must not close an item.\n if (!emphasised && !punctuated && !shouted) continue;\n\n // Every phrase the pattern matches is a table phrase respelled, so this\n // narrows the type and never skips: its mutant is equivalent.\n const disposition = MARKER_LOOKUP.get(normalisePhrase(phrase));\n if (!disposition) continue;\n\n const start = (m.index ?? 0) + (m[0] as string).indexOf(phrase);\n out.push({ disposition, raw: phrase, start, end: start + phrase.length });\n\n // Overlapping alternatives would double-count; resume after this match.\n MARKER_PATTERN.lastIndex = start + phrase.length;\n }\n\n return out;\n}\n\n/**\n * `exec` from past where the last match began.\n *\n * The other scans over a /g pattern use `matchAll`, which steps past an empty\n * match. This one cannot: it resumes inside its match, on the stop a marker's\n * qualifier took, so that the stop can start the next statement. No marker is\n * empty, so no match is, and the resume is always past where its match began.\n * A marker emptied by mistake would match empty at the start of every line,\n * and `exec` would find it there for ever; this steps past it, and the marker\n * tests fail instead.\n */\nexport function execPast(pattern: RegExp, text: string, index: number): RegExpExecArray | null {\n if (pattern.lastIndex <= index) pattern.lastIndex = index + 1;\n return pattern.exec(text);\n}\n\n/** Built once: this used to be rebuilt for every item in the corpus. */\nfunction markerLookup(): Map<string, Disposition> {\n const map = new Map<string, Disposition>();\n for (const [disposition, phrases] of Object.entries(MARKERS) as [Disposition, readonly string[]][]) {\n for (const phrase of phrases) map.set(normalisePhrase(phrase), disposition);\n }\n return map;\n}\n\n/**\n * The key a phrase is looked up by, from the table and from a match alike.\n *\n * Its case and separator mutants are equivalent, because it folds both sides\n * of one lookup: folded to upper case, or with the separator dropped, the same\n * phrases share a key - no two of the table's dispositions collide without\n * their spaces - and neither a table phrase nor a match ever holds two\n * separators in a row. The apostrophe is folded for the match alone: the table\n * spells it straight.\n */\nfunction normalisePhrase(phrase: string): string {\n return phrase.toLowerCase().replace(/’/g, \"'\").replace(/[\\s_-]+/g, ' ');\n}\n\n/** Returns the struck text when the whole line, which arrives trimmed, is wrapped in `~~`. */\nfunction strikethroughOf(line: string): string | null {\n if (!line.startsWith('~~') || !line.endsWith('~~')) return null;\n const inner = line.slice(2, -2);\n // Something has to be struck: `~~~~~` is the fence of a code block nested\n // under the item, and strikes nothing.\n return /[^~]/.test(inner) && !inner.includes('~~') ? line : null;\n}\n\n/**\n * No phrase in the table holds a metacharacter today, so blanking the\n * replacement is an equivalent mutant; this keeps the dots of a future `n.a.`\n * dots, rather than any character.\n */\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/** Exposed for the query language, which lets users filter on these names. */\nexport const KNOWN_STATE_WORDS: readonly string[] = Object.keys(STATE_ALIASES);\n"]}
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,OAAO,EAAE,KAAK,EAAkB,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,WAAW,EAAqE,MAAM,YAAY,CAAC;AAE5G,oEAAoE;AACpE,MAAM,eAAe,GAAoD;IACvE,SAAS,EAAE,CAAC;IACZ,MAAM,EAAE,CAAC;IACT,aAAa,EAAE,CAAC;IAChB,QAAQ,EAAE,CAAC;IACX,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,CAAC;CACX,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,cAAc,GAA0C;IAC5D,GAAG,EAAE,YAAY;IACjB,CAAC,EAAE,WAAW;IACd,CAAC,EAAE,WAAW;IACd,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,YAAY;CAClB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,OAAO,GAAqD;IAChE,SAAS,EAAE;QACT,UAAU;QACV,UAAU;QACV,SAAS;QACT,SAAS;QACT,MAAM;QACN,UAAU;QACV,WAAW;QACX,QAAQ;QACR,OAAO;QACP,SAAS;QACT,WAAW;QACX,aAAa;QACb,QAAQ;QACR,WAAW;KACZ;IACD,QAAQ,EAAE;QACR,oBAAoB;QACpB,oBAAoB;QACpB,gBAAgB;QAChB,iBAAiB;QACjB,UAAU;QACV,aAAa;QACb,aAAa;QACb,eAAe;QACf,YAAY;QACZ,SAAS;QACT,aAAa;QACb,WAAW;QACX,OAAO;KACR;IACD,SAAS,EAAE;QACT,WAAW;QACX,cAAc;QACd,UAAU;QACV,YAAY;QACZ,YAAY;QACZ,YAAY;QACZ,eAAe;QACf,WAAW;QACX,aAAa;QACb,cAAc;QACd,cAAc;QACd,aAAa;QACb,cAAc;QACd,UAAU;KACX;IACD,eAAe,EAAE;QACf,eAAe;QACf,kBAAkB;QAClB,eAAe;QACf,kBAAkB;QAClB,aAAa;QACb,WAAW;QACX,gBAAgB;QAChB,WAAW;QACX,SAAS;QACT,WAAW;QACX,cAAc;QACd,WAAW;QACX,WAAW;QACX,gBAAgB;KACjB;IACD,QAAQ,EAAE;QACR,UAAU;QACV,UAAU;QACV,SAAS;QACT,WAAW;QACX,aAAa;QACb,WAAW;QACX,WAAW;QACX,UAAU;QACV,WAAW;KACZ;IACD,QAAQ,EAAE;QACR,sBAAsB;QACtB,oBAAoB;QACpB,kBAAkB;QAClB,qBAAqB;QACrB,iBAAiB;QACjB,UAAU;QACV,UAAU;QACV,gBAAgB;QAChB,MAAM;QACN,MAAM;QACN,KAAK;QACL,KAAK;KACN;IACD,UAAU,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC;CAC1F,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,aAAa,GAAqC,IAAI,GAAG,CAAsB;IACnF,CAAC,MAAM,EAAE,YAAY,CAAC;IACtB,CAAC,YAAY,EAAE,YAAY,CAAC;IAC5B,CAAC,MAAM,EAAE,YAAY,CAAC;IACtB,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,SAAS,EAAE,UAAU,CAAC;IACvB,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,UAAU,EAAE,WAAW,CAAC;IACzB,CAAC,MAAM,EAAE,WAAW,CAAC;IACrB,CAAC,QAAQ,EAAE,WAAW,CAAC;IACvB,CAAC,eAAe,EAAE,eAAe,CAAC;IAClC,CAAC,MAAM,EAAE,eAAe,CAAC;IACzB,CAAC,SAAS,EAAE,eAAe,CAAC;IAC5B,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,MAAM,EAAE,UAAU,CAAC;CACrB,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,kBAAkB,EAAE,CAAC;AAC5C,MAAM,aAAa,GAAG,YAAY,EAAE,CAAC;AAsBrC,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IACpC,MAAM,OAAO,GAAkB,EAAE,CAAC;IAElC,MAAM,EAAE,GAAG,CAAC,KAAa,EAAE,GAAW,EAAa,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAErF,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAChD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;YAC/D,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,CAAC,IAAI,CAAC;oBACX,MAAM,EAAE,WAAW;oBACnB,WAAW,EAAE,MAAM;oBACnB,GAAG,EAAE,QAAQ,CAAC,KAAK;oBACnB,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC;iBACrC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,uCAAuC;IACvC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,QAAQ;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC;SACjE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,eAAe;YACvB,WAAW,EAAE,WAAW;YACxB,GAAG,EAAE,MAAM;YACX,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;SAC/D,CAAC,CAAC;IACL,CAAC;IAED,6EAA6E;IAC7E,6EAA6E;IAC7E,uDAAuD;IACvD,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC;gBACX,MAAM,EAAE,UAAU;gBAClB,WAAW,EAAE,MAAM;gBACnB,GAAG,EAAE,IAAI,IAAI,CAAC,QAAQ,GAAG;gBACzB,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;aACnD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,mBAAmB,EAAE,CAAC;QAC9B,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,SAAS;YACjB,WAAW,EAAE,YAAY;YACzB,uEAAuE;YACvE,sCAAsC;YACtC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE;YAClD,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC;SAC7B,CAAC,CAAC;IACL,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAgB;YAC5B,MAAM,EAAE,SAAS;YACjB,WAAW,EAAE,YAAY;YACzB,GAAG,EAAE,EAAE;YACP,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC;SAC7B,CAAC;QACF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC5F,CAAC;IAED,2EAA2E;IAC3E,2DAA2D;IAC3D,EAAE;IACF,2EAA2E;IAC3E,yEAAyE;IACzE,6EAA6E;IAC7E,2EAA2E;IAC3E,IAAI,MAAM,GAAG,OAAO,CAAC,CAAC,CAAgB,CAAC;IACvC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC/E,IAAI,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9F,MAAM,GAAG,MAAM,CAAC;QAClB,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACjD,qEAAqE;IACrE,wEAAwE;IACxE,6EAA6E;IAC7E,0CAA0C;IAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAC9B,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,QAAQ,CACxF,CAAC;IAEF,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AACpF,CAAC;AAaD,SAAS,kBAAkB;IACzB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;SACnC,IAAI,EAAE;QACP,oEAAoE;SACnE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;SACnC,GAAG,CAAC,YAAY,CAAC;QAClB,yEAAyE;QACzE,+DAA+D;SAC9D,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAEhF,OAAO,IAAI,MAAM;IACf,2EAA2E;IAC3E,kEAAkE;IAClE,MAAM,CAAC,GAAG,CAAA,yBAAyB;QACjC,2EAA2E;QAC3E,uEAAuE;QACvE,sDAAsD;QACtD,MAAM,CAAC,GAAG,CAAA,0CAA0C;QACpD,iFAAiF;QACjF,MAAM,CAAC,GAAG,CAAA,mBAAmB;QAC7B,MAAM,CAAC,GAAG,CAAA,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG;QACrC,MAAM,CAAC,GAAG,CAAA,mBAAmB;QAC7B,0DAA0D;QAC1D,MAAM,CAAC,GAAG,CAAA,kCAAkC;QAC5C,yEAAyE;QACzE,wDAAwD;QACxD,MAAM,CAAC,GAAG,CAAA,4BAA4B,EACxC,KAAK,CACN,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,GAAG,GAAgB,EAAE,CAAC;IAE5B,cAAc,CAAC,SAAS,GAAG,CAAC,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,QAAQ,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QAChG,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,CAAW,CAAC;QAC9B,MAAM,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3B,6EAA6E;QAC7E,yEAAyE;QACzE,wEAAwE;QACxE,kDAAkD;QAClD,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAE7B,MAAM,UAAU,GAAG,YAAY,KAAK,SAAS,IAAI,aAAa,KAAK,SAAS,CAAC;QAC7E,MAAM,UAAU,GAAG,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,SAAS,KAAK,GAAG,CAAC;QAC3G,MAAM,OAAO,GAAG,MAAM,KAAK,MAAM,CAAC,WAAW,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAExE,0EAA0E;QAC1E,0CAA0C;QAC1C,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,IAAI,CAAC,OAAO;YAAE,SAAS;QAErD,wEAAwE;QACxE,8DAA8D;QAC9D,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,WAAW;YAAE,SAAS;QAE3B,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAI,CAAC,CAAC,CAAC,CAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAChE,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAE1E,wEAAwE;QACxE,cAAc,CAAC,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IACnD,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,IAAY,EAAE,KAAa;IACnE,IAAI,OAAO,CAAC,SAAS,IAAI,KAAK;QAAE,OAAO,CAAC,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;IAC9D,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,wEAAwE;AACxE,SAAS,YAAY;IACnB,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC3C,KAAK,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAuC,EAAE,CAAC;QACnG,KAAK,MAAM,MAAM,IAAI,OAAO;YAAE,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,eAAe,CAAC,MAAc;IACrC,OAAO,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;AAC1E,CAAC;AAED,8FAA8F;AAC9F,SAAS,eAAe,CAAC,IAAY;IACnC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAChC,0EAA0E;IAC1E,uCAAuC;IACvC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,iBAAiB,GAAsB,CAAC,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC","sourcesContent":["/**\n * Item state resolution.\n *\n * The premise of this module is that `- [ ]` and `- [x]` are the *least*\n * reliable statement a document makes about an obligation. Real specifications\n * record outcomes in prose, on a continuation line, days after the checkbox was\n * written and nobody went back to tick it:\n *\n * ```md\n * - [ ] Should the write path shard before the migration?\n * **Resolved (2026-03):** no - one node holds three years of growth.\n * ```\n *\n * A grep for `- [ ]` calls that an open question and re-opens a settled debate.\n * A grep for `- [x]` misses it entirely. spec-graph reads both signals, lets the\n * more specific one win, and *reports the disagreement* rather than hiding it,\n * because a checkbox that contradicts its own body is itself a defect worth\n * knowing about.\n */\n\nimport type { Directive } from './directives.js';\nimport { attr } from './directives.js';\nimport type { ListItem } from './markdown.js';\nimport { refOf, type LineIndex } from './source.js';\nimport { OPENNESS_OF, type Disposition, type Openness, type SourceRef, type StateSignal } from './types.js';\n\n/** Which signal wins when two disagree. Higher is more specific. */\nconst SIGNAL_PRIORITY: Readonly<Record<StateSignal['source'], number>> = {\n directive: 5,\n marker: 4,\n strikethrough: 3,\n checkbox: 2,\n section: 1,\n default: 0,\n};\n\n/**\n * Checkbox characters.\n *\n * `[ ]` and `[x]` are universal; the rest come from conventions that several\n * ecosystems share (`[~]` partial, `[-]` dropped, `[?]` question, `[/]` in\n * progress). Reading them costs nothing and refusing to would throw away the\n * one place a document states its nuance unambiguously.\n */\nconst CHECKBOX_STATE: Readonly<Record<string, Disposition>> = {\n ' ': 'unresolved',\n x: 'satisfied',\n X: 'satisfied',\n '+': 'satisfied',\n '~': 'narrowed',\n '/': 'narrowed',\n '-': 'rejected',\n '?': 'unresolved',\n '!': 'unresolved',\n '*': 'unresolved',\n};\n\n/**\n * Prose markers, by the state they announce.\n *\n * Matched only in the qualified forms real documents use - `**Resolved:**`,\n * `RESOLVED -`, `**Moot**` - never as bare words, so ordinary prose such as\n * \"we resolved to keep the queue\" does not close an item.\n */\nconst MARKERS: Readonly<Record<Disposition, readonly string[]>> = {\n satisfied: [\n 'resolved',\n 'answered',\n 'decided',\n 'settled',\n 'done',\n 'complete',\n 'completed',\n 'closed',\n 'fixed',\n 'shipped',\n 'addressed',\n 'implemented',\n 'solved',\n 'confirmed',\n ],\n narrowed: [\n 'partially resolved',\n 'partially answered',\n 'partially done',\n 'partly resolved',\n 'narrowed',\n 'narrowed to',\n 'scoped down',\n 'reduced scope',\n 'reduced to',\n 'partial',\n 'in progress',\n 'remaining',\n 'split',\n ],\n delegated: [\n 'delegated',\n 'delegated to',\n 'moved to',\n 'tracked in',\n 'tracked by',\n 'handed off',\n 'handed off to',\n 'handed to',\n 'deferred to',\n 'follow up in',\n 'follow-up in',\n 'followup in',\n 'continued in',\n 'owned by',\n ],\n 'accepted-debt': [\n 'accepted debt',\n 'accepted as debt',\n 'accepted risk',\n 'known limitation',\n 'known issue',\n 'known gap',\n 'technical debt',\n 'tech debt',\n 'wontfix',\n \"won't fix\",\n 'will not fix',\n 'by design',\n 'tolerated',\n 'living with it',\n ],\n rejected: [\n 'rejected',\n 'declined',\n 'dropped',\n 'not doing',\n 'will not do',\n 'abandoned',\n 'cancelled',\n 'canceled',\n 'withdrawn',\n ],\n obviated: [\n 'no longer applicable',\n 'no longer relevant',\n 'no longer needed',\n 'overtaken by events',\n 'premise invalid',\n 'obviated',\n 'obsolete',\n 'not applicable',\n 'moot',\n 'void',\n 'n/a',\n 'obe',\n ],\n unresolved: ['unresolved', 'still open', 'open question', 'tbd', 'undecided', 'reopened'],\n};\n\n/**\n * Words accepted in a `state=` attribute, including friendly synonyms.\n *\n * A map, because the word comes from a document and an object answers to more\n * names than it was given: `state=\"constructor\"` was a state, of no\n * disposition, and the run that met it ended there.\n */\nconst STATE_ALIASES: ReadonlyMap<string, Disposition> = new Map<string, Disposition>([\n ['open', 'unresolved'],\n ['unresolved', 'unresolved'],\n ['todo', 'unresolved'],\n ['narrowed', 'narrowed'],\n ['partial', 'narrowed'],\n ['delegated', 'delegated'],\n ['satisfied', 'satisfied'],\n ['resolved', 'satisfied'],\n ['done', 'satisfied'],\n ['closed', 'satisfied'],\n ['accepted-debt', 'accepted-debt'],\n ['debt', 'accepted-debt'],\n ['wontfix', 'accepted-debt'],\n ['rejected', 'rejected'],\n ['declined', 'rejected'],\n ['obviated', 'obviated'],\n ['moot', 'obviated'],\n]);\n\nconst MARKER_PATTERN = buildMarkerPattern();\nconst MARKER_LOOKUP = markerLookup();\n\nexport interface StateInput {\n readonly file: string;\n readonly index: LineIndex;\n readonly item: ListItem;\n /** Heading path the item sits under. */\n readonly section: readonly string[];\n /** A `@spec-item` directive attached to the item, when there is one. */\n readonly directive: Directive | null;\n /** True when the item sits under a heading that declares obligations. */\n readonly inObligationSection: boolean;\n}\n\nexport interface ResolvedState {\n readonly disposition: Disposition;\n readonly openness: Openness;\n readonly evidence: StateSignal;\n /** Signals that disagree with `evidence` about how open the item is. */\n readonly conflicts: readonly StateSignal[];\n}\n\n/** Collects every state signal on an item and decides between them. */\nexport function resolveItemState(input: StateInput): ResolvedState {\n const { file, index, item } = input;\n const signals: StateSignal[] = [];\n\n const at = (start: number, end: number): SourceRef => refOf(file, index, start, end);\n\n if (input.directive) {\n const declared = attr(input.directive, 'state');\n if (declared) {\n const mapped = STATE_ALIASES.get(declared.value.toLowerCase());\n if (mapped) {\n signals.push({\n source: 'directive',\n disposition: mapped,\n raw: declared.value,\n at: at(declared.start, declared.end),\n });\n }\n }\n }\n\n // Markers are searched in the masked body: a fenced example nested under an\n // item must never be able to close it.\n for (const found of findMarkers(item.maskedBody)) {\n signals.push({\n source: 'marker',\n disposition: found.disposition,\n raw: found.raw,\n at: at(item.textStart + found.start, item.textStart + found.end),\n });\n }\n\n const struck = strikethroughOf(item.firstLine);\n if (struck) {\n signals.push({\n source: 'strikethrough',\n disposition: 'satisfied',\n raw: struck,\n at: at(item.textStart, item.textStart + item.firstLine.length),\n });\n }\n\n // The scanner reads a checkbox only from the ten characters above, and gives\n // it a start exactly when it gives it a character: these guards narrow types\n // and decide nothing, so their mutants are equivalent.\n if (item.checkbox !== null && item.checkboxStart !== null) {\n const mapped = CHECKBOX_STATE[item.checkbox];\n if (mapped) {\n signals.push({\n source: 'checkbox',\n disposition: mapped,\n raw: `[${item.checkbox}]`,\n at: at(item.checkboxStart, item.checkboxStart + 3),\n });\n }\n }\n\n if (input.inObligationSection) {\n signals.push({\n source: 'section',\n disposition: 'unresolved',\n // An item in an obligation section is under a heading, so the fallback\n // narrows the type and is never read.\n raw: input.section[input.section.length - 1] ?? '',\n at: at(item.start, item.end),\n });\n }\n\n if (signals.length === 0) {\n const fallback: StateSignal = {\n source: 'default',\n disposition: 'unresolved',\n raw: '',\n at: at(item.start, item.end),\n };\n return { disposition: 'unresolved', openness: 'open', evidence: fallback, conflicts: [] };\n }\n\n // The most specific signal wins; ties go to the one written last, which is\n // the one a reader of the document sees as the final word.\n //\n // Signals are pushed most specific first and markers in document order, so\n // the winner only changes on a tie, where array order and document order\n // agree. Mutants of the comparisons are equivalent for that reason; they are\n // written out so that neither fact about the pushes above is load-bearing.\n let winner = signals[0] as StateSignal;\n for (const signal of signals) {\n const better = SIGNAL_PRIORITY[signal.source] - SIGNAL_PRIORITY[winner.source];\n if (better > 0 || (better === 0 && signal.at.span.start.offset > winner.at.span.start.offset)) {\n winner = signal;\n }\n }\n\n const openness = OPENNESS_OF[winner.disposition];\n // Only disagreement about *openness* is a conflict. A body that says\n // \"Resolved\" under a ticked checkbox agrees with it; one under an empty\n // checkbox does not, and that is worth a human looking at. The winner agrees\n // with itself, so it is never among them.\n const conflicts = signals.filter(\n (signal) => signal.source !== 'section' && OPENNESS_OF[signal.disposition] !== openness,\n );\n\n return { disposition: winner.disposition, openness, evidence: winner, conflicts };\n}\n\n/* -------------------------------------------------------------------------- */\n/* Marker matching */\n/* -------------------------------------------------------------------------- */\n\ninterface MarkerHit {\n readonly disposition: Disposition;\n readonly raw: string;\n readonly start: number;\n readonly end: number;\n}\n\nfunction buildMarkerPattern(): RegExp {\n const phrases = Object.values(MARKERS)\n .flat()\n // Longest first so `partially resolved` is not eaten by `resolved`.\n .sort((a, b) => b.length - a.length)\n .map(escapeRegExp)\n // Allow the writer to space, hyphenate or underscore a multi-word marker\n // whichever way, and to type its apostrophe straight or curly.\n .map((phrase) => phrase.replace(/\\\\?[ -]/g, '[ _\\\\-]').replace(/'/g, \"['’]\"));\n\n return new RegExp(\n // A marker only counts at the start of a statement: the start of the item,\n // a new line, after a sentence ends, or after an emphasis opener.\n String.raw`(?:^|\\n|[.?!]\\s+|[(\\[])` +\n // Skip block-quote and list markers, but only when the marker character is\n // followed by whitespace. Without that guard the `*` of `**Resolved**`\n // reads as a bullet and the emphasis wrapper is lost.\n String.raw`(?:[ \\t>•]|(?:[-*+]|\\d{1,9}[.)])[ \\t]+)*` +\n // Optional emphasis wrapper, remembered so `**Moot**` qualifies without a colon.\n String.raw`(\\*{1,2}|_{1,2})?` +\n String.raw`\\s*(${phrases.join('|')})` +\n String.raw`(\\*{1,2}|_{1,2})?` +\n // An optional dated parenthetical: `Resolved (2026-03):`.\n String.raw`\\s*(?:[(\\[][^)\\]\\n]{0,60}[)\\]])?` +\n // Optional, and validated in code below: an emphasised or shouted marker\n // stands on its own, a bare lower-case word never does.\n String.raw`\\s*([::]|[-–—]\\s|\\.|$|\\n)?`,\n 'gim',\n );\n}\n\nfunction findMarkers(body: string): MarkerHit[] {\n const out: MarkerHit[] = [];\n\n MARKER_PATTERN.lastIndex = 0;\n for (let m = MARKER_PATTERN.exec(body); m !== null; m = execPast(MARKER_PATTERN, body, m.index)) {\n const phrase = m[2] as string;\n const emphasisOpen = m[1];\n const emphasisClose = m[3];\n // A dash arrives with the space after it. An absent qualifier is none of the\n // four, so what stands in for it is equivalent as long as it is not one.\n // Only the dash alternative puts a dash in the qualifier at all, so the\n // anchor on the dash test decides nothing either.\n const qualifier = m[4] ?? '';\n\n const emphasised = emphasisOpen !== undefined && emphasisClose !== undefined;\n const punctuated = qualifier === ':' || qualifier === ':' || /^[-–—]/.test(qualifier) || qualifier === '.';\n const shouted = phrase === phrase.toUpperCase() && /[A-Z]/.test(phrase);\n\n // Without one of these three the match is ordinary prose. \"We resolved to\n // keep the queue\" must not close an item.\n if (!emphasised && !punctuated && !shouted) continue;\n\n // Every phrase the pattern matches is a table phrase respelled, so this\n // narrows the type and never skips: its mutant is equivalent.\n const disposition = MARKER_LOOKUP.get(normalisePhrase(phrase));\n if (!disposition) continue;\n\n const start = (m.index ?? 0) + (m[0] as string).indexOf(phrase);\n out.push({ disposition, raw: phrase, start, end: start + phrase.length });\n\n // Overlapping alternatives would double-count; resume after this match.\n MARKER_PATTERN.lastIndex = start + phrase.length;\n }\n\n return out;\n}\n\n/**\n * `exec` from past where the last match began.\n *\n * The other scans over a /g pattern use `matchAll`, which steps past an empty\n * match. This one cannot: it resumes inside its match, on the stop a marker's\n * qualifier took, so that the stop can start the next statement. No marker is\n * empty, so no match is, and the resume is always past where its match began.\n * A marker emptied by mistake would match empty at the start of every line,\n * and `exec` would find it there for ever; this steps past it, and the marker\n * tests fail instead.\n */\nexport function execPast(pattern: RegExp, text: string, index: number): RegExpExecArray | null {\n if (pattern.lastIndex <= index) pattern.lastIndex = index + 1;\n return pattern.exec(text);\n}\n\n/** Built once: this used to be rebuilt for every item in the corpus. */\nfunction markerLookup(): Map<string, Disposition> {\n const map = new Map<string, Disposition>();\n for (const [disposition, phrases] of Object.entries(MARKERS) as [Disposition, readonly string[]][]) {\n for (const phrase of phrases) map.set(normalisePhrase(phrase), disposition);\n }\n return map;\n}\n\n/**\n * The key a phrase is looked up by, from the table and from a match alike.\n *\n * Its case and separator mutants are equivalent, because it folds both sides\n * of one lookup: folded to upper case, or with the separator dropped, the same\n * phrases share a key - no two of the table's dispositions collide without\n * their spaces - and neither a table phrase nor a match ever holds two\n * separators in a row. The apostrophe is folded for the match alone: the table\n * spells it straight.\n */\nfunction normalisePhrase(phrase: string): string {\n return phrase.toLowerCase().replace(/’/g, \"'\").replace(/[\\s_-]+/g, ' ');\n}\n\n/** Returns the struck text when the whole line, which arrives trimmed, is wrapped in `~~`. */\nfunction strikethroughOf(line: string): string | null {\n if (!line.startsWith('~~') || !line.endsWith('~~')) return null;\n const inner = line.slice(2, -2);\n // Something has to be struck: `~~~~~` is the fence of a code block nested\n // under the item, and strikes nothing.\n return /[^~]/.test(inner) && !inner.includes('~~') ? line : null;\n}\n\n/**\n * No phrase in the table holds a metacharacter today, so blanking the\n * replacement is an equivalent mutant; this keeps the dots of a future `n.a.`\n * dots, rather than any character.\n */\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/** Exposed for the query language, which lets users filter on these names. */\nexport const KNOWN_STATE_WORDS: readonly string[] = [...STATE_ALIASES.keys()];\n"]}
package/dist/yaml.js CHANGED
@@ -142,10 +142,10 @@ function entriesOf(read, shift) {
142
142
  }
143
143
  /** Collapses parsed entries into the flat record stored on a document node. */
144
144
  export function toRecord(entries) {
145
- const out = {};
146
- for (const entry of entries)
147
- out[entry.key] = entry.value;
148
- return out;
145
+ // Defined rather than assigned: assigning `__proto__` sets what the record
146
+ // inherits from, so a list under that key answered `fm.0` and the key
147
+ // itself was lost. Of a key written twice the last is kept, as it was.
148
+ return Object.fromEntries(entries.map((entry) => [entry.key, entry.value]));
149
149
  }
150
150
  /** Every value of a key as a flat list, whether it was a scalar or a sequence. */
151
151
  export function valuesOf(entry) {
package/dist/yaml.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"yaml.js","sourceRoot":"","sources":["../src/yaml.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What spec-graph reads out of a document's front matter.\n *\n * The reader is spec-core's, copied into `src/vendor/spec-core/markdown/`: the\n * subset of YAML that specifications use - scalars, quoted scalars, inline and\n * block sequences, `#` comments and, as spec-graph reads it, one level of nested\n * mapping flattened to `parent.child` - with the offset of every key and value,\n * because when a status is wrong the report has to point at the line that\n * declares it.\n *\n * Anything richer is recognised and not guessed at: a value continued on the\n * next line, a plain value holding `: `, a block scalar, an anchor. Such a value\n * is not read, and the reason is a parse problem at the value, so a key that\n * says nothing to the graph says why under `--verbose`. Each problem says\n * whether it left something out and which key, for the count a run prints\n * without `--verbose`.\n *\n * What stays here is the shape the rest of spec-graph reads: a key in lower\n * case, which is how a relation key, a status key and `fm.<key>` in a selector\n * have always been compared, and a value that is a string or a list of them.\n */\n\nimport { readFrontMatter, type FrontMatter as Read } from './vendor/spec-core/markdown/index.js';\nimport type { ScannedDocument } from './markdown.js';\n\nexport interface YamlEntry {\n /** Lower-cased key. Nested keys are flattened with a dot. */\n readonly key: string;\n readonly value: string | readonly string[];\n /** Offset of the key. */\n readonly start: number;\n /** Offset just past the value. */\n readonly end: number;\n /** Offset of the first character of the value, for precise diagnostics. */\n readonly valueStart: number;\n}\n\n/** Something in the front matter that was not read, and where. */\nexport interface FrontMatterProblem {\n readonly message: string;\n readonly start: number;\n readonly end: number;\n /**\n * Whether it left something out: a value, a line or the whole block. A key\n * written twice is a problem that leaves nothing out, since the last is read.\n */\n readonly unread: boolean;\n /**\n * The key, lower-cased, whose value or line was left out, when the line\n * names one; `null` for a line that names none and for the whole block.\n */\n readonly key: string | null;\n}\n\nexport interface FrontMatterReading {\n readonly entries: readonly YamlEntry[];\n readonly problems: readonly FrontMatterProblem[];\n}\n\nconst NOTHING: FrontMatterReading = { entries: [], problems: [] };\n\n/**\n * A line that reads as `key:` at any indentation, and the key it names: a key\n * as spec-core's reader takes one, of any script, since YAML allows one. A\n * full-width colon ends no key there.\n */\nconst KEY_SHAPE = /^[ \\t]*([\\p{L}_][\\p{L}\\p{M}\\p{N}_.-]*)[ \\t]*:/u;\n\n/** Reads a scanned document's front matter. */\nexport function readEntries(scanned: ScannedDocument): FrontMatterReading {\n const block = scanned.frontMatter;\n if (block === null) return neverClosed(scanned);\n const index = scanned.index;\n // The block and nothing after it: the reader splits what it is given into\n // lines, and the body is none of its business. It stops at the closing\n // delimiter all the same, so the mutant that hands it the whole text reads\n // the same entries, and is equivalent.\n const read = readFrontMatter(scanned.text.slice(0, block.bodyStart), { nested: true });\n // The scan found a closed block, and the reader closes one by the same rule,\n // so this never returns and its mutant is equivalent.\n if (read === null) return NOTHING;\n const { entries, problems } = entriesOf(read, 0);\n return {\n entries,\n problems: [\n ...read.problems.map((problem) => {\n // A key written twice is read, the last of it; any other line with a\n // problem is not, and the key it looks like it names is what went\n // missing - ` status: accepted`, indented under nothing, is a status.\n const unread = !read.entries.some((entry) => entry.line === problem.line);\n const text = index.lineText(problem.line + 1);\n return {\n message: `front matter: ${problem.message}`,\n start: index.lineStart(problem.line + 1),\n end: index.lineEnd(problem.line + 1),\n unread,\n // A match always has its one group, so `?.[1].` reads the same as\n // `?.[1]?.`: that mutant is equivalent.\n key: KEY_SHAPE.exec(text)?.[1]?.toLowerCase() ?? null,\n };\n }),\n ...problems,\n ].sort((a, b) => a.start - b.start),\n };\n}\n\n/**\n * Front matter that opens and never closes, which the scan reads as a rule\n * and Markdown after it, so nothing in it reaches the graph.\n *\n * The scan reports it, with its opening line, rather than leaving a document\n * that wrote front matter looking like one that wrote none. The usual cause is\n * a closer that only looks like one: `----` and `....` closed front matter\n * here before 0.9.0, and neither is one of YAML's document markers.\n */\nfunction neverClosed(scanned: ScannedDocument): FrontMatterReading {\n const open = scanned.unclosedFrontMatter;\n if (open === null) return NOTHING;\n const delimiter = open.kind === 'yaml' ? '---' : '+++';\n return {\n entries: [],\n problems: [\n {\n message: `front matter: opened here and never closed, so none of it is read - close it with \"${delimiter}\" on a line of its own`,\n start: open.start,\n end: open.end,\n unread: true,\n key: null,\n },\n ],\n };\n}\n\n/**\n * Parses front-matter text: what lies between the delimiters, as\n * `FrontMatter.raw` holds it.\n *\n * @param raw The text between the delimiters. It cannot hold a line that would\n * close them, as text a scan read between them never does.\n * @param baseOffset Offset of `raw` within the containing file, so the returned\n * spans address the file rather than the fragment.\n */\nexport function parseFrontMatter(raw: string, baseOffset = 0): YamlEntry[] {\n // The reader reads front matter at the top of a document, so the text goes\n // back between delimiters, and every offset back by the four characters the\n // opening one takes. The line break before the closing one is a blank line\n // when `raw` already ends in one, and a blank line is no key's.\n const read = readFrontMatter(`---\\n${raw}\\n---`, { nested: true });\n // Never null: the text opens with a delimiter and closes with one. The\n // mutants of that branch are equivalent.\n return read === null ? [] : entriesOf(read, baseOffset - 4).entries;\n}\n\n/** The entries a reading holds, and the values it could not read as problems. */\nfunction entriesOf(read: Read, shift: number): { entries: YamlEntry[]; problems: FrontMatterProblem[] } {\n const entries: YamlEntry[] = [];\n const problems: FrontMatterProblem[] = [];\n for (const entry of read.entries) {\n const { value } = entry;\n if (value.kind === 'unsupported') {\n // Only `readEntries` keeps these, and it shifts by 0, where adding and\n // subtracting agree: the mutants of the two sums below are equivalent.\n problems.push({\n message: `front matter: \"${entry.key}\" is not read: ${value.reason}`,\n start: entry.valueStart + shift,\n end: entry.valueEnd + shift,\n unread: true,\n key: entry.key.toLowerCase(),\n });\n continue;\n }\n entries.push({\n key: entry.key.toLowerCase(),\n value: value.kind === 'scalar' ? value.scalar.text : value.items.map((item) => item.text),\n start: entry.keyStart + shift,\n end: entry.valueEnd + shift,\n valueStart: entry.valueStart + shift,\n });\n }\n return { entries, problems };\n}\n\n/** Collapses parsed entries into the flat record stored on a document node. */\nexport function toRecord(entries: readonly YamlEntry[]): Record<string, string | readonly string[]> {\n const out: Record<string, string | readonly string[]> = {};\n for (const entry of entries) out[entry.key] = entry.value;\n return out;\n}\n\n/** Every value of a key as a flat list, whether it was a scalar or a sequence. */\nexport function valuesOf(entry: YamlEntry | undefined): string[] {\n if (!entry) return [];\n return typeof entry.value === 'string'\n ? entry.value.length > 0\n ? [entry.value]\n : []\n : [...entry.value];\n}\n"]}
1
+ {"version":3,"file":"yaml.js","sourceRoot":"","sources":["../src/yaml.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What spec-graph reads out of a document's front matter.\n *\n * The reader is spec-core's, copied into `src/vendor/spec-core/markdown/`: the\n * subset of YAML that specifications use - scalars, quoted scalars, inline and\n * block sequences, `#` comments and, as spec-graph reads it, one level of nested\n * mapping flattened to `parent.child` - with the offset of every key and value,\n * because when a status is wrong the report has to point at the line that\n * declares it.\n *\n * Anything richer is recognised and not guessed at: a value continued on the\n * next line, a plain value holding `: `, a block scalar, an anchor. Such a value\n * is not read, and the reason is a parse problem at the value, so a key that\n * says nothing to the graph says why under `--verbose`. Each problem says\n * whether it left something out and which key, for the count a run prints\n * without `--verbose`.\n *\n * What stays here is the shape the rest of spec-graph reads: a key in lower\n * case, which is how a relation key, a status key and `fm.<key>` in a selector\n * have always been compared, and a value that is a string or a list of them.\n */\n\nimport { readFrontMatter, type FrontMatter as Read } from './vendor/spec-core/markdown/index.js';\nimport type { ScannedDocument } from './markdown.js';\n\nexport interface YamlEntry {\n /** Lower-cased key. Nested keys are flattened with a dot. */\n readonly key: string;\n readonly value: string | readonly string[];\n /** Offset of the key. */\n readonly start: number;\n /** Offset just past the value. */\n readonly end: number;\n /** Offset of the first character of the value, for precise diagnostics. */\n readonly valueStart: number;\n}\n\n/** Something in the front matter that was not read, and where. */\nexport interface FrontMatterProblem {\n readonly message: string;\n readonly start: number;\n readonly end: number;\n /**\n * Whether it left something out: a value, a line or the whole block. A key\n * written twice is a problem that leaves nothing out, since the last is read.\n */\n readonly unread: boolean;\n /**\n * The key, lower-cased, whose value or line was left out, when the line\n * names one; `null` for a line that names none and for the whole block.\n */\n readonly key: string | null;\n}\n\nexport interface FrontMatterReading {\n readonly entries: readonly YamlEntry[];\n readonly problems: readonly FrontMatterProblem[];\n}\n\nconst NOTHING: FrontMatterReading = { entries: [], problems: [] };\n\n/**\n * A line that reads as `key:` at any indentation, and the key it names: a key\n * as spec-core's reader takes one, of any script, since YAML allows one. A\n * full-width colon ends no key there.\n */\nconst KEY_SHAPE = /^[ \\t]*([\\p{L}_][\\p{L}\\p{M}\\p{N}_.-]*)[ \\t]*:/u;\n\n/** Reads a scanned document's front matter. */\nexport function readEntries(scanned: ScannedDocument): FrontMatterReading {\n const block = scanned.frontMatter;\n if (block === null) return neverClosed(scanned);\n const index = scanned.index;\n // The block and nothing after it: the reader splits what it is given into\n // lines, and the body is none of its business. It stops at the closing\n // delimiter all the same, so the mutant that hands it the whole text reads\n // the same entries, and is equivalent.\n const read = readFrontMatter(scanned.text.slice(0, block.bodyStart), { nested: true });\n // The scan found a closed block, and the reader closes one by the same rule,\n // so this never returns and its mutant is equivalent.\n if (read === null) return NOTHING;\n const { entries, problems } = entriesOf(read, 0);\n return {\n entries,\n problems: [\n ...read.problems.map((problem) => {\n // A key written twice is read, the last of it; any other line with a\n // problem is not, and the key it looks like it names is what went\n // missing - ` status: accepted`, indented under nothing, is a status.\n const unread = !read.entries.some((entry) => entry.line === problem.line);\n const text = index.lineText(problem.line + 1);\n return {\n message: `front matter: ${problem.message}`,\n start: index.lineStart(problem.line + 1),\n end: index.lineEnd(problem.line + 1),\n unread,\n // A match always has its one group, so `?.[1].` reads the same as\n // `?.[1]?.`: that mutant is equivalent.\n key: KEY_SHAPE.exec(text)?.[1]?.toLowerCase() ?? null,\n };\n }),\n ...problems,\n ].sort((a, b) => a.start - b.start),\n };\n}\n\n/**\n * Front matter that opens and never closes, which the scan reads as a rule\n * and Markdown after it, so nothing in it reaches the graph.\n *\n * The scan reports it, with its opening line, rather than leaving a document\n * that wrote front matter looking like one that wrote none. The usual cause is\n * a closer that only looks like one: `----` and `....` closed front matter\n * here before 0.9.0, and neither is one of YAML's document markers.\n */\nfunction neverClosed(scanned: ScannedDocument): FrontMatterReading {\n const open = scanned.unclosedFrontMatter;\n if (open === null) return NOTHING;\n const delimiter = open.kind === 'yaml' ? '---' : '+++';\n return {\n entries: [],\n problems: [\n {\n message: `front matter: opened here and never closed, so none of it is read - close it with \"${delimiter}\" on a line of its own`,\n start: open.start,\n end: open.end,\n unread: true,\n key: null,\n },\n ],\n };\n}\n\n/**\n * Parses front-matter text: what lies between the delimiters, as\n * `FrontMatter.raw` holds it.\n *\n * @param raw The text between the delimiters. It cannot hold a line that would\n * close them, as text a scan read between them never does.\n * @param baseOffset Offset of `raw` within the containing file, so the returned\n * spans address the file rather than the fragment.\n */\nexport function parseFrontMatter(raw: string, baseOffset = 0): YamlEntry[] {\n // The reader reads front matter at the top of a document, so the text goes\n // back between delimiters, and every offset back by the four characters the\n // opening one takes. The line break before the closing one is a blank line\n // when `raw` already ends in one, and a blank line is no key's.\n const read = readFrontMatter(`---\\n${raw}\\n---`, { nested: true });\n // Never null: the text opens with a delimiter and closes with one. The\n // mutants of that branch are equivalent.\n return read === null ? [] : entriesOf(read, baseOffset - 4).entries;\n}\n\n/** The entries a reading holds, and the values it could not read as problems. */\nfunction entriesOf(read: Read, shift: number): { entries: YamlEntry[]; problems: FrontMatterProblem[] } {\n const entries: YamlEntry[] = [];\n const problems: FrontMatterProblem[] = [];\n for (const entry of read.entries) {\n const { value } = entry;\n if (value.kind === 'unsupported') {\n // Only `readEntries` keeps these, and it shifts by 0, where adding and\n // subtracting agree: the mutants of the two sums below are equivalent.\n problems.push({\n message: `front matter: \"${entry.key}\" is not read: ${value.reason}`,\n start: entry.valueStart + shift,\n end: entry.valueEnd + shift,\n unread: true,\n key: entry.key.toLowerCase(),\n });\n continue;\n }\n entries.push({\n key: entry.key.toLowerCase(),\n value: value.kind === 'scalar' ? value.scalar.text : value.items.map((item) => item.text),\n start: entry.keyStart + shift,\n end: entry.valueEnd + shift,\n valueStart: entry.valueStart + shift,\n });\n }\n return { entries, problems };\n}\n\n/** Collapses parsed entries into the flat record stored on a document node. */\nexport function toRecord(entries: readonly YamlEntry[]): Record<string, string | readonly string[]> {\n // Defined rather than assigned: assigning `__proto__` sets what the record\n // inherits from, so a list under that key answered `fm.0` and the key\n // itself was lost. Of a key written twice the last is kept, as it was.\n return Object.fromEntries(entries.map((entry) => [entry.key, entry.value]));\n}\n\n/** Every value of a key as a flat list, whether it was a scalar or a sequence. */\nexport function valuesOf(entry: YamlEntry | undefined): string[] {\n if (!entry) return [];\n return typeof entry.value === 'string'\n ? entry.value.length > 0\n ? [entry.value]\n : []\n : [...entry.value];\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@descent-vtt/spec-graph",
3
- "version": "0.11.1",
3
+ "version": "0.12.1",
4
4
  "description": "Turn Markdown specs into a verifiable relational graph. Catches ghost handovers, stale premises and broken references in CI.",
5
5
  "keywords": [
6
6
  "adr",
@@ -57,7 +57,7 @@
57
57
  "devDependencies": {
58
58
  "@stryker-mutator/core": "^10.0.0",
59
59
  "@stryker-mutator/vitest-runner": "^10.0.0",
60
- "@types/node": "^26.4.1",
60
+ "@types/node": "^22.20.4",
61
61
  "@vitest/coverage-v8": "^4.1.11",
62
62
  "typescript": "^7.0.2",
63
63
  "vitest": "^4.1.11"