diff --git a/apps/api/coverage/clover.xml b/apps/api/coverage/clover.xml new file mode 100644 index 0000000..c21f632 --- /dev/null +++ b/apps/api/coverage/clover.xml @@ -0,0 +1,876 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/api/coverage/coverage-final.json b/apps/api/coverage/coverage-final.json new file mode 100644 index 0000000..4099f32 --- /dev/null +++ b/apps/api/coverage/coverage-final.json @@ -0,0 +1,30 @@ +{"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\app.controller.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\app.controller.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":49}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":32}},"2":{"start":{"line":5,"column":7},"end":{"line":30,"column":null}},"3":{"start":{"line":13,"column":8},"end":{"line":17,"column":10}},"4":{"start":{"line":28,"column":8},"end":{"line":28,"column":32}},"5":{"start":{"line":5,"column":13},"end":{"line":5,"column":26}},"6":{"start":{"line":12,"column":4},"end":{"line":18,"column":null}},"7":{"start":{"line":27,"column":4},"end":{"line":29,"column":null}},"8":{"start":{"line":5,"column":13},"end":{"line":30,"column":null}}},"fnMap":{"0":{"name":"(anonymous_2)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":11}},"loc":{"start":{"line":12,"column":11},"end":{"line":18,"column":5}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":27,"column":4},"end":{"line":27,"column":13}},"loc":{"start":{"line":27,"column":13},"end":{"line":29,"column":5}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"f":{"0":0,"1":0},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\app.module.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\app.module.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":40}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":41}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":46}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":68}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":49}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":65}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":90}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":83}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":81}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":66}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":85}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":74}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":62}},"13":{"start":{"line":62,"column":7},"end":{"line":62,"column":null}},"14":{"start":{"line":62,"column":13},"end":{"line":62,"column":22}},"15":{"start":{"line":62,"column":13},"end":{"line":62,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"f":{},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\index.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\index.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":42}},"1":{"start":{"line":3,"column":13},"end":{"line":3,"column":45}},"2":{"start":{"line":4,"column":12},"end":{"line":4,"column":35}},"3":{"start":{"line":6,"column":0},"end":{"line":6,"column":null}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":17},"end":{"line":6,"column":20}},"loc":{"start":{"line":6,"column":22},"end":{"line":6,"column":25}}}},"branchMap":{"0":{"loc":{"start":{"line":3,"column":20},"end":{"line":3,"column":44}},"type":"binary-expr","locations":[{"start":{"line":3,"column":20},"end":{"line":3,"column":36}},{"start":{"line":3,"column":40},"end":{"line":3,"column":44}}]}},"s":{"0":0,"1":0,"2":0,"3":0},"f":{"0":0},"b":{"0":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\main.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\main.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":43}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":48}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":41}},"3":{"start":{"line":6,"column":14},"end":{"line":6,"column":49}},"4":{"start":{"line":10,"column":2},"end":{"line":13,"column":5}},"5":{"start":{"line":15,"column":15},"end":{"line":15,"column":39}},"6":{"start":{"line":16,"column":2},"end":{"line":16,"column":25}},"7":{"start":{"line":17,"column":2},"end":{"line":17,"column":73}},"8":{"start":{"line":18,"column":2},"end":{"line":18,"column":76}},"9":{"start":{"line":21,"column":0},"end":{"line":21,"column":12}}},"fnMap":{"0":{"name":"bootstrap","decl":{"start":{"line":5,"column":15},"end":{"line":5,"column":24}},"loc":{"start":{"line":5,"column":24},"end":{"line":19,"column":1}}}},"branchMap":{"0":{"loc":{"start":{"line":15,"column":15},"end":{"line":15,"column":39}},"type":"binary-expr","locations":[{"start":{"line":15,"column":15},"end":{"line":15,"column":31}},{"start":{"line":15,"column":35},"end":{"line":15,"column":39}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0},"f":{"0":0},"b":{"0":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\scan.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\scan.ts","statementMap":{"0":{"start":{"line":3,"column":0},"end":{"line":3,"column":7}},"1":{"start":{"line":4,"column":2},"end":{"line":4,"column":null}},"2":{"start":{"line":5,"column":2},"end":{"line":5,"column":null}},"3":{"start":{"line":6,"column":2},"end":{"line":6,"column":null}},"4":{"start":{"line":7,"column":2},"end":{"line":7,"column":null}},"5":{"start":{"line":8,"column":2},"end":{"line":8,"column":null}},"6":{"start":{"line":9,"column":2},"end":{"line":9,"column":null}},"7":{"start":{"line":10,"column":17},"end":{"line":10,"column":93}},"8":{"start":{"line":11,"column":2},"end":{"line":11,"column":null}},"9":{"start":{"line":12,"column":2},"end":{"line":12,"column":null}},"10":{"start":{"line":16,"column":2},"end":{"line":16,"column":null}},"11":{"start":{"line":16,"column":32},"end":{"line":16,"column":55}}},"fnMap":{"0":{"name":"performScan","decl":{"start":{"line":3,"column":22},"end":{"line":3,"column":33}},"loc":{"start":{"line":3,"column":68},"end":{"line":13,"column":1}}},"1":{"name":"sleep","decl":{"start":{"line":15,"column":9},"end":{"line":15,"column":14}},"loc":{"start":{"line":15,"column":25},"end":{"line":17,"column":1}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":16,"column":21},"end":{"line":16,"column":28}},"loc":{"start":{"line":16,"column":32},"end":{"line":16,"column":55}}}},"branchMap":{"0":{"loc":{"start":{"line":10,"column":72},"end":{"line":10,"column":83}},"type":"binary-expr","locations":[{"start":{"line":10,"column":72},"end":{"line":10,"column":77}},{"start":{"line":10,"column":81},"end":{"line":10,"column":83}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\server.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\server.ts","statementMap":{"0":{"start":{"line":6,"column":0},"end":{"line":6,"column":16}},"1":{"start":{"line":1,"column":0},"end":{"line":1,"column":52}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":63}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":95}},"4":{"start":{"line":7,"column":14},"end":{"line":7,"column":23}},"5":{"start":{"line":10,"column":2},"end":{"line":10,"column":null}},"6":{"start":{"line":11,"column":2},"end":{"line":11,"column":null}},"7":{"start":{"line":14,"column":2},"end":{"line":25,"column":null}},"8":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"9":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"10":{"start":{"line":17,"column":4},"end":{"line":17,"column":null}},"11":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"12":{"start":{"line":20,"column":4},"end":{"line":23,"column":5}},"13":{"start":{"line":21,"column":6},"end":{"line":21,"column":null}},"14":{"start":{"line":22,"column":6},"end":{"line":22,"column":12}},"15":{"start":{"line":24,"column":4},"end":{"line":24,"column":null}},"16":{"start":{"line":28,"column":2},"end":{"line":35,"column":null}},"17":{"start":{"line":29,"column":4},"end":{"line":34,"column":null}},"18":{"start":{"line":38,"column":2},"end":{"line":52,"column":null}},"19":{"start":{"line":39,"column":4},"end":{"line":51,"column":null}},"20":{"start":{"line":55,"column":2},"end":{"line":60,"column":null}},"21":{"start":{"line":56,"column":20},"end":{"line":56,"column":34}},"22":{"start":{"line":57,"column":20},"end":{"line":57,"column":87}},"23":{"start":{"line":58,"column":16},"end":{"line":58,"column":109}},"24":{"start":{"line":59,"column":4},"end":{"line":59,"column":null}},"25":{"start":{"line":62,"column":2},"end":{"line":72,"column":null}},"26":{"start":{"line":63,"column":15},"end":{"line":63,"column":28}},"27":{"start":{"line":64,"column":16},"end":{"line":64,"column":38}},"28":{"start":{"line":65,"column":4},"end":{"line":68,"column":5}},"29":{"start":{"line":66,"column":6},"end":{"line":66,"column":null}},"30":{"start":{"line":67,"column":6},"end":{"line":67,"column":12}},"31":{"start":{"line":69,"column":18},"end":{"line":69,"column":38}},"32":{"start":{"line":70,"column":21},"end":{"line":70,"column":38}},"33":{"start":{"line":71,"column":4},"end":{"line":71,"column":null}},"34":{"start":{"line":74,"column":2},"end":{"line":88,"column":null}},"35":{"start":{"line":75,"column":15},"end":{"line":75,"column":28}},"36":{"start":{"line":76,"column":16},"end":{"line":76,"column":38}},"37":{"start":{"line":77,"column":4},"end":{"line":80,"column":5}},"38":{"start":{"line":78,"column":6},"end":{"line":78,"column":null}},"39":{"start":{"line":79,"column":6},"end":{"line":79,"column":12}},"40":{"start":{"line":81,"column":18},"end":{"line":81,"column":38}},"41":{"start":{"line":82,"column":4},"end":{"line":85,"column":5}},"42":{"start":{"line":83,"column":6},"end":{"line":83,"column":null}},"43":{"start":{"line":84,"column":6},"end":{"line":84,"column":12}},"44":{"start":{"line":86,"column":19},"end":{"line":86,"column":34}},"45":{"start":{"line":87,"column":4},"end":{"line":87,"column":null}},"46":{"start":{"line":91,"column":2},"end":{"line":91,"column":null}},"47":{"start":{"line":94,"column":2},"end":{"line":94,"column":null}},"48":{"start":{"line":95,"column":2},"end":{"line":95,"column":null}},"49":{"start":{"line":97,"column":2},"end":{"line":97,"column":null}}},"fnMap":{"0":{"name":"createServer","decl":{"start":{"line":6,"column":16},"end":{"line":6,"column":28}},"loc":{"start":{"line":6,"column":41},"end":{"line":98,"column":1}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":14,"column":10},"end":{"line":14,"column":11}},"loc":{"start":{"line":14,"column":29},"end":{"line":25,"column":3}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":28,"column":21},"end":{"line":28,"column":22}},"loc":{"start":{"line":28,"column":53},"end":{"line":35,"column":3}}},"3":{"name":"(anonymous_3)","decl":{"start":{"line":38,"column":19},"end":{"line":38,"column":20}},"loc":{"start":{"line":38,"column":51},"end":{"line":52,"column":3}}},"4":{"name":"(anonymous_4)","decl":{"start":{"line":55,"column":20},"end":{"line":55,"column":25}},"loc":{"start":{"line":55,"column":58},"end":{"line":60,"column":3}}},"5":{"name":"(anonymous_5)","decl":{"start":{"line":62,"column":30},"end":{"line":62,"column":35}},"loc":{"start":{"line":62,"column":68},"end":{"line":72,"column":3}}},"6":{"name":"(anonymous_6)","decl":{"start":{"line":74,"column":30},"end":{"line":74,"column":35}},"loc":{"start":{"line":74,"column":68},"end":{"line":88,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":20,"column":4},"end":{"line":23,"column":5}},"type":"if","locations":[{"start":{"line":20,"column":4},"end":{"line":23,"column":5}},{"start":{},"end":{}}]},"1":{"loc":{"start":{"line":32,"column":15},"end":{"line":32,"column":57}},"type":"binary-expr","locations":[{"start":{"line":32,"column":15},"end":{"line":32,"column":46}},{"start":{"line":32,"column":50},"end":{"line":32,"column":57}}]},"2":{"loc":{"start":{"line":56,"column":20},"end":{"line":56,"column":34}},"type":"binary-expr","locations":[{"start":{"line":56,"column":20},"end":{"line":56,"column":28}},{"start":{"line":56,"column":32},"end":{"line":56,"column":34}}]},"3":{"loc":{"start":{"line":57,"column":20},"end":{"line":57,"column":87}},"type":"binary-expr","locations":[{"start":{"line":57,"column":20},"end":{"line":57,"column":60}},{"start":{"line":57,"column":64},"end":{"line":57,"column":87}}]},"4":{"loc":{"start":{"line":65,"column":4},"end":{"line":68,"column":5}},"type":"if","locations":[{"start":{"line":65,"column":4},"end":{"line":68,"column":5}},{"start":{},"end":{}}]},"5":{"loc":{"start":{"line":70,"column":21},"end":{"line":70,"column":38}},"type":"binary-expr","locations":[{"start":{"line":70,"column":21},"end":{"line":70,"column":33}},{"start":{"line":70,"column":37},"end":{"line":70,"column":38}}]},"6":{"loc":{"start":{"line":77,"column":4},"end":{"line":80,"column":5}},"type":"if","locations":[{"start":{"line":77,"column":4},"end":{"line":80,"column":5}},{"start":{},"end":{}}]},"7":{"loc":{"start":{"line":82,"column":4},"end":{"line":85,"column":5}},"type":"if","locations":[{"start":{"line":82,"column":4},"end":{"line":85,"column":5}},{"start":{},"end":{}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\auth.module.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\auth.module.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":40}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":40}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":50}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":61}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":56}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":55}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":50}},"7":{"start":{"line":27,"column":7},"end":{"line":27,"column":null}},"8":{"start":{"line":27,"column":13},"end":{"line":27,"column":23}},"9":{"start":{"line":27,"column":13},"end":{"line":27,"column":null}},"10":{"start":{"line":14,"column":59},"end":{"line":20,"column":8}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":14,"column":18},"end":{"line":14,"column":23}},"loc":{"start":{"line":14,"column":59},"end":{"line":20,"column":8}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":14},"f":{"0":14},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\index.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\index.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":9}},"1":{"start":{"line":1,"column":9},"end":{"line":1,"column":43}},"2":{"start":{"line":2,"column":0},"end":{"line":2,"column":29}},"3":{"start":{"line":3,"column":0},"end":{"line":3,"column":25}},"4":{"start":{"line":4,"column":0},"end":{"line":4,"column":29}}},"fnMap":{"0":{"name":"(anonymous_4)","decl":{"start":{"line":1,"column":9},"end":{"line":1,"column":19}},"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":43}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0},"f":{"0":0},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\__tests__\\index.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\__tests__\\index.ts","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":36}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":38}},"2":{"start":{"line":4,"column":0},"end":{"line":4,"column":35}},"3":{"start":{"line":5,"column":0},"end":{"line":5,"column":34}},"4":{"start":{"line":6,"column":0},"end":{"line":6,"column":35}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0},"f":{},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\decorators\\current-user.decorator.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\decorators\\current-user.decorator.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":72}},"1":{"start":{"line":20,"column":13},"end":{"line":31,"column":2}},"2":{"start":{"line":22,"column":20},"end":{"line":22,"column":70}},"3":{"start":{"line":23,"column":17},"end":{"line":23,"column":29}},"4":{"start":{"line":25,"column":4},"end":{"line":27,"column":5}},"5":{"start":{"line":26,"column":6},"end":{"line":26,"column":23}},"6":{"start":{"line":29,"column":4},"end":{"line":29,"column":34}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":21,"column":2},"end":{"line":21,"column":3}},"loc":{"start":{"line":21,"column":60},"end":{"line":30,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":25,"column":4},"end":{"line":27,"column":5}},"type":"if","locations":[{"start":{"line":25,"column":4},"end":{"line":27,"column":5}},{"start":{},"end":{}}]},"1":{"loc":{"start":{"line":29,"column":11},"end":{"line":29,"column":33}},"type":"cond-expr","locations":[{"start":{"line":29,"column":17},"end":{"line":29,"column":26}},{"start":{"line":29,"column":29},"end":{"line":29,"column":33}}]}},"s":{"0":1,"1":1,"2":0,"3":0,"4":0,"5":0,"6":0},"f":{"0":0},"b":{"0":[0,0],"1":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\decorators\\index.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\decorators\\index.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":9}},"1":{"start":{"line":1,"column":9},"end":{"line":1,"column":17}},"2":{"start":{"line":1,"column":17},"end":{"line":1,"column":59}},"3":{"start":{"line":2,"column":0},"end":{"line":2,"column":9}},"4":{"start":{"line":2,"column":9},"end":{"line":2,"column":16}},"5":{"start":{"line":2,"column":16},"end":{"line":2,"column":32}},"6":{"start":{"line":2,"column":32},"end":{"line":2,"column":69}},"7":{"start":{"line":3,"column":0},"end":{"line":3,"column":9}},"8":{"start":{"line":3,"column":9},"end":{"line":3,"column":55}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":1,"column":9},"end":{"line":1,"column":15}},"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":17}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":1,"column":17},"end":{"line":1,"column":30}},"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":59}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":2,"column":9},"end":{"line":2,"column":14}},"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":16}}},"3":{"name":"(anonymous_3)","decl":{"start":{"line":2,"column":16},"end":{"line":2,"column":20}},"loc":{"start":{"line":2,"column":16},"end":{"line":2,"column":32}}},"4":{"name":"(anonymous_4)","decl":{"start":{"line":2,"column":32},"end":{"line":2,"column":41}},"loc":{"start":{"line":2,"column":32},"end":{"line":2,"column":69}}},"5":{"name":"(anonymous_5)","decl":{"start":{"line":3,"column":9},"end":{"line":3,"column":20}},"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":55}}}},"branchMap":{},"s":{"0":1,"1":2,"2":2,"3":1,"4":2,"5":9,"6":2,"7":1,"8":1},"f":{"0":1,"1":1,"2":1,"3":8,"4":1,"5":0},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\decorators\\public.decorator.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\decorators\\public.decorator.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":45}},"1":{"start":{"line":3,"column":13},"end":{"line":3,"column":40}},"2":{"start":{"line":16,"column":22},"end":{"line":16,"column":60}},"3":{"start":{"line":16,"column":28},"end":{"line":16,"column":60}},"4":{"start":{"line":16,"column":13},"end":{"line":16,"column":22}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":16,"column":22},"end":{"line":16,"column":25}},"loc":{"start":{"line":16,"column":28},"end":{"line":16,"column":60}}}},"branchMap":{},"s":{"0":3,"1":3,"2":3,"3":0,"4":3},"f":{"0":0},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\decorators\\roles.decorator.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\decorators\\roles.decorator.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":45}},"1":{"start":{"line":3,"column":13},"end":{"line":3,"column":33}},"2":{"start":{"line":18,"column":21},"end":{"line":18,"column":74}},"3":{"start":{"line":18,"column":45},"end":{"line":18,"column":74}},"4":{"start":{"line":18,"column":13},"end":{"line":18,"column":21}},"5":{"start":{"line":23,"column":13},"end":{"line":28,"column":11}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":18,"column":21},"end":{"line":18,"column":22}},"loc":{"start":{"line":18,"column":45},"end":{"line":18,"column":74}}}},"branchMap":{},"s":{"0":3,"1":3,"2":3,"3":0,"4":3,"5":3},"f":{"0":0},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\guards\\index.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\guards\\index.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":9}},"1":{"start":{"line":1,"column":9},"end":{"line":1,"column":48}},"2":{"start":{"line":2,"column":0},"end":{"line":2,"column":9}},"3":{"start":{"line":2,"column":9},"end":{"line":2,"column":43}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":1,"column":9},"end":{"line":1,"column":21}},"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":48}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":2,"column":9},"end":{"line":2,"column":19}},"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":43}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0},"f":{"0":0,"1":0},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\guards\\jwt-auth.guard.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\guards\\jwt-auth.guard.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":6,"column":0},"end":{"line":6,"column":41}},"2":{"start":{"line":7,"column":0},"end":{"line":7,"column":45}},"3":{"start":{"line":8,"column":0},"end":{"line":8,"column":63}},"4":{"start":{"line":11,"column":7},"end":{"line":53,"column":null}},"5":{"start":{"line":13,"column":4},"end":{"line":13,"column":12}},"6":{"start":{"line":12,"column":22},"end":{"line":12,"column":33}},"7":{"start":{"line":18,"column":21},"end":{"line":21,"column":6}},"8":{"start":{"line":23,"column":4},"end":{"line":25,"column":5}},"9":{"start":{"line":24,"column":6},"end":{"line":24,"column":18}},"10":{"start":{"line":28,"column":4},"end":{"line":28,"column":38}},"11":{"start":{"line":33,"column":4},"end":{"line":49,"column":5}},"12":{"start":{"line":34,"column":20},"end":{"line":34,"column":58}},"13":{"start":{"line":36,"column":6},"end":{"line":42,"column":7}},"14":{"start":{"line":37,"column":8},"end":{"line":37,"column":50}},"15":{"start":{"line":38,"column":13},"end":{"line":42,"column":7}},"16":{"start":{"line":39,"column":8},"end":{"line":39,"column":53}},"17":{"start":{"line":40,"column":13},"end":{"line":42,"column":7}},"18":{"start":{"line":41,"column":8},"end":{"line":41,"column":31}},"19":{"start":{"line":44,"column":6},"end":{"line":48,"column":9}},"20":{"start":{"line":51,"column":4},"end":{"line":51,"column":16}},"21":{"start":{"line":11,"column":13},"end":{"line":11,"column":25}},"22":{"start":{"line":11,"column":13},"end":{"line":53,"column":null}}},"fnMap":{"0":{"name":"(anonymous_2)","decl":{"start":{"line":12,"column":2},"end":{"line":12,"column":22}},"loc":{"start":{"line":12,"column":42},"end":{"line":14,"column":3}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":16,"column":2},"end":{"line":16,"column":13}},"loc":{"start":{"line":16,"column":39},"end":{"line":29,"column":3}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":31,"column":2},"end":{"line":31,"column":15}},"loc":{"start":{"line":31,"column":46},"end":{"line":52,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":23,"column":4},"end":{"line":25,"column":5}},"type":"if","locations":[{"start":{"line":23,"column":4},"end":{"line":25,"column":5}},{"start":{},"end":{}}]},"1":{"loc":{"start":{"line":33,"column":4},"end":{"line":49,"column":5}},"type":"if","locations":[{"start":{"line":33,"column":4},"end":{"line":49,"column":5}},{"start":{},"end":{}}]},"2":{"loc":{"start":{"line":33,"column":8},"end":{"line":33,"column":20}},"type":"binary-expr","locations":[{"start":{"line":33,"column":8},"end":{"line":33,"column":11}},{"start":{"line":33,"column":15},"end":{"line":33,"column":20}}]},"3":{"loc":{"start":{"line":36,"column":6},"end":{"line":42,"column":7}},"type":"if","locations":[{"start":{"line":36,"column":6},"end":{"line":42,"column":7}},{"start":{"line":38,"column":13},"end":{"line":42,"column":7}}]},"4":{"loc":{"start":{"line":38,"column":13},"end":{"line":42,"column":7}},"type":"if","locations":[{"start":{"line":38,"column":13},"end":{"line":42,"column":7}},{"start":{"line":40,"column":13},"end":{"line":42,"column":7}}]},"5":{"loc":{"start":{"line":40,"column":13},"end":{"line":42,"column":7}},"type":"if","locations":[{"start":{"line":40,"column":13},"end":{"line":42,"column":7}},{"start":{},"end":{}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":23,"6":23,"7":1,"8":1,"9":1,"10":0,"11":8,"12":7,"13":7,"14":1,"15":6,"16":1,"17":5,"18":1,"19":7,"20":1,"21":2,"22":2},"f":{"0":23,"1":1,"2":8},"b":{"0":[1,0],"1":[7,1],"2":[8,7],"3":[1,6],"4":[1,5],"5":[1,4]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\guards\\roles.guard.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\guards\\roles.guard.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":7,"column":0},"end":{"line":7,"column":41}},"2":{"start":{"line":8,"column":0},"end":{"line":8,"column":58}},"3":{"start":{"line":12,"column":7},"end":{"line":51,"column":null}},"4":{"start":{"line":13,"column":22},"end":{"line":13,"column":33}},"5":{"start":{"line":16,"column":26},"end":{"line":19,"column":6}},"6":{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},"7":{"start":{"line":23,"column":6},"end":{"line":23,"column":18}},"8":{"start":{"line":26,"column":21},"end":{"line":26,"column":75}},"9":{"start":{"line":28,"column":4},"end":{"line":34,"column":5}},"10":{"start":{"line":29,"column":6},"end":{"line":33,"column":9}},"11":{"start":{"line":36,"column":22},"end":{"line":36,"column":38}},"12":{"start":{"line":39,"column":20},"end":{"line":39,"column":74}},"13":{"start":{"line":39,"column":49},"end":{"line":39,"column":73}},"14":{"start":{"line":41,"column":4},"end":{"line":47,"column":5}},"15":{"start":{"line":42,"column":6},"end":{"line":46,"column":9}},"16":{"start":{"line":49,"column":4},"end":{"line":49,"column":16}},"17":{"start":{"line":12,"column":13},"end":{"line":12,"column":23}},"18":{"start":{"line":12,"column":13},"end":{"line":51,"column":null}}},"fnMap":{"0":{"name":"(anonymous_2)","decl":{"start":{"line":13,"column":2},"end":{"line":13,"column":22}},"loc":{"start":{"line":13,"column":42},"end":{"line":13,"column":46}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":15,"column":2},"end":{"line":15,"column":13}},"loc":{"start":{"line":15,"column":39},"end":{"line":50,"column":3}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":39,"column":39},"end":{"line":39,"column":40}},"loc":{"start":{"line":39,"column":49},"end":{"line":39,"column":73}}}},"branchMap":{"0":{"loc":{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},"type":"if","locations":[{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},{"start":{},"end":{}}]},"1":{"loc":{"start":{"line":22,"column":8},"end":{"line":22,"column":52}},"type":"binary-expr","locations":[{"start":{"line":22,"column":8},"end":{"line":22,"column":22}},{"start":{"line":22,"column":26},"end":{"line":22,"column":52}}]},"2":{"loc":{"start":{"line":28,"column":4},"end":{"line":34,"column":5}},"type":"if","locations":[{"start":{"line":28,"column":4},"end":{"line":34,"column":5}},{"start":{},"end":{}}]},"3":{"loc":{"start":{"line":36,"column":22},"end":{"line":36,"column":38}},"type":"binary-expr","locations":[{"start":{"line":36,"column":22},"end":{"line":36,"column":32}},{"start":{"line":36,"column":36},"end":{"line":36,"column":38}}]},"4":{"loc":{"start":{"line":41,"column":4},"end":{"line":47,"column":5}},"type":"if","locations":[{"start":{"line":41,"column":4},"end":{"line":47,"column":5}},{"start":{},"end":{}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":25,"5":13,"6":13,"7":3,"8":10,"9":10,"10":2,"11":8,"12":8,"13":11,"14":8,"15":6,"16":2,"17":2,"18":2},"f":{"0":25,"1":13,"2":11},"b":{"0":[3,10],"1":[13,12],"2":[2,8],"3":[8,1],"4":[6,2]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\strategies\\index.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\strategies\\index.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":9}},"1":{"start":{"line":1,"column":9},"end":{"line":1,"column":66}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":1,"column":9},"end":{"line":1,"column":20}},"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":66}}}},"branchMap":{},"s":{"0":0,"1":0},"f":{"0":0},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\strategies\\jwt.strategy.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\auth\\strategies\\jwt.strategy.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":67}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":47}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":52}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":52}},"4":{"start":{"line":24,"column":7},"end":{"line":72,"column":null}},"5":{"start":{"line":26,"column":19},"end":{"line":26,"column":58}},"6":{"start":{"line":27,"column":4},"end":{"line":29,"column":5}},"7":{"start":{"line":28,"column":6},"end":{"line":28,"column":69}},"8":{"start":{"line":31,"column":4},"end":{"line":38,"column":7}},"9":{"start":{"line":25,"column":31},"end":{"line":25,"column":46}},"10":{"start":{"line":43,"column":4},"end":{"line":45,"column":5}},"11":{"start":{"line":44,"column":6},"end":{"line":44,"column":78}},"12":{"start":{"line":47,"column":4},"end":{"line":49,"column":5}},"13":{"start":{"line":48,"column":6},"end":{"line":48,"column":77}},"14":{"start":{"line":51,"column":4},"end":{"line":53,"column":5}},"15":{"start":{"line":52,"column":6},"end":{"line":52,"column":79}},"16":{"start":{"line":55,"column":4},"end":{"line":57,"column":5}},"17":{"start":{"line":56,"column":6},"end":{"line":56,"column":81}},"18":{"start":{"line":60,"column":16},"end":{"line":60,"column":45}},"19":{"start":{"line":61,"column":4},"end":{"line":63,"column":5}},"20":{"start":{"line":62,"column":6},"end":{"line":62,"column":59}},"21":{"start":{"line":66,"column":4},"end":{"line":70,"column":6}},"22":{"start":{"line":24,"column":13},"end":{"line":24,"column":24}},"23":{"start":{"line":24,"column":13},"end":{"line":72,"column":null}}},"fnMap":{"0":{"name":"(anonymous_2)","decl":{"start":{"line":25,"column":2},"end":{"line":25,"column":31}},"loc":{"start":{"line":25,"column":59},"end":{"line":39,"column":3}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":41,"column":2},"end":{"line":41,"column":7}},"loc":{"start":{"line":41,"column":36},"end":{"line":71,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":27,"column":4},"end":{"line":29,"column":5}},"type":"if","locations":[{"start":{"line":27,"column":4},"end":{"line":29,"column":5}},{"start":{},"end":{}}]},"1":{"loc":{"start":{"line":43,"column":4},"end":{"line":45,"column":5}},"type":"if","locations":[{"start":{"line":43,"column":4},"end":{"line":45,"column":5}},{"start":{},"end":{}}]},"2":{"loc":{"start":{"line":47,"column":4},"end":{"line":49,"column":5}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":49,"column":5}},{"start":{},"end":{}}]},"3":{"loc":{"start":{"line":51,"column":4},"end":{"line":53,"column":5}},"type":"if","locations":[{"start":{"line":51,"column":4},"end":{"line":53,"column":5}},{"start":{},"end":{}}]},"4":{"loc":{"start":{"line":55,"column":4},"end":{"line":57,"column":5}},"type":"if","locations":[{"start":{"line":55,"column":4},"end":{"line":57,"column":5}},{"start":{},"end":{}}]},"5":{"loc":{"start":{"line":61,"column":4},"end":{"line":63,"column":5}},"type":"if","locations":[{"start":{"line":61,"column":4},"end":{"line":63,"column":5}},{"start":{},"end":{}}]},"6":{"loc":{"start":{"line":68,"column":13},"end":{"line":68,"column":32}},"type":"binary-expr","locations":[{"start":{"line":68,"column":13},"end":{"line":68,"column":26}},{"start":{"line":68,"column":30},"end":{"line":68,"column":32}}]},"7":{"loc":{"start":{"line":69,"column":19},"end":{"line":69,"column":44}},"type":"binary-expr","locations":[{"start":{"line":69,"column":19},"end":{"line":69,"column":38}},{"start":{"line":69,"column":42},"end":{"line":69,"column":44}}]}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":26,"6":26,"7":2,"8":24,"9":24,"10":8,"11":1,"12":7,"13":1,"14":6,"15":1,"16":5,"17":1,"18":4,"19":4,"20":2,"21":2,"22":2,"23":2},"f":{"0":26,"1":8},"b":{"0":[2,24],"1":[1,7],"2":[1,6],"3":[1,5],"4":[1,4],"5":[2,2],"6":[2,1],"7":[2,1]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\controllers\\analysis.controller.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\controllers\\analysis.controller.ts","statementMap":{"0":{"start":{"line":13,"column":22},"end":{"line":13,"column":34}},"1":{"start":{"line":16,"column":4},"end":{"line":52,"column":5}},"2":{"start":{"line":17,"column":22},"end":{"line":17,"column":59}},"3":{"start":{"line":20,"column":22},"end":{"line":20,"column":53}},"4":{"start":{"line":23,"column":18},"end":{"line":37,"column":8}},"5":{"start":{"line":39,"column":41},"end":{"line":46,"column":8}},"6":{"start":{"line":48,"column":6},"end":{"line":48,"column":37}},"7":{"start":{"line":50,"column":6},"end":{"line":50,"column":57}},"8":{"start":{"line":51,"column":6},"end":{"line":51,"column":103}},"9":{"start":{"line":56,"column":4},"end":{"line":106,"column":5}},"10":{"start":{"line":57,"column":20},"end":{"line":57,"column":33}},"11":{"start":{"line":59,"column":6},"end":{"line":69,"column":7}},"12":{"start":{"line":60,"column":8},"end":{"line":67,"column":11}},"13":{"start":{"line":68,"column":8},"end":{"line":68,"column":15}},"14":{"start":{"line":71,"column":18},"end":{"line":71,"column":48}},"15":{"start":{"line":73,"column":6},"end":{"line":83,"column":7}},"16":{"start":{"line":74,"column":8},"end":{"line":81,"column":11}},"17":{"start":{"line":82,"column":8},"end":{"line":82,"column":15}},"18":{"start":{"line":85,"column":20},"end":{"line":85,"column":40}},"19":{"start":{"line":86,"column":23},"end":{"line":86,"column":40}},"20":{"start":{"line":88,"column":37},"end":{"line":100,"column":8}},"21":{"start":{"line":102,"column":6},"end":{"line":102,"column":23}},"22":{"start":{"line":104,"column":6},"end":{"line":104,"column":61}},"23":{"start":{"line":105,"column":6},"end":{"line":105,"column":95}},"24":{"start":{"line":110,"column":4},"end":{"line":172,"column":5}},"25":{"start":{"line":111,"column":20},"end":{"line":111,"column":33}},"26":{"start":{"line":113,"column":6},"end":{"line":123,"column":7}},"27":{"start":{"line":114,"column":8},"end":{"line":121,"column":11}},"28":{"start":{"line":122,"column":8},"end":{"line":122,"column":15}},"29":{"start":{"line":125,"column":18},"end":{"line":125,"column":48}},"30":{"start":{"line":127,"column":6},"end":{"line":137,"column":7}},"31":{"start":{"line":128,"column":8},"end":{"line":135,"column":11}},"32":{"start":{"line":136,"column":8},"end":{"line":136,"column":15}},"33":{"start":{"line":139,"column":20},"end":{"line":139,"column":40}},"34":{"start":{"line":141,"column":6},"end":{"line":152,"column":7}},"35":{"start":{"line":142,"column":39},"end":{"line":148,"column":10}},"36":{"start":{"line":150,"column":8},"end":{"line":150,"column":37}},"37":{"start":{"line":151,"column":8},"end":{"line":151,"column":15}},"38":{"start":{"line":154,"column":21},"end":{"line":154,"column":54}},"39":{"start":{"line":156,"column":6},"end":{"line":166,"column":7}},"40":{"start":{"line":157,"column":8},"end":{"line":164,"column":11}},"41":{"start":{"line":165,"column":8},"end":{"line":165,"column":15}},"42":{"start":{"line":168,"column":6},"end":{"line":168,"column":27}},"43":{"start":{"line":170,"column":6},"end":{"line":170,"column":61}},"44":{"start":{"line":171,"column":6},"end":{"line":171,"column":95}},"45":{"start":{"line":176,"column":4},"end":{"line":229,"column":5}},"46":{"start":{"line":177,"column":20},"end":{"line":177,"column":33}},"47":{"start":{"line":179,"column":6},"end":{"line":189,"column":7}},"48":{"start":{"line":180,"column":8},"end":{"line":187,"column":11}},"49":{"start":{"line":188,"column":8},"end":{"line":188,"column":15}},"50":{"start":{"line":191,"column":18},"end":{"line":191,"column":48}},"51":{"start":{"line":193,"column":6},"end":{"line":203,"column":7}},"52":{"start":{"line":194,"column":8},"end":{"line":201,"column":11}},"53":{"start":{"line":202,"column":8},"end":{"line":202,"column":15}},"54":{"start":{"line":205,"column":20},"end":{"line":205,"column":40}},"55":{"start":{"line":207,"column":6},"end":{"line":217,"column":7}},"56":{"start":{"line":208,"column":8},"end":{"line":215,"column":11}},"57":{"start":{"line":216,"column":8},"end":{"line":216,"column":15}},"58":{"start":{"line":219,"column":6},"end":{"line":219,"column":25}},"59":{"start":{"line":221,"column":6},"end":{"line":225,"column":9}},"60":{"start":{"line":227,"column":6},"end":{"line":227,"column":57}},"61":{"start":{"line":228,"column":6},"end":{"line":228,"column":86}},"62":{"start":{"line":233,"column":22},"end":{"line":233,"column":84}},"63":{"start":{"line":233,"column":58},"end":{"line":233,"column":80}},"64":{"start":{"line":234,"column":22},"end":{"line":234,"column":42}},"65":{"start":{"line":236,"column":4},"end":{"line":236,"column":101}},"66":{"start":{"line":240,"column":22},"end":{"line":240,"column":42}},"67":{"start":{"line":241,"column":26},"end":{"line":241,"column":81}},"68":{"start":{"line":241,"column":52},"end":{"line":241,"column":73}},"69":{"start":{"line":242,"column":23},"end":{"line":242,"column":102}},"70":{"start":{"line":242,"column":59},"end":{"line":242,"column":98}},"71":{"start":{"line":245,"column":27},"end":{"line":245,"column":29}},"72":{"start":{"line":248,"column":4},"end":{"line":248,"column":38}},"73":{"start":{"line":251,"column":4},"end":{"line":251,"column":42}},"74":{"start":{"line":254,"column":4},"end":{"line":254,"column":58}},"75":{"start":{"line":257,"column":4},"end":{"line":257,"column":43}},"76":{"start":{"line":261,"column":4},"end":{"line":273,"column":5}},"77":{"start":{"line":264,"column":8},"end":{"line":264,"column":24}},"78":{"start":{"line":266,"column":8},"end":{"line":266,"column":28}},"79":{"start":{"line":268,"column":8},"end":{"line":268,"column":27}},"80":{"start":{"line":270,"column":8},"end":{"line":270,"column":24}},"81":{"start":{"line":272,"column":8},"end":{"line":272,"column":24}},"82":{"start":{"line":277,"column":39},"end":{"line":285,"column":6}},"83":{"start":{"line":287,"column":4},"end":{"line":287,"column":35}},"84":{"start":{"line":12,"column":0},"end":{"line":12,"column":13}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":13,"column":2},"end":{"line":13,"column":22}},"loc":{"start":{"line":13,"column":34},"end":{"line":13,"column":38}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":15,"column":2},"end":{"line":15,"column":7}},"loc":{"start":{"line":15,"column":50},"end":{"line":53,"column":3}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":55,"column":2},"end":{"line":55,"column":7}},"loc":{"start":{"line":55,"column":53},"end":{"line":107,"column":3}}},"3":{"name":"(anonymous_3)","decl":{"start":{"line":109,"column":2},"end":{"line":109,"column":7}},"loc":{"start":{"line":109,"column":53},"end":{"line":173,"column":3}}},"4":{"name":"(anonymous_4)","decl":{"start":{"line":175,"column":2},"end":{"line":175,"column":7}},"loc":{"start":{"line":175,"column":50},"end":{"line":230,"column":3}}},"5":{"name":"(anonymous_5)","decl":{"start":{"line":232,"column":10},"end":{"line":232,"column":27}},"loc":{"start":{"line":232,"column":62},"end":{"line":237,"column":3}}},"6":{"name":"(anonymous_6)","decl":{"start":{"line":233,"column":43},"end":{"line":233,"column":44}},"loc":{"start":{"line":233,"column":58},"end":{"line":233,"column":80}}},"7":{"name":"(anonymous_7)","decl":{"start":{"line":239,"column":10},"end":{"line":239,"column":34}},"loc":{"start":{"line":239,"column":69},"end":{"line":258,"column":3}}},"8":{"name":"(anonymous_8)","decl":{"start":{"line":241,"column":47},"end":{"line":241,"column":48}},"loc":{"start":{"line":241,"column":52},"end":{"line":241,"column":73}}},"9":{"name":"(anonymous_9)","decl":{"start":{"line":242,"column":44},"end":{"line":242,"column":45}},"loc":{"start":{"line":242,"column":59},"end":{"line":242,"column":98}}},"10":{"name":"(anonymous_10)","decl":{"start":{"line":260,"column":10},"end":{"line":260,"column":21}},"loc":{"start":{"line":260,"column":35},"end":{"line":274,"column":3}}},"11":{"name":"(anonymous_11)","decl":{"start":{"line":276,"column":10},"end":{"line":276,"column":27}},"loc":{"start":{"line":276,"column":85},"end":{"line":288,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":27,"column":19},"end":{"line":27,"column":69}},"type":"binary-expr","locations":[{"start":{"line":27,"column":19},"end":{"line":27,"column":56}},{"start":{"line":27,"column":60},"end":{"line":27,"column":69}}]},"1":{"loc":{"start":{"line":31,"column":18},"end":{"line":31,"column":33}},"type":"cond-expr","locations":[{"start":{"line":31,"column":28},"end":{"line":31,"column":29}},{"start":{"line":31,"column":32},"end":{"line":31,"column":33}}]},"2":{"loc":{"start":{"line":36,"column":18},"end":{"line":36,"column":36}},"type":"cond-expr","locations":[{"start":{"line":36,"column":28},"end":{"line":36,"column":30}},{"start":{"line":36,"column":33},"end":{"line":36,"column":36}}]},"3":{"loc":{"start":{"line":59,"column":6},"end":{"line":69,"column":7}},"type":"if","locations":[{"start":{"line":59,"column":6},"end":{"line":69,"column":7}},{"start":{},"end":{}}]},"4":{"loc":{"start":{"line":65,"column":23},"end":{"line":65,"column":73}},"type":"binary-expr","locations":[{"start":{"line":65,"column":23},"end":{"line":65,"column":60}},{"start":{"line":65,"column":64},"end":{"line":65,"column":73}}]},"5":{"loc":{"start":{"line":73,"column":6},"end":{"line":83,"column":7}},"type":"if","locations":[{"start":{"line":73,"column":6},"end":{"line":83,"column":7}},{"start":{},"end":{}}]},"6":{"loc":{"start":{"line":79,"column":23},"end":{"line":79,"column":73}},"type":"binary-expr","locations":[{"start":{"line":79,"column":23},"end":{"line":79,"column":60}},{"start":{"line":79,"column":64},"end":{"line":79,"column":73}}]},"7":{"loc":{"start":{"line":86,"column":23},"end":{"line":86,"column":40}},"type":"binary-expr","locations":[{"start":{"line":86,"column":23},"end":{"line":86,"column":35}},{"start":{"line":86,"column":39},"end":{"line":86,"column":40}}]},"8":{"loc":{"start":{"line":91,"column":18},"end":{"line":91,"column":61}},"type":"cond-expr","locations":[{"start":{"line":91,"column":49},"end":{"line":91,"column":57}},{"start":{"line":91,"column":60},"end":{"line":91,"column":61}}]},"9":{"loc":{"start":{"line":94,"column":21},"end":{"line":94,"column":88}},"type":"cond-expr","locations":[{"start":{"line":94,"column":38},"end":{"line":94,"column":76}},{"start":{"line":94,"column":79},"end":{"line":94,"column":88}}]},"10":{"loc":{"start":{"line":95,"column":15},"end":{"line":99,"column":21}},"type":"cond-expr","locations":[{"start":{"line":95,"column":34},"end":{"line":99,"column":10}},{"start":{"line":99,"column":12},"end":{"line":99,"column":21}}]},"11":{"loc":{"start":{"line":113,"column":6},"end":{"line":123,"column":7}},"type":"if","locations":[{"start":{"line":113,"column":6},"end":{"line":123,"column":7}},{"start":{},"end":{}}]},"12":{"loc":{"start":{"line":119,"column":23},"end":{"line":119,"column":73}},"type":"binary-expr","locations":[{"start":{"line":119,"column":23},"end":{"line":119,"column":60}},{"start":{"line":119,"column":64},"end":{"line":119,"column":73}}]},"13":{"loc":{"start":{"line":127,"column":6},"end":{"line":137,"column":7}},"type":"if","locations":[{"start":{"line":127,"column":6},"end":{"line":137,"column":7}},{"start":{},"end":{}}]},"14":{"loc":{"start":{"line":133,"column":23},"end":{"line":133,"column":73}},"type":"binary-expr","locations":[{"start":{"line":133,"column":23},"end":{"line":133,"column":60}},{"start":{"line":133,"column":64},"end":{"line":133,"column":73}}]},"15":{"loc":{"start":{"line":141,"column":6},"end":{"line":152,"column":7}},"type":"if","locations":[{"start":{"line":141,"column":6},"end":{"line":152,"column":7}},{"start":{},"end":{}}]},"16":{"loc":{"start":{"line":145,"column":20},"end":{"line":145,"column":71}},"type":"cond-expr","locations":[{"start":{"line":145,"column":55},"end":{"line":145,"column":67}},{"start":{"line":145,"column":70},"end":{"line":145,"column":71}}]},"17":{"loc":{"start":{"line":147,"column":23},"end":{"line":147,"column":90}},"type":"cond-expr","locations":[{"start":{"line":147,"column":40},"end":{"line":147,"column":78}},{"start":{"line":147,"column":81},"end":{"line":147,"column":90}}]},"18":{"loc":{"start":{"line":156,"column":6},"end":{"line":166,"column":7}},"type":"if","locations":[{"start":{"line":156,"column":6},"end":{"line":166,"column":7}},{"start":{},"end":{}}]},"19":{"loc":{"start":{"line":162,"column":23},"end":{"line":162,"column":73}},"type":"binary-expr","locations":[{"start":{"line":162,"column":23},"end":{"line":162,"column":60}},{"start":{"line":162,"column":64},"end":{"line":162,"column":73}}]},"20":{"loc":{"start":{"line":179,"column":6},"end":{"line":189,"column":7}},"type":"if","locations":[{"start":{"line":179,"column":6},"end":{"line":189,"column":7}},{"start":{},"end":{}}]},"21":{"loc":{"start":{"line":185,"column":23},"end":{"line":185,"column":73}},"type":"binary-expr","locations":[{"start":{"line":185,"column":23},"end":{"line":185,"column":60}},{"start":{"line":185,"column":64},"end":{"line":185,"column":73}}]},"22":{"loc":{"start":{"line":193,"column":6},"end":{"line":203,"column":7}},"type":"if","locations":[{"start":{"line":193,"column":6},"end":{"line":203,"column":7}},{"start":{},"end":{}}]},"23":{"loc":{"start":{"line":199,"column":23},"end":{"line":199,"column":73}},"type":"binary-expr","locations":[{"start":{"line":199,"column":23},"end":{"line":199,"column":60}},{"start":{"line":199,"column":64},"end":{"line":199,"column":73}}]},"24":{"loc":{"start":{"line":207,"column":6},"end":{"line":217,"column":7}},"type":"if","locations":[{"start":{"line":207,"column":6},"end":{"line":217,"column":7}},{"start":{},"end":{}}]},"25":{"loc":{"start":{"line":213,"column":23},"end":{"line":213,"column":73}},"type":"binary-expr","locations":[{"start":{"line":213,"column":23},"end":{"line":213,"column":60}},{"start":{"line":213,"column":64},"end":{"line":213,"column":73}}]},"26":{"loc":{"start":{"line":233,"column":65},"end":{"line":233,"column":79}},"type":"binary-expr","locations":[{"start":{"line":233,"column":65},"end":{"line":233,"column":74}},{"start":{"line":233,"column":78},"end":{"line":233,"column":79}}]},"27":{"loc":{"start":{"line":236,"column":11},"end":{"line":236,"column":100}},"type":"binary-expr","locations":[{"start":{"line":236,"column":11},"end":{"line":236,"column":38}},{"start":{"line":236,"column":42},"end":{"line":236,"column":56}},{"start":{"line":236,"column":60},"end":{"line":236,"column":100}}]},"28":{"loc":{"start":{"line":261,"column":4},"end":{"line":273,"column":5}},"type":"switch","locations":[{"start":{"line":262,"column":6},"end":{"line":262,"column":21}},{"start":{"line":263,"column":6},"end":{"line":264,"column":24}},{"start":{"line":265,"column":6},"end":{"line":266,"column":28}},{"start":{"line":267,"column":6},"end":{"line":268,"column":27}},{"start":{"line":269,"column":6},"end":{"line":270,"column":24}},{"start":{"line":271,"column":6},"end":{"line":272,"column":24}}]},"29":{"loc":{"start":{"line":281,"column":17},"end":{"line":281,"column":40}},"type":"binary-expr","locations":[{"start":{"line":281,"column":17},"end":{"line":281,"column":31}},{"start":{"line":281,"column":35},"end":{"line":281,"column":40}}]},"30":{"loc":{"start":{"line":283,"column":19},"end":{"line":283,"column":52}},"type":"binary-expr","locations":[{"start":{"line":283,"column":19},"end":{"line":283,"column":39}},{"start":{"line":283,"column":43},"end":{"line":283,"column":52}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0,0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[0,0],"21":[0,0],"22":[0,0],"23":[0,0],"24":[0,0],"25":[0,0],"26":[0,0],"27":[0,0,0],"28":[0,0,0,0,0,0],"29":[0,0],"30":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\controllers\\cross-chain-gas.controller.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\controllers\\cross-chain-gas.controller.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":88}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":94}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":75}},"3":{"start":{"line":9,"column":0},"end":{"line":9,"column":72}},"4":{"start":{"line":14,"column":7},"end":{"line":116,"column":null}},"5":{"start":{"line":15,"column":31},"end":{"line":15,"column":53}},"6":{"start":{"line":39,"column":4},"end":{"line":41,"column":5}},"7":{"start":{"line":40,"column":6},"end":{"line":40,"column":111}},"8":{"start":{"line":43,"column":42},"end":{"line":45,"column":6}},"9":{"start":{"line":47,"column":4},"end":{"line":47,"column":73}},"10":{"start":{"line":60,"column":4},"end":{"line":60,"column":58}},"11":{"start":{"line":78,"column":4},"end":{"line":78,"column":62}},"12":{"start":{"line":79,"column":4},"end":{"line":82,"column":6}},"13":{"start":{"line":110,"column":4},"end":{"line":112,"column":5}},"14":{"start":{"line":111,"column":6},"end":{"line":111,"column":66}},"15":{"start":{"line":114,"column":4},"end":{"line":114,"column":79}},"16":{"start":{"line":14,"column":13},"end":{"line":14,"column":36}},"17":{"start":{"line":36,"column":8},"end":{"line":48,"column":null}},"18":{"start":{"line":59,"column":8},"end":{"line":61,"column":null}},"19":{"start":{"line":77,"column":8},"end":{"line":83,"column":null}},"20":{"start":{"line":106,"column":8},"end":{"line":115,"column":null}},"21":{"start":{"line":14,"column":13},"end":{"line":116,"column":null}}},"fnMap":{"0":{"name":"(anonymous_4)","decl":{"start":{"line":15,"column":2},"end":{"line":15,"column":31}},"loc":{"start":{"line":15,"column":73},"end":{"line":15,"column":77}}},"1":{"name":"(anonymous_5)","decl":{"start":{"line":36,"column":8},"end":{"line":36,"column":34}},"loc":{"start":{"line":37,"column":35},"end":{"line":48,"column":3}}},"2":{"name":"(anonymous_6)","decl":{"start":{"line":59,"column":8},"end":{"line":59,"column":26}},"loc":{"start":{"line":59,"column":26},"end":{"line":61,"column":3}}},"3":{"name":"(anonymous_7)","decl":{"start":{"line":77,"column":8},"end":{"line":77,"column":22}},"loc":{"start":{"line":77,"column":22},"end":{"line":83,"column":3}}},"4":{"name":"(anonymous_8)","decl":{"start":{"line":106,"column":8},"end":{"line":106,"column":21}},"loc":{"start":{"line":108,"column":34},"end":{"line":115,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":39,"column":4},"end":{"line":41,"column":5}},"type":"if","locations":[{"start":{"line":39,"column":4},"end":{"line":41,"column":5}},{"start":{},"end":{}}]},"1":{"loc":{"start":{"line":110,"column":4},"end":{"line":112,"column":5}},"type":"if","locations":[{"start":{"line":110,"column":4},"end":{"line":112,"column":5}},{"start":{},"end":{}}]},"2":{"loc":{"start":{"line":110,"column":8},"end":{"line":110,"column":34}},"type":"binary-expr","locations":[{"start":{"line":110,"column":8},"end":{"line":110,"column":16}},{"start":{"line":110,"column":20},"end":{"line":110,"column":34}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\controllers\\failed-transaction.controller.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\controllers\\failed-transaction.controller.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":null}},"1":{"start":{"line":13,"column":0},"end":{"line":13,"column":104}},"2":{"start":{"line":20,"column":0},"end":{"line":20,"column":86}},"3":{"start":{"line":21,"column":0},"end":{"line":21,"column":72}},"4":{"start":{"line":26,"column":40},"end":{"line":255,"column":null}},"5":{"start":{"line":29,"column":31},"end":{"line":29,"column":59}},"6":{"start":{"line":27,"column":19},"end":{"line":27,"column":73}},"7":{"start":{"line":47,"column":4},"end":{"line":61,"column":5}},"8":{"start":{"line":48,"column":6},"end":{"line":48,"column":85}},"9":{"start":{"line":50,"column":6},"end":{"line":52,"column":7}},"10":{"start":{"line":51,"column":8},"end":{"line":51,"column":82}},"11":{"start":{"line":54,"column":6},"end":{"line":54,"column":82}},"12":{"start":{"line":56,"column":6},"end":{"line":56,"column":77}},"13":{"start":{"line":57,"column":6},"end":{"line":60,"column":8}},"14":{"start":{"line":87,"column":4},"end":{"line":103,"column":5}},"15":{"start":{"line":88,"column":6},"end":{"line":90,"column":7}},"16":{"start":{"line":89,"column":8},"end":{"line":89,"column":82}},"17":{"start":{"line":92,"column":27},"end":{"line":94,"column":19}},"18":{"start":{"line":93,"column":40},"end":{"line":93,"column":59}},"19":{"start":{"line":93,"column":74},"end":{"line":93,"column":84}},"20":{"start":{"line":96,"column":6},"end":{"line":96,"column":90}},"21":{"start":{"line":98,"column":6},"end":{"line":98,"column":74}},"22":{"start":{"line":99,"column":6},"end":{"line":102,"column":8}},"23":{"start":{"line":128,"column":4},"end":{"line":140,"column":5}},"24":{"start":{"line":129,"column":6},"end":{"line":131,"column":7}},"25":{"start":{"line":130,"column":8},"end":{"line":130,"column":82}},"26":{"start":{"line":133,"column":6},"end":{"line":133,"column":90}},"27":{"start":{"line":135,"column":6},"end":{"line":135,"column":80}},"28":{"start":{"line":136,"column":6},"end":{"line":139,"column":8}},"29":{"start":{"line":159,"column":4},"end":{"line":185,"column":5}},"30":{"start":{"line":161,"column":6},"end":{"line":166,"column":7}},"31":{"start":{"line":162,"column":8},"end":{"line":165,"column":10}},"32":{"start":{"line":168,"column":6},"end":{"line":170,"column":7}},"33":{"start":{"line":169,"column":8},"end":{"line":169,"column":82}},"34":{"start":{"line":172,"column":6},"end":{"line":174,"column":7}},"35":{"start":{"line":173,"column":8},"end":{"line":173,"column":84}},"36":{"start":{"line":176,"column":6},"end":{"line":176,"column":116}},"37":{"start":{"line":178,"column":6},"end":{"line":178,"column":93}},"38":{"start":{"line":180,"column":6},"end":{"line":180,"column":79}},"39":{"start":{"line":181,"column":6},"end":{"line":184,"column":8}},"40":{"start":{"line":211,"column":4},"end":{"line":234,"column":5}},"41":{"start":{"line":212,"column":25},"end":{"line":212,"column":42}},"42":{"start":{"line":213,"column":6},"end":{"line":215,"column":7}},"43":{"start":{"line":214,"column":8},"end":{"line":214,"column":76}},"44":{"start":{"line":219,"column":6},"end":{"line":227,"column":8}},"45":{"start":{"line":229,"column":6},"end":{"line":229,"column":71}},"46":{"start":{"line":230,"column":6},"end":{"line":233,"column":8}},"47":{"start":{"line":248,"column":4},"end":{"line":253,"column":6}},"48":{"start":{"line":26,"column":13},"end":{"line":26,"column":40}},"49":{"start":{"line":44,"column":8},"end":{"line":62,"column":null}},"50":{"start":{"line":83,"column":8},"end":{"line":104,"column":null}},"51":{"start":{"line":124,"column":8},"end":{"line":141,"column":null}},"52":{"start":{"line":156,"column":8},"end":{"line":186,"column":null}},"53":{"start":{"line":207,"column":8},"end":{"line":235,"column":null}},"54":{"start":{"line":247,"column":8},"end":{"line":254,"column":null}},"55":{"start":{"line":26,"column":13},"end":{"line":255,"column":null}}},"fnMap":{"0":{"name":"(anonymous_4)","decl":{"start":{"line":29,"column":2},"end":{"line":29,"column":31}},"loc":{"start":{"line":29,"column":85},"end":{"line":29,"column":89}}},"1":{"name":"(anonymous_5)","decl":{"start":{"line":44,"column":8},"end":{"line":44,"column":29}},"loc":{"start":{"line":45,"column":47},"end":{"line":62,"column":3}}},"2":{"name":"(anonymous_6)","decl":{"start":{"line":83,"column":8},"end":{"line":83,"column":24}},"loc":{"start":{"line":85,"column":40},"end":{"line":104,"column":3}}},"3":{"name":"(anonymous_7)","decl":{"start":{"line":93,"column":34},"end":{"line":93,"column":36}},"loc":{"start":{"line":93,"column":40},"end":{"line":93,"column":59}}},"4":{"name":"(anonymous_8)","decl":{"start":{"line":93,"column":68},"end":{"line":93,"column":70}},"loc":{"start":{"line":93,"column":74},"end":{"line":93,"column":84}}},"5":{"name":"(anonymous_9)","decl":{"start":{"line":124,"column":8},"end":{"line":124,"column":30}},"loc":{"start":{"line":126,"column":36},"end":{"line":141,"column":3}}},"6":{"name":"(anonymous_10)","decl":{"start":{"line":156,"column":8},"end":{"line":156,"column":30}},"loc":{"start":{"line":157,"column":55},"end":{"line":186,"column":3}}},"7":{"name":"(anonymous_11)","decl":{"start":{"line":207,"column":8},"end":{"line":207,"column":21}},"loc":{"start":{"line":209,"column":42},"end":{"line":235,"column":3}}},"8":{"name":"(anonymous_12)","decl":{"start":{"line":247,"column":8},"end":{"line":247,"column":17}},"loc":{"start":{"line":247,"column":17},"end":{"line":254,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":50,"column":6},"end":{"line":52,"column":7}},"type":"if","locations":[{"start":{"line":50,"column":6},"end":{"line":52,"column":7}},{"start":{},"end":{}}]},"1":{"loc":{"start":{"line":50,"column":10},"end":{"line":50,"column":73}},"type":"binary-expr","locations":[{"start":{"line":50,"column":10},"end":{"line":50,"column":25}},{"start":{"line":50,"column":29},"end":{"line":50,"column":73}}]},"2":{"loc":{"start":{"line":88,"column":6},"end":{"line":90,"column":7}},"type":"if","locations":[{"start":{"line":88,"column":6},"end":{"line":90,"column":7}},{"start":{},"end":{}}]},"3":{"loc":{"start":{"line":92,"column":27},"end":{"line":94,"column":19}},"type":"cond-expr","locations":[{"start":{"line":93,"column":10},"end":{"line":93,"column":85}},{"start":{"line":94,"column":10},"end":{"line":94,"column":19}}]},"4":{"loc":{"start":{"line":129,"column":6},"end":{"line":131,"column":7}},"type":"if","locations":[{"start":{"line":129,"column":6},"end":{"line":131,"column":7}},{"start":{},"end":{}}]},"5":{"loc":{"start":{"line":161,"column":6},"end":{"line":166,"column":7}},"type":"if","locations":[{"start":{"line":161,"column":6},"end":{"line":166,"column":7}},{"start":{},"end":{}}]},"6":{"loc":{"start":{"line":161,"column":10},"end":{"line":161,"column":86}},"type":"binary-expr","locations":[{"start":{"line":161,"column":10},"end":{"line":161,"column":31}},{"start":{"line":161,"column":35},"end":{"line":161,"column":58}},{"start":{"line":161,"column":62},"end":{"line":161,"column":86}}]},"7":{"loc":{"start":{"line":168,"column":6},"end":{"line":170,"column":7}},"type":"if","locations":[{"start":{"line":168,"column":6},"end":{"line":170,"column":7}},{"start":{},"end":{}}]},"8":{"loc":{"start":{"line":172,"column":6},"end":{"line":174,"column":7}},"type":"if","locations":[{"start":{"line":172,"column":6},"end":{"line":174,"column":7}},{"start":{},"end":{}}]},"9":{"loc":{"start":{"line":213,"column":6},"end":{"line":215,"column":7}},"type":"if","locations":[{"start":{"line":213,"column":6},"end":{"line":215,"column":7}},{"start":{},"end":{}}]},"10":{"loc":{"start":{"line":221,"column":19},"end":{"line":221,"column":37}},"type":"binary-expr","locations":[{"start":{"line":221,"column":19},"end":{"line":221,"column":28}},{"start":{"line":221,"column":32},"end":{"line":221,"column":37}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\example\\example.controller.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\example\\example.controller.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":49}},"1":{"start":{"line":12,"column":7},"end":{"line":21,"column":null}},"2":{"start":{"line":15,"column":4},"end":{"line":19,"column":6}},"3":{"start":{"line":12,"column":13},"end":{"line":12,"column":30}},"4":{"start":{"line":14,"column":2},"end":{"line":20,"column":null}},"5":{"start":{"line":12,"column":13},"end":{"line":21,"column":null}}},"fnMap":{"0":{"name":"(anonymous_2)","decl":{"start":{"line":14,"column":2},"end":{"line":14,"column":12}},"loc":{"start":{"line":14,"column":12},"end":{"line":20,"column":3}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0},"f":{"0":0},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\middleware\\error.middleware.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\middleware\\error.middleware.ts","statementMap":{"0":{"start":{"line":10,"column":0},"end":{"line":10,"column":16}},"1":{"start":{"line":49,"column":0},"end":{"line":49,"column":16}},"2":{"start":{"line":60,"column":0},"end":{"line":60,"column":16}},"3":{"start":{"line":66,"column":0},"end":{"line":66,"column":16}},"4":{"start":{"line":16,"column":2},"end":{"line":23,"column":5}},"5":{"start":{"line":25,"column":21},"end":{"line":25,"column":44}},"6":{"start":{"line":26,"column":20},"end":{"line":26,"column":57}},"7":{"start":{"line":28,"column":37},"end":{"line":36,"column":4}},"8":{"start":{"line":39,"column":2},"end":{"line":44,"column":3}},"9":{"start":{"line":40,"column":4},"end":{"line":43,"column":6}},"10":{"start":{"line":46,"column":2},"end":{"line":46,"column":40}},"11":{"start":{"line":50,"column":2},"end":{"line":57,"column":5}},"12":{"start":{"line":61,"column":2},"end":{"line":62,"column":67}},"13":{"start":{"line":63,"column":2},"end":{"line":63,"column":9}},"14":{"start":{"line":67,"column":2},"end":{"line":74,"column":5}}},"fnMap":{"0":{"name":"errorHandler","decl":{"start":{"line":10,"column":16},"end":{"line":10,"column":28}},"loc":{"start":{"line":14,"column":20},"end":{"line":47,"column":1}}},"1":{"name":"notFoundHandler","decl":{"start":{"line":49,"column":16},"end":{"line":49,"column":31}},"loc":{"start":{"line":49,"column":59},"end":{"line":58,"column":1}}},"2":{"name":"requestIdHandler","decl":{"start":{"line":60,"column":16},"end":{"line":60,"column":32}},"loc":{"start":{"line":60,"column":80},"end":{"line":64,"column":1}}},"3":{"name":"rateLimitHandler","decl":{"start":{"line":66,"column":16},"end":{"line":66,"column":32}},"loc":{"start":{"line":66,"column":60},"end":{"line":75,"column":1}}}},"branchMap":{"0":{"loc":{"start":{"line":25,"column":21},"end":{"line":25,"column":44}},"type":"binary-expr","locations":[{"start":{"line":25,"column":21},"end":{"line":25,"column":37}},{"start":{"line":25,"column":41},"end":{"line":25,"column":44}}]},"1":{"loc":{"start":{"line":26,"column":20},"end":{"line":26,"column":57}},"type":"binary-expr","locations":[{"start":{"line":26,"column":20},"end":{"line":26,"column":30}},{"start":{"line":26,"column":34},"end":{"line":26,"column":57}}]},"2":{"loc":{"start":{"line":31,"column":15},"end":{"line":31,"column":62}},"type":"binary-expr","locations":[{"start":{"line":31,"column":15},"end":{"line":31,"column":28}},{"start":{"line":31,"column":32},"end":{"line":31,"column":62}}]},"3":{"loc":{"start":{"line":34,"column":17},"end":{"line":34,"column":67}},"type":"binary-expr","locations":[{"start":{"line":34,"column":17},"end":{"line":34,"column":54}},{"start":{"line":34,"column":58},"end":{"line":34,"column":67}}]},"4":{"loc":{"start":{"line":39,"column":2},"end":{"line":44,"column":3}},"type":"if","locations":[{"start":{"line":39,"column":2},"end":{"line":44,"column":3}},{"start":{},"end":{}}]},"5":{"loc":{"start":{"line":55,"column":17},"end":{"line":55,"column":67}},"type":"binary-expr","locations":[{"start":{"line":55,"column":17},"end":{"line":55,"column":54}},{"start":{"line":55,"column":58},"end":{"line":55,"column":67}}]},"6":{"loc":{"start":{"line":61,"column":32},"end":{"line":62,"column":66}},"type":"binary-expr","locations":[{"start":{"line":61,"column":32},"end":{"line":61,"column":59}},{"start":{"line":62,"column":4},"end":{"line":62,"column":66}}]},"7":{"loc":{"start":{"line":72,"column":17},"end":{"line":72,"column":67}},"type":"binary-expr","locations":[{"start":{"line":72,"column":17},"end":{"line":72,"column":54}},{"start":{"line":72,"column":58},"end":{"line":72,"column":67}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\routes\\analysis.routes.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\routes\\analysis.routes.ts","statementMap":{"0":{"start":{"line":5,"column":0},"end":{"line":5,"column":16}},"1":{"start":{"line":1,"column":0},"end":{"line":1,"column":33}},"2":{"start":{"line":2,"column":0},"end":{"line":2,"column":72}},"3":{"start":{"line":3,"column":0},"end":{"line":3,"column":69}},"4":{"start":{"line":6,"column":17},"end":{"line":6,"column":25}},"5":{"start":{"line":7,"column":21},"end":{"line":7,"column":50}},"6":{"start":{"line":10,"column":2},"end":{"line":13,"column":4}},"7":{"start":{"line":12,"column":18},"end":{"line":12,"column":53}},"8":{"start":{"line":16,"column":2},"end":{"line":18,"column":4}},"9":{"start":{"line":17,"column":18},"end":{"line":17,"column":56}},"10":{"start":{"line":21,"column":2},"end":{"line":23,"column":4}},"11":{"start":{"line":22,"column":18},"end":{"line":22,"column":56}},"12":{"start":{"line":26,"column":2},"end":{"line":28,"column":4}},"13":{"start":{"line":27,"column":18},"end":{"line":27,"column":53}},"14":{"start":{"line":30,"column":2},"end":{"line":30,"column":16}}},"fnMap":{"0":{"name":"createAnalysisRoutes","decl":{"start":{"line":5,"column":16},"end":{"line":5,"column":36}},"loc":{"start":{"line":5,"column":47},"end":{"line":31,"column":1}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":5}},"loc":{"start":{"line":12,"column":18},"end":{"line":12,"column":53}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":17,"column":4},"end":{"line":17,"column":5}},"loc":{"start":{"line":17,"column":18},"end":{"line":17,"column":56}}},"3":{"name":"(anonymous_3)","decl":{"start":{"line":22,"column":4},"end":{"line":22,"column":5}},"loc":{"start":{"line":22,"column":18},"end":{"line":22,"column":56}}},"4":{"name":"(anonymous_4)","decl":{"start":{"line":27,"column":4},"end":{"line":27,"column":5}},"loc":{"start":{"line":27,"column":18},"end":{"line":27,"column":53}}}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\services\\cross-chain-gas.service.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\services\\cross-chain-gas.service.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":44}},"1":{"start":{"line":12,"column":7},"end":{"line":170,"column":null}},"2":{"start":{"line":13,"column":19},"end":{"line":49,"column":4}},"3":{"start":{"line":51,"column":19},"end":{"line":55,"column":4}},"4":{"start":{"line":57,"column":19},"end":{"line":63,"column":4}},"5":{"start":{"line":66,"column":25},"end":{"line":67,"column":null}},"6":{"start":{"line":67,"column":40},"end":{"line":67,"column":70}},"7":{"start":{"line":70,"column":28},"end":{"line":71,"column":null}},"8":{"start":{"line":71,"column":6},"end":{"line":71,"column":52}},"9":{"start":{"line":74,"column":25},"end":{"line":74,"column":63}},"10":{"start":{"line":76,"column":4},"end":{"line":80,"column":6}},"11":{"start":{"line":85,"column":26},"end":{"line":91,"column":6}},"12":{"start":{"line":93,"column":22},"end":{"line":93,"column":168}},"13":{"start":{"line":95,"column":4},"end":{"line":103,"column":6}},"14":{"start":{"line":107,"column":20},"end":{"line":107,"column":89}},"15":{"start":{"line":108,"column":20},"end":{"line":108,"column":60}},"16":{"start":{"line":109,"column":24},"end":{"line":109,"column":67}},"17":{"start":{"line":110,"column":30},"end":{"line":110,"column":51}},"18":{"start":{"line":112,"column":25},"end":{"line":112,"column":60}},"19":{"start":{"line":113,"column":28},"end":{"line":113,"column":60}},"20":{"start":{"line":114,"column":26},"end":{"line":114,"column":83}},"21":{"start":{"line":116,"column":4},"end":{"line":123,"column":6}},"22":{"start":{"line":127,"column":24},"end":{"line":127,"column":77}},"23":{"start":{"line":127,"column":45},"end":{"line":127,"column":76}},"24":{"start":{"line":129,"column":4},"end":{"line":136,"column":8}},"25":{"start":{"line":129,"column":45},"end":{"line":136,"column":6}},"26":{"start":{"line":140,"column":18},"end":{"line":140,"column":71}},"27":{"start":{"line":140,"column":49},"end":{"line":140,"column":70}},"28":{"start":{"line":141,"column":4},"end":{"line":141,"column":50}},"29":{"start":{"line":145,"column":18},"end":{"line":145,"column":71}},"30":{"start":{"line":145,"column":49},"end":{"line":145,"column":70}},"31":{"start":{"line":146,"column":20},"end":{"line":146,"column":42}},"32":{"start":{"line":148,"column":4},"end":{"line":154,"column":5}},"33":{"start":{"line":149,"column":6},"end":{"line":149,"column":47}},"34":{"start":{"line":150,"column":11},"end":{"line":154,"column":5}},"35":{"start":{"line":151,"column":6},"end":{"line":151,"column":46}},"36":{"start":{"line":153,"column":6},"end":{"line":153,"column":51}},"37":{"start":{"line":158,"column":4},"end":{"line":158,"column":32}},"38":{"start":{"line":163,"column":4},"end":{"line":163,"column":51}},"39":{"start":{"line":168,"column":4},"end":{"line":168,"column":14}},"40":{"start":{"line":12,"column":13},"end":{"line":12,"column":33}},"41":{"start":{"line":12,"column":13},"end":{"line":170,"column":null}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":7},"end":{"line":12,"column":13}},"loc":{"start":{"line":12,"column":7},"end":{"line":170,"column":1}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":65,"column":2},"end":{"line":65,"column":7}},"loc":{"start":{"line":65,"column":64},"end":{"line":81,"column":3}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":67,"column":31},"end":{"line":67,"column":36}},"loc":{"start":{"line":67,"column":40},"end":{"line":67,"column":70}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":70,"column":45},"end":{"line":70,"column":52}},"loc":{"start":{"line":71,"column":6},"end":{"line":71,"column":52}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":83,"column":10},"end":{"line":83,"column":15}},"loc":{"start":{"line":83,"column":56},"end":{"line":104,"column":3}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":106,"column":10},"end":{"line":106,"column":26}},"loc":{"start":{"line":106,"column":67},"end":{"line":124,"column":3}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":126,"column":10},"end":{"line":126,"column":26}},"loc":{"start":{"line":126,"column":58},"end":{"line":137,"column":3}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":127,"column":35},"end":{"line":127,"column":36}},"loc":{"start":{"line":127,"column":45},"end":{"line":127,"column":76}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":129,"column":27},"end":{"line":129,"column":28}},"loc":{"start":{"line":129,"column":45},"end":{"line":136,"column":6}}},"9":{"name":"(anonymous_10)","decl":{"start":{"line":139,"column":10},"end":{"line":139,"column":22}},"loc":{"start":{"line":139,"column":38},"end":{"line":142,"column":3}}},"10":{"name":"(anonymous_11)","decl":{"start":{"line":140,"column":44},"end":{"line":140,"column":45}},"loc":{"start":{"line":140,"column":49},"end":{"line":140,"column":70}}},"11":{"name":"(anonymous_12)","decl":{"start":{"line":144,"column":10},"end":{"line":144,"column":32}},"loc":{"start":{"line":144,"column":48},"end":{"line":155,"column":3}}},"12":{"name":"(anonymous_13)","decl":{"start":{"line":145,"column":44},"end":{"line":145,"column":45}},"loc":{"start":{"line":145,"column":49},"end":{"line":145,"column":70}}},"13":{"name":"(anonymous_14)","decl":{"start":{"line":157,"column":2},"end":{"line":157,"column":7}},"loc":{"start":{"line":157,"column":26},"end":{"line":159,"column":3}}},"14":{"name":"(anonymous_15)","decl":{"start":{"line":161,"column":2},"end":{"line":161,"column":7}},"loc":{"start":{"line":161,"column":31},"end":{"line":164,"column":3}}},"15":{"name":"(anonymous_16)","decl":{"start":{"line":166,"column":2},"end":{"line":166,"column":7}},"loc":{"start":{"line":166,"column":69},"end":{"line":169,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":14},"end":{"line":18,"column":89}},"type":"binary-expr","locations":[{"start":{"line":18,"column":14},"end":{"line":18,"column":42}},{"start":{"line":18,"column":46},"end":{"line":18,"column":89}}]},"1":{"loc":{"start":{"line":25,"column":14},"end":{"line":25,"column":92}},"type":"binary-expr","locations":[{"start":{"line":25,"column":14},"end":{"line":25,"column":41}},{"start":{"line":25,"column":45},"end":{"line":25,"column":92}}]},"2":{"loc":{"start":{"line":32,"column":14},"end":{"line":32,"column":75}},"type":"binary-expr","locations":[{"start":{"line":32,"column":14},"end":{"line":32,"column":37}},{"start":{"line":32,"column":41},"end":{"line":32,"column":75}}]},"3":{"loc":{"start":{"line":39,"column":14},"end":{"line":39,"column":76}},"type":"binary-expr","locations":[{"start":{"line":39,"column":14},"end":{"line":39,"column":42}},{"start":{"line":39,"column":46},"end":{"line":39,"column":76}}]},"4":{"loc":{"start":{"line":46,"column":14},"end":{"line":46,"column":75}},"type":"binary-expr","locations":[{"start":{"line":46,"column":14},"end":{"line":46,"column":42}},{"start":{"line":46,"column":46},"end":{"line":46,"column":75}}]},"5":{"loc":{"start":{"line":93,"column":22},"end":{"line":93,"column":168}},"type":"binary-expr","locations":[{"start":{"line":93,"column":22},"end":{"line":93,"column":111}},{"start":{"line":93,"column":115},"end":{"line":93,"column":168}}]},"6":{"loc":{"start":{"line":101,"column":28},"end":{"line":101,"column":100}},"type":"binary-expr","locations":[{"start":{"line":101,"column":28},"end":{"line":101,"column":95}},{"start":{"line":101,"column":99},"end":{"line":101,"column":100}}]},"7":{"loc":{"start":{"line":108,"column":27},"end":{"line":108,"column":59}},"type":"binary-expr","locations":[{"start":{"line":108,"column":27},"end":{"line":108,"column":42}},{"start":{"line":108,"column":46},"end":{"line":108,"column":59}}]},"8":{"loc":{"start":{"line":109,"column":31},"end":{"line":109,"column":66}},"type":"binary-expr","locations":[{"start":{"line":109,"column":31},"end":{"line":109,"column":50}},{"start":{"line":109,"column":54},"end":{"line":109,"column":66}}]},"9":{"loc":{"start":{"line":141,"column":11},"end":{"line":141,"column":49}},"type":"binary-expr","locations":[{"start":{"line":141,"column":11},"end":{"line":141,"column":27}},{"start":{"line":141,"column":31},"end":{"line":141,"column":49}}]},"10":{"loc":{"start":{"line":146,"column":20},"end":{"line":146,"column":42}},"type":"binary-expr","locations":[{"start":{"line":146,"column":20},"end":{"line":146,"column":36}},{"start":{"line":146,"column":40},"end":{"line":146,"column":42}}]},"11":{"loc":{"start":{"line":148,"column":4},"end":{"line":154,"column":5}},"type":"if","locations":[{"start":{"line":148,"column":4},"end":{"line":154,"column":5}},{"start":{"line":150,"column":11},"end":{"line":154,"column":5}}]},"12":{"loc":{"start":{"line":150,"column":11},"end":{"line":154,"column":5}},"type":"if","locations":[{"start":{"line":150,"column":11},"end":{"line":154,"column":5}},{"start":{"line":152,"column":11},"end":{"line":154,"column":5}}]},"13":{"loc":{"start":{"line":166,"column":51},"end":{"line":166,"column":69}},"type":"default-arg","locations":[{"start":{"line":166,"column":67},"end":{"line":166,"column":69}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\services\\failed-transaction.service.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\services\\failed-transaction.service.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":44}},"1":{"start":{"line":11,"column":7},"end":{"line":316,"column":null}},"2":{"start":{"line":12,"column":10},"end":{"line":12,"column":73}},"3":{"start":{"line":13,"column":10},"end":{"line":13,"column":71}},"4":{"start":{"line":19,"column":21},"end":{"line":19,"column":64}},"5":{"start":{"line":20,"column":22},"end":{"line":20,"column":76}},"6":{"start":{"line":22,"column":40},"end":{"line":35,"column":6}},"7":{"start":{"line":38,"column":4},"end":{"line":38,"column":55}},"8":{"start":{"line":41,"column":22},"end":{"line":41,"column":68}},"9":{"start":{"line":42,"column":4},"end":{"line":42,"column":29}},"10":{"start":{"line":43,"column":4},"end":{"line":43,"column":56}},"11":{"start":{"line":45,"column":4},"end":{"line":45,"column":20}},"12":{"start":{"line":52,"column":21},"end":{"line":52,"column":58}},"13":{"start":{"line":54,"column":4},"end":{"line":56,"column":5}},"14":{"start":{"line":55,"column":6},"end":{"line":55,"column":66}},"15":{"start":{"line":55,"column":35},"end":{"line":55,"column":64}},"16":{"start":{"line":58,"column":4},"end":{"line":58,"column":20}},"17":{"start":{"line":65,"column":58},"end":{"line":65,"column":73}},"18":{"start":{"line":68,"column":4},"end":{"line":70,"column":5}},"19":{"start":{"line":69,"column":6},"end":{"line":69,"column":31}},"20":{"start":{"line":73,"column":4},"end":{"line":78,"column":5}},"21":{"start":{"line":74,"column":26},"end":{"line":74,"column":81}},"22":{"start":{"line":75,"column":6},"end":{"line":77,"column":7}},"23":{"start":{"line":76,"column":8},"end":{"line":76,"column":28}},"24":{"start":{"line":81,"column":4},"end":{"line":99,"column":5}},"25":{"start":{"line":82,"column":21},"end":{"line":82,"column":47}},"26":{"start":{"line":84,"column":6},"end":{"line":86,"column":7}},"27":{"start":{"line":85,"column":8},"end":{"line":85,"column":38}},"28":{"start":{"line":88,"column":6},"end":{"line":90,"column":7}},"29":{"start":{"line":89,"column":8},"end":{"line":89,"column":32}},"30":{"start":{"line":92,"column":6},"end":{"line":94,"column":7}},"31":{"start":{"line":93,"column":8},"end":{"line":93,"column":35}},"32":{"start":{"line":96,"column":6},"end":{"line":98,"column":7}},"33":{"start":{"line":97,"column":8},"end":{"line":97,"column":33}},"34":{"start":{"line":102,"column":4},"end":{"line":112,"column":5}},"35":{"start":{"line":103,"column":24},"end":{"line":103,"column":78}},"36":{"start":{"line":104,"column":27},"end":{"line":107,"column":8}},"37":{"start":{"line":105,"column":8},"end":{"line":106,"column":66}},"38":{"start":{"line":109,"column":6},"end":{"line":111,"column":7}},"39":{"start":{"line":110,"column":8},"end":{"line":110,"column":32}},"40":{"start":{"line":114,"column":4},"end":{"line":114,"column":21}},"41":{"start":{"line":124,"column":31},"end":{"line":124,"column":33}},"42":{"start":{"line":125,"column":26},"end":{"line":125,"column":28}},"43":{"start":{"line":127,"column":4},"end":{"line":167,"column":5}},"44":{"start":{"line":129,"column":8},"end":{"line":129,"column":68}},"45":{"start":{"line":130,"column":8},"end":{"line":130,"column":62}},"46":{"start":{"line":131,"column":8},"end":{"line":135,"column":10}},"47":{"start":{"line":136,"column":8},"end":{"line":136,"column":14}},"48":{"start":{"line":139,"column":8},"end":{"line":139,"column":62}},"49":{"start":{"line":140,"column":8},"end":{"line":140,"column":74}},"50":{"start":{"line":141,"column":8},"end":{"line":149,"column":9}},"51":{"start":{"line":142,"column":30},"end":{"line":142,"column":117}},"52":{"start":{"line":143,"column":10},"end":{"line":143,"column":67}},"53":{"start":{"line":144,"column":10},"end":{"line":148,"column":12}},"54":{"start":{"line":150,"column":8},"end":{"line":150,"column":14}},"55":{"start":{"line":153,"column":8},"end":{"line":153,"column":72}},"56":{"start":{"line":154,"column":8},"end":{"line":154,"column":50}},"57":{"start":{"line":155,"column":8},"end":{"line":155,"column":14}},"58":{"start":{"line":158,"column":8},"end":{"line":158,"column":67}},"59":{"start":{"line":159,"column":8},"end":{"line":159,"column":73}},"60":{"start":{"line":160,"column":8},"end":{"line":160,"column":14}},"61":{"start":{"line":163,"column":8},"end":{"line":163,"column":67}},"62":{"start":{"line":164,"column":8},"end":{"line":164,"column":67}},"63":{"start":{"line":165,"column":8},"end":{"line":165,"column":72}},"64":{"start":{"line":166,"column":8},"end":{"line":166,"column":14}},"65":{"start":{"line":169,"column":4},"end":{"line":174,"column":6}},"66":{"start":{"line":181,"column":21},"end":{"line":181,"column":67}},"67":{"start":{"line":183,"column":25},"end":{"line":183,"column":34}},"68":{"start":{"line":184,"column":61},"end":{"line":184,"column":70}},"69":{"start":{"line":185,"column":49},"end":{"line":185,"column":58}},"70":{"start":{"line":188,"column":42},"end":{"line":192,"column":6}},"71":{"start":{"line":194,"column":4},"end":{"line":196,"column":7}},"72":{"start":{"line":195,"column":6},"end":{"line":195,"column":33}},"73":{"start":{"line":199,"column":4},"end":{"line":215,"column":7}},"74":{"start":{"line":200,"column":20},"end":{"line":200,"column":43}},"75":{"start":{"line":201,"column":6},"end":{"line":201,"column":30}},"76":{"start":{"line":204,"column":6},"end":{"line":206,"column":19}},"77":{"start":{"line":209,"column":6},"end":{"line":211,"column":7}},"78":{"start":{"line":210,"column":8},"end":{"line":210,"column":39}},"79":{"start":{"line":212,"column":6},"end":{"line":214,"column":19}},"80":{"start":{"line":218,"column":26},"end":{"line":218,"column":73}},"81":{"start":{"line":219,"column":26},"end":{"line":219,"column":77}},"82":{"start":{"line":221,"column":4},"end":{"line":229,"column":9}},"83":{"start":{"line":222,"column":20},"end":{"line":222,"column":59}},"84":{"start":{"line":224,"column":21},"end":{"line":224,"column":47}},"85":{"start":{"line":225,"column":24},"end":{"line":225,"column":81}},"86":{"start":{"line":226,"column":8},"end":{"line":226,"column":49}},"87":{"start":{"line":227,"column":8},"end":{"line":227,"column":27}},"88":{"start":{"line":228,"column":8},"end":{"line":228,"column":41}},"89":{"start":{"line":231,"column":28},"end":{"line":237,"column":51}},"90":{"start":{"line":232,"column":30},"end":{"line":236,"column":8}},"91":{"start":{"line":237,"column":22},"end":{"line":237,"column":50}},"92":{"start":{"line":239,"column":4},"end":{"line":248,"column":6}},"93":{"start":{"line":255,"column":4},"end":{"line":255,"column":73}},"94":{"start":{"line":259,"column":20},"end":{"line":259,"column":58}},"95":{"start":{"line":260,"column":21},"end":{"line":260,"column":60}},"96":{"start":{"line":261,"column":4},"end":{"line":261,"column":43}},"97":{"start":{"line":265,"column":28},"end":{"line":265,"column":66}},"98":{"start":{"line":266,"column":22},"end":{"line":266,"column":78}},"99":{"start":{"line":267,"column":4},"end":{"line":267,"column":27}},"100":{"start":{"line":272,"column":47},"end":{"line":278,"column":6}},"101":{"start":{"line":279,"column":4},"end":{"line":279,"column":48}},"102":{"start":{"line":283,"column":28},"end":{"line":283,"column":66}},"103":{"start":{"line":284,"column":18},"end":{"line":284,"column":34}},"104":{"start":{"line":285,"column":20},"end":{"line":285,"column":43}},"105":{"start":{"line":286,"column":4},"end":{"line":286,"column":63}},"106":{"start":{"line":291,"column":60},"end":{"line":300,"column":6}},"107":{"start":{"line":303,"column":21},"end":{"line":303,"column":45}},"108":{"start":{"line":304,"column":4},"end":{"line":304,"column":48}},"109":{"start":{"line":304,"column":29},"end":{"line":304,"column":48}},"110":{"start":{"line":305,"column":4},"end":{"line":305,"column":48}},"111":{"start":{"line":305,"column":29},"end":{"line":305,"column":48}},"112":{"start":{"line":307,"column":4},"end":{"line":307,"column":37}},"113":{"start":{"line":312,"column":24},"end":{"line":312,"column":28}},"114":{"start":{"line":313,"column":22},"end":{"line":313,"column":46}},"115":{"start":{"line":314,"column":4},"end":{"line":314,"column":35}},"116":{"start":{"line":11,"column":13},"end":{"line":11,"column":37}},"117":{"start":{"line":11,"column":13},"end":{"line":316,"column":null}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":11,"column":7},"end":{"line":11,"column":13}},"loc":{"start":{"line":11,"column":7},"end":{"line":316,"column":1}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":18,"column":2},"end":{"line":18,"column":7}},"loc":{"start":{"line":18,"column":74},"end":{"line":46,"column":3}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":51,"column":2},"end":{"line":51,"column":7}},"loc":{"start":{"line":51,"column":61},"end":{"line":59,"column":3}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":55,"column":29},"end":{"line":55,"column":31}},"loc":{"start":{"line":55,"column":35},"end":{"line":55,"column":64}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":64,"column":10},"end":{"line":64,"column":15}},"loc":{"start":{"line":64,"column":75},"end":{"line":115,"column":3}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":104,"column":42},"end":{"line":104,"column":44}},"loc":{"start":{"line":105,"column":8},"end":{"line":106,"column":66}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":120,"column":10},"end":{"line":120,"column":15}},"loc":{"start":{"line":122,"column":29},"end":{"line":175,"column":3}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":180,"column":2},"end":{"line":180,"column":7}},"loc":{"start":{"line":180,"column":64},"end":{"line":249,"column":3}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":194,"column":23},"end":{"line":194,"column":26}},"loc":{"start":{"line":194,"column":29},"end":{"line":196,"column":5}}},"9":{"name":"(anonymous_10)","decl":{"start":{"line":199,"column":21},"end":{"line":199,"column":23}},"loc":{"start":{"line":199,"column":26},"end":{"line":215,"column":5}}},"10":{"name":"(anonymous_11)","decl":{"start":{"line":222,"column":14},"end":{"line":222,"column":16}},"loc":{"start":{"line":222,"column":20},"end":{"line":222,"column":59}}},"11":{"name":"(anonymous_12)","decl":{"start":{"line":223,"column":15},"end":{"line":223,"column":17}},"loc":{"start":{"line":223,"column":20},"end":{"line":229,"column":7}}},"12":{"name":"(anonymous_13)","decl":{"start":{"line":232,"column":11},"end":{"line":232,"column":12}},"loc":{"start":{"line":232,"column":30},"end":{"line":236,"column":8}}},"13":{"name":"(anonymous_14)","decl":{"start":{"line":237,"column":12},"end":{"line":237,"column":13}},"loc":{"start":{"line":237,"column":22},"end":{"line":237,"column":50}}},"14":{"name":"(anonymous_15)","decl":{"start":{"line":254,"column":10},"end":{"line":254,"column":20}},"loc":{"start":{"line":254,"column":20},"end":{"line":256,"column":3}}},"15":{"name":"(anonymous_16)","decl":{"start":{"line":258,"column":10},"end":{"line":258,"column":31}},"loc":{"start":{"line":258,"column":75},"end":{"line":262,"column":3}}},"16":{"name":"(anonymous_17)","decl":{"start":{"line":264,"column":10},"end":{"line":264,"column":15}},"loc":{"start":{"line":264,"column":63},"end":{"line":268,"column":3}}},"17":{"name":"(anonymous_18)","decl":{"start":{"line":270,"column":10},"end":{"line":270,"column":15}},"loc":{"start":{"line":270,"column":50},"end":{"line":280,"column":3}}},"18":{"name":"(anonymous_19)","decl":{"start":{"line":282,"column":10},"end":{"line":282,"column":15}},"loc":{"start":{"line":282,"column":76},"end":{"line":287,"column":3}}},"19":{"name":"(anonymous_20)","decl":{"start":{"line":289,"column":10},"end":{"line":289,"column":29}},"loc":{"start":{"line":289,"column":75},"end":{"line":308,"column":3}}},"20":{"name":"(anonymous_21)","decl":{"start":{"line":310,"column":10},"end":{"line":310,"column":15}},"loc":{"start":{"line":310,"column":46},"end":{"line":315,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":28,"column":15},"end":{"line":28,"column":45}},"type":"binary-expr","locations":[{"start":{"line":28,"column":15},"end":{"line":28,"column":38}},{"start":{"line":28,"column":42},"end":{"line":28,"column":45}}]},"1":{"loc":{"start":{"line":29,"column":16},"end":{"line":29,"column":47}},"type":"binary-expr","locations":[{"start":{"line":29,"column":16},"end":{"line":29,"column":40}},{"start":{"line":29,"column":44},"end":{"line":29,"column":47}}]},"2":{"loc":{"start":{"line":32,"column":20},"end":{"line":32,"column":81}},"type":"binary-expr","locations":[{"start":{"line":32,"column":20},"end":{"line":32,"column":48}},{"start":{"line":32,"column":52},"end":{"line":32,"column":81}}]},"3":{"loc":{"start":{"line":33,"column":17},"end":{"line":33,"column":70}},"type":"binary-expr","locations":[{"start":{"line":33,"column":17},"end":{"line":33,"column":42}},{"start":{"line":33,"column":46},"end":{"line":33,"column":70}}]},"4":{"loc":{"start":{"line":41,"column":22},"end":{"line":41,"column":68}},"type":"binary-expr","locations":[{"start":{"line":41,"column":22},"end":{"line":41,"column":62}},{"start":{"line":41,"column":66},"end":{"line":41,"column":68}}]},"5":{"loc":{"start":{"line":52,"column":21},"end":{"line":52,"column":58}},"type":"binary-expr","locations":[{"start":{"line":52,"column":21},"end":{"line":52,"column":52}},{"start":{"line":52,"column":56},"end":{"line":52,"column":58}}]},"6":{"loc":{"start":{"line":54,"column":4},"end":{"line":56,"column":5}},"type":"if","locations":[{"start":{"line":54,"column":4},"end":{"line":56,"column":5}},{"start":{},"end":{}}]},"7":{"loc":{"start":{"line":54,"column":8},"end":{"line":54,"column":39}},"type":"binary-expr","locations":[{"start":{"line":54,"column":8},"end":{"line":54,"column":16}},{"start":{"line":54,"column":20},"end":{"line":54,"column":39}}]},"8":{"loc":{"start":{"line":68,"column":4},"end":{"line":70,"column":5}},"type":"if","locations":[{"start":{"line":68,"column":4},"end":{"line":70,"column":5}},{"start":{},"end":{}}]},"9":{"loc":{"start":{"line":68,"column":8},"end":{"line":68,"column":80}},"type":"binary-expr","locations":[{"start":{"line":68,"column":8},"end":{"line":68,"column":16}},{"start":{"line":68,"column":20},"end":{"line":68,"column":80}}]},"10":{"loc":{"start":{"line":73,"column":4},"end":{"line":78,"column":5}},"type":"if","locations":[{"start":{"line":73,"column":4},"end":{"line":78,"column":5}},{"start":{},"end":{}}]},"11":{"loc":{"start":{"line":73,"column":8},"end":{"line":73,"column":37}},"type":"binary-expr","locations":[{"start":{"line":73,"column":8},"end":{"line":73,"column":15}},{"start":{"line":73,"column":19},"end":{"line":73,"column":37}}]},"12":{"loc":{"start":{"line":75,"column":6},"end":{"line":77,"column":7}},"type":"if","locations":[{"start":{"line":75,"column":6},"end":{"line":77,"column":7}},{"start":{},"end":{}}]},"13":{"loc":{"start":{"line":81,"column":4},"end":{"line":99,"column":5}},"type":"if","locations":[{"start":{"line":81,"column":4},"end":{"line":99,"column":5}},{"start":{},"end":{}}]},"14":{"loc":{"start":{"line":84,"column":6},"end":{"line":86,"column":7}},"type":"if","locations":[{"start":{"line":84,"column":6},"end":{"line":86,"column":7}},{"start":{},"end":{}}]},"15":{"loc":{"start":{"line":84,"column":10},"end":{"line":84,"column":77}},"type":"binary-expr","locations":[{"start":{"line":84,"column":10},"end":{"line":84,"column":47}},{"start":{"line":84,"column":51},"end":{"line":84,"column":77}}]},"16":{"loc":{"start":{"line":88,"column":6},"end":{"line":90,"column":7}},"type":"if","locations":[{"start":{"line":88,"column":6},"end":{"line":90,"column":7}},{"start":{},"end":{}}]},"17":{"loc":{"start":{"line":88,"column":10},"end":{"line":88,"column":68}},"type":"binary-expr","locations":[{"start":{"line":88,"column":10},"end":{"line":88,"column":34}},{"start":{"line":88,"column":38},"end":{"line":88,"column":68}}]},"18":{"loc":{"start":{"line":92,"column":6},"end":{"line":94,"column":7}},"type":"if","locations":[{"start":{"line":92,"column":6},"end":{"line":94,"column":7}},{"start":{},"end":{}}]},"19":{"loc":{"start":{"line":92,"column":10},"end":{"line":92,"column":78}},"type":"binary-expr","locations":[{"start":{"line":92,"column":10},"end":{"line":92,"column":37}},{"start":{"line":92,"column":41},"end":{"line":92,"column":78}}]},"20":{"loc":{"start":{"line":96,"column":6},"end":{"line":98,"column":7}},"type":"if","locations":[{"start":{"line":96,"column":6},"end":{"line":98,"column":7}},{"start":{},"end":{}}]},"21":{"loc":{"start":{"line":96,"column":10},"end":{"line":96,"column":65}},"type":"binary-expr","locations":[{"start":{"line":96,"column":10},"end":{"line":96,"column":35}},{"start":{"line":96,"column":39},"end":{"line":96,"column":65}}]},"22":{"loc":{"start":{"line":102,"column":4},"end":{"line":112,"column":5}},"type":"if","locations":[{"start":{"line":102,"column":4},"end":{"line":112,"column":5}},{"start":{},"end":{}}]},"23":{"loc":{"start":{"line":103,"column":24},"end":{"line":103,"column":78}},"type":"binary-expr","locations":[{"start":{"line":103,"column":24},"end":{"line":103,"column":72}},{"start":{"line":103,"column":76},"end":{"line":103,"column":78}}]},"24":{"loc":{"start":{"line":105,"column":8},"end":{"line":106,"column":66}},"type":"binary-expr","locations":[{"start":{"line":105,"column":8},"end":{"line":105,"column":44}},{"start":{"line":106,"column":8},"end":{"line":106,"column":66}}]},"25":{"loc":{"start":{"line":109,"column":6},"end":{"line":111,"column":7}},"type":"if","locations":[{"start":{"line":109,"column":6},"end":{"line":111,"column":7}},{"start":{},"end":{}}]},"26":{"loc":{"start":{"line":127,"column":4},"end":{"line":167,"column":5}},"type":"switch","locations":[{"start":{"line":128,"column":6},"end":{"line":136,"column":14}},{"start":{"line":138,"column":6},"end":{"line":150,"column":14}},{"start":{"line":152,"column":6},"end":{"line":155,"column":14}},{"start":{"line":157,"column":6},"end":{"line":160,"column":14}},{"start":{"line":162,"column":6},"end":{"line":166,"column":14}}]},"27":{"loc":{"start":{"line":141,"column":8},"end":{"line":149,"column":9}},"type":"if","locations":[{"start":{"line":141,"column":8},"end":{"line":149,"column":9}},{"start":{},"end":{}}]},"28":{"loc":{"start":{"line":141,"column":12},"end":{"line":141,"column":73}},"type":"binary-expr","locations":[{"start":{"line":141,"column":12},"end":{"line":141,"column":35}},{"start":{"line":141,"column":39},"end":{"line":141,"column":73}}]},"29":{"loc":{"start":{"line":209,"column":6},"end":{"line":211,"column":7}},"type":"if","locations":[{"start":{"line":209,"column":6},"end":{"line":211,"column":7}},{"start":{},"end":{}}]},"30":{"loc":{"start":{"line":225,"column":24},"end":{"line":225,"column":81}},"type":"binary-expr","locations":[{"start":{"line":225,"column":24},"end":{"line":225,"column":47}},{"start":{"line":225,"column":51},"end":{"line":225,"column":81}}]},"31":{"loc":{"start":{"line":242,"column":30},"end":{"line":244,"column":13}},"type":"cond-expr","locations":[{"start":{"line":243,"column":10},"end":{"line":243,"column":63}},{"start":{"line":244,"column":10},"end":{"line":244,"column":13}}]},"32":{"loc":{"start":{"line":259,"column":27},"end":{"line":259,"column":57}},"type":"binary-expr","locations":[{"start":{"line":259,"column":27},"end":{"line":259,"column":50}},{"start":{"line":259,"column":54},"end":{"line":259,"column":57}}]},"33":{"loc":{"start":{"line":260,"column":28},"end":{"line":260,"column":59}},"type":"binary-expr","locations":[{"start":{"line":260,"column":28},"end":{"line":260,"column":52}},{"start":{"line":260,"column":56},"end":{"line":260,"column":59}}]},"34":{"loc":{"start":{"line":279,"column":11},"end":{"line":279,"column":47}},"type":"binary-expr","locations":[{"start":{"line":279,"column":11},"end":{"line":279,"column":30}},{"start":{"line":279,"column":34},"end":{"line":279,"column":47}}]},"35":{"loc":{"start":{"line":304,"column":4},"end":{"line":304,"column":48}},"type":"if","locations":[{"start":{"line":304,"column":4},"end":{"line":304,"column":48}},{"start":{},"end":{}}]},"36":{"loc":{"start":{"line":305,"column":4},"end":{"line":305,"column":48}},"type":"if","locations":[{"start":{"line":305,"column":4},"end":{"line":305,"column":48}},{"start":{},"end":{}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"116":0,"117":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0,0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[0,0],"21":[0,0],"22":[0,0],"23":[0,0],"24":[0,0],"25":[0,0],"26":[0,0,0,0,0],"27":[0,0],"28":[0,0],"29":[0,0],"30":[0,0],"31":[0,0],"32":[0,0],"33":[0,0],"34":[0,0],"35":[0,0],"36":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\services\\mitigation.service.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\services\\mitigation.service.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":44}},"1":{"start":{"line":10,"column":7},"end":{"line":337,"column":null}},"2":{"start":{"line":15,"column":56},"end":{"line":15,"column":58}},"3":{"start":{"line":18,"column":24},"end":{"line":18,"column":51}},"4":{"start":{"line":19,"column":4},"end":{"line":19,"column":98}},"5":{"start":{"line":22,"column":4},"end":{"line":22,"column":81}},"6":{"start":{"line":25,"column":4},"end":{"line":25,"column":82}},"7":{"start":{"line":28,"column":4},"end":{"line":30,"column":19}},"8":{"start":{"line":29,"column":22},"end":{"line":29,"column":91}},"9":{"start":{"line":40,"column":56},"end":{"line":40,"column":58}},"10":{"start":{"line":42,"column":4},"end":{"line":145,"column":5}},"11":{"start":{"line":44,"column":8},"end":{"line":56,"column":11}},"12":{"start":{"line":57,"column":8},"end":{"line":65,"column":11}},"13":{"start":{"line":66,"column":8},"end":{"line":66,"column":14}},"14":{"start":{"line":69,"column":8},"end":{"line":81,"column":11}},"15":{"start":{"line":82,"column":8},"end":{"line":90,"column":11}},"16":{"start":{"line":91,"column":8},"end":{"line":91,"column":14}},"17":{"start":{"line":94,"column":8},"end":{"line":106,"column":11}},"18":{"start":{"line":107,"column":8},"end":{"line":115,"column":11}},"19":{"start":{"line":116,"column":8},"end":{"line":116,"column":14}},"20":{"start":{"line":119,"column":8},"end":{"line":131,"column":11}},"21":{"start":{"line":132,"column":8},"end":{"line":132,"column":14}},"22":{"start":{"line":135,"column":8},"end":{"line":143,"column":11}},"23":{"start":{"line":144,"column":8},"end":{"line":144,"column":14}},"24":{"start":{"line":147,"column":4},"end":{"line":147,"column":27}},"25":{"start":{"line":154,"column":56},"end":{"line":154,"column":58}},"26":{"start":{"line":157,"column":4},"end":{"line":167,"column":5}},"27":{"start":{"line":158,"column":6},"end":{"line":166,"column":9}},"28":{"start":{"line":170,"column":21},"end":{"line":170,"column":69}},"29":{"start":{"line":171,"column":4},"end":{"line":181,"column":5}},"30":{"start":{"line":172,"column":6},"end":{"line":180,"column":9}},"31":{"start":{"line":183,"column":4},"end":{"line":183,"column":27}},"32":{"start":{"line":190,"column":56},"end":{"line":190,"column":58}},"33":{"start":{"line":192,"column":4},"end":{"line":234,"column":5}},"34":{"start":{"line":193,"column":23},"end":{"line":193,"column":40}},"35":{"start":{"line":195,"column":6},"end":{"line":233,"column":7}},"36":{"start":{"line":196,"column":8},"end":{"line":232,"column":9}},"37":{"start":{"line":198,"column":12},"end":{"line":206,"column":15}},"38":{"start":{"line":207,"column":12},"end":{"line":207,"column":18}},"39":{"start":{"line":210,"column":12},"end":{"line":218,"column":15}},"40":{"start":{"line":219,"column":12},"end":{"line":219,"column":18}},"41":{"start":{"line":222,"column":12},"end":{"line":230,"column":15}},"42":{"start":{"line":231,"column":12},"end":{"line":231,"column":18}},"43":{"start":{"line":236,"column":4},"end":{"line":236,"column":27}},"44":{"start":{"line":243,"column":56},"end":{"line":243,"column":58}},"45":{"start":{"line":245,"column":4},"end":{"line":290,"column":5}},"46":{"start":{"line":247,"column":34},"end":{"line":247,"column":90}},"47":{"start":{"line":248,"column":8},"end":{"line":260,"column":11}},"48":{"start":{"line":261,"column":8},"end":{"line":261,"column":14}},"49":{"start":{"line":264,"column":34},"end":{"line":264,"column":76}},"50":{"start":{"line":265,"column":8},"end":{"line":276,"column":11}},"51":{"start":{"line":277,"column":8},"end":{"line":277,"column":14}},"52":{"start":{"line":280,"column":8},"end":{"line":288,"column":11}},"53":{"start":{"line":289,"column":8},"end":{"line":289,"column":14}},"54":{"start":{"line":292,"column":4},"end":{"line":292,"column":27}},"55":{"start":{"line":299,"column":19},"end":{"line":299,"column":49}},"56":{"start":{"line":300,"column":4},"end":{"line":300,"column":56}},"57":{"start":{"line":305,"column":50},"end":{"line":311,"column":6}},"58":{"start":{"line":313,"column":22},"end":{"line":313,"column":61}},"59":{"start":{"line":314,"column":23},"end":{"line":314,"column":26}},"60":{"start":{"line":316,"column":4},"end":{"line":316,"column":95}},"61":{"start":{"line":320,"column":25},"end":{"line":320,"column":62}},"62":{"start":{"line":321,"column":17},"end":{"line":321,"column":44}},"63":{"start":{"line":324,"column":24},"end":{"line":324,"column":67}},"64":{"start":{"line":326,"column":4},"end":{"line":335,"column":5}},"65":{"start":{"line":328,"column":6},"end":{"line":328,"column":67}},"66":{"start":{"line":329,"column":11},"end":{"line":335,"column":5}},"67":{"start":{"line":331,"column":6},"end":{"line":331,"column":67}},"68":{"start":{"line":334,"column":6},"end":{"line":334,"column":67}},"69":{"start":{"line":10,"column":13},"end":{"line":10,"column":30}},"70":{"start":{"line":10,"column":13},"end":{"line":337,"column":null}}},"fnMap":{"0":{"name":"(anonymous_1)","decl":{"start":{"line":14,"column":2},"end":{"line":14,"column":7}},"loc":{"start":{"line":14,"column":57},"end":{"line":31,"column":3}}},"1":{"name":"(anonymous_2)","decl":{"start":{"line":29,"column":12},"end":{"line":29,"column":13}},"loc":{"start":{"line":29,"column":22},"end":{"line":29,"column":91}}},"2":{"name":"(anonymous_3)","decl":{"start":{"line":36,"column":10},"end":{"line":36,"column":15}},"loc":{"start":{"line":38,"column":29},"end":{"line":148,"column":3}}},"3":{"name":"(anonymous_4)","decl":{"start":{"line":153,"column":10},"end":{"line":153,"column":15}},"loc":{"start":{"line":153,"column":72},"end":{"line":184,"column":3}}},"4":{"name":"(anonymous_5)","decl":{"start":{"line":189,"column":10},"end":{"line":189,"column":15}},"loc":{"start":{"line":189,"column":73},"end":{"line":237,"column":3}}},"5":{"name":"(anonymous_6)","decl":{"start":{"line":242,"column":2},"end":{"line":242,"column":7}},"loc":{"start":{"line":242,"column":61},"end":{"line":293,"column":3}}},"6":{"name":"(anonymous_7)","decl":{"start":{"line":298,"column":10},"end":{"line":298,"column":26}},"loc":{"start":{"line":298,"column":43},"end":{"line":301,"column":3}}},"7":{"name":"(anonymous_8)","decl":{"start":{"line":303,"column":10},"end":{"line":303,"column":15}},"loc":{"start":{"line":303,"column":56},"end":{"line":317,"column":3}}},"8":{"name":"(anonymous_9)","decl":{"start":{"line":319,"column":10},"end":{"line":319,"column":34}},"loc":{"start":{"line":319,"column":65},"end":{"line":336,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":42,"column":4},"end":{"line":145,"column":5}},"type":"switch","locations":[{"start":{"line":43,"column":6},"end":{"line":66,"column":14}},{"start":{"line":68,"column":6},"end":{"line":91,"column":14}},{"start":{"line":93,"column":6},"end":{"line":116,"column":14}},{"start":{"line":118,"column":6},"end":{"line":132,"column":14}},{"start":{"line":134,"column":6},"end":{"line":144,"column":14}}]},"1":{"loc":{"start":{"line":157,"column":4},"end":{"line":167,"column":5}},"type":"if","locations":[{"start":{"line":157,"column":4},"end":{"line":167,"column":5}},{"start":{},"end":{}}]},"2":{"loc":{"start":{"line":171,"column":4},"end":{"line":181,"column":5}},"type":"if","locations":[{"start":{"line":171,"column":4},"end":{"line":181,"column":5}},{"start":{},"end":{}}]},"3":{"loc":{"start":{"line":195,"column":6},"end":{"line":233,"column":7}},"type":"if","locations":[{"start":{"line":195,"column":6},"end":{"line":233,"column":7}},{"start":{},"end":{}}]},"4":{"loc":{"start":{"line":196,"column":8},"end":{"line":232,"column":9}},"type":"switch","locations":[{"start":{"line":197,"column":10},"end":{"line":207,"column":18}},{"start":{"line":209,"column":10},"end":{"line":219,"column":18}},{"start":{"line":221,"column":10},"end":{"line":231,"column":18}}]},"5":{"loc":{"start":{"line":245,"column":4},"end":{"line":290,"column":5}},"type":"switch","locations":[{"start":{"line":246,"column":6},"end":{"line":261,"column":14}},{"start":{"line":263,"column":6},"end":{"line":277,"column":14}},{"start":{"line":279,"column":6},"end":{"line":289,"column":14}}]},"6":{"loc":{"start":{"line":300,"column":11},"end":{"line":300,"column":55}},"type":"binary-expr","locations":[{"start":{"line":300,"column":11},"end":{"line":300,"column":50}},{"start":{"line":300,"column":54},"end":{"line":300,"column":55}}]},"7":{"loc":{"start":{"line":313,"column":22},"end":{"line":313,"column":61}},"type":"binary-expr","locations":[{"start":{"line":313,"column":22},"end":{"line":313,"column":44}},{"start":{"line":313,"column":48},"end":{"line":313,"column":61}}]},"8":{"loc":{"start":{"line":326,"column":4},"end":{"line":335,"column":5}},"type":"if","locations":[{"start":{"line":326,"column":4},"end":{"line":335,"column":5}},{"start":{"line":329,"column":11},"end":{"line":335,"column":5}}]},"9":{"loc":{"start":{"line":329,"column":11},"end":{"line":335,"column":5}},"type":"if","locations":[{"start":{"line":329,"column":11},"end":{"line":335,"column":5}},{"start":{"line":332,"column":11},"end":{"line":335,"column":5}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"b":{"0":[0,0,0,0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0,0],"5":[0,0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\services\\transaction-analysis.service.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\services\\transaction-analysis.service.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":44}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":72}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":57}},"3":{"start":{"line":14,"column":7},"end":{"line":248,"column":null}},"4":{"start":{"line":16,"column":21},"end":{"line":16,"column":47}},"5":{"start":{"line":17,"column":21},"end":{"line":17,"column":40}},"6":{"start":{"line":24,"column":75},"end":{"line":24,"column":82}},"7":{"start":{"line":27,"column":21},"end":{"line":27,"column":92}},"8":{"start":{"line":30,"column":29},"end":{"line":30,"column":72}},"9":{"start":{"line":33,"column":24},"end":{"line":33,"column":98}},"10":{"start":{"line":36,"column":30},"end":{"line":36,"column":71}},"11":{"start":{"line":39,"column":27},"end":{"line":39,"column":73}},"12":{"start":{"line":42,"column":28},"end":{"line":57,"column":10}},"13":{"start":{"line":59,"column":38},"end":{"line":72,"column":6}},"14":{"start":{"line":74,"column":4},"end":{"line":79,"column":6}},"15":{"start":{"line":86,"column":21},"end":{"line":86,"column":82}},"16":{"start":{"line":89,"column":26},"end":{"line":91,"column":101}},"17":{"start":{"line":90,"column":27},"end":{"line":90,"column":53}},"18":{"start":{"line":91,"column":32},"end":{"line":91,"column":97}},"19":{"start":{"line":93,"column":4},"end":{"line":95,"column":5}},"20":{"start":{"line":94,"column":6},"end":{"line":94,"column":53}},"21":{"start":{"line":97,"column":28},"end":{"line":97,"column":94}},"22":{"start":{"line":99,"column":4},"end":{"line":103,"column":6}},"23":{"start":{"line":110,"column":21},"end":{"line":110,"column":92}},"24":{"start":{"line":111,"column":24},"end":{"line":111,"column":98}},"25":{"start":{"line":113,"column":23},"end":{"line":114,"column":null}},"26":{"start":{"line":114,"column":6},"end":{"line":114,"column":78}},"27":{"start":{"line":117,"column":22},"end":{"line":118,"column":null}},"28":{"start":{"line":118,"column":6},"end":{"line":118,"column":77}},"29":{"start":{"line":121,"column":4},"end":{"line":134,"column":6}},"30":{"start":{"line":141,"column":4},"end":{"line":141,"column":87}},"31":{"start":{"line":148,"column":4},"end":{"line":148,"column":36}},"32":{"start":{"line":148,"column":20},"end":{"line":148,"column":36}},"33":{"start":{"line":150,"column":4},"end":{"line":156,"column":7}},"34":{"start":{"line":151,"column":21},"end":{"line":151,"column":43}},"35":{"start":{"line":152,"column":24},"end":{"line":152,"column":81}},"36":{"start":{"line":153,"column":22},"end":{"line":153,"column":74}},"37":{"start":{"line":155,"column":6},"end":{"line":155,"column":54}},"38":{"start":{"line":160,"column":47},"end":{"line":160,"column":49}},"39":{"start":{"line":162,"column":4},"end":{"line":164,"column":7}},"40":{"start":{"line":163,"column":6},"end":{"line":163,"column":81}},"41":{"start":{"line":166,"column":4},"end":{"line":166,"column":22}},"42":{"start":{"line":170,"column":58},"end":{"line":170,"column":60}},"43":{"start":{"line":172,"column":4},"end":{"line":187,"column":7}},"44":{"start":{"line":173,"column":6},"end":{"line":180,"column":7}},"45":{"start":{"line":174,"column":8},"end":{"line":179,"column":10}},"46":{"start":{"line":182,"column":20},"end":{"line":182,"column":42}},"47":{"start":{"line":183,"column":6},"end":{"line":183,"column":26}},"48":{"start":{"line":184,"column":6},"end":{"line":186,"column":19}},"49":{"start":{"line":190,"column":4},"end":{"line":194,"column":7}},"50":{"start":{"line":191,"column":28},"end":{"line":191,"column":83}},"51":{"start":{"line":191,"column":50},"end":{"line":191,"column":82}},"52":{"start":{"line":192,"column":25},"end":{"line":192,"column":63}},"53":{"start":{"line":193,"column":6},"end":{"line":193,"column":96}},"54":{"start":{"line":196,"column":4},"end":{"line":196,"column":22}},"55":{"start":{"line":200,"column":19},"end":{"line":200,"column":20}},"56":{"start":{"line":201,"column":39},"end":{"line":201,"column":48}},"57":{"start":{"line":203,"column":4},"end":{"line":208,"column":7}},"58":{"start":{"line":204,"column":6},"end":{"line":207,"column":7}},"59":{"start":{"line":205,"column":8},"end":{"line":205,"column":25}},"60":{"start":{"line":206,"column":8},"end":{"line":206,"column":50}},"61":{"start":{"line":210,"column":4},"end":{"line":210,"column":23}},"62":{"start":{"line":214,"column":48},"end":{"line":214,"column":50}},"63":{"start":{"line":216,"column":4},"end":{"line":218,"column":7}},"64":{"start":{"line":217,"column":6},"end":{"line":217,"column":67}},"65":{"start":{"line":220,"column":19},"end":{"line":220,"column":20}},"66":{"start":{"line":221,"column":19},"end":{"line":221,"column":20}},"67":{"start":{"line":223,"column":4},"end":{"line":228,"column":7}},"68":{"start":{"line":224,"column":6},"end":{"line":227,"column":7}},"69":{"start":{"line":225,"column":8},"end":{"line":225,"column":25}},"70":{"start":{"line":226,"column":8},"end":{"line":226,"column":37}},"71":{"start":{"line":230,"column":4},"end":{"line":230,"column":20}},"72":{"start":{"line":234,"column":4},"end":{"line":236,"column":5}},"73":{"start":{"line":235,"column":6},"end":{"line":235,"column":75}},"74":{"start":{"line":238,"column":21},"end":{"line":239,"column":null}},"75":{"start":{"line":239,"column":6},"end":{"line":239,"column":81}},"76":{"start":{"line":242,"column":4},"end":{"line":242,"column":30}},"77":{"start":{"line":246,"column":4},"end":{"line":246,"column":74}},"78":{"start":{"line":14,"column":13},"end":{"line":14,"column":39}},"79":{"start":{"line":14,"column":13},"end":{"line":248,"column":null}}},"fnMap":{"0":{"name":"(anonymous_2)","decl":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"loc":{"start":{"line":17,"column":57},"end":{"line":18,"column":6}}},"1":{"name":"(anonymous_3)","decl":{"start":{"line":23,"column":2},"end":{"line":23,"column":7}},"loc":{"start":{"line":23,"column":65},"end":{"line":80,"column":3}}},"2":{"name":"(anonymous_4)","decl":{"start":{"line":85,"column":2},"end":{"line":85,"column":7}},"loc":{"start":{"line":85,"column":71},"end":{"line":104,"column":3}}},"3":{"name":"(anonymous_5)","decl":{"start":{"line":90,"column":22},"end":{"line":90,"column":23}},"loc":{"start":{"line":90,"column":27},"end":{"line":90,"column":53}}},"4":{"name":"(anonymous_6)","decl":{"start":{"line":91,"column":22},"end":{"line":91,"column":23}},"loc":{"start":{"line":91,"column":32},"end":{"line":91,"column":97}}},"5":{"name":"(anonymous_7)","decl":{"start":{"line":109,"column":2},"end":{"line":109,"column":7}},"loc":{"start":{"line":109,"column":60},"end":{"line":135,"column":3}}},"6":{"name":"(anonymous_8)","decl":{"start":{"line":113,"column":39},"end":{"line":113,"column":40}},"loc":{"start":{"line":114,"column":6},"end":{"line":114,"column":78}}},"7":{"name":"(anonymous_9)","decl":{"start":{"line":117,"column":38},"end":{"line":117,"column":39}},"loc":{"start":{"line":118,"column":6},"end":{"line":118,"column":77}}},"8":{"name":"(anonymous_10)","decl":{"start":{"line":140,"column":2},"end":{"line":140,"column":7}},"loc":{"start":{"line":140,"column":76},"end":{"line":142,"column":3}}},"9":{"name":"(anonymous_11)","decl":{"start":{"line":147,"column":10},"end":{"line":147,"column":27}},"loc":{"start":{"line":147,"column":103},"end":{"line":157,"column":3}}},"10":{"name":"(anonymous_12)","decl":{"start":{"line":150,"column":27},"end":{"line":150,"column":29}},"loc":{"start":{"line":150,"column":32},"end":{"line":156,"column":5}}},"11":{"name":"(anonymous_13)","decl":{"start":{"line":159,"column":10},"end":{"line":159,"column":28}},"loc":{"start":{"line":159,"column":58},"end":{"line":167,"column":3}}},"12":{"name":"(anonymous_14)","decl":{"start":{"line":162,"column":21},"end":{"line":162,"column":23}},"loc":{"start":{"line":162,"column":26},"end":{"line":164,"column":5}}},"13":{"name":"(anonymous_15)","decl":{"start":{"line":169,"column":10},"end":{"line":169,"column":33}},"loc":{"start":{"line":169,"column":63},"end":{"line":197,"column":3}}},"14":{"name":"(anonymous_16)","decl":{"start":{"line":172,"column":21},"end":{"line":172,"column":23}},"loc":{"start":{"line":172,"column":26},"end":{"line":187,"column":5}}},"15":{"name":"(anonymous_17)","decl":{"start":{"line":190,"column":36},"end":{"line":190,"column":43}},"loc":{"start":{"line":190,"column":46},"end":{"line":194,"column":5}}},"16":{"name":"(anonymous_18)","decl":{"start":{"line":191,"column":44},"end":{"line":191,"column":46}},"loc":{"start":{"line":191,"column":50},"end":{"line":191,"column":82}}},"17":{"name":"(anonymous_19)","decl":{"start":{"line":199,"column":10},"end":{"line":199,"column":31}},"loc":{"start":{"line":199,"column":66},"end":{"line":211,"column":3}}},"18":{"name":"(anonymous_20)","decl":{"start":{"line":203,"column":39},"end":{"line":203,"column":40}},"loc":{"start":{"line":203,"column":61},"end":{"line":208,"column":5}}},"19":{"name":"(anonymous_21)","decl":{"start":{"line":213,"column":10},"end":{"line":213,"column":28}},"loc":{"start":{"line":213,"column":58},"end":{"line":231,"column":3}}},"20":{"name":"(anonymous_22)","decl":{"start":{"line":216,"column":21},"end":{"line":216,"column":23}},"loc":{"start":{"line":216,"column":26},"end":{"line":218,"column":5}}},"21":{"name":"(anonymous_23)","decl":{"start":{"line":223,"column":40},"end":{"line":223,"column":41}},"loc":{"start":{"line":223,"column":61},"end":{"line":228,"column":5}}},"22":{"name":"(anonymous_24)","decl":{"start":{"line":233,"column":10},"end":{"line":233,"column":29}},"loc":{"start":{"line":233,"column":59},"end":{"line":243,"column":3}}},"23":{"name":"(anonymous_25)","decl":{"start":{"line":238,"column":37},"end":{"line":238,"column":38}},"loc":{"start":{"line":239,"column":6},"end":{"line":239,"column":81}}},"24":{"name":"(anonymous_26)","decl":{"start":{"line":245,"column":10},"end":{"line":245,"column":27}},"loc":{"start":{"line":245,"column":27},"end":{"line":247,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":24,"column":41},"end":{"line":24,"column":70}},"type":"default-arg","locations":[{"start":{"line":24,"column":66},"end":{"line":24,"column":70}}]},"1":{"loc":{"start":{"line":42,"column":28},"end":{"line":57,"column":10}},"type":"cond-expr","locations":[{"start":{"line":43,"column":8},"end":{"line":56,"column":10}},{"start":{"line":57,"column":8},"end":{"line":57,"column":10}}]},"2":{"loc":{"start":{"line":52,"column":19},"end":{"line":52,"column":81}},"type":"binary-expr","locations":[{"start":{"line":52,"column":19},"end":{"line":52,"column":35}},{"start":{"line":52,"column":39},"end":{"line":52,"column":81}}]},"3":{"loc":{"start":{"line":53,"column":17},"end":{"line":53,"column":59}},"type":"binary-expr","locations":[{"start":{"line":53,"column":17},"end":{"line":53,"column":31}},{"start":{"line":53,"column":35},"end":{"line":53,"column":59}}]},"4":{"loc":{"start":{"line":68,"column":15},"end":{"line":68,"column":77}},"type":"binary-expr","locations":[{"start":{"line":68,"column":15},"end":{"line":68,"column":31}},{"start":{"line":68,"column":35},"end":{"line":68,"column":77}}]},"5":{"loc":{"start":{"line":69,"column":13},"end":{"line":69,"column":55}},"type":"binary-expr","locations":[{"start":{"line":69,"column":13},"end":{"line":69,"column":27}},{"start":{"line":69,"column":31},"end":{"line":69,"column":55}}]},"6":{"loc":{"start":{"line":89,"column":26},"end":{"line":91,"column":101}},"type":"cond-expr","locations":[{"start":{"line":90,"column":8},"end":{"line":90,"column":54}},{"start":{"line":91,"column":8},"end":{"line":91,"column":101}}]},"7":{"loc":{"start":{"line":93,"column":4},"end":{"line":95,"column":5}},"type":"if","locations":[{"start":{"line":93,"column":4},"end":{"line":95,"column":5}},{"start":{},"end":{}}]},"8":{"loc":{"start":{"line":148,"column":4},"end":{"line":148,"column":36}},"type":"if","locations":[{"start":{"line":148,"column":4},"end":{"line":148,"column":36}},{"start":{},"end":{}}]},"9":{"loc":{"start":{"line":152,"column":24},"end":{"line":152,"column":81}},"type":"cond-expr","locations":[{"start":{"line":152,"column":42},"end":{"line":152,"column":67}},{"start":{"line":152,"column":70},"end":{"line":152,"column":81}}]},"10":{"loc":{"start":{"line":153,"column":22},"end":{"line":153,"column":74}},"type":"cond-expr","locations":[{"start":{"line":153,"column":38},"end":{"line":153,"column":61}},{"start":{"line":153,"column":64},"end":{"line":153,"column":74}}]},"11":{"loc":{"start":{"line":155,"column":13},"end":{"line":155,"column":53}},"type":"binary-expr","locations":[{"start":{"line":155,"column":13},"end":{"line":155,"column":32}},{"start":{"line":155,"column":36},"end":{"line":155,"column":53}}]},"12":{"loc":{"start":{"line":163,"column":40},"end":{"line":163,"column":75}},"type":"binary-expr","locations":[{"start":{"line":163,"column":40},"end":{"line":163,"column":70}},{"start":{"line":163,"column":74},"end":{"line":163,"column":75}}]},"13":{"loc":{"start":{"line":173,"column":6},"end":{"line":180,"column":7}},"type":"if","locations":[{"start":{"line":173,"column":6},"end":{"line":180,"column":7}},{"start":{},"end":{}}]},"14":{"loc":{"start":{"line":204,"column":6},"end":{"line":207,"column":7}},"type":"if","locations":[{"start":{"line":204,"column":6},"end":{"line":207,"column":7}},{"start":{},"end":{}}]},"15":{"loc":{"start":{"line":217,"column":33},"end":{"line":217,"column":61}},"type":"binary-expr","locations":[{"start":{"line":217,"column":33},"end":{"line":217,"column":56}},{"start":{"line":217,"column":60},"end":{"line":217,"column":61}}]},"16":{"loc":{"start":{"line":224,"column":6},"end":{"line":227,"column":7}},"type":"if","locations":[{"start":{"line":224,"column":6},"end":{"line":227,"column":7}},{"start":{},"end":{}}]},"17":{"loc":{"start":{"line":234,"column":4},"end":{"line":236,"column":5}},"type":"if","locations":[{"start":{"line":234,"column":4},"end":{"line":236,"column":5}},{"start":{},"end":{}}]},"18":{"loc":{"start":{"line":239,"column":6},"end":{"line":239,"column":81}},"type":"cond-expr","locations":[{"start":{"line":239,"column":65},"end":{"line":239,"column":72}},{"start":{"line":239,"column":75},"end":{"line":239,"column":81}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0},"b":{"0":[0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0,0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0,0]}} +,"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\validation\\analysis.validator.ts": {"path":"C:\\Users\\Surface\\Documents\\Drips\\GasGuard\\apps\\api\\src\\validation\\analysis.validator.ts","statementMap":{"0":{"start":{"line":12,"column":17},"end":{"line":12,"column":54}},"1":{"start":{"line":13,"column":38},"end":{"line":13,"column":40}},"2":{"start":{"line":15,"column":4},"end":{"line":181,"column":5}},"3":{"start":{"line":17,"column":6},"end":{"line":50,"column":7}},"4":{"start":{"line":18,"column":8},"end":{"line":22,"column":11}},"5":{"start":{"line":24,"column":8},"end":{"line":31,"column":9}},"6":{"start":{"line":25,"column":10},"end":{"line":30,"column":13}},"7":{"start":{"line":33,"column":8},"end":{"line":40,"column":9}},"8":{"start":{"line":34,"column":10},"end":{"line":39,"column":13}},"9":{"start":{"line":42,"column":8},"end":{"line":49,"column":9}},"10":{"start":{"line":43,"column":10},"end":{"line":48,"column":13}},"11":{"start":{"line":53,"column":6},"end":{"line":118,"column":7}},"12":{"start":{"line":54,"column":8},"end":{"line":58,"column":11}},"13":{"start":{"line":60,"column":8},"end":{"line":67,"column":9}},"14":{"start":{"line":61,"column":10},"end":{"line":66,"column":13}},"15":{"start":{"line":69,"column":24},"end":{"line":69,"column":25}},"16":{"start":{"line":70,"column":8},"end":{"line":108,"column":11}},"17":{"start":{"line":71,"column":25},"end":{"line":71,"column":42}},"18":{"start":{"line":73,"column":10},"end":{"line":79,"column":11}},"19":{"start":{"line":74,"column":12},"end":{"line":78,"column":15}},"20":{"start":{"line":81,"column":10},"end":{"line":87,"column":11}},"21":{"start":{"line":82,"column":12},"end":{"line":86,"column":15}},"22":{"start":{"line":89,"column":10},"end":{"line":96,"column":11}},"23":{"start":{"line":90,"column":12},"end":{"line":95,"column":15}},"24":{"start":{"line":98,"column":10},"end":{"line":105,"column":11}},"25":{"start":{"line":99,"column":12},"end":{"line":104,"column":15}},"26":{"start":{"line":107,"column":10},"end":{"line":107,"column":38}},"27":{"start":{"line":110,"column":8},"end":{"line":117,"column":9}},"28":{"start":{"line":111,"column":10},"end":{"line":116,"column":13}},"29":{"start":{"line":121,"column":6},"end":{"line":139,"column":7}},"30":{"start":{"line":122,"column":8},"end":{"line":129,"column":9}},"31":{"start":{"line":123,"column":10},"end":{"line":128,"column":13}},"32":{"start":{"line":131,"column":8},"end":{"line":138,"column":9}},"33":{"start":{"line":132,"column":10},"end":{"line":137,"column":13}},"34":{"start":{"line":142,"column":6},"end":{"line":156,"column":7}},"35":{"start":{"line":143,"column":8},"end":{"line":150,"column":9}},"36":{"start":{"line":144,"column":10},"end":{"line":149,"column":13}},"37":{"start":{"line":153,"column":8},"end":{"line":155,"column":9}},"38":{"start":{"line":154,"column":10},"end":{"line":154,"column":73}},"39":{"start":{"line":158,"column":6},"end":{"line":169,"column":7}},"40":{"start":{"line":159,"column":8},"end":{"line":167,"column":11}},"41":{"start":{"line":168,"column":8},"end":{"line":168,"column":15}},"42":{"start":{"line":171,"column":6},"end":{"line":171,"column":13}},"43":{"start":{"line":173,"column":6},"end":{"line":180,"column":9}},"44":{"start":{"line":185,"column":22},"end":{"line":185,"column":62}},"45":{"start":{"line":185,"column":40},"end":{"line":185,"column":61}},"46":{"start":{"line":187,"column":4},"end":{"line":194,"column":5}},"47":{"start":{"line":188,"column":6},"end":{"line":192,"column":9}},"48":{"start":{"line":193,"column":6},"end":{"line":193,"column":13}},"49":{"start":{"line":197,"column":25},"end":{"line":197,"column":99}},"50":{"start":{"line":197,"column":41},"end":{"line":197,"column":98}},"51":{"start":{"line":198,"column":4},"end":{"line":204,"column":5}},"52":{"start":{"line":199,"column":6},"end":{"line":203,"column":9}},"53":{"start":{"line":207,"column":22},"end":{"line":207,"column":68}},"54":{"start":{"line":207,"column":38},"end":{"line":207,"column":67}},"55":{"start":{"line":208,"column":4},"end":{"line":219,"column":5}},"56":{"start":{"line":209,"column":22},"end":{"line":209,"column":39}},"57":{"start":{"line":210,"column":28},"end":{"line":210,"column":94}},"58":{"start":{"line":212,"column":6},"end":{"line":218,"column":7}},"59":{"start":{"line":213,"column":8},"end":{"line":217,"column":11}},"60":{"start":{"line":222,"column":26},"end":{"line":225,"column":null}},"61":{"start":{"line":223,"column":6},"end":{"line":225,"column":40}},"62":{"start":{"line":228,"column":4},"end":{"line":234,"column":5}},"63":{"start":{"line":229,"column":6},"end":{"line":233,"column":9}},"64":{"start":{"line":238,"column":4},"end":{"line":243,"column":5}},"65":{"start":{"line":239,"column":6},"end":{"line":239,"column":19}},"66":{"start":{"line":240,"column":6},"end":{"line":240,"column":18}},"67":{"start":{"line":242,"column":6},"end":{"line":242,"column":19}},"68":{"start":{"line":4,"column":0},"end":{"line":4,"column":13}},"69":{"start":{"line":5,"column":26},"end":{"line":5,"column":108}},"70":{"start":{"line":6,"column":26},"end":{"line":6,"column":84}},"71":{"start":{"line":7,"column":26},"end":{"line":7,"column":59}},"72":{"start":{"line":8,"column":26},"end":{"line":8,"column":42}},"73":{"start":{"line":9,"column":26},"end":{"line":9,"column":64}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":11,"column":2},"end":{"line":11,"column":8}},"loc":{"start":{"line":11,"column":75},"end":{"line":182,"column":3}}},"1":{"name":"(anonymous_1)","decl":{"start":{"line":70,"column":27},"end":{"line":70,"column":28}},"loc":{"start":{"line":70,"column":43},"end":{"line":108,"column":9}}},"2":{"name":"(anonymous_2)","decl":{"start":{"line":184,"column":10},"end":{"line":184,"column":16}},"loc":{"start":{"line":184,"column":94},"end":{"line":235,"column":3}}},"3":{"name":"(anonymous_3)","decl":{"start":{"line":185,"column":35},"end":{"line":185,"column":36}},"loc":{"start":{"line":185,"column":40},"end":{"line":185,"column":61}}},"4":{"name":"(anonymous_4)","decl":{"start":{"line":197,"column":36},"end":{"line":197,"column":37}},"loc":{"start":{"line":197,"column":41},"end":{"line":197,"column":98}}},"5":{"name":"(anonymous_5)","decl":{"start":{"line":207,"column":33},"end":{"line":207,"column":34}},"loc":{"start":{"line":207,"column":38},"end":{"line":207,"column":67}}},"6":{"name":"(anonymous_6)","decl":{"start":{"line":222,"column":43},"end":{"line":222,"column":44}},"loc":{"start":{"line":223,"column":6},"end":{"line":225,"column":40}}},"7":{"name":"(anonymous_7)","decl":{"start":{"line":237,"column":10},"end":{"line":237,"column":16}},"loc":{"start":{"line":237,"column":39},"end":{"line":244,"column":3}}}},"branchMap":{"0":{"loc":{"start":{"line":17,"column":6},"end":{"line":50,"column":7}},"type":"if","locations":[{"start":{"line":17,"column":6},"end":{"line":50,"column":7}},{"start":{"line":23,"column":13},"end":{"line":50,"column":7}}]},"1":{"loc":{"start":{"line":24,"column":8},"end":{"line":31,"column":9}},"type":"if","locations":[{"start":{"line":24,"column":8},"end":{"line":31,"column":9}},{"start":{},"end":{}}]},"2":{"loc":{"start":{"line":24,"column":12},"end":{"line":24,"column":71}},"type":"binary-expr","locations":[{"start":{"line":24,"column":12},"end":{"line":24,"column":30}},{"start":{"line":24,"column":34},"end":{"line":24,"column":71}}]},"3":{"loc":{"start":{"line":33,"column":8},"end":{"line":40,"column":9}},"type":"if","locations":[{"start":{"line":33,"column":8},"end":{"line":40,"column":9}},{"start":{},"end":{}}]},"4":{"loc":{"start":{"line":33,"column":12},"end":{"line":33,"column":63}},"type":"binary-expr","locations":[{"start":{"line":33,"column":12},"end":{"line":33,"column":29}},{"start":{"line":33,"column":33},"end":{"line":33,"column":63}}]},"5":{"loc":{"start":{"line":42,"column":8},"end":{"line":49,"column":9}},"type":"if","locations":[{"start":{"line":42,"column":8},"end":{"line":49,"column":9}},{"start":{},"end":{}}]},"6":{"loc":{"start":{"line":42,"column":12},"end":{"line":42,"column":86}},"type":"binary-expr","locations":[{"start":{"line":42,"column":12},"end":{"line":42,"column":38}},{"start":{"line":42,"column":42},"end":{"line":42,"column":86}}]},"7":{"loc":{"start":{"line":53,"column":6},"end":{"line":118,"column":7}},"type":"if","locations":[{"start":{"line":53,"column":6},"end":{"line":118,"column":7}},{"start":{"line":59,"column":13},"end":{"line":118,"column":7}}]},"8":{"loc":{"start":{"line":53,"column":10},"end":{"line":53,"column":78}},"type":"binary-expr","locations":[{"start":{"line":53,"column":10},"end":{"line":53,"column":21}},{"start":{"line":53,"column":25},"end":{"line":53,"column":51}},{"start":{"line":53,"column":55},"end":{"line":53,"column":78}}]},"9":{"loc":{"start":{"line":60,"column":8},"end":{"line":67,"column":9}},"type":"if","locations":[{"start":{"line":60,"column":8},"end":{"line":67,"column":9}},{"start":{},"end":{}}]},"10":{"loc":{"start":{"line":73,"column":10},"end":{"line":79,"column":11}},"type":"if","locations":[{"start":{"line":73,"column":10},"end":{"line":79,"column":11}},{"start":{},"end":{}}]},"11":{"loc":{"start":{"line":73,"column":14},"end":{"line":73,"column":57}},"type":"binary-expr","locations":[{"start":{"line":73,"column":14},"end":{"line":73,"column":24}},{"start":{"line":73,"column":28},"end":{"line":73,"column":57}}]},"12":{"loc":{"start":{"line":81,"column":10},"end":{"line":87,"column":11}},"type":"if","locations":[{"start":{"line":81,"column":10},"end":{"line":87,"column":11}},{"start":{},"end":{}}]},"13":{"loc":{"start":{"line":81,"column":14},"end":{"line":81,"column":63}},"type":"binary-expr","locations":[{"start":{"line":81,"column":14},"end":{"line":81,"column":27}},{"start":{"line":81,"column":31},"end":{"line":81,"column":63}}]},"14":{"loc":{"start":{"line":89,"column":10},"end":{"line":96,"column":11}},"type":"if","locations":[{"start":{"line":89,"column":10},"end":{"line":96,"column":11}},{"start":{},"end":{}}]},"15":{"loc":{"start":{"line":89,"column":14},"end":{"line":89,"column":81}},"type":"binary-expr","locations":[{"start":{"line":89,"column":14},"end":{"line":89,"column":28}},{"start":{"line":89,"column":32},"end":{"line":89,"column":81}}]},"16":{"loc":{"start":{"line":98,"column":10},"end":{"line":105,"column":11}},"type":"if","locations":[{"start":{"line":98,"column":10},"end":{"line":105,"column":11}},{"start":{},"end":{}}]},"17":{"loc":{"start":{"line":98,"column":14},"end":{"line":98,"column":57}},"type":"binary-expr","locations":[{"start":{"line":98,"column":14},"end":{"line":98,"column":23}},{"start":{"line":98,"column":27},"end":{"line":98,"column":57}}]},"18":{"loc":{"start":{"line":107,"column":23},"end":{"line":107,"column":37}},"type":"binary-expr","locations":[{"start":{"line":107,"column":23},"end":{"line":107,"column":32}},{"start":{"line":107,"column":36},"end":{"line":107,"column":37}}]},"19":{"loc":{"start":{"line":110,"column":8},"end":{"line":117,"column":9}},"type":"if","locations":[{"start":{"line":110,"column":8},"end":{"line":117,"column":9}},{"start":{},"end":{}}]},"20":{"loc":{"start":{"line":121,"column":6},"end":{"line":139,"column":7}},"type":"if","locations":[{"start":{"line":121,"column":6},"end":{"line":139,"column":7}},{"start":{},"end":{}}]},"21":{"loc":{"start":{"line":122,"column":8},"end":{"line":129,"column":9}},"type":"if","locations":[{"start":{"line":122,"column":8},"end":{"line":129,"column":9}},{"start":{},"end":{}}]},"22":{"loc":{"start":{"line":131,"column":8},"end":{"line":138,"column":9}},"type":"if","locations":[{"start":{"line":131,"column":8},"end":{"line":138,"column":9}},{"start":{},"end":{}}]},"23":{"loc":{"start":{"line":142,"column":6},"end":{"line":156,"column":7}},"type":"if","locations":[{"start":{"line":142,"column":6},"end":{"line":156,"column":7}},{"start":{},"end":{}}]},"24":{"loc":{"start":{"line":143,"column":8},"end":{"line":150,"column":9}},"type":"if","locations":[{"start":{"line":143,"column":8},"end":{"line":150,"column":9}},{"start":{},"end":{}}]},"25":{"loc":{"start":{"line":143,"column":12},"end":{"line":143,"column":99}},"type":"binary-expr","locations":[{"start":{"line":143,"column":12},"end":{"line":143,"column":35}},{"start":{"line":143,"column":39},"end":{"line":143,"column":99}}]},"26":{"loc":{"start":{"line":153,"column":8},"end":{"line":155,"column":9}},"type":"if","locations":[{"start":{"line":153,"column":8},"end":{"line":155,"column":9}},{"start":{},"end":{}}]},"27":{"loc":{"start":{"line":158,"column":6},"end":{"line":169,"column":7}},"type":"if","locations":[{"start":{"line":158,"column":6},"end":{"line":169,"column":7}},{"start":{},"end":{}}]},"28":{"loc":{"start":{"line":164,"column":23},"end":{"line":164,"column":73}},"type":"binary-expr","locations":[{"start":{"line":164,"column":23},"end":{"line":164,"column":60}},{"start":{"line":164,"column":64},"end":{"line":164,"column":73}}]},"29":{"loc":{"start":{"line":178,"column":21},"end":{"line":178,"column":71}},"type":"binary-expr","locations":[{"start":{"line":178,"column":21},"end":{"line":178,"column":58}},{"start":{"line":178,"column":62},"end":{"line":178,"column":71}}]},"30":{"loc":{"start":{"line":187,"column":4},"end":{"line":194,"column":5}},"type":"if","locations":[{"start":{"line":187,"column":4},"end":{"line":194,"column":5}},{"start":{},"end":{}}]},"31":{"loc":{"start":{"line":197,"column":41},"end":{"line":197,"column":98}},"type":"binary-expr","locations":[{"start":{"line":197,"column":41},"end":{"line":197,"column":64}},{"start":{"line":197,"column":68},"end":{"line":197,"column":98}}]},"32":{"loc":{"start":{"line":198,"column":4},"end":{"line":204,"column":5}},"type":"if","locations":[{"start":{"line":198,"column":4},"end":{"line":204,"column":5}},{"start":{},"end":{}}]},"33":{"loc":{"start":{"line":208,"column":4},"end":{"line":219,"column":5}},"type":"if","locations":[{"start":{"line":208,"column":4},"end":{"line":219,"column":5}},{"start":{},"end":{}}]},"34":{"loc":{"start":{"line":210,"column":28},"end":{"line":210,"column":94}},"type":"binary-expr","locations":[{"start":{"line":210,"column":28},"end":{"line":210,"column":59}},{"start":{"line":210,"column":63},"end":{"line":210,"column":94}}]},"35":{"loc":{"start":{"line":212,"column":6},"end":{"line":218,"column":7}},"type":"if","locations":[{"start":{"line":212,"column":6},"end":{"line":218,"column":7}},{"start":{},"end":{}}]},"36":{"loc":{"start":{"line":223,"column":6},"end":{"line":225,"column":40}},"type":"binary-expr","locations":[{"start":{"line":223,"column":6},"end":{"line":223,"column":43}},{"start":{"line":224,"column":6},"end":{"line":224,"column":49}},{"start":{"line":225,"column":6},"end":{"line":225,"column":40}}]},"37":{"loc":{"start":{"line":228,"column":4},"end":{"line":234,"column":5}},"type":"if","locations":[{"start":{"line":228,"column":4},"end":{"line":234,"column":5}},{"start":{},"end":{}}]}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0,0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[0,0],"21":[0,0],"22":[0,0],"23":[0,0],"24":[0,0],"25":[0,0],"26":[0,0],"27":[0,0],"28":[0,0],"29":[0,0],"30":[0,0],"31":[0,0],"32":[0,0],"33":[0,0],"34":[0,0],"35":[0,0],"36":[0,0,0],"37":[0,0]}} +} diff --git a/apps/api/coverage/lcov-report/base.css b/apps/api/coverage/lcov-report/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/apps/api/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/apps/api/coverage/lcov-report/block-navigation.js b/apps/api/coverage/lcov-report/block-navigation.js new file mode 100644 index 0000000..530d1ed --- /dev/null +++ b/apps/api/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selector that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/apps/api/coverage/lcov-report/favicon.png b/apps/api/coverage/lcov-report/favicon.png new file mode 100644 index 0000000..c1525b8 Binary files /dev/null and b/apps/api/coverage/lcov-report/favicon.png differ diff --git a/apps/api/coverage/lcov-report/index.html b/apps/api/coverage/lcov-report/index.html new file mode 100644 index 0000000..8f115fd --- /dev/null +++ b/apps/api/coverage/lcov-report/index.html @@ -0,0 +1,281 @@ + + + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 11.92% + Statements + 96/805 +
+ + +
+ 8.8% + Branches + 37/420 +
+ + +
+ 9.27% + Functions + 14/151 +
+ + +
+ 10.57% + Lines + 79/747 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
src +
+
0%0/1010%0/220%0/140%0/96
src/auth +
+
68.75%11/16100%0/050%1/269.23%9/13
src/auth/__tests__ +
+
0%0/5100%0/0100%0/00%0/5
src/auth/decorators +
+
74.07%20/270%0/455.55%5/970.58%12/17
src/auth/guards +
+
89.13%41/4695.45%21/2275%6/892.3%36/39
src/auth/strategies +
+
92.3%24/26100%16/1666.66%2/395.65%22/23
src/controllers +
+
0%0/1630%0/960%0/260%0/155
src/example +
+
0%0/6100%0/00%0/10%0/4
src/middleware +
+
0%0/150%0/160%0/40%0/15
src/routes +
+
0%0/15100%0/00%0/50%0/15
src/services +
+
0%0/3110%0/1660%0/710%0/294
src/validation +
+
0%0/740%0/780%0/80%0/71
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/prettify.css b/apps/api/coverage/lcov-report/prettify.css new file mode 100644 index 0000000..b317a7c --- /dev/null +++ b/apps/api/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/apps/api/coverage/lcov-report/prettify.js b/apps/api/coverage/lcov-report/prettify.js new file mode 100644 index 0000000..b322523 --- /dev/null +++ b/apps/api/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/apps/api/coverage/lcov-report/sort-arrow-sprite.png b/apps/api/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000..6ed6831 Binary files /dev/null and b/apps/api/coverage/lcov-report/sort-arrow-sprite.png differ diff --git a/apps/api/coverage/lcov-report/sorter.js b/apps/api/coverage/lcov-report/sorter.js new file mode 100644 index 0000000..4ed70ae --- /dev/null +++ b/apps/api/coverage/lcov-report/sorter.js @@ -0,0 +1,210 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + + // Try to create a RegExp from the searchValue. If it fails (invalid regex), + // it will be treated as a plain text search + let searchRegex; + try { + searchRegex = new RegExp(searchValue, 'i'); // 'i' for case-insensitive + } catch (error) { + searchRegex = null; + } + + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + let isMatch = false; + + if (searchRegex) { + // If a valid regex was created, use it for matching + isMatch = searchRegex.test(row.textContent); + } else { + // Otherwise, fall back to the original plain text search + isMatch = row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()); + } + + row.style.display = isMatch ? '' : 'none'; + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/apps/api/coverage/lcov-report/src/app.controller.ts.html b/apps/api/coverage/lcov-report/src/app.controller.ts.html new file mode 100644 index 0000000..efc3f2b --- /dev/null +++ b/apps/api/coverage/lcov-report/src/app.controller.ts.html @@ -0,0 +1,175 @@ + + + + + + Code coverage report for src/app.controller.ts + + + + + + + + + +
+
+

All files / src app.controller.ts

+
+ +
+ 0% + Statements + 0/9 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 0% + Lines + 0/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Controller, Get } from '@nestjs/common';
+import { Public } from './auth';
+ 
+@Controller()
+export class AppController {
+    /**
+     * Root endpoint - API info
+     * Public endpoint for API discovery
+     */
+    @Public()
+    @Get()
+    getRoot(): { name: string; version: string; health: string } {
+        return {
+            name: 'GasGuard API',
+            version: '0.1.0',
+            health: '/health',
+        };
+    }
+ 
+    /**
+     * Health check endpoint
+     * Returns a simple status to verify the API is running
+     * Public endpoint for monitoring and load balancers
+     */
+    @Public()
+    @Get('health')
+    getHealth(): { status: string } {
+        return { status: 'ok' };
+    }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/app.module.ts.html b/apps/api/coverage/lcov-report/src/app.module.ts.html new file mode 100644 index 0000000..15d1c2c --- /dev/null +++ b/apps/api/coverage/lcov-report/src/app.module.ts.html @@ -0,0 +1,271 @@ + + + + + + Code coverage report for src/app.module.ts + + + + + + + + + +
+
+

All files / src app.module.ts

+
+ +
+ 0% + Statements + 0/16 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 0% + Lines + 0/14 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Module } from '@nestjs/common';
+import { APP_GUARD } from '@nestjs/core';
+import { ConfigModule } from '@nestjs/config';
+import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
+import { AppController } from './app.controller';
+import { ExampleController } from './example/example.controller';
+import { FailedTransactionController } from './controllers/failed-transaction.controller';
+import { CrossChainGasController } from './controllers/cross-chain-gas.controller';
+import { FailedTransactionService } from './services/failed-transaction.service';
+import { MitigationService } from './services/mitigation.service';
+import { TransactionAnalysisService } from './services/transaction-analysis.service';
+import { CrossChainGasService } from './services/cross-chain-gas.service';
+import { AuthModule, JwtAuthGuard, RolesGuard } from './auth';
+ 
+@Module({
+    imports: [
+        // Global configuration module for environment variables
+        ConfigModule.forRoot({
+            isGlobal: true,
+            envFilePath: ['.env', '.env.local'],
+        }),
+        // Configure rate limiting: 10 requests per 60 seconds per IP
+        ThrottlerModule.forRoot([
+            {
+                name: 'default',
+                ttl: 60000,  // 60 seconds in milliseconds
+                limit: 10,   // 10 requests per TTL window
+            },
+        ]),
+        // JWT Authentication module
+        AuthModule,
+    ],
+    controllers: [
+        AppController,
+        ExampleController,
+        FailedTransactionController,
+        CrossChainGasController,
+        // Add your controllers here - remember to add @Version('1') decorator
+    ],
+    providers: [
+        // Apply ThrottlerGuard globally to all routes
+        {
+            provide: APP_GUARD,
+            useClass: ThrottlerGuard,
+        },
+        // Apply JWT authentication guard globally to all routes
+        {
+            provide: APP_GUARD,
+            useClass: JwtAuthGuard,
+        },
+        // Apply roles guard globally for RBAC enforcement
+        {
+            provide: APP_GUARD,
+            useClass: RolesGuard,
+        },
+        FailedTransactionService,
+        MitigationService,
+        TransactionAnalysisService,
+        CrossChainGasService,
+    ],
+})
+export class AppModule { }
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/__tests__/index.html b/apps/api/coverage/lcov-report/src/auth/__tests__/index.html new file mode 100644 index 0000000..dc4c82d --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/__tests__/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/auth/__tests__ + + + + + + + + + +
+
+

All files src/auth/__tests__

+
+ +
+ 0% + Statements + 0/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 0% + Lines + 0/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
index.ts +
+
0%0/5100%0/0100%0/00%0/5
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/__tests__/index.ts.html b/apps/api/coverage/lcov-report/src/auth/__tests__/index.ts.html new file mode 100644 index 0000000..ddc05d3 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/__tests__/index.ts.html @@ -0,0 +1,103 @@ + + + + + + Code coverage report for src/auth/__tests__/index.ts + + + + + + + + + +
+
+

All files / src/auth/__tests__ index.ts

+
+ +
+ 0% + Statements + 0/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 0% + Lines + 0/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7  +  +  +  +  +  + 
// Auth module test exports
+export * from './jwt.strategy.spec';
+export * from './jwt-auth.guard.spec';
+export * from './roles.guard.spec';
+export * from './decorators.spec';
+export * from './auth.module.spec';
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/auth.module.ts.html b/apps/api/coverage/lcov-report/src/auth/auth.module.ts.html new file mode 100644 index 0000000..7bae571 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/auth.module.ts.html @@ -0,0 +1,166 @@ + + + + + + Code coverage report for src/auth/auth.module.ts + + + + + + + + + +
+
+

All files / src/auth auth.module.ts

+
+ +
+ 100% + Statements + 11/11 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 9/9 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +281x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +14x +  +  +  +  +  +  +  +  +  +  +  +  +1x + 
import { Module } from '@nestjs/common';
+import { JwtModule } from '@nestjs/jwt';
+import { PassportModule } from '@nestjs/passport';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { JwtStrategy } from './strategies/jwt.strategy';
+import { JwtAuthGuard } from './guards/jwt-auth.guard';
+import { RolesGuard } from './guards/roles.guard';
+ 
+@Module({
+  imports: [
+    PassportModule.register({ defaultStrategy: 'jwt' }),
+    JwtModule.registerAsync({
+      imports: [ConfigModule],
+      useFactory: async (configService: ConfigService) => ({
+        secret: configService.get<string>('JWT_SECRET'),
+        signOptions: {
+          issuer: configService.get<string>('JWT_ISSUER', 'gasguard-api'),
+          audience: configService.get<string>('JWT_AUDIENCE', 'gasguard-client'),
+        },
+      }),
+      inject: [ConfigService],
+    }),
+  ],
+  providers: [JwtStrategy, JwtAuthGuard, RolesGuard],
+  exports: [JwtAuthGuard, RolesGuard, PassportModule, JwtModule],
+})
+export class AuthModule {}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/decorators/current-user.decorator.ts.html b/apps/api/coverage/lcov-report/src/auth/decorators/current-user.decorator.ts.html new file mode 100644 index 0000000..03cf622 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/decorators/current-user.decorator.ts.html @@ -0,0 +1,178 @@ + + + + + + Code coverage report for src/auth/decorators/current-user.decorator.ts + + + + + + + + + +
+
+

All files / src/auth/decorators current-user.decorator.ts

+
+ +
+ 28.57% + Statements + 2/7 +
+ + +
+ 0% + Branches + 0/4 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 28.57% + Lines + 2/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +321x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  + 
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
+import { JwtUser } from '../strategies/jwt.strategy';
+ 
+/**
+ * Extracts the current authenticated user from the request.
+ * Returns the full user object or a specific property if key is provided.
+ * 
+ * @param key - Optional property key to extract from user object
+ * @returns The user object or the specified property value
+ * 
+ * @example
+ * ```typescript
+ * @Get('profile')
+ * getProfile(@CurrentUser() user: JwtUser) { ... }
+ * 
+ * @Get('user-id')
+ * getUserId(@CurrentUser('userId') userId: string) { ... }
+ * ```
+ */
+export const CurrentUser = createParamDecorator(
+  (key: keyof JwtUser | undefined, ctx: ExecutionContext) => {
+    const request = ctx.switchToHttp().getRequest<{ user: JwtUser }>();
+    const user = request.user;
+ 
+    if (!user) {
+      return undefined;
+    }
+ 
+    return key ? user[key] : user;
+  },
+);
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/decorators/index.html b/apps/api/coverage/lcov-report/src/auth/decorators/index.html new file mode 100644 index 0000000..0ac44bc --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/decorators/index.html @@ -0,0 +1,161 @@ + + + + + + Code coverage report for src/auth/decorators + + + + + + + + + +
+
+

All files src/auth/decorators

+
+ +
+ 74.07% + Statements + 20/27 +
+ + +
+ 0% + Branches + 0/4 +
+ + +
+ 55.55% + Functions + 5/9 +
+ + +
+ 70.58% + Lines + 12/17 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
current-user.decorator.ts +
+
28.57%2/70%0/40%0/128.57%2/7
index.ts +
+
100%9/9100%0/083.33%5/6100%3/3
public.decorator.ts +
+
80%4/5100%0/00%0/1100%3/3
roles.decorator.ts +
+
83.33%5/6100%0/00%0/1100%4/4
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/decorators/index.ts.html b/apps/api/coverage/lcov-report/src/auth/decorators/index.ts.html new file mode 100644 index 0000000..5799aff --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/decorators/index.ts.html @@ -0,0 +1,94 @@ + + + + + + Code coverage report for src/auth/decorators/index.ts + + + + + + + + + +
+
+

All files / src/auth/decorators index.ts

+
+ +
+ 100% + Statements + 9/9 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 83.33% + Functions + 5/6 +
+ + +
+ 100% + Lines + 3/3 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +42x +9x +1x + 
export { Public, IS_PUBLIC_KEY } from './public.decorator';
+export { Roles, Role, RoleType, ROLES_KEY } from './roles.decorator';
+export { CurrentUser } from './current-user.decorator';
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/decorators/public.decorator.ts.html b/apps/api/coverage/lcov-report/src/auth/decorators/public.decorator.ts.html new file mode 100644 index 0000000..44c5eb6 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/decorators/public.decorator.ts.html @@ -0,0 +1,133 @@ + + + + + + Code coverage report for src/auth/decorators/public.decorator.ts + + + + + + + + + +
+
+

All files / src/auth/decorators public.decorator.ts

+
+ +
+ 80% + Statements + 4/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 100% + Lines + 3/3 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +173x +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +3x + 
import { SetMetadata } from '@nestjs/common';
+ 
+export const IS_PUBLIC_KEY = 'isPublic';
+ 
+/**
+ * Marks a route or controller as public, bypassing JWT authentication.
+ * Use this decorator on endpoints that don't require authentication.
+ * 
+ * @example
+ * ```typescript
+ * @Public()
+ * @Get('health')
+ * getHealth() { ... }
+ * ```
+ */
+export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/decorators/roles.decorator.ts.html b/apps/api/coverage/lcov-report/src/auth/decorators/roles.decorator.ts.html new file mode 100644 index 0000000..271ba1b --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/decorators/roles.decorator.ts.html @@ -0,0 +1,175 @@ + + + + + + Code coverage report for src/auth/decorators/roles.decorator.ts + + + + + + + + + +
+
+

All files / src/auth/decorators roles.decorator.ts

+
+ +
+ 83.33% + Statements + 5/6 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 100% + Lines + 4/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +313x +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +  +  +  +3x +  +  +  +  +  +  +  + 
import { SetMetadata } from '@nestjs/common';
+ 
+export const ROLES_KEY = 'roles';
+ 
+/**
+ * Defines the roles required to access a route or controller.
+ * Must be used in combination with JwtAuthGuard and RolesGuard.
+ * 
+ * @param roles - Array of role names required for access
+ * 
+ * @example
+ * ```typescript
+ * @Roles('admin', 'analyst')
+ * @Get('sensitive-data')
+ * getSensitiveData() { ... }
+ * ```
+ */
+export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
+ 
+/**
+ * Predefined role constants for consistent role naming across the application.
+ */
+export const Role = {
+  ADMIN: 'admin',
+  ANALYST: 'analyst',
+  USER: 'user',
+  READONLY: 'readonly',
+} as const;
+ 
+export type RoleType = (typeof Role)[keyof typeof Role];
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/guards/index.html b/apps/api/coverage/lcov-report/src/auth/guards/index.html new file mode 100644 index 0000000..db14af1 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/guards/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for src/auth/guards + + + + + + + + + +
+
+

All files src/auth/guards

+
+ +
+ 89.13% + Statements + 41/46 +
+ + +
+ 95.45% + Branches + 21/22 +
+ + +
+ 75% + Functions + 6/8 +
+ + +
+ 92.3% + Lines + 36/39 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
index.ts +
+
0%0/4100%0/00%0/20%0/2
jwt-auth.guard.ts +
+
95.65%22/2391.66%11/12100%3/395.23%20/21
roles.guard.ts +
+
100%19/19100%10/10100%3/3100%16/16
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/guards/index.ts.html b/apps/api/coverage/lcov-report/src/auth/guards/index.ts.html new file mode 100644 index 0000000..2016a86 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/guards/index.ts.html @@ -0,0 +1,91 @@ + + + + + + Code coverage report for src/auth/guards/index.ts + + + + + + + + + +
+
+

All files / src/auth/guards index.ts

+
+ +
+ 0% + Statements + 0/4 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 0% + Lines + 0/2 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3  +  + 
export { JwtAuthGuard } from './jwt-auth.guard';
+export { RolesGuard } from './roles.guard';
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/guards/jwt-auth.guard.ts.html b/apps/api/coverage/lcov-report/src/auth/guards/jwt-auth.guard.ts.html new file mode 100644 index 0000000..a691029 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/guards/jwt-auth.guard.ts.html @@ -0,0 +1,244 @@ + + + + + + Code coverage report for src/auth/guards/jwt-auth.guard.ts + + + + + + + + + +
+
+

All files / src/auth/guards jwt-auth.guard.ts

+
+ +
+ 95.65% + Statements + 22/23 +
+ + +
+ 91.66% + Branches + 11/12 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 95.23% + Lines + 20/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +542x +  +  +  +  +2x +2x +2x +  +  +2x +23x +23x +  +  +  +  +1x +  +  +  +  +1x +1x +  +  +  +  +  +  +  +  +8x +7x +  +7x +1x +6x +1x +5x +1x +  +  +7x +  +  +  +  +  +  +1x +  +  + 
import {
+  Injectable,
+  ExecutionContext,
+  UnauthorizedException,
+} from '@nestjs/common';
+import { Reflector } from '@nestjs/core';
+import { AuthGuard } from '@nestjs/passport';
+import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
+ 
+@Injectable()
+export class JwtAuthGuard extends AuthGuard('jwt') {
+  constructor(private reflector: Reflector) {
+    super();
+  }
+ 
+  canActivate(context: ExecutionContext) {
+    // Check if route is marked as public
+    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
+      context.getHandler(),
+      context.getClass(),
+    ]);
+ 
+    Eif (isPublic) {
+      return true;
+    }
+ 
+    // Otherwise, apply JWT authentication
+    return super.canActivate(context);
+  }
+ 
+  handleRequest(err: any, user: any, info: any) {
+    // Custom error handling
+    if (err || !user) {
+      let message = 'Invalid or expired JWT access token.';
+ 
+      if (info?.name === 'TokenExpiredError') {
+        message = 'JWT access token has expired.';
+      } else if (info?.name === 'JsonWebTokenError') {
+        message = 'Invalid JWT access token format.';
+      } else if (info?.message) {
+        message = info.message;
+      }
+ 
+      throw new UnauthorizedException({
+        error: 'Unauthorized',
+        message,
+        timestamp: new Date().toISOString(),
+      });
+    }
+ 
+    return user;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/guards/roles.guard.ts.html b/apps/api/coverage/lcov-report/src/auth/guards/roles.guard.ts.html new file mode 100644 index 0000000..c631206 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/guards/roles.guard.ts.html @@ -0,0 +1,238 @@ + + + + + + Code coverage report for src/auth/guards/roles.guard.ts + + + + + + + + + +
+
+

All files / src/auth/guards roles.guard.ts

+
+ +
+ 100% + Statements + 19/19 +
+ + +
+ 100% + Branches + 10/10 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 100% + Lines + 16/16 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +522x +  +  +  +  +  +2x +2x +  +  +  +2x +25x +  +  +13x +  +  +  +  +  +13x +3x +  +  +10x +  +10x +2x +  +  +  +  +  +  +8x +  +  +11x +  +8x +6x +  +  +  +  +  +  +2x +  +  + 
import {
+  Injectable,
+  CanActivate,
+  ExecutionContext,
+  ForbiddenException,
+} from '@nestjs/common';
+import { Reflector } from '@nestjs/core';
+import { ROLES_KEY } from '../decorators/roles.decorator';
+import { JwtUser } from '../strategies/jwt.strategy';
+ 
+@Injectable()
+export class RolesGuard implements CanActivate {
+  constructor(private reflector: Reflector) {}
+ 
+  canActivate(context: ExecutionContext): boolean {
+    const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
+      context.getHandler(),
+      context.getClass(),
+    ]);
+ 
+    // If no roles are required, allow access
+    if (!requiredRoles || requiredRoles.length === 0) {
+      return true;
+    }
+ 
+    const { user } = context.switchToHttp().getRequest<{ user: JwtUser }>();
+ 
+    if (!user) {
+      throw new ForbiddenException({
+        error: 'Forbidden',
+        message: 'Access denied: User not authenticated',
+        timestamp: new Date().toISOString(),
+      });
+    }
+ 
+    const userRoles = user.roles || [];
+ 
+    // Check if user has at least one of the required roles
+    const hasRole = requiredRoles.some((role) => userRoles.includes(role));
+ 
+    if (!hasRole) {
+      throw new ForbiddenException({
+        error: 'Forbidden',
+        message: `Access denied: Required roles are [${requiredRoles.join(', ')}]`,
+        timestamp: new Date().toISOString(),
+      });
+    }
+ 
+    return true;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/index.html b/apps/api/coverage/lcov-report/src/auth/index.html new file mode 100644 index 0000000..30c381c --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/auth + + + + + + + + + +
+
+

All files src/auth

+
+ +
+ 68.75% + Statements + 11/16 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 50% + Functions + 1/2 +
+ + +
+ 69.23% + Lines + 9/13 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
auth.module.ts +
+
100%11/11100%0/0100%1/1100%9/9
index.ts +
+
0%0/5100%0/00%0/10%0/4
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/index.ts.html b/apps/api/coverage/lcov-report/src/auth/index.ts.html new file mode 100644 index 0000000..6db7b7c --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/index.ts.html @@ -0,0 +1,97 @@ + + + + + + Code coverage report for src/auth/index.ts + + + + + + + + + +
+
+

All files / src/auth index.ts

+
+ +
+ 0% + Statements + 0/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5  +  +  +  + 
export { AuthModule } from './auth.module';
+export * from './decorators';
+export * from './guards';
+export * from './strategies';
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/strategies/index.html b/apps/api/coverage/lcov-report/src/auth/strategies/index.html new file mode 100644 index 0000000..b636f7f --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/strategies/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src/auth/strategies + + + + + + + + + +
+
+

All files src/auth/strategies

+
+ +
+ 92.3% + Statements + 24/26 +
+ + +
+ 100% + Branches + 16/16 +
+ + +
+ 66.66% + Functions + 2/3 +
+ + +
+ 95.65% + Lines + 22/23 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
index.ts +
+
0%0/2100%0/00%0/10%0/1
jwt.strategy.ts +
+
100%24/24100%16/16100%2/2100%22/22
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/strategies/index.ts.html b/apps/api/coverage/lcov-report/src/auth/strategies/index.ts.html new file mode 100644 index 0000000..d114edc --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/strategies/index.ts.html @@ -0,0 +1,88 @@ + + + + + + Code coverage report for src/auth/strategies/index.ts + + + + + + + + + +
+
+

All files / src/auth/strategies index.ts

+
+ +
+ 0% + Statements + 0/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/1 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2  + 
export { JwtStrategy, JwtPayload, JwtUser } from './jwt.strategy';
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/auth/strategies/jwt.strategy.ts.html b/apps/api/coverage/lcov-report/src/auth/strategies/jwt.strategy.ts.html new file mode 100644 index 0000000..5546e0c --- /dev/null +++ b/apps/api/coverage/lcov-report/src/auth/strategies/jwt.strategy.ts.html @@ -0,0 +1,301 @@ + + + + + + Code coverage report for src/auth/strategies/jwt.strategy.ts + + + + + + + + + +
+
+

All files / src/auth/strategies jwt.strategy.ts

+
+ +
+ 100% + Statements + 24/24 +
+ + +
+ 100% + Branches + 16/16 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 22/22 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +732x +2x +2x +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +24x +26x +26x +2x +  +  +24x +  +  +  +  +  +  +  +  +  +  +  +8x +1x +  +  +7x +1x +  +  +6x +1x +  +  +5x +1x +  +  +  +4x +4x +2x +  +  +  +2x +  +  +  +  +  +  + 
import { Injectable, UnauthorizedException } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { PassportStrategy } from '@nestjs/passport';
+import { ExtractJwt, Strategy } from 'passport-jwt';
+ 
+export interface JwtPayload {
+  sub: string;
+  iss: string;
+  aud: string;
+  exp: number;
+  iat: number;
+  roles?: string[];
+  permissions?: string[];
+  [key: string]: any;
+}
+ 
+export interface JwtUser {
+  userId: string;
+  roles: string[];
+  permissions: string[];
+}
+ 
+@Injectable()
+export class JwtStrategy extends PassportStrategy(Strategy) {
+  constructor(private readonly configService: ConfigService) {
+    const secret = configService.get<string>('JWT_SECRET');
+    if (!secret) {
+      throw new Error('JWT_SECRET environment variable is required');
+    }
+ 
+    super({
+      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+      ignoreExpiration: false,
+      secretOrKey: secret,
+      issuer: configService.get<string>('JWT_ISSUER', 'gasguard-api'),
+      audience: configService.get<string>('JWT_AUDIENCE', 'gasguard-client'),
+      algorithms: ['HS256'],
+    });
+  }
+ 
+  async validate(payload: JwtPayload): Promise<JwtUser> {
+    // Validate required claims
+    if (!payload.sub) {
+      throw new UnauthorizedException('Invalid token: missing subject claim');
+    }
+ 
+    if (!payload.iss) {
+      throw new UnauthorizedException('Invalid token: missing issuer claim');
+    }
+ 
+    if (!payload.aud) {
+      throw new UnauthorizedException('Invalid token: missing audience claim');
+    }
+ 
+    if (!payload.exp) {
+      throw new UnauthorizedException('Invalid token: missing expiration claim');
+    }
+ 
+    // Check token expiration (passport-jwt also does this, but double-check)
+    const now = Math.floor(Date.now() / 1000);
+    if (payload.exp < now) {
+      throw new UnauthorizedException('Token has expired');
+    }
+ 
+    // Return user object that will be attached to request
+    return {
+      userId: payload.sub,
+      roles: payload.roles || [],
+      permissions: payload.permissions || [],
+    };
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/controllers/analysis.controller.ts.html b/apps/api/coverage/lcov-report/src/controllers/analysis.controller.ts.html new file mode 100644 index 0000000..411a68c --- /dev/null +++ b/apps/api/coverage/lcov-report/src/controllers/analysis.controller.ts.html @@ -0,0 +1,952 @@ + + + + + + Code coverage report for src/controllers/analysis.controller.ts + + + + + + + + + +
+
+

All files / src/controllers analysis.controller.ts

+
+ +
+ 0% + Statements + 0/85 +
+ + +
+ 0% + Branches + 0/67 +
+ + +
+ 0% + Functions + 0/12 +
+ + +
+ 0% + Lines + 0/82 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Request, Response } from 'express';
+import { Queue } from 'bullmq';
+import { 
+  CodebaseSubmissionRequest, 
+  AnalysisResponse, 
+  AnalysisStatus, 
+  AnalysisResult,
+  ApiErrorResponse 
+} from '../schemas/analysis.schema';
+import { AnalysisValidator } from '../validation/analysis.validator';
+ 
+export class AnalysisController {
+  constructor(private queue: Queue) {}
+ 
+  async submitCodebase(req: Request, res: Response): Promise<void> {
+    try {
+      const payload = req.body as CodebaseSubmissionRequest;
+      
+      // Determine if this is a large job
+      const isLarge = this.isLargeSubmission(payload);
+      
+      // Add job to queue with appropriate priority and options
+      const job = await this.queue.add('analyze-codebase', {
+        payload,
+        isLarge,
+        submittedAt: new Date().toISOString(),
+        requestId: req.headers['x-request-id'] as string || 'unknown'
+      }, {
+        removeOnComplete: false,
+        removeOnFail: false,
+        attempts: isLarge ? 3 : 5,
+        backoff: {
+          type: 'exponential',
+          delay: 2000
+        },
+        priority: isLarge ? 10 : 100
+      });
+ 
+      const response: AnalysisResponse = {
+        jobId: job.id as string,
+        status: 'queued',
+        submittedAt: new Date().toISOString(),
+        estimatedDuration: this.estimateAnalysisDuration(payload),
+        statusUrl: `/analysis/${job.id}/status`,
+        resultUrl: `/analysis/${job.id}/result`
+      };
+ 
+      res.status(202).json(response);
+    } catch (error) {
+      console.error('Error submitting codebase:', error);
+      this.sendErrorResponse(res, 'SUBMISSION_ERROR', 'Failed to submit codebase for analysis', error);
+    }
+  }
+ 
+  async getAnalysisStatus(req: Request, res: Response): Promise<void> {
+    try {
+      const jobId = req.params.id;
+      
+      if (!jobId) {
+        res.status(400).json({
+          error: {
+            code: 'MISSING_JOB_ID',
+            message: 'Job ID is required',
+            timestamp: new Date().toISOString(),
+            requestId: req.headers['x-request-id'] as string || 'unknown'
+          }
+        });
+        return;
+      }
+ 
+      const job = await this.queue.getJob(jobId);
+      
+      if (!job) {
+        res.status(404).json({
+          error: {
+            code: 'JOB_NOT_FOUND',
+            message: `Analysis job with ID ${jobId} not found`,
+            timestamp: new Date().toISOString(),
+            requestId: req.headers['x-request-id'] as string || 'unknown'
+          }
+        });
+        return;
+      }
+ 
+      const state = await job.getState();
+      const progress = job.progress || 0;
+      
+      const status: AnalysisStatus = {
+        jobId,
+        status: this.mapJobState(state),
+        progress: typeof progress === 'number' ? progress : 0,
+        currentStep: job.data?.currentStep,
+        startedAt: job.data?.startedAt,
+        completedAt: job.finishedOn ? new Date(job.finishedOn).toISOString() : undefined,
+        error: job.failedReason ? {
+          code: 'JOB_FAILED',
+          message: job.failedReason,
+          timestamp: new Date().toISOString()
+        } : undefined
+      };
+ 
+      res.json(status);
+    } catch (error) {
+      console.error('Error getting analysis status:', error);
+      this.sendErrorResponse(res, 'STATUS_ERROR', 'Failed to retrieve analysis status', error);
+    }
+  }
+ 
+  async getAnalysisResult(req: Request, res: Response): Promise<void> {
+    try {
+      const jobId = req.params.id;
+      
+      if (!jobId) {
+        res.status(400).json({
+          error: {
+            code: 'MISSING_JOB_ID',
+            message: 'Job ID is required',
+            timestamp: new Date().toISOString(),
+            requestId: req.headers['x-request-id'] as string || 'unknown'
+          }
+        });
+        return;
+      }
+ 
+      const job = await this.queue.getJob(jobId);
+      
+      if (!job) {
+        res.status(404).json({
+          error: {
+            code: 'JOB_NOT_FOUND',
+            message: `Analysis job with ID ${jobId} not found`,
+            timestamp: new Date().toISOString(),
+            requestId: req.headers['x-request-id'] as string || 'unknown'
+          }
+        });
+        return;
+      }
+ 
+      const state = await job.getState();
+      
+      if (state !== 'completed') {
+        const status: AnalysisStatus = {
+          jobId,
+          status: this.mapJobState(state),
+          progress: typeof job.progress === 'number' ? job.progress : 0,
+          startedAt: job.data?.startedAt,
+          completedAt: job.finishedOn ? new Date(job.finishedOn).toISOString() : undefined
+        };
+        
+        res.status(202).json(status);
+        return;
+      }
+ 
+      const result = job.returnvalue as AnalysisResult;
+      
+      if (!result) {
+        res.status(500).json({
+          error: {
+            code: 'NO_RESULT',
+            message: 'Analysis completed but no result available',
+            timestamp: new Date().toISOString(),
+            requestId: req.headers['x-request-id'] as string || 'unknown'
+          }
+        });
+        return;
+      }
+ 
+      res.json({ result });
+    } catch (error) {
+      console.error('Error getting analysis result:', error);
+      this.sendErrorResponse(res, 'RESULT_ERROR', 'Failed to retrieve analysis result', error);
+    }
+  }
+ 
+  async cancelAnalysis(req: Request, res: Response): Promise<void> {
+    try {
+      const jobId = req.params.id;
+      
+      if (!jobId) {
+        res.status(400).json({
+          error: {
+            code: 'MISSING_JOB_ID',
+            message: 'Job ID is required',
+            timestamp: new Date().toISOString(),
+            requestId: req.headers['x-request-id'] as string || 'unknown'
+          }
+        });
+        return;
+      }
+ 
+      const job = await this.queue.getJob(jobId);
+      
+      if (!job) {
+        res.status(404).json({
+          error: {
+            code: 'JOB_NOT_FOUND',
+            message: `Analysis job with ID ${jobId} not found`,
+            timestamp: new Date().toISOString(),
+            requestId: req.headers['x-request-id'] as string || 'unknown'
+          }
+        });
+        return;
+      }
+ 
+      const state = await job.getState();
+      
+      if (state === 'completed') {
+        res.status(400).json({
+          error: {
+            code: 'ALREADY_COMPLETED',
+            message: 'Cannot cancel a completed analysis',
+            timestamp: new Date().toISOString(),
+            requestId: req.headers['x-request-id'] as string || 'unknown'
+          }
+        });
+        return;
+      }
+ 
+      await job.remove();
+      
+      res.json({
+        message: 'Analysis cancelled successfully',
+        jobId,
+        cancelledAt: new Date().toISOString()
+      });
+    } catch (error) {
+      console.error('Error cancelling analysis:', error);
+      this.sendErrorResponse(res, 'CANCEL_ERROR', 'Failed to cancel analysis', error);
+    }
+  }
+ 
+  private isLargeSubmission(payload: CodebaseSubmissionRequest): boolean {
+    const totalSize = payload.files.reduce((sum, file) => sum + (file.size || 0), 0);
+    const fileCount = payload.files.length;
+    
+    return totalSize > 5 * 1024 * 1024 || fileCount > 50 || JSON.stringify(payload).length > 200_000;
+  }
+ 
+  private estimateAnalysisDuration(payload: CodebaseSubmissionRequest): number {
+    const fileCount = payload.files.length;
+    const rustFileCount = payload.files.filter(f => f.language === 'rust').length;
+    const totalLines = payload.files.reduce((sum, file) => sum + (file.content.split('\n').length), 0);
+    
+    // Base time: 30 seconds
+    let estimatedSeconds = 30;
+    
+    // Add time per file
+    estimatedSeconds += fileCount * 2;
+    
+    // Add extra time for Rust files (more complex analysis)
+    estimatedSeconds += rustFileCount * 5;
+    
+    // Add time per 1000 lines of code
+    estimatedSeconds += Math.ceil(totalLines / 1000) * 10;
+    
+    // Cap at 10 minutes
+    return Math.min(estimatedSeconds, 600);
+  }
+ 
+  private mapJobState(state: string): 'queued' | 'processing' | 'completed' | 'failed' {
+    switch (state) {
+      case 'waiting':
+      case 'waiting-children':
+        return 'queued';
+      case 'active':
+        return 'processing';
+      case 'completed':
+        return 'completed';
+      case 'failed':
+        return 'failed';
+      default:
+        return 'queued';
+    }
+  }
+ 
+  private sendErrorResponse(res: Response, code: string, message: string, error?: any): void {
+    const response: ApiErrorResponse = {
+      error: {
+        code,
+        message,
+        details: error?.message || error,
+        timestamp: new Date().toISOString(),
+        requestId: res.locals.requestId || 'unknown'
+      }
+    };
+    
+    res.status(500).json(response);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/controllers/cross-chain-gas.controller.ts.html b/apps/api/coverage/lcov-report/src/controllers/cross-chain-gas.controller.ts.html new file mode 100644 index 0000000..5d6369a --- /dev/null +++ b/apps/api/coverage/lcov-report/src/controllers/cross-chain-gas.controller.ts.html @@ -0,0 +1,433 @@ + + + + + + Code coverage report for src/controllers/cross-chain-gas.controller.ts + + + + + + + + + +
+
+

All files / src/controllers cross-chain-gas.controller.ts

+
+ +
+ 0% + Statements + 0/22 +
+ + +
+ 0% + Branches + 0/6 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 0% + Lines + 0/20 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Controller, Get, Query, BadRequestException, UseGuards } from '@nestjs/common';
+import { ApiTags, ApiOperation, ApiResponse, ApiQuery, ApiBearerAuth } from '@nestjs/swagger';
+import { CrossChainGasService } from '../services/cross-chain-gas.service';
+import { 
+  CrossChainGasRequest, 
+  CrossChainGasResponse,
+  SupportedChain 
+} from '../schemas/cross-chain-gas.schema';
+import { Public, Roles, Role, JwtAuthGuard, RolesGuard } from '../auth';
+ 
+@ApiTags('Cross-Chain Gas Comparison')
+@ApiBearerAuth()
+@Controller()
+export class CrossChainGasController {
+  constructor(private readonly crossChainGasService: CrossChainGasService) {}
+ 
+  @Get('v1/analytics/cross-chain-gas')
+  @ApiOperation({ 
+    summary: 'Compare gas costs across supported chains',
+    description: 'Get real-time gas cost comparison across all supported chains, normalized to USD and ranked by efficiency'
+  })
+  @ApiQuery({ 
+    name: 'txType', 
+    enum: ['transfer', 'contract-call', 'swap'],
+    required: true,
+    description: 'Type of transaction to compare costs for'
+  })
+  @ApiResponse({ 
+    status: 200, 
+    description: 'Cross-chain gas comparison retrieved successfully'
+  })
+  @ApiResponse({ 
+    status: 400, 
+    description: 'Invalid transaction type provided'
+  })
+  async getCrossChainGasComparison(
+    @Query('txType') txType: string
+  ): Promise<CrossChainGasResponse> {
+    if (!['transfer', 'contract-call', 'swap'].includes(txType)) {
+      throw new BadRequestException('Invalid transaction type. Must be one of: transfer, contract-call, swap');
+    }
+ 
+    const request: CrossChainGasRequest = {
+      txType: txType as 'transfer' | 'contract-call' | 'swap'
+    };
+ 
+    return this.crossChainGasService.getCrossChainGasComparison(request);
+  }
+ 
+  @Get('v1/analytics/supported-chains')
+  @ApiOperation({ 
+    summary: 'Get list of supported chains',
+    description: 'Retrieve all supported chains with their metadata and configuration'
+  })
+  @ApiResponse({ 
+    status: 200, 
+    description: 'Supported chains retrieved successfully'
+  })
+  async getSupportedChains(): Promise<SupportedChain[]> {
+    return this.crossChainGasService.getSupportedChains();
+  }
+ 
+  @Roles(Role.ADMIN)
+  @Get('v1/analytics/cross-chain-gas/refresh')
+  @ApiOperation({ 
+    summary: 'Refresh gas price data',
+    description: 'Force refresh of gas price data and native token prices (admin endpoint). Requires admin role.'
+  })
+  @ApiResponse({ 
+    status: 200, 
+    description: 'Gas price data refreshed successfully'
+  })
+  @ApiResponse({ 
+    status: 403, 
+    description: 'Forbidden - Requires admin role'
+  })
+  async refreshGasData(): Promise<{ message: string; timestamp: number }> {
+    await this.crossChainGasService.updateNativeTokenPrices();
+    return {
+      message: 'Gas price data refreshed successfully',
+      timestamp: Date.now()
+    };
+  }
+ 
+  @Get('v1/analytics/cross-chain-gas/history')
+  @ApiOperation({ 
+    summary: 'Get historical gas price data',
+    description: 'Retrieve historical gas price data for a specific chain'
+  })
+  @ApiQuery({ 
+    name: 'chainId', 
+    type: 'number',
+    required: true,
+    description: 'Chain ID to fetch historical data for'
+  })
+  @ApiQuery({ 
+    name: 'hours', 
+    type: 'number',
+    required: false,
+    description: 'Number of hours of historical data to fetch (default: 24)'
+  })
+  @ApiResponse({ 
+    status: 200, 
+    description: 'Historical gas data retrieved successfully'
+  })
+  async getGasHistory(
+    @Query('chainId') chainId: number,
+    @Query('hours') hours?: number
+  ): Promise<any[]> {
+    if (!chainId || isNaN(chainId)) {
+      throw new BadRequestException('Valid chain ID is required');
+    }
+ 
+    return this.crossChainGasService.getChainGasMetricsHistory(chainId, hours);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/controllers/failed-transaction.controller.ts.html b/apps/api/coverage/lcov-report/src/controllers/failed-transaction.controller.ts.html new file mode 100644 index 0000000..8a92243 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/controllers/failed-transaction.controller.ts.html @@ -0,0 +1,850 @@ + + + + + + Code coverage report for src/controllers/failed-transaction.controller.ts + + + + + + + + + +
+
+

All files / src/controllers failed-transaction.controller.ts

+
+ +
+ 0% + Statements + 0/56 +
+ + +
+ 0% + Branches + 0/23 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 0% + Lines + 0/53 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { 
+  Controller, 
+  Get, 
+  Post, 
+  Body, 
+  Param, 
+  Query, 
+  HttpException,
+  HttpStatus,
+  Logger,
+  UseGuards,
+} from '@nestjs/common';
+import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery, ApiBearerAuth } from '@nestjs/swagger';
+import { 
+  TransactionAnalysisRequest,
+  TransactionAnalysisResponse,
+  FailedTransaction,
+  FailedTransactionEvent
+} from '../schemas/failed-transaction.schema';
+import { TransactionAnalysisService } from '../services/transaction-analysis.service';
+import { Public, Roles, Role, JwtAuthGuard, RolesGuard } from '../auth';
+ 
+@ApiTags('failed-transactions')
+@ApiBearerAuth()
+@Controller({ path: 'failed-transactions', version: '1' })
+export class FailedTransactionController {
+  private readonly logger = new Logger(FailedTransactionController.name);
+ 
+  constructor(private readonly transactionAnalysisService: TransactionAnalysisService) {}
+ 
+  @Post('analyze')
+  @ApiOperation({ 
+    summary: 'Analyze failed transactions for a wallet',
+    description: 'Provides comprehensive analysis of failed transactions including cost metrics, failure categories, and mitigation recommendations'
+  })
+  @ApiResponse({ 
+    status: 200, 
+    description: 'Analysis completed successfully'
+  })
+  @ApiResponse({ 
+    status: 400, 
+    description: 'Invalid request parameters' 
+  })
+  async analyzeWalletFailures(
+    @Body() request: TransactionAnalysisRequest
+  ): Promise<TransactionAnalysisResponse> {
+    try {
+      this.logger.log(`Analyzing failed transactions for wallet: ${request.wallet}`);
+      
+      if (!request.wallet || !request.wallet.match(/^0x[a-fA-F0-9]{40}$/)) {
+        throw new HttpException('Invalid wallet address', HttpStatus.BAD_REQUEST);
+      }
+ 
+      return await this.transactionAnalysisService.analyzeWalletFailures(request);
+    } catch (error) {
+      this.logger.error(`Error analyzing wallet failures: ${error.message}`);
+      throw new HttpException(
+        `Failed to analyze wallet failures: ${error.message}`,
+        HttpStatus.INTERNAL_SERVER_ERROR
+      );
+    }
+  }
+ 
+  @Get(':wallet/summary')
+  @ApiOperation({ 
+    summary: 'Get wallet failure summary',
+    description: 'Returns a quick summary of failed transaction metrics for a wallet'
+  })
+  @ApiParam({ 
+    name: 'wallet', 
+    description: 'Wallet address to analyze' 
+  })
+  @ApiQuery({ 
+    name: 'chainIds', 
+    required: false, 
+    description: 'Comma-separated list of chain IDs to filter by',
+    example: '1,137,42161'
+  })
+  @ApiResponse({ 
+    status: 200, 
+    description: 'Summary retrieved successfully' 
+  })
+  async getWalletSummary(
+    @Param('wallet') wallet: string,
+    @Query('chainIds') chainIds?: string
+  ): Promise<any> {
+    try {
+      if (!wallet.match(/^0x[a-fA-F0-9]{40}$/)) {
+        throw new HttpException('Invalid wallet address', HttpStatus.BAD_REQUEST);
+      }
+ 
+      const chainIdArray = chainIds 
+        ? chainIds.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id))
+        : undefined;
+ 
+      return await this.transactionAnalysisService.getWalletSummary(wallet, chainIdArray);
+    } catch (error) {
+      this.logger.error(`Error getting wallet summary: ${error.message}`);
+      throw new HttpException(
+        `Failed to get wallet summary: ${error.message}`,
+        HttpStatus.INTERNAL_SERVER_ERROR
+      );
+    }
+  }
+ 
+  @Get(':wallet/mitigation')
+  @ApiOperation({ 
+    summary: 'Get immediate mitigation for recent failure',
+    description: 'Provides immediate action steps to resolve the most recent failed transaction'
+  })
+  @ApiParam({ 
+    name: 'wallet', 
+    description: 'Wallet address' 
+  })
+  @ApiQuery({ 
+    name: 'txHash', 
+    required: false, 
+    description: 'Specific transaction hash to get mitigation for' 
+  })
+  @ApiResponse({ 
+    status: 200, 
+    description: 'Mitigation recommendations retrieved successfully' 
+  })
+  async getImmediateMitigation(
+    @Param('wallet') wallet: string,
+    @Query('txHash') txHash?: string
+  ): Promise<any> {
+    try {
+      if (!wallet.match(/^0x[a-fA-F0-9]{40}$/)) {
+        throw new HttpException('Invalid wallet address', HttpStatus.BAD_REQUEST);
+      }
+ 
+      return await this.transactionAnalysisService.getImmediateMitigation(wallet, txHash);
+    } catch (error) {
+      this.logger.error(`Error getting immediate mitigation: ${error.message}`);
+      throw new HttpException(
+        `Failed to get mitigation: ${error.message}`,
+        HttpStatus.INTERNAL_SERVER_ERROR
+      );
+    }
+  }
+ 
+  @Post('track')
+  @ApiOperation({ 
+    summary: 'Track a failed transaction',
+    description: 'Records a failed transaction for analysis and future recommendations'
+  })
+  @ApiResponse({ 
+    status: 201, 
+    description: 'Failed transaction tracked successfully' 
+  })
+  @ApiResponse({ 
+    status: 400, 
+    description: 'Invalid transaction data' 
+  })
+  async trackFailedTransaction(
+    @Body() transactionData: Partial<FailedTransaction>
+  ): Promise<FailedTransaction> {
+    try {
+      // Validate required fields
+      if (!transactionData.hash || !transactionData.wallet || !transactionData.chainId) {
+        throw new HttpException(
+          'Missing required fields: hash, wallet, chainId',
+          HttpStatus.BAD_REQUEST
+        );
+      }
+ 
+      if (!transactionData.wallet.match(/^0x[a-fA-F0-9]{40}$/)) {
+        throw new HttpException('Invalid wallet address', HttpStatus.BAD_REQUEST);
+      }
+ 
+      if (!transactionData.hash.match(/^0x[a-fA-F0-9]{64}$/)) {
+        throw new HttpException('Invalid transaction hash', HttpStatus.BAD_REQUEST);
+      }
+ 
+      this.logger.log(`Tracking failed transaction: ${transactionData.hash} for wallet: ${transactionData.wallet}`);
+      
+      return await this.transactionAnalysisService.processFailedTransaction(transactionData);
+    } catch (error) {
+      this.logger.error(`Error tracking failed transaction: ${error.message}`);
+      throw new HttpException(
+        `Failed to track transaction: ${error.message}`,
+        HttpStatus.INTERNAL_SERVER_ERROR
+      );
+    }
+  }
+ 
+  @Get('chains/:chainId/stats')
+  @ApiOperation({ 
+    summary: 'Get chain-specific failure statistics',
+    description: 'Returns failure statistics for a specific blockchain network' 
+  })
+  @ApiParam({ 
+    name: 'chainId', 
+    description: 'Blockchain network ID' 
+  })
+  @ApiQuery({ 
+    name: 'timeframe', 
+    required: false, 
+    description: 'Timeframe for analysis (7d, 30d, 90d)',
+    example: '30d'
+  })
+  @ApiResponse({ 
+    status: 200, 
+    description: 'Chain statistics retrieved successfully' 
+  })
+  async getChainStats(
+    @Param('chainId') chainId: string,
+    @Query('timeframe') timeframe?: string
+  ): Promise<any> {
+    try {
+      const chainIdNum = parseInt(chainId);
+      if (isNaN(chainIdNum)) {
+        throw new HttpException('Invalid chain ID', HttpStatus.BAD_REQUEST);
+      }
+ 
+      // This would typically query a database for chain-wide statistics
+      // For now, return a mock response
+      return {
+        chainId: chainIdNum,
+        timeframe: timeframe || '30d',
+        totalFailures: Math.floor(Math.random() * 1000),
+        totalGasWasted: (Math.random() * 10).toFixed(4) + ' ETH',
+        topFailureCategory: 'underpriced_gas',
+        averageGasWastePerFailure: (Math.random() * 0.1).toFixed(6) + ' ETH',
+        processedAt: new Date().toISOString()
+      };
+    } catch (error) {
+      this.logger.error(`Error getting chain stats: ${error.message}`);
+      throw new HttpException(
+        `Failed to get chain stats: ${error.message}`,
+        HttpStatus.INTERNAL_SERVER_ERROR
+      );
+    }
+  }
+ 
+  @Public()
+  @Get('health')
+  @ApiOperation({ 
+    summary: 'Health check endpoint',
+    description: 'Returns the health status of the failed transaction analyzer service' 
+  })
+  @ApiResponse({ 
+    status: 200, 
+    description: 'Service is healthy' 
+  })
+  async getHealth(): Promise<any> {
+    return {
+      status: 'healthy',
+      service: 'failed-transaction-analyzer',
+      timestamp: new Date().toISOString(),
+      version: '1.0.0'
+    };
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/controllers/index.html b/apps/api/coverage/lcov-report/src/controllers/index.html new file mode 100644 index 0000000..5d2d2c2 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/controllers/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for src/controllers + + + + + + + + + +
+
+

All files src/controllers

+
+ +
+ 0% + Statements + 0/163 +
+ + +
+ 0% + Branches + 0/96 +
+ + +
+ 0% + Functions + 0/26 +
+ + +
+ 0% + Lines + 0/155 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
analysis.controller.ts +
+
0%0/850%0/670%0/120%0/82
cross-chain-gas.controller.ts +
+
0%0/220%0/60%0/50%0/20
failed-transaction.controller.ts +
+
0%0/560%0/230%0/90%0/53
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/example/example.controller.ts.html b/apps/api/coverage/lcov-report/src/example/example.controller.ts.html new file mode 100644 index 0000000..b27b8b2 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/example/example.controller.ts.html @@ -0,0 +1,148 @@ + + + + + + Code coverage report for src/example/example.controller.ts + + + + + + + + + +
+
+

All files / src/example example.controller.ts

+
+ +
+ 0% + Statements + 0/6 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Controller, Get } from '@nestjs/common';
+ 
+/**
+ * Example controller demonstrating API versioning
+ * 
+ * This controller is accessible at:
+ * - GET /v1/example
+ * 
+ * Unversioned requests (e.g., /example) will return 404
+ */
+@Controller({ path: 'example', version: '1' })
+export class ExampleController {
+  @Get()
+  getExample() {
+    return {
+      message: 'This is a versioned endpoint',
+      version: '1',
+      path: '/v1/example',
+    };
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/example/index.html b/apps/api/coverage/lcov-report/src/example/index.html new file mode 100644 index 0000000..d805743 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/example/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/example + + + + + + + + + +
+
+

All files src/example

+
+ +
+ 0% + Statements + 0/6 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
example.controller.ts +
+
0%0/6100%0/00%0/10%0/4
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/index.html b/apps/api/coverage/lcov-report/src/index.html new file mode 100644 index 0000000..fbfd472 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/index.html @@ -0,0 +1,191 @@ + + + + + + Code coverage report for src + + + + + + + + + +
+
+

All files src

+
+ +
+ 0% + Statements + 0/101 +
+ + +
+ 0% + Branches + 0/22 +
+ + +
+ 0% + Functions + 0/14 +
+ + +
+ 0% + Lines + 0/96 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
app.controller.ts +
+
0%0/9100%0/00%0/20%0/7
app.module.ts +
+
0%0/16100%0/0100%0/00%0/14
index.ts +
+
0%0/40%0/20%0/10%0/4
main.ts +
+
0%0/100%0/20%0/10%0/10
scan.ts +
+
0%0/120%0/20%0/30%0/11
server.ts +
+
0%0/500%0/160%0/70%0/50
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/index.ts.html b/apps/api/coverage/lcov-report/src/index.ts.html new file mode 100644 index 0000000..822e50b --- /dev/null +++ b/apps/api/coverage/lcov-report/src/index.ts.html @@ -0,0 +1,100 @@ + + + + + + Code coverage report for src/index.ts + + + + + + + + + +
+
+

All files / src index.ts

+
+ +
+ 0% + Statements + 0/4 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6  +  +  +  +  + 
import { createServer } from './server.js'
+ 
+const port = Number(process.env.PORT || 3000)
+const app = createServer({} as any) // Pass empty queue for now
+ 
+app.listen(port, () => {})
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/main.ts.html b/apps/api/coverage/lcov-report/src/main.ts.html new file mode 100644 index 0000000..f4e25fe --- /dev/null +++ b/apps/api/coverage/lcov-report/src/main.ts.html @@ -0,0 +1,148 @@ + + + + + + Code coverage report for src/main.ts + + + + + + + + + +
+
+

All files / src main.ts

+
+ +
+ 0% + Statements + 0/10 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { NestFactory } from '@nestjs/core';
+import { VersioningType } from '@nestjs/common';
+import { AppModule } from './app.module';
+ 
+async function bootstrap() {
+  const app = await NestFactory.create(AppModule);
+ 
+  // Enable API versioning with URI-based strategy
+  // All routes must include /v1/ prefix, unversioned requests return 404
+  app.enableVersioning({
+    type: VersioningType.URI,
+    // No defaultVersion - unversioned requests will return 404
+  });
+ 
+  const port = process.env.PORT || 3000;
+  await app.listen(port);
+  console.log(`🚀 GasGuard API is running on: http://localhost:${port}`);
+  console.log(`API versioning enabled - all endpoints require /v1/ prefix`);
+}
+ 
+bootstrap();
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/middleware/error.middleware.ts.html b/apps/api/coverage/lcov-report/src/middleware/error.middleware.ts.html new file mode 100644 index 0000000..49ffcd1 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/middleware/error.middleware.ts.html @@ -0,0 +1,310 @@ + + + + + + Code coverage report for src/middleware/error.middleware.ts + + + + + + + + + +
+
+

All files / src/middleware error.middleware.ts

+
+ +
+ 0% + Statements + 0/15 +
+ + +
+ 0% + Branches + 0/16 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 0% + Lines + 0/15 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Request, Response, NextFunction } from 'express';
+import { ApiErrorResponse } from '../schemas/analysis.schema';
+ 
+export interface CustomError extends Error {
+  statusCode?: number;
+  code?: string;
+  details?: any;
+}
+ 
+export function errorHandler(
+  error: CustomError,
+  req: Request,
+  res: Response,
+  next: NextFunction
+): void {
+  console.error('Error occurred:', {
+    message: error.message,
+    stack: error.stack,
+    url: req.url,
+    method: req.method,
+    requestId: req.headers['x-request-id'],
+    timestamp: new Date().toISOString()
+  });
+ 
+  const statusCode = error.statusCode || 500;
+  const errorCode = error.code || 'INTERNAL_SERVER_ERROR';
+  
+  const response: ApiErrorResponse = {
+    error: {
+      code: errorCode,
+      message: error.message || 'An unexpected error occurred',
+      details: error.details,
+      timestamp: new Date().toISOString(),
+      requestId: req.headers['x-request-id'] as string || 'unknown'
+    }
+  };
+ 
+  // Don't expose stack trace in production
+  if (process.env.NODE_ENV !== 'production') {
+    response.error.details = {
+      ...response.error.details,
+      stack: error.stack
+    };
+  }
+ 
+  res.status(statusCode).json(response);
+}
+ 
+export function notFoundHandler(req: Request, res: Response): void {
+  res.status(404).json({
+    error: {
+      code: 'NOT_FOUND',
+      message: `Route ${req.method} ${req.path} not found`,
+      timestamp: new Date().toISOString(),
+      requestId: req.headers['x-request-id'] as string || 'unknown'
+    }
+  });
+}
+ 
+export function requestIdHandler(req: Request, res: Response, next: NextFunction): void {
+  req.headers['x-request-id'] = req.headers['x-request-id'] || 
+    `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
+  next();
+}
+ 
+export function rateLimitHandler(req: Request, res: Response): void {
+  res.status(429).json({
+    error: {
+      code: 'RATE_LIMIT_EXCEEDED',
+      message: 'Too many requests, please try again later',
+      timestamp: new Date().toISOString(),
+      requestId: req.headers['x-request-id'] as string || 'unknown'
+    }
+  });
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/middleware/index.html b/apps/api/coverage/lcov-report/src/middleware/index.html new file mode 100644 index 0000000..4060678 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/middleware/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/middleware + + + + + + + + + +
+
+

All files src/middleware

+
+ +
+ 0% + Statements + 0/15 +
+ + +
+ 0% + Branches + 0/16 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 0% + Lines + 0/15 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
error.middleware.ts +
+
0%0/150%0/160%0/40%0/15
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/routes/analysis.routes.ts.html b/apps/api/coverage/lcov-report/src/routes/analysis.routes.ts.html new file mode 100644 index 0000000..e174d73 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/routes/analysis.routes.ts.html @@ -0,0 +1,178 @@ + + + + + + Code coverage report for src/routes/analysis.routes.ts + + + + + + + + + +
+
+

All files / src/routes analysis.routes.ts

+
+ +
+ 0% + Statements + 0/15 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 0% + Lines + 0/15 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Router } from 'express';
+import { AnalysisController } from '../controllers/analysis.controller';
+import { AnalysisValidator } from '../validation/analysis.validator';
+ 
+export function createAnalysisRoutes(queue: any): Router {
+  const router = Router();
+  const controller = new AnalysisController(queue);
+ 
+  // Submit codebase for analysis
+  router.post('/analysis', 
+    AnalysisValidator.validateSubmission,
+    (req, res) => controller.submitCodebase(req, res)
+  );
+ 
+  // Get analysis status
+  router.get('/analysis/:id/status', 
+    (req, res) => controller.getAnalysisStatus(req, res)
+  );
+ 
+  // Get analysis result
+  router.get('/analysis/:id/result', 
+    (req, res) => controller.getAnalysisResult(req, res)
+  );
+ 
+  // Cancel analysis
+  router.delete('/analysis/:id', 
+    (req, res) => controller.cancelAnalysis(req, res)
+  );
+ 
+  return router;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/routes/index.html b/apps/api/coverage/lcov-report/src/routes/index.html new file mode 100644 index 0000000..88f09da --- /dev/null +++ b/apps/api/coverage/lcov-report/src/routes/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/routes + + + + + + + + + +
+
+

All files src/routes

+
+ +
+ 0% + Statements + 0/15 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 0% + Lines + 0/15 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
analysis.routes.ts +
+
0%0/15100%0/00%0/50%0/15
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/scan.ts.html b/apps/api/coverage/lcov-report/src/scan.ts.html new file mode 100644 index 0000000..ab67cdc --- /dev/null +++ b/apps/api/coverage/lcov-report/src/scan.ts.html @@ -0,0 +1,133 @@ + + + + + + Code coverage report for src/scan.ts + + + + + + + + + +
+
+

All files / src scan.ts

+
+ +
+ 0% + Statements + 0/12 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 0% + Lines + 0/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
type ProgressCb = (p: number) => Promise<void> | void
+ 
+export async function performScan(input: any, onProgress: ProgressCb) {
+  await onProgress(25)
+  await sleep(500)
+  await onProgress(50)
+  await sleep(500)
+  await onProgress(75)
+  await sleep(500)
+  const result = { summary: 'ok', issues: [], inputSize: JSON.stringify(input || {}).length }
+  await onProgress(100)
+  return result
+}
+ 
+function sleep(ms: number) {
+  return new Promise(resolve => setTimeout(resolve, ms))
+}
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/server.ts.html b/apps/api/coverage/lcov-report/src/server.ts.html new file mode 100644 index 0000000..50cebac --- /dev/null +++ b/apps/api/coverage/lcov-report/src/server.ts.html @@ -0,0 +1,376 @@ + + + + + + Code coverage report for src/server.ts + + + + + + + + + +
+
+

All files / src server.ts

+
+ +
+ 0% + Statements + 0/50 +
+ + +
+ 0% + Branches + 0/16 +
+ + +
+ 0% + Functions + 0/7 +
+ + +
+ 0% + Lines + 0/50 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import express, { Request, Response } from 'express'
+import { Queue } from 'bullmq'
+import { createAnalysisRoutes } from './routes/analysis.routes'
+import { errorHandler, notFoundHandler, requestIdHandler } from './middleware/error.middleware'
+ 
+export function createServer(queue: Queue) {
+  const app = express()
+  
+  // Middleware
+  app.use(express.json({ limit: '50mb' }))
+  app.use(requestIdHandler)
+  
+  // CORS headers
+  app.use((req, res, next) => {
+    res.header('Access-Control-Allow-Origin', '*')
+    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
+    res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Request-ID')
+    res.header('Access-Control-Max-Age', '86400')
+    
+    if (req.method === 'OPTIONS') {
+      res.status(200).end()
+      return
+    }
+    next()
+  })
+ 
+  // Health check endpoint
+  app.get('/health', (req: Request, res: Response) => {
+    res.json({
+      status: 'healthy',
+      timestamp: new Date().toISOString(),
+      version: process.env.npm_package_version || '1.0.0',
+      requestId: req.headers['x-request-id']
+    })
+  })
+ 
+  // API documentation endpoint
+  app.get('/docs', (req: Request, res: Response) => {
+    res.json({
+      title: 'GasGuard Analysis API',
+      version: '1.0.0',
+      description: 'API for submitting codebases for security and performance analysis',
+      endpoints: {
+        'POST /analysis': 'Submit codebase for analysis',
+        'GET /analysis/:id/status': 'Get analysis status',
+        'GET /analysis/:id/result': 'Get analysis results',
+        'DELETE /analysis/:id': 'Cancel analysis',
+        'GET /health': 'Health check'
+      },
+      documentation: 'https://docs.gasguard.dev'
+    })
+  })
+ 
+  // Legacy scan endpoints (for backward compatibility)
+  app.post('/scan', async (req: Request, res: Response) => {
+    const payload = req.body || {}
+    const isLarge = JSON.stringify(payload).length > 200_000 || payload?.large === true
+    const job = await queue.add('scan', { payload, isLarge }, { removeOnComplete: true, removeOnFail: true })
+    res.status(202).json({ jobId: job.id, statusUrl: `/scan/${job.id}/status`, resultUrl: `/scan/${job.id}/result` })
+  })
+ 
+  app.get('/scan/:id/status', async (req: Request, res: Response) => {
+    const id = req.params.id
+    const job = await queue.getJob(id)
+    if (!job) {
+      res.status(404).json({ error: 'not_found' })
+      return
+    }
+    const state = await job.getState()
+    const progress = job.progress || 0
+    res.json({ state, progress })
+  })
+ 
+  app.get('/scan/:id/result', async (req: Request, res: Response) => {
+    const id = req.params.id
+    const job = await queue.getJob(id)
+    if (!job) {
+      res.status(404).json({ error: 'not_found' })
+      return
+    }
+    const state = await job.getState()
+    if (state !== 'completed') {
+      res.status(202).json({ state })
+      return
+    }
+    const result = job.returnvalue
+    res.json({ result })
+  })
+ 
+  // New analysis endpoints
+  app.use('/', createAnalysisRoutes(queue))
+ 
+  // Error handling
+  app.use(notFoundHandler)
+  app.use(errorHandler)
+ 
+  return app
+}
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/services/cross-chain-gas.service.ts.html b/apps/api/coverage/lcov-report/src/services/cross-chain-gas.service.ts.html new file mode 100644 index 0000000..abcfec9 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/services/cross-chain-gas.service.ts.html @@ -0,0 +1,595 @@ + + + + + + Code coverage report for src/services/cross-chain-gas.service.ts + + + + + + + + + +
+
+

All files / src/services cross-chain-gas.service.ts

+
+ +
+ 0% + Statements + 0/42 +
+ + +
+ 0% + Branches + 0/27 +
+ + +
+ 0% + Functions + 0/16 +
+ + +
+ 0% + Lines + 0/36 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Injectable } from '@nestjs/common';
+import { 
+  ChainGasMetrics, 
+  CrossChainGasRequest, 
+  CrossChainGasResponse,
+  TransactionCost,
+  GasNormalizationResult,
+  SupportedChain 
+} from '../schemas/cross-chain-gas.schema';
+ 
+@Injectable()
+export class CrossChainGasService {
+  private readonly supportedChains: SupportedChain[] = [
+    {
+      chainId: 1,
+      chainName: 'Ethereum',
+      nativeToken: 'ETH',
+      rpcUrl: process.env.ETHEREUM_RPC_URL || 'https://eth-mainnet.alchemyapi.io/v2/demo',
+      blockTime: 12
+    },
+    {
+      chainId: 137,
+      chainName: 'Polygon',
+      nativeToken: 'MATIC',
+      rpcUrl: process.env.POLYGON_RPC_URL || 'https://polygon-mainnet.alchemyapi.io/v2/demo',
+      blockTime: 2
+    },
+    {
+      chainId: 56,
+      chainName: 'Binance Smart Chain',
+      nativeToken: 'BNB',
+      rpcUrl: process.env.BSC_RPC_URL || 'https://bsc-dataseed.binance.org',
+      blockTime: 3
+    },
+    {
+      chainId: 42161,
+      chainName: 'Arbitrum',
+      nativeToken: 'ETH',
+      rpcUrl: process.env.ARBITRUM_RPC_URL || 'https://arb1.arbitrum.io/rpc',
+      blockTime: 0.5
+    },
+    {
+      chainId: 10,
+      chainName: 'Optimism',
+      nativeToken: 'ETH',
+      rpcUrl: process.env.OPTIMISM_RPC_URL || 'https://mainnet.optimism.io',
+      blockTime: 2
+    }
+  ];
+ 
+  private readonly averageGasUsage = {
+    transfer: 21000,
+    'contract-call': 50000,
+    swap: 150000
+  };
+ 
+  private readonly nativeTokenPricesUSD = {
+    1: 2000, // ETH
+    137: 0.85, // MATIC
+    56: 300, // BNB
+    42161: 2000, // ETH (Arbitrum)
+    10: 2000 // ETH (Optimism)
+  };
+ 
+  async getCrossChainGasComparison(request: CrossChainGasRequest): Promise<CrossChainGasResponse> {
+    const chainMetrics = await Promise.all(
+      this.supportedChains.map(chain => this.getChainGasMetrics(chain))
+    );
+ 
+    const normalizedCosts = chainMetrics.map(metrics => 
+      this.normalizeGasCost(metrics, request.txType)
+    );
+ 
+    const rankedChains = this.rankChainsByCost(normalizedCosts);
+ 
+    return {
+      txType: request.txType,
+      timestamp: Date.now(),
+      chains: rankedChains
+    };
+  }
+ 
+  private async getChainGasMetrics(chain: SupportedChain): Promise<ChainGasMetrics> {
+    // Mock implementation - in production, this would fetch real-time data
+    const mockGasPrices = {
+      1: { baseFee: '20000000000', priorityFee: '2000000000' },
+      137: { baseFee: '30000000000', priorityFee: '1000000000' },
+      56: { baseFee: '5000000000', priorityFee: '1000000000' },
+      42161: { baseFee: '10000000000', priorityFee: '500000000' },
+      10: { baseFee: '15000000000', priorityFee: '1000000000' }
+    };
+ 
+    const gasPrice = (mockGasPrices as Record<number, { baseFee: string; priorityFee: string }>)[chain.chainId] || { baseFee: '20000000000', priorityFee: '2000000000' };
+ 
+    return {
+      chainId: chain.chainId,
+      chainName: chain.chainName,
+      baseFee: gasPrice.baseFee,
+      priorityFee: gasPrice.priorityFee,
+      averageGasUsed: this.averageGasUsage,
+      nativeTokenPriceUSD: (this.nativeTokenPricesUSD as Record<number, number>)[chain.chainId] || 1,
+      averageConfirmationTime: chain.blockTime
+    };
+  }
+ 
+  private normalizeGasCost(metrics: ChainGasMetrics, txType: string): GasNormalizationResult {
+    const gasUsed = metrics.averageGasUsed[txType as keyof typeof metrics.averageGasUsed];
+    const baseFee = BigInt(metrics.baseFee || '20000000000');
+    const priorityFee = BigInt(metrics.priorityFee || '2000000000');
+    const effectiveGasPrice = baseFee + priorityFee;
+    
+    const totalCostWei = gasUsed * Number(effectiveGasPrice);
+    const totalCostNative = (totalCostWei / 1e18).toFixed(6);
+    const totalCostUSD = (parseFloat(totalCostNative) * metrics.nativeTokenPriceUSD);
+ 
+    return {
+      chainId: metrics.chainId,
+      txType,
+      gasUsed,
+      effectiveGasPrice: effectiveGasPrice.toString(),
+      totalCostNative,
+      totalCostUSD
+    };
+  }
+ 
+  private rankChainsByCost(costs: GasNormalizationResult[]): TransactionCost[] {
+    const sortedCosts = costs.sort((a, b) => a.totalCostUSD - b.totalCostUSD);
+    
+    return sortedCosts.map((cost, index) => ({
+      chainId: cost.chainId,
+      chainName: this.getChainName(cost.chainId),
+      estimatedCostUSD: cost.totalCostUSD,
+      estimatedCostNative: cost.totalCostNative,
+      averageConfirmationTime: this.formatConfirmationTime(cost.chainId),
+      rank: index + 1
+    }));
+  }
+ 
+  private getChainName(chainId: number): string {
+    const chain = this.supportedChains.find(c => c.chainId === chainId);
+    return chain?.chainName || `Chain ${chainId}`;
+  }
+ 
+  private formatConfirmationTime(chainId: number): string {
+    const chain = this.supportedChains.find(c => c.chainId === chainId);
+    const seconds = chain?.blockTime || 12;
+    
+    if (seconds < 1) {
+      return `${Math.round(seconds * 1000)}ms`;
+    } else if (seconds < 60) {
+      return `${Math.round(seconds)} seconds`;
+    } else {
+      return `${Math.round(seconds / 60)} minutes`;
+    }
+  }
+ 
+  async getSupportedChains(): Promise<SupportedChain[]> {
+    return this.supportedChains;
+  }
+ 
+  async updateNativeTokenPrices(): Promise<void> {
+    // Mock implementation - in production, this would fetch real prices from price oracle
+    console.log('Updating native token prices...');
+  }
+ 
+  async getChainGasMetricsHistory(chainId: number, hours: number = 24): Promise<any[]> {
+    // Mock implementation - in production, this would fetch historical data
+    return [];
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/services/failed-transaction.service.ts.html b/apps/api/coverage/lcov-report/src/services/failed-transaction.service.ts.html new file mode 100644 index 0000000..8d6f0b6 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/services/failed-transaction.service.ts.html @@ -0,0 +1,1033 @@ + + + + + + Code coverage report for src/services/failed-transaction.service.ts + + + + + + + + + +
+
+

All files / src/services failed-transaction.service.ts

+
+ +
+ 0% + Statements + 0/118 +
+ + +
+ 0% + Branches + 0/77 +
+ + +
+ 0% + Functions + 0/21 +
+ + +
+ 0% + Lines + 0/113 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Injectable } from '@nestjs/common';
+import { 
+  FailedTransaction, 
+  FailureCategory, 
+  TransactionMetadata, 
+  RootCauseAnalysis,
+  CostMetrics 
+} from '../schemas/failed-transaction.schema';
+ 
+@Injectable()
+export class FailedTransactionService {
+  private failedTransactions: Map<string, FailedTransaction> = new Map();
+  private walletFailures: Map<string, FailedTransaction[]> = new Map();
+ 
+  /**
+   * Track a failed transaction
+   */
+  async trackFailedTransaction(transactionData: Partial<FailedTransaction>): Promise<FailedTransaction> {
+    const category = await this.classifyFailure(transactionData);
+    const rootCause = await this.analyzeRootCause(transactionData, category);
+    
+    const failedTx: FailedTransaction = {
+      id: this.generateId(),
+      hash: transactionData.hash!,
+      wallet: transactionData.wallet!,
+      chainId: transactionData.chainId!,
+      blockNumber: transactionData.blockNumber,
+      gasUsed: transactionData.gasUsed || '0',
+      gasPrice: transactionData.gasPrice || '0',
+      effectiveFee: this.calculateEffectiveFee(transactionData),
+      failureCategory: category,
+      revertReason: transactionData.revertReason || rootCause.evidence.join('; '),
+      timestamp: transactionData.timestamp || new Date().toISOString(),
+      metadata: transactionData.metadata!
+    };
+ 
+    // Store transaction
+    this.failedTransactions.set(failedTx.id, failedTx);
+    
+    // Update wallet failures
+    const walletTxs = this.walletFailures.get(failedTx.wallet) || [];
+    walletTxs.push(failedTx);
+    this.walletFailures.set(failedTx.wallet, walletTxs);
+ 
+    return failedTx;
+  }
+ 
+  /**
+   * Get failed transactions for a wallet
+   */
+  async getWalletFailures(wallet: string, chainIds?: number[]): Promise<FailedTransaction[]> {
+    const failures = this.walletFailures.get(wallet) || [];
+    
+    if (chainIds && chainIds.length > 0) {
+      return failures.filter(tx => chainIds.includes(tx.chainId));
+    }
+    
+    return failures;
+  }
+ 
+  /**
+   * Classify the failure category based on transaction data
+   */
+  private async classifyFailure(transactionData: Partial<FailedTransaction>): Promise<FailureCategory> {
+    const { revertReason, metadata, gasUsed, gasPrice } = transactionData;
+ 
+    // Check for underpriced gas
+    if (gasPrice && await this.isUnderpriced(gasPrice, transactionData.chainId!)) {
+      return 'underpriced_gas';
+    }
+ 
+    // Check for out of gas
+    if (gasUsed && metadata?.gasLimit) {
+      const utilization = (parseInt(gasUsed) / parseInt(metadata.gasLimit)) * 100;
+      if (utilization >= 99.5) {
+        return 'out_of_gas';
+      }
+    }
+ 
+    // Check revert reason patterns
+    if (revertReason) {
+      const reason = revertReason.toLowerCase();
+      
+      if (reason.includes('insufficient funds') || reason.includes('balance')) {
+        return 'insufficient_balance';
+      }
+      
+      if (reason.includes('nonce') || reason.includes('replacement')) {
+        return 'nonce_conflict';
+      }
+      
+      if (reason.includes('slippage') || reason.includes('too much requested')) {
+        return 'slippage_exceeded';
+      }
+      
+      if (reason.includes('revert') || reason.includes('require')) {
+        return 'contract_revert';
+      }
+    }
+ 
+    // Check nonce conflicts
+    if (metadata?.nonce !== undefined) {
+      const walletTxs = this.walletFailures.get(transactionData.wallet!) || [];
+      const hasSameNonce = walletTxs.some(tx => 
+        tx.metadata.nonce === metadata.nonce && 
+        tx.timestamp > new Date(Date.now() - 300000).toISOString() // Last 5 minutes
+      );
+      
+      if (hasSameNonce) {
+        return 'nonce_conflict';
+      }
+    }
+ 
+    return 'unknown';
+  }
+ 
+  /**
+   * Analyze root cause with detailed evidence
+   */
+  private async analyzeRootCause(
+    transactionData: Partial<FailedTransaction>, 
+    category: FailureCategory
+  ): Promise<RootCauseAnalysis> {
+    const evidence: string[] = [];
+    const patterns: any = {};
+ 
+    switch (category) {
+      case 'underpriced_gas':
+        evidence.push(`Gas price: ${transactionData.gasPrice} wei`);
+        evidence.push(`Chain ID: ${transactionData.chainId}`);
+        patterns.pricing = {
+          gasPrice: transactionData.gasPrice!,
+          networkGasPrice: await this.getNetworkGasPrice(transactionData.chainId!),
+          deviation: await this.calculateGasPriceDeviation(transactionData.gasPrice!, transactionData.chainId!)
+        };
+        break;
+ 
+      case 'out_of_gas':
+        evidence.push(`Gas used: ${transactionData.gasUsed}`);
+        evidence.push(`Gas limit: ${transactionData.metadata?.gasLimit}`);
+        if (transactionData.gasUsed && transactionData.metadata?.gasLimit) {
+          const utilization = (parseInt(transactionData.gasUsed) / parseInt(transactionData.metadata.gasLimit)) * 100;
+          evidence.push(`Utilization: ${utilization.toFixed(2)}%`);
+          patterns.gasUsage = {
+            used: transactionData.gasUsed,
+            limit: transactionData.metadata.gasLimit,
+            utilization
+          };
+        }
+        break;
+ 
+      case 'slippage_exceeded':
+        evidence.push(`Revert reason: ${transactionData.revertReason}`);
+        evidence.push('DEX transaction detected');
+        break;
+ 
+      case 'nonce_conflict':
+        evidence.push(`Nonce: ${transactionData.metadata?.nonce}`);
+        evidence.push('Duplicate nonce detected in recent transactions');
+        break;
+ 
+      case 'insufficient_balance':
+        evidence.push(`Value: ${transactionData.metadata?.value}`);
+        evidence.push(`Gas cost: ${transactionData.effectiveFee}`);
+        evidence.push(`Revert reason: ${transactionData.revertReason}`);
+        break;
+    }
+ 
+    return {
+      category,
+      confidence: this.calculateConfidence(category, evidence),
+      evidence,
+      patterns
+    };
+  }
+ 
+  /**
+   * Calculate cost metrics for a wallet
+   */
+  async calculateCostMetrics(wallet: string, chainIds?: number[]): Promise<CostMetrics> {
+    const failures = await this.getWalletFailures(wallet, chainIds);
+    
+    let totalGasWasted = BigInt(0);
+    const wasteByCategory: Record<FailureCategory, string> = {} as any;
+    const wasteByChain: Record<number, string> = {} as any;
+ 
+    // Initialize category counters
+    const categories: FailureCategory[] = [
+      'underpriced_gas', 'out_of_gas', 'contract_revert', 
+      'slippage_exceeded', 'nonce_conflict', 'insufficient_balance',
+      'network_error', 'unknown'
+    ];
+    
+    categories.forEach(cat => {
+      wasteByCategory[cat] = '0';
+    });
+ 
+    // Calculate totals
+    failures.forEach(tx => {
+      const waste = BigInt(tx.effectiveFee);
+      totalGasWasted += waste;
+ 
+      // Add to category total
+      wasteByCategory[tx.failureCategory] = (
+        BigInt(wasteByCategory[tx.failureCategory]) + waste
+      ).toString();
+ 
+      // Add to chain total
+      if (!wasteByChain[tx.chainId]) {
+        wasteByChain[tx.chainId] = '0';
+      }
+      wasteByChain[tx.chainId] = (
+        BigInt(wasteByChain[tx.chainId]) + waste
+      ).toString();
+    });
+ 
+    // Calculate historical trend (last 30 days)
+    const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
+    const dailyFailures = new Map<string, { waste: bigint; count: number }>();
+ 
+    failures
+      .filter(tx => new Date(tx.timestamp) >= thirtyDaysAgo)
+      .forEach(tx => {
+        const date = tx.timestamp.split('T')[0];
+        const current = dailyFailures.get(date) || { waste: BigInt(0), count: 0 };
+        current.waste += BigInt(tx.effectiveFee);
+        current.count += 1;
+        dailyFailures.set(date, current);
+      });
+ 
+    const historicalTrend = Array.from(dailyFailures.entries())
+      .map(([date, data]) => ({
+        date,
+        waste: data.waste.toString(),
+        failures: data.count
+      }))
+      .sort((a, b) => a.date.localeCompare(b.date));
+ 
+    return {
+      totalGasWasted: totalGasWasted.toString(),
+      totalGasWastedUSD: await this.convertToUSD(totalGasWasted.toString()),
+      averageWastePerFailure: failures.length > 0 
+        ? (totalGasWasted / BigInt(failures.length)).toString()
+        : '0',
+      wasteByCategory,
+      wasteByChain,
+      historicalTrend
+    };
+  }
+ 
+  /**
+   * Helper methods
+   */
+  private generateId(): string {
+    return `ft_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
+  }
+ 
+  private calculateEffectiveFee(transactionData: Partial<FailedTransaction>): string {
+    const gasUsed = BigInt(transactionData.gasUsed || '0');
+    const gasPrice = BigInt(transactionData.gasPrice || '0');
+    return (gasUsed * gasPrice).toString();
+  }
+ 
+  private async isUnderpriced(gasPrice: string, chainId: number): Promise<boolean> {
+    const networkGasPrice = await this.getNetworkGasPrice(chainId);
+    const deviation = await this.calculateGasPriceDeviation(gasPrice, chainId);
+    return deviation < -20; // More than 20% below network gas price
+  }
+ 
+  private async getNetworkGasPrice(chainId: number): Promise<string> {
+    // Mock implementation - in real scenario, this would call RPC
+    const mockPrices: Record<number, string> = {
+      1: '20000000000',    // Ethereum mainnet
+      137: '30000000000',   // Polygon
+      56: '5000000000',     // BSC
+      42161: '100000000',   // Arbitrum
+      10: '100000000'       // Optimism
+    };
+    return mockPrices[chainId] || '20000000000';
+  }
+ 
+  private async calculateGasPriceDeviation(gasPrice: string, chainId: number): Promise<number> {
+    const networkGasPrice = await this.getNetworkGasPrice(chainId);
+    const price = BigInt(gasPrice);
+    const network = BigInt(networkGasPrice);
+    return Number(((price - network) * BigInt(100)) / network);
+  }
+ 
+  private calculateConfidence(category: FailureCategory, evidence: string[]): number {
+    // Base confidence by category
+    const baseConfidence: Record<FailureCategory, number> = {
+      'underpriced_gas': 0.9,
+      'out_of_gas': 0.95,
+      'contract_revert': 0.8,
+      'slippage_exceeded': 0.85,
+      'nonce_conflict': 0.9,
+      'insufficient_balance': 0.95,
+      'network_error': 0.7,
+      'unknown': 0.3
+    };
+ 
+    // Adjust based on evidence strength
+    let confidence = baseConfidence[category];
+    if (evidence.length > 2) confidence += 0.05;
+    if (evidence.length > 4) confidence += 0.05;
+ 
+    return Math.min(confidence, 1.0);
+  }
+ 
+  private async convertToUSD(weiAmount: string): Promise<number> {
+    // Mock implementation - in real scenario, this would use price oracle
+    const ethPriceUSD = 2000; // Mock ETH price
+    const ethAmount = Number(weiAmount) / 1e18;
+    return ethAmount * ethPriceUSD;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/services/index.html b/apps/api/coverage/lcov-report/src/services/index.html new file mode 100644 index 0000000..32ced23 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/services/index.html @@ -0,0 +1,161 @@ + + + + + + Code coverage report for src/services + + + + + + + + + +
+
+

All files src/services

+
+ +
+ 0% + Statements + 0/311 +
+ + +
+ 0% + Branches + 0/166 +
+ + +
+ 0% + Functions + 0/71 +
+ + +
+ 0% + Lines + 0/294 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
cross-chain-gas.service.ts +
+
0%0/420%0/270%0/160%0/36
failed-transaction.service.ts +
+
0%0/1180%0/770%0/210%0/113
mitigation.service.ts +
+
0%0/710%0/250%0/90%0/69
transaction-analysis.service.ts +
+
0%0/800%0/370%0/250%0/76
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/services/mitigation.service.ts.html b/apps/api/coverage/lcov-report/src/services/mitigation.service.ts.html new file mode 100644 index 0000000..2363e39 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/services/mitigation.service.ts.html @@ -0,0 +1,1096 @@ + + + + + + Code coverage report for src/services/mitigation.service.ts + + + + + + + + + +
+
+

All files / src/services mitigation.service.ts

+
+ +
+ 0% + Statements + 0/71 +
+ + +
+ 0% + Branches + 0/25 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 0% + Lines + 0/69 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Injectable } from '@nestjs/common';
+import { 
+  FailureCategory, 
+  MitigationRecommendation, 
+  FailedTransaction,
+  FailureAnalysis 
+} from '../schemas/failed-transaction.schema';
+ 
+@Injectable()
+export class MitigationService {
+  /**
+   * Generate mitigation recommendations based on failure analysis
+   */
+  async generateRecommendations(analysis: FailureAnalysis): Promise<MitigationRecommendation[]> {
+    const recommendations: MitigationRecommendation[] = [];
+    
+    // Generate recommendations based on top failure category
+    const topCategory = analysis.topFailureCategory;
+    recommendations.push(...await this.getCategorySpecificRecommendations(topCategory, analysis));
+    
+    // Generate recommendations based on patterns
+    recommendations.push(...await this.getPatternBasedRecommendations(analysis));
+    
+    // Generate chain-specific recommendations
+    recommendations.push(...await this.getChainSpecificRecommendations(analysis));
+    
+    // Sort by priority and return top recommendations
+    return recommendations
+      .sort((a, b) => this.getPriorityScore(b.priority) - this.getPriorityScore(a.priority))
+      .slice(0, 5); // Return top 5 recommendations
+  }
+ 
+  /**
+   * Get category-specific mitigation recommendations
+   */
+  private async getCategorySpecificRecommendations(
+    category: FailureCategory, 
+    analysis: FailureAnalysis
+  ): Promise<MitigationRecommendation[]> {
+    const recommendations: MitigationRecommendation[] = [];
+ 
+    switch (category) {
+      case 'underpriced_gas':
+        recommendations.push({
+          id: 'increase_gas_price',
+          category: 'underpriced_gas',
+          priority: 'high',
+          title: 'Increase Gas Price',
+          description: 'Your transactions are failing due to gas prices that are too low for network conditions.',
+          action: 'Increase priority fee by 25-50% during peak hours',
+          estimatedImpact: 'Reduces failure rate by 80-90%',
+          parameters: {
+            priorityFeeIncrease: '30%',
+            congestionMultiplier: 1.5
+          }
+        });
+        recommendations.push({
+          id: 'use_gas_tier',
+          category: 'underpriced_gas',
+          priority: 'medium',
+          title: 'Use Dynamic Gas Tiers',
+          description: 'Implement adaptive gas pricing based on network congestion.',
+          action: 'Use Medium/High gas tiers during peak hours, Low during off-peak',
+          estimatedImpact: 'Optimizes cost while maintaining reliability'
+        });
+        break;
+ 
+      case 'out_of_gas':
+        recommendations.push({
+          id: 'increase_gas_limit',
+          category: 'out_of_gas',
+          priority: 'high',
+          title: 'Increase Gas Limit',
+          description: 'Transactions are failing due to insufficient gas limits.',
+          action: 'Increase gas limit by 20-30% for complex operations',
+          estimatedImpact: 'Eliminates out-of-gas failures',
+          parameters: {
+            gasLimitMultiplier: 1.25,
+            minIncrease: '21000'
+          }
+        });
+        recommendations.push({
+          id: 'gas_limit_optimization',
+          category: 'out_of_gas',
+          priority: 'medium',
+          title: 'Optimize Gas Limit Estimation',
+          description: 'Implement better gas estimation algorithms.',
+          action: 'Use historical data to predict gas requirements more accurately',
+          estimatedImpact: 'Reduces wasted gas by 15-25%'
+        });
+        break;
+ 
+      case 'slippage_exceeded':
+        recommendations.push({
+          id: 'adjust_slippage',
+          category: 'slippage_exceeded',
+          priority: 'high',
+          title: 'Adjust Slippage Tolerance',
+          description: 'DEX trades are failing due to price movement exceeding slippage tolerance.',
+          action: 'Increase slippage tolerance to 1-2% during high volatility',
+          estimatedImpact: 'Reduces DEX failure rate by 70%',
+          parameters: {
+            defaultSlippage: '1%',
+            highVolatilitySlippage: '2%'
+          }
+        });
+        recommendations.push({
+          id: 'timing_optimization',
+          category: 'slippage_exceeded',
+          priority: 'medium',
+          title: 'Optimize Trading Timing',
+          description: 'Avoid trading during high volatility periods.',
+          action: 'Use volatility indicators to time trades optimally',
+          estimatedImpact: 'Improves success rate and reduces slippage'
+        });
+        break;
+ 
+      case 'nonce_conflict':
+        recommendations.push({
+          id: 'nonce_management',
+          category: 'nonce_conflict',
+          priority: 'high',
+          title: 'Implement Nonce Management',
+          description: 'Transactions are failing due to nonce conflicts.',
+          action: 'Replace stuck transactions with higher gas prices',
+          estimatedImpact: 'Resolves stuck transactions immediately',
+          parameters: {
+            replacementMultiplier: 1.1,
+            maxRetries: 3
+          }
+        });
+        break;
+ 
+      case 'insufficient_balance':
+        recommendations.push({
+          id: 'balance_check',
+          category: 'insufficient_balance',
+          priority: 'high',
+          title: 'Pre-transaction Balance Check',
+          description: 'Transactions are failing due to insufficient balance.',
+          action: 'Verify balance + gas costs before submitting transactions',
+          estimatedImpact: 'Eliminates balance-related failures'
+        });
+        break;
+    }
+ 
+    return recommendations;
+  }
+ 
+  /**
+   * Get pattern-based recommendations from failure patterns
+   */
+  private async getPatternBasedRecommendations(analysis: FailureAnalysis): Promise<MitigationRecommendation[]> {
+    const recommendations: MitigationRecommendation[] = [];
+ 
+    // Check for repeated failures
+    if (analysis.totalFailures > 10) {
+      recommendations.push({
+        id: 'batch_optimization',
+        category: 'unknown',
+        priority: 'medium',
+        title: 'Implement Batch Processing',
+        description: 'High failure rate suggests systematic issues with transaction handling.',
+        action: 'Group similar transactions and optimize batch processing',
+        estimatedImpact: 'Reduces overall failure rate by 40%'
+      });
+    }
+ 
+    // Check for chain-specific patterns
+    const chainIds = Object.keys(analysis.chainBreakdown).map(Number);
+    if (chainIds.length > 3) {
+      recommendations.push({
+        id: 'chain_optimization',
+        category: 'unknown',
+        priority: 'low',
+        title: 'Optimize Multi-chain Strategy',
+        description: 'Failures across multiple chains suggest suboptimal chain selection.',
+        action: 'Use chain-specific optimization strategies',
+        estimatedImpact: 'Improves cross-chain success rates'
+      });
+    }
+ 
+    return recommendations;
+  }
+ 
+  /**
+   * Get chain-specific recommendations
+   */
+  private async getChainSpecificRecommendations(analysis: FailureAnalysis): Promise<MitigationRecommendation[]> {
+    const recommendations: MitigationRecommendation[] = [];
+ 
+    for (const [chainId, stats] of Object.entries(analysis.chainBreakdown)) {
+      const chainNum = parseInt(chainId);
+      
+      if (stats.failures > 5) {
+        switch (chainNum) {
+          case 1: // Ethereum Mainnet
+            recommendations.push({
+              id: `ethereum_optimization_${chainId}`,
+              category: stats.mostCommonCategory,
+              priority: 'medium',
+              title: 'Ethereum Mainnet Optimization',
+              description: 'Optimize transactions for Ethereum mainnet conditions.',
+              action: 'Use EIP-1559 with dynamic base fee tracking',
+              estimatedImpact: 'Reduces mainnet failure rate by 30%'
+            });
+            break;
+ 
+          case 137: // Polygon
+            recommendations.push({
+              id: `polygon_optimization_${chainId}`,
+              category: stats.mostCommonCategory,
+              priority: 'medium',
+              title: 'Polygon Network Optimization',
+              description: 'Optimize for Polygon\'s faster block times.',
+              action: 'Reduce confirmation time expectations',
+              estimatedImpact: 'Improves Polygon success rates'
+            });
+            break;
+ 
+          case 42161: // Arbitrum
+            recommendations.push({
+              id: `arbitrum_optimization_${chainId}`,
+              category: stats.mostCommonCategory,
+              priority: 'medium',
+              title: 'Arbitrum Optimization',
+              description: 'Optimize for Arbitrum\'s L2 characteristics.',
+              action: 'Account for Arbitrum\'s gas pricing model',
+              estimatedImpact: 'Optimizes L2 transaction costs'
+            });
+            break;
+        }
+      }
+    }
+ 
+    return recommendations;
+  }
+ 
+  /**
+   * Get real-time mitigation for a specific failed transaction
+   */
+  async getImmediateMitigation(transaction: FailedTransaction): Promise<MitigationRecommendation[]> {
+    const recommendations: MitigationRecommendation[] = [];
+ 
+    switch (transaction.failureCategory) {
+      case 'underpriced_gas':
+        const suggestedGasPrice = await this.calculateOptimalGasPrice(transaction.chainId);
+        recommendations.push({
+          id: 'immediate_gas_increase',
+          category: 'underpriced_gas',
+          priority: 'high',
+          title: 'Immediate Gas Price Increase',
+          description: 'Replace transaction with higher gas price.',
+          action: `Set gas price to ${suggestedGasPrice} wei`,
+          estimatedImpact: 'Immediate transaction confirmation',
+          parameters: {
+            suggestedGasPrice,
+            replacementMultiplier: 1.1
+          }
+        });
+        break;
+ 
+      case 'out_of_gas':
+        const suggestedGasLimit = this.calculateOptimalGasLimit(transaction);
+        recommendations.push({
+          id: 'immediate_gas_limit_increase',
+          category: 'out_of_gas',
+          priority: 'high',
+          title: 'Immediate Gas Limit Increase',
+          description: 'Resubmit with higher gas limit.',
+          action: `Set gas limit to ${suggestedGasLimit}`,
+          estimatedImpact: 'Transaction will execute successfully',
+          parameters: {
+            suggestedGasLimit
+          }
+        });
+        break;
+ 
+      case 'nonce_conflict':
+        recommendations.push({
+          id: 'immediate_nonce_fix',
+          category: 'nonce_conflict',
+          priority: 'high',
+          title: 'Replace Stuck Transaction',
+          description: 'Cancel and replace the stuck transaction.',
+          action: 'Send 0 ETH transaction with same nonce and higher gas',
+          estimatedImpact: 'Clears nonce conflict immediately'
+        });
+        break;
+    }
+ 
+    return recommendations;
+  }
+ 
+  /**
+   * Helper methods
+   */
+  private getPriorityScore(priority: string): number {
+    const scores = { high: 3, medium: 2, low: 1 };
+    return scores[priority as keyof typeof scores] || 0;
+  }
+ 
+  private async calculateOptimalGasPrice(chainId: number): Promise<string> {
+    // Mock implementation - in real scenario, this would call gas price oracle
+    const baseGasPrices: Record<number, string> = {
+      1: '30000000000',     // Ethereum mainnet
+      137: '50000000000',    // Polygon
+      56: '10000000000',     // BSC
+      42161: '200000000',    // Arbitrum
+      10: '200000000'        // Optimism
+    };
+    
+    const basePrice = baseGasPrices[chainId] || '20000000000';
+    const multiplier = 1.2; // 20% above base for reliability
+    
+    return (BigInt(basePrice) * BigInt(Math.floor(multiplier * 100)) / BigInt(100)).toString();
+  }
+ 
+  private calculateOptimalGasLimit(transaction: FailedTransaction): string {
+    const currentLimit = BigInt(transaction.metadata.gasLimit);
+    const used = BigInt(transaction.gasUsed);
+    
+    // Calculate utilization
+    const utilization = Number((used * BigInt(100)) / currentLimit);
+    
+    if (utilization >= 99.5) {
+      // Out of gas - increase by 30%
+      return (currentLimit * BigInt(130) / BigInt(100)).toString();
+    } else if (utilization >= 95) {
+      // Near limit - increase by 20%
+      return (currentLimit * BigInt(120) / BigInt(100)).toString();
+    } else {
+      // Moderate utilization - increase by 10%
+      return (currentLimit * BigInt(110) / BigInt(100)).toString();
+    }
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/services/transaction-analysis.service.ts.html b/apps/api/coverage/lcov-report/src/services/transaction-analysis.service.ts.html new file mode 100644 index 0000000..5c5ee5f --- /dev/null +++ b/apps/api/coverage/lcov-report/src/services/transaction-analysis.service.ts.html @@ -0,0 +1,829 @@ + + + + + + Code coverage report for src/services/transaction-analysis.service.ts + + + + + + + + + +
+
+

All files / src/services transaction-analysis.service.ts

+
+ +
+ 0% + Statements + 0/80 +
+ + +
+ 0% + Branches + 0/37 +
+ + +
+ 0% + Functions + 0/25 +
+ + +
+ 0% + Lines + 0/76 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Injectable } from '@nestjs/common';
+import { FailedTransactionService } from './failed-transaction.service';
+import { MitigationService } from './mitigation.service';
+import { 
+  TransactionAnalysisRequest, 
+  TransactionAnalysisResponse,
+  FailureAnalysis,
+  ChainFailureStats,
+  FailedTransaction,
+  FailureCategory
+} from '../schemas/failed-transaction.schema';
+ 
+@Injectable()
+export class TransactionAnalysisService {
+  constructor(
+    private readonly failedTransactionService: FailedTransactionService,
+    private readonly mitigationService: MitigationService,
+  ) {}
+ 
+  /**
+   * Analyze failed transactions for a wallet
+   */
+  async analyzeWalletFailures(request: TransactionAnalysisRequest): Promise<TransactionAnalysisResponse> {
+    const { wallet, chainIds, timeframe, includeRecommendations = true } = request;
+ 
+    // Get failed transactions
+    const failures = await this.failedTransactionService.getWalletFailures(wallet, chainIds);
+    
+    // Filter by timeframe if specified
+    const filteredFailures = this.filterByTimeframe(failures, timeframe);
+    
+    // Calculate cost metrics
+    const costMetrics = await this.failedTransactionService.calculateCostMetrics(wallet, chainIds);
+    
+    // Analyze failure categories
+    const failureCategories = this.categorizeFailures(filteredFailures);
+    
+    // Calculate chain breakdown
+    const chainBreakdown = this.calculateChainBreakdown(filteredFailures);
+    
+    // Generate recommendations
+    const recommendations = includeRecommendations 
+      ? await this.mitigationService.generateRecommendations({
+          wallet,
+          totalFailures: filteredFailures.length,
+          totalGasWasted: costMetrics.totalGasWasted,
+          totalGasWastedUSD: costMetrics.totalGasWastedUSD,
+          failureCategories,
+          topFailureCategory: this.getTopFailureCategory(failureCategories),
+          recommendations: [], // Will be populated
+          timeframe: {
+            start: timeframe?.start || this.getDefaultStartDate(filteredFailures),
+            end: timeframe?.end || new Date().toISOString()
+          },
+          chainBreakdown
+        })
+      : [];
+ 
+    const analysis: FailureAnalysis = {
+      wallet,
+      totalFailures: filteredFailures.length,
+      totalGasWasted: costMetrics.totalGasWasted,
+      totalGasWastedUSD: costMetrics.totalGasWastedUSD,
+      failureCategories,
+      topFailureCategory: this.getTopFailureCategory(failureCategories),
+      recommendations,
+      timeframe: {
+        start: timeframe?.start || this.getDefaultStartDate(filteredFailures),
+        end: timeframe?.end || new Date().toISOString()
+      },
+      chainBreakdown
+    };
+ 
+    return {
+      wallet,
+      analysis,
+      processedAt: new Date().toISOString(),
+      requestId: this.generateRequestId()
+    };
+  }
+ 
+  /**
+   * Get immediate mitigation for a recent failure
+   */
+  async getImmediateMitigation(wallet: string, transactionHash?: string): Promise<any> {
+    const failures = await this.failedTransactionService.getWalletFailures(wallet);
+    
+    // Get the most recent failure or specific transaction
+    const targetFailure = transactionHash
+      ? failures.find(f => f.hash === transactionHash)
+      : failures.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())[0];
+ 
+    if (!targetFailure) {
+      throw new Error('No failed transaction found');
+    }
+ 
+    const recommendations = await this.mitigationService.getImmediateMitigation(targetFailure);
+ 
+    return {
+      transaction: targetFailure,
+      recommendations,
+      processedAt: new Date().toISOString()
+    };
+  }
+ 
+  /**
+   * Get wallet statistics summary
+   */
+  async getWalletSummary(wallet: string, chainIds?: number[]): Promise<any> {
+    const failures = await this.failedTransactionService.getWalletFailures(wallet, chainIds);
+    const costMetrics = await this.failedTransactionService.calculateCostMetrics(wallet, chainIds);
+    
+    const last30Days = failures.filter(f => 
+      new Date(f.timestamp) >= new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
+    );
+    
+    const last7Days = failures.filter(f => 
+      new Date(f.timestamp) >= new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
+    );
+ 
+    return {
+      wallet,
+      summary: {
+        totalFailures: failures.length,
+        totalGasWasted: costMetrics.totalGasWasted,
+        totalGasWastedUSD: costMetrics.totalGasWastedUSD,
+        last30DaysFailures: last30Days.length,
+        last7DaysFailures: last7Days.length,
+        averageFailuresPerDay: (last30Days.length / 30).toFixed(2),
+        mostActiveChain: this.getMostActiveChain(failures),
+        topFailureCategory: this.getTopFailureCategory(this.categorizeFailures(failures))
+      },
+      processedAt: new Date().toISOString()
+    };
+  }
+ 
+  /**
+   * Process a failed transaction event
+   */
+  async processFailedTransaction(transactionData: Partial<FailedTransaction>): Promise<FailedTransaction> {
+    return await this.failedTransactionService.trackFailedTransaction(transactionData);
+  }
+ 
+  /**
+   * Helper methods
+   */
+  private filterByTimeframe(failures: FailedTransaction[], timeframe?: { start?: string; end?: string }): FailedTransaction[] {
+    if (!timeframe) return failures;
+ 
+    return failures.filter(tx => {
+      const txDate = new Date(tx.timestamp);
+      const startDate = timeframe.start ? new Date(timeframe.start) : new Date(0);
+      const endDate = timeframe.end ? new Date(timeframe.end) : new Date();
+      
+      return txDate >= startDate && txDate <= endDate;
+    });
+  }
+ 
+  private categorizeFailures(failures: FailedTransaction[]): Record<string, number> {
+    const categories: Record<string, number> = {};
+    
+    failures.forEach(tx => {
+      categories[tx.failureCategory] = (categories[tx.failureCategory] || 0) + 1;
+    });
+ 
+    return categories;
+  }
+ 
+  private calculateChainBreakdown(failures: FailedTransaction[]): Record<number, ChainFailureStats> {
+    const chainStats: Record<number, ChainFailureStats> = {};
+    
+    failures.forEach(tx => {
+      if (!chainStats[tx.chainId]) {
+        chainStats[tx.chainId] = {
+          chainId: tx.chainId,
+          failures: 0,
+          gasWasted: '0',
+          mostCommonCategory: 'unknown'
+        };
+      }
+      
+      const stats = chainStats[tx.chainId];
+      stats.failures += 1;
+      stats.gasWasted = (
+        BigInt(stats.gasWasted) + BigInt(tx.effectiveFee)
+      ).toString();
+    });
+ 
+    // Calculate most common category for each chain
+    Object.keys(chainStats).forEach(chainId => {
+      const chainFailures = failures.filter(tx => tx.chainId === parseInt(chainId));
+      const categories = this.categorizeFailures(chainFailures);
+      chainStats[parseInt(chainId)].mostCommonCategory = this.getTopFailureCategory(categories);
+    });
+ 
+    return chainStats;
+  }
+ 
+  private getTopFailureCategory(categories: Record<string, number>): FailureCategory {
+    let maxCount = 0;
+    let topCategory: FailureCategory = 'unknown';
+    
+    Object.entries(categories).forEach(([category, count]) => {
+      if (count > maxCount) {
+        maxCount = count;
+        topCategory = category as FailureCategory;
+      }
+    });
+ 
+    return topCategory;
+  }
+ 
+  private getMostActiveChain(failures: FailedTransaction[]): number {
+    const chainCounts: Record<number, number> = {};
+    
+    failures.forEach(tx => {
+      chainCounts[tx.chainId] = (chainCounts[tx.chainId] || 0) + 1;
+    });
+ 
+    let maxCount = 0;
+    let topChain = 1;
+    
+    Object.entries(chainCounts).forEach(([chainId, count]) => {
+      if (count > maxCount) {
+        maxCount = count;
+        topChain = parseInt(chainId);
+      }
+    });
+ 
+    return topChain;
+  }
+ 
+  private getDefaultStartDate(failures: FailedTransaction[]): string {
+    if (failures.length === 0) {
+      return new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(); // 30 days ago
+    }
+    
+    const oldestTx = failures.reduce((oldest, current) => 
+      new Date(current.timestamp) < new Date(oldest.timestamp) ? current : oldest
+    );
+    
+    return oldestTx.timestamp;
+  }
+ 
+  private generateRequestId(): string {
+    return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/validation/analysis.validator.ts.html b/apps/api/coverage/lcov-report/src/validation/analysis.validator.ts.html new file mode 100644 index 0000000..3c4cc66 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/validation/analysis.validator.ts.html @@ -0,0 +1,820 @@ + + + + + + Code coverage report for src/validation/analysis.validator.ts + + + + + + + + + +
+
+

All files / src/validation analysis.validator.ts

+
+ +
+ 0% + Statements + 0/74 +
+ + +
+ 0% + Branches + 0/78 +
+ + +
+ 0% + Functions + 0/8 +
+ + +
+ 0% + Lines + 0/71 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Request, Response, NextFunction } from 'express';
+import { CodebaseSubmissionRequest, ValidationError } from '../schemas/analysis.schema';
+ 
+export class AnalysisValidator {
+  private static readonly SUPPORTED_LANGUAGES = ['rust', 'typescript', 'javascript', 'solidity', 'soroban'];
+  private static readonly SUPPORTED_FRAMEWORKS = ['soroban', 'solidity', 'general'];
+  private static readonly MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
+  private static readonly MAX_FILES = 100;
+  private static readonly MAX_CONTENT_LENGTH = 50 * 1024 * 1024; // 50MB
+ 
+  static validateSubmission(req: Request, res: Response, next: NextFunction): void {
+    const body = req.body as CodebaseSubmissionRequest;
+    const errors: ValidationError[] = [];
+ 
+    try {
+      // Validate project information
+      if (!body.project) {
+        errors.push({
+          field: 'project',
+          message: 'Project information is required',
+          constraint: 'required'
+        });
+      } else {
+        if (!body.project.name || body.project.name.trim().length === 0) {
+          errors.push({
+            field: 'project.name',
+            message: 'Project name is required',
+            value: body.project.name,
+            constraint: 'required'
+          });
+        }
+ 
+        if (body.project.name && body.project.name.length > 100) {
+          errors.push({
+            field: 'project.name',
+            message: 'Project name must be less than 100 characters',
+            value: body.project.name,
+            constraint: 'maxLength'
+          });
+        }
+ 
+        if (body.project.repositoryUrl && !this.isValidUrl(body.project.repositoryUrl)) {
+          errors.push({
+            field: 'project.repositoryUrl',
+            message: 'Invalid repository URL format',
+            value: body.project.repositoryUrl,
+            constraint: 'url'
+          });
+        }
+      }
+ 
+      // Validate files
+      if (!body.files || !Array.isArray(body.files) || body.files.length === 0) {
+        errors.push({
+          field: 'files',
+          message: 'At least one file must be submitted',
+          constraint: 'required'
+        });
+      } else {
+        if (body.files.length > this.MAX_FILES) {
+          errors.push({
+            field: 'files',
+            message: `Maximum ${this.MAX_FILES} files allowed`,
+            value: body.files.length,
+            constraint: 'maxFiles'
+          });
+        }
+ 
+        let totalSize = 0;
+        body.files.forEach((file, index) => {
+          const prefix = `files[${index}]`;
+          
+          if (!file.path || file.path.trim().length === 0) {
+            errors.push({
+              field: `${prefix}.path`,
+              message: 'File path is required',
+              constraint: 'required'
+            });
+          }
+ 
+          if (!file.content || file.content.trim().length === 0) {
+            errors.push({
+              field: `${prefix}.content`,
+              message: 'File content is required',
+              constraint: 'required'
+            });
+          }
+ 
+          if (!file.language || !this.SUPPORTED_LANGUAGES.includes(file.language)) {
+            errors.push({
+              field: `${prefix}.language`,
+              message: `Language must be one of: ${this.SUPPORTED_LANGUAGES.join(', ')}`,
+              value: file.language,
+              constraint: 'enum'
+            });
+          }
+ 
+          if (file.size && file.size > this.MAX_FILE_SIZE) {
+            errors.push({
+              field: `${prefix}.size`,
+              message: `File size exceeds maximum of ${this.MAX_FILE_SIZE} bytes`,
+              value: file.size,
+              constraint: 'maxSize'
+            });
+          }
+ 
+          totalSize += file.size || 0;
+        });
+ 
+        if (totalSize > this.MAX_CONTENT_LENGTH) {
+          errors.push({
+            field: 'files',
+            message: `Total content size exceeds maximum of ${this.MAX_CONTENT_LENGTH} bytes`,
+            value: totalSize,
+            constraint: 'maxTotalSize'
+          });
+        }
+      }
+ 
+      // Validate options
+      if (body.options) {
+        if (!this.SUPPORTED_FRAMEWORKS.includes(body.options.scanType)) {
+          errors.push({
+            field: 'options.scanType',
+            message: `Scan type must be one of: security, performance, gas-optimization, full`,
+            value: body.options.scanType,
+            constraint: 'enum'
+          });
+        }
+ 
+        if (!['low', 'medium', 'high', 'critical'].includes(body.options.severity)) {
+          errors.push({
+            field: 'options.severity',
+            message: `Severity must be one of: low, medium, high, critical`,
+            value: body.options.severity,
+            constraint: 'enum'
+          });
+        }
+      }
+ 
+      // Validate metadata
+      if (body.metadata) {
+        if (body.metadata.framework && !this.SUPPORTED_FRAMEWORKS.includes(body.metadata.framework)) {
+          errors.push({
+            field: 'metadata.framework',
+            message: `Framework must be one of: ${this.SUPPORTED_FRAMEWORKS.join(', ')}`,
+            value: body.metadata.framework,
+            constraint: 'enum'
+          });
+        }
+ 
+        // Soroban-specific validation
+        if (body.metadata.framework === 'soroban') {
+          this.validateSorobanProject(body.files, body.metadata, errors);
+        }
+      }
+ 
+      if (errors.length > 0) {
+        res.status(400).json({
+          error: {
+            code: 'VALIDATION_ERROR',
+            message: 'Request validation failed',
+            timestamp: new Date().toISOString(),
+            requestId: req.headers['x-request-id'] as string || 'unknown'
+          },
+          validationErrors: errors
+        });
+        return;
+      }
+ 
+      next();
+    } catch (error) {
+      res.status(500).json({
+        error: {
+          code: 'VALIDATION_EXCEPTION',
+          message: 'An error occurred during validation',
+          timestamp: new Date().toISOString(),
+          requestId: req.headers['x-request-id'] as string || 'unknown'
+        }
+      });
+    }
+  }
+ 
+  private static validateSorobanProject(files: any[], metadata: any, errors: ValidationError[]): void {
+    const rustFiles = files.filter(f => f.language === 'rust');
+    
+    if (rustFiles.length === 0) {
+      errors.push({
+        field: 'files',
+        message: 'Soroban projects must contain at least one Rust file',
+        constraint: 'sorobanRequiresRust'
+      });
+      return;
+    }
+ 
+    // Check for Cargo.toml
+    const hasCargoToml = files.some(f => f.path === 'Cargo.toml' || f.path.endsWith('/Cargo.toml'));
+    if (!hasCargoToml) {
+      errors.push({
+        field: 'files',
+        message: 'Soroban projects must include a Cargo.toml file',
+        constraint: 'sorobanRequiresCargoToml'
+      });
+    }
+ 
+    // Validate Soroban-specific dependencies in Cargo.toml if present
+    const cargoToml = files.find(f => f.path.endsWith('Cargo.toml'));
+    if (cargoToml) {
+      const content = cargoToml.content;
+      const hasSorobanSdk = content.includes('soroban-sdk') || content.includes('stellar-sdk');
+      
+      if (!hasSorobanSdk) {
+        errors.push({
+          field: 'metadata.dependencies',
+          message: 'Soroban projects must include soroban-sdk or stellar-sdk dependency',
+          constraint: 'sorobanRequiresSdk'
+        });
+      }
+    }
+ 
+    // Check for contract structure
+    const contractFiles = rustFiles.filter(f => 
+      f.content.includes('#[contractimpl]') || 
+      f.content.includes('soroban_sdk::contract') ||
+      f.content.includes('ContractType')
+    );
+ 
+    if (contractFiles.length === 0) {
+      errors.push({
+        field: 'files',
+        message: 'No Soroban contract implementation found. Files should contain #[contractimpl] or contract definitions.',
+        constraint: 'sorobanRequiresContract'
+      });
+    }
+  }
+ 
+  private static isValidUrl(url: string): boolean {
+    try {
+      new URL(url);
+      return true;
+    } catch {
+      return false;
+    }
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov-report/src/validation/index.html b/apps/api/coverage/lcov-report/src/validation/index.html new file mode 100644 index 0000000..4958656 --- /dev/null +++ b/apps/api/coverage/lcov-report/src/validation/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/validation + + + + + + + + + +
+
+

All files src/validation

+
+ +
+ 0% + Statements + 0/74 +
+ + +
+ 0% + Branches + 0/78 +
+ + +
+ 0% + Functions + 0/8 +
+ + +
+ 0% + Lines + 0/71 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
analysis.validator.ts +
+
0%0/740%0/780%0/80%0/71
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/api/coverage/lcov.info b/apps/api/coverage/lcov.info new file mode 100644 index 0000000..ca45e9a --- /dev/null +++ b/apps/api/coverage/lcov.info @@ -0,0 +1,1730 @@ +TN: +SF:src\app.controller.ts +FN:12,(anonymous_2) +FN:27,(anonymous_3) +FNF:2 +FNH:0 +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +DA:1,0 +DA:2,0 +DA:5,0 +DA:12,0 +DA:13,0 +DA:27,0 +DA:28,0 +LF:7 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\app.module.ts +FNF:0 +FNH:0 +DA:1,0 +DA:2,0 +DA:3,0 +DA:4,0 +DA:5,0 +DA:6,0 +DA:7,0 +DA:8,0 +DA:9,0 +DA:10,0 +DA:11,0 +DA:12,0 +DA:13,0 +DA:62,0 +LF:14 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\index.ts +FN:6,(anonymous_0) +FNF:1 +FNH:0 +FNDA:0,(anonymous_0) +DA:1,0 +DA:3,0 +DA:4,0 +DA:6,0 +LF:4 +LH:0 +BRDA:3,0,0,0 +BRDA:3,0,1,0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:src\main.ts +FN:5,bootstrap +FNF:1 +FNH:0 +FNDA:0,bootstrap +DA:1,0 +DA:2,0 +DA:3,0 +DA:6,0 +DA:10,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:21,0 +LF:10 +LH:0 +BRDA:15,0,0,0 +BRDA:15,0,1,0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:src\scan.ts +FN:3,performScan +FN:15,sleep +FN:16,(anonymous_2) +FNF:3 +FNH:0 +FNDA:0,performScan +FNDA:0,sleep +FNDA:0,(anonymous_2) +DA:3,0 +DA:4,0 +DA:5,0 +DA:6,0 +DA:7,0 +DA:8,0 +DA:9,0 +DA:10,0 +DA:11,0 +DA:12,0 +DA:16,0 +LF:11 +LH:0 +BRDA:10,0,0,0 +BRDA:10,0,1,0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:src\server.ts +FN:6,createServer +FN:14,(anonymous_1) +FN:28,(anonymous_2) +FN:38,(anonymous_3) +FN:55,(anonymous_4) +FN:62,(anonymous_5) +FN:74,(anonymous_6) +FNF:7 +FNH:0 +FNDA:0,createServer +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +DA:1,0 +DA:3,0 +DA:4,0 +DA:6,0 +DA:7,0 +DA:10,0 +DA:11,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:24,0 +DA:28,0 +DA:29,0 +DA:38,0 +DA:39,0 +DA:55,0 +DA:56,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:62,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:86,0 +DA:87,0 +DA:91,0 +DA:94,0 +DA:95,0 +DA:97,0 +LF:50 +LH:0 +BRDA:20,0,0,0 +BRDA:20,0,1,0 +BRDA:32,1,0,0 +BRDA:32,1,1,0 +BRDA:56,2,0,0 +BRDA:56,2,1,0 +BRDA:57,3,0,0 +BRDA:57,3,1,0 +BRDA:65,4,0,0 +BRDA:65,4,1,0 +BRDA:70,5,0,0 +BRDA:70,5,1,0 +BRDA:77,6,0,0 +BRDA:77,6,1,0 +BRDA:82,7,0,0 +BRDA:82,7,1,0 +BRF:16 +BRH:0 +end_of_record +TN: +SF:src\auth\auth.module.ts +FN:14,(anonymous_1) +FNF:1 +FNH:1 +FNDA:14,(anonymous_1) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:14,14 +DA:27,1 +LF:9 +LH:9 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\auth\index.ts +FN:1,(anonymous_4) +FNF:1 +FNH:0 +FNDA:0,(anonymous_4) +DA:1,0 +DA:2,0 +DA:3,0 +DA:4,0 +LF:4 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\auth\__tests__\index.ts +FNF:0 +FNH:0 +DA:2,0 +DA:3,0 +DA:4,0 +DA:5,0 +DA:6,0 +LF:5 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\auth\decorators\current-user.decorator.ts +FN:21,(anonymous_0) +FNF:1 +FNH:0 +FNDA:0,(anonymous_0) +DA:1,1 +DA:20,1 +DA:22,0 +DA:23,0 +DA:25,0 +DA:26,0 +DA:29,0 +LF:7 +LH:2 +BRDA:25,0,0,0 +BRDA:25,0,1,0 +BRDA:29,1,0,0 +BRDA:29,1,1,0 +BRF:4 +BRH:0 +end_of_record +TN: +SF:src\auth\decorators\index.ts +FN:1,(anonymous_0) +FN:1,(anonymous_1) +FN:2,(anonymous_2) +FN:2,(anonymous_3) +FN:2,(anonymous_4) +FN:3,(anonymous_5) +FNF:6 +FNH:5 +FNDA:1,(anonymous_0) +FNDA:1,(anonymous_1) +FNDA:1,(anonymous_2) +FNDA:8,(anonymous_3) +FNDA:1,(anonymous_4) +FNDA:0,(anonymous_5) +DA:1,2 +DA:2,9 +DA:3,1 +LF:3 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\auth\decorators\public.decorator.ts +FN:16,(anonymous_0) +FNF:1 +FNH:0 +FNDA:0,(anonymous_0) +DA:1,3 +DA:3,3 +DA:16,3 +LF:3 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\auth\decorators\roles.decorator.ts +FN:18,(anonymous_0) +FNF:1 +FNH:0 +FNDA:0,(anonymous_0) +DA:1,3 +DA:3,3 +DA:18,3 +DA:23,3 +LF:4 +LH:4 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\auth\guards\index.ts +FN:1,(anonymous_0) +FN:2,(anonymous_1) +FNF:2 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +DA:1,0 +DA:2,0 +LF:2 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\auth\guards\jwt-auth.guard.ts +FN:12,(anonymous_2) +FN:16,(anonymous_3) +FN:31,(anonymous_4) +FNF:3 +FNH:3 +FNDA:23,(anonymous_2) +FNDA:1,(anonymous_3) +FNDA:8,(anonymous_4) +DA:1,2 +DA:6,2 +DA:7,2 +DA:8,2 +DA:11,2 +DA:12,23 +DA:13,23 +DA:18,1 +DA:23,1 +DA:24,1 +DA:28,0 +DA:33,8 +DA:34,7 +DA:36,7 +DA:37,1 +DA:38,6 +DA:39,1 +DA:40,5 +DA:41,1 +DA:44,7 +DA:51,1 +LF:21 +LH:20 +BRDA:23,0,0,1 +BRDA:23,0,1,0 +BRDA:33,1,0,7 +BRDA:33,1,1,1 +BRDA:33,2,0,8 +BRDA:33,2,1,7 +BRDA:36,3,0,1 +BRDA:36,3,1,6 +BRDA:38,4,0,1 +BRDA:38,4,1,5 +BRDA:40,5,0,1 +BRDA:40,5,1,4 +BRF:12 +BRH:11 +end_of_record +TN: +SF:src\auth\guards\roles.guard.ts +FN:13,(anonymous_2) +FN:15,(anonymous_3) +FN:39,(anonymous_4) +FNF:3 +FNH:3 +FNDA:25,(anonymous_2) +FNDA:13,(anonymous_3) +FNDA:11,(anonymous_4) +DA:1,2 +DA:7,2 +DA:8,2 +DA:12,2 +DA:13,25 +DA:16,13 +DA:22,13 +DA:23,3 +DA:26,10 +DA:28,10 +DA:29,2 +DA:36,8 +DA:39,11 +DA:41,8 +DA:42,6 +DA:49,2 +LF:16 +LH:16 +BRDA:22,0,0,3 +BRDA:22,0,1,10 +BRDA:22,1,0,13 +BRDA:22,1,1,12 +BRDA:28,2,0,2 +BRDA:28,2,1,8 +BRDA:36,3,0,8 +BRDA:36,3,1,1 +BRDA:41,4,0,6 +BRDA:41,4,1,2 +BRF:10 +BRH:10 +end_of_record +TN: +SF:src\auth\strategies\index.ts +FN:1,(anonymous_0) +FNF:1 +FNH:0 +FNDA:0,(anonymous_0) +DA:1,0 +LF:1 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\auth\strategies\jwt.strategy.ts +FN:25,(anonymous_2) +FN:41,(anonymous_3) +FNF:2 +FNH:2 +FNDA:26,(anonymous_2) +FNDA:8,(anonymous_3) +DA:1,2 +DA:2,2 +DA:3,2 +DA:4,2 +DA:24,2 +DA:25,24 +DA:26,26 +DA:27,26 +DA:28,2 +DA:31,24 +DA:43,8 +DA:44,1 +DA:47,7 +DA:48,1 +DA:51,6 +DA:52,1 +DA:55,5 +DA:56,1 +DA:60,4 +DA:61,4 +DA:62,2 +DA:66,2 +LF:22 +LH:22 +BRDA:27,0,0,2 +BRDA:27,0,1,24 +BRDA:43,1,0,1 +BRDA:43,1,1,7 +BRDA:47,2,0,1 +BRDA:47,2,1,6 +BRDA:51,3,0,1 +BRDA:51,3,1,5 +BRDA:55,4,0,1 +BRDA:55,4,1,4 +BRDA:61,5,0,2 +BRDA:61,5,1,2 +BRDA:68,6,0,2 +BRDA:68,6,1,1 +BRDA:69,7,0,2 +BRDA:69,7,1,1 +BRF:16 +BRH:16 +end_of_record +TN: +SF:src\controllers\analysis.controller.ts +FN:13,(anonymous_0) +FN:15,(anonymous_1) +FN:55,(anonymous_2) +FN:109,(anonymous_3) +FN:175,(anonymous_4) +FN:232,(anonymous_5) +FN:233,(anonymous_6) +FN:239,(anonymous_7) +FN:241,(anonymous_8) +FN:242,(anonymous_9) +FN:260,(anonymous_10) +FN:276,(anonymous_11) +FNF:12 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +DA:12,0 +DA:13,0 +DA:16,0 +DA:17,0 +DA:20,0 +DA:23,0 +DA:39,0 +DA:48,0 +DA:50,0 +DA:51,0 +DA:56,0 +DA:57,0 +DA:59,0 +DA:60,0 +DA:68,0 +DA:71,0 +DA:73,0 +DA:74,0 +DA:82,0 +DA:85,0 +DA:86,0 +DA:88,0 +DA:102,0 +DA:104,0 +DA:105,0 +DA:110,0 +DA:111,0 +DA:113,0 +DA:114,0 +DA:122,0 +DA:125,0 +DA:127,0 +DA:128,0 +DA:136,0 +DA:139,0 +DA:141,0 +DA:142,0 +DA:150,0 +DA:151,0 +DA:154,0 +DA:156,0 +DA:157,0 +DA:165,0 +DA:168,0 +DA:170,0 +DA:171,0 +DA:176,0 +DA:177,0 +DA:179,0 +DA:180,0 +DA:188,0 +DA:191,0 +DA:193,0 +DA:194,0 +DA:202,0 +DA:205,0 +DA:207,0 +DA:208,0 +DA:216,0 +DA:219,0 +DA:221,0 +DA:227,0 +DA:228,0 +DA:233,0 +DA:234,0 +DA:236,0 +DA:240,0 +DA:241,0 +DA:242,0 +DA:245,0 +DA:248,0 +DA:251,0 +DA:254,0 +DA:257,0 +DA:261,0 +DA:264,0 +DA:266,0 +DA:268,0 +DA:270,0 +DA:272,0 +DA:277,0 +DA:287,0 +LF:82 +LH:0 +BRDA:27,0,0,0 +BRDA:27,0,1,0 +BRDA:31,1,0,0 +BRDA:31,1,1,0 +BRDA:36,2,0,0 +BRDA:36,2,1,0 +BRDA:59,3,0,0 +BRDA:59,3,1,0 +BRDA:65,4,0,0 +BRDA:65,4,1,0 +BRDA:73,5,0,0 +BRDA:73,5,1,0 +BRDA:79,6,0,0 +BRDA:79,6,1,0 +BRDA:86,7,0,0 +BRDA:86,7,1,0 +BRDA:91,8,0,0 +BRDA:91,8,1,0 +BRDA:94,9,0,0 +BRDA:94,9,1,0 +BRDA:95,10,0,0 +BRDA:95,10,1,0 +BRDA:113,11,0,0 +BRDA:113,11,1,0 +BRDA:119,12,0,0 +BRDA:119,12,1,0 +BRDA:127,13,0,0 +BRDA:127,13,1,0 +BRDA:133,14,0,0 +BRDA:133,14,1,0 +BRDA:141,15,0,0 +BRDA:141,15,1,0 +BRDA:145,16,0,0 +BRDA:145,16,1,0 +BRDA:147,17,0,0 +BRDA:147,17,1,0 +BRDA:156,18,0,0 +BRDA:156,18,1,0 +BRDA:162,19,0,0 +BRDA:162,19,1,0 +BRDA:179,20,0,0 +BRDA:179,20,1,0 +BRDA:185,21,0,0 +BRDA:185,21,1,0 +BRDA:193,22,0,0 +BRDA:193,22,1,0 +BRDA:199,23,0,0 +BRDA:199,23,1,0 +BRDA:207,24,0,0 +BRDA:207,24,1,0 +BRDA:213,25,0,0 +BRDA:213,25,1,0 +BRDA:233,26,0,0 +BRDA:233,26,1,0 +BRDA:236,27,0,0 +BRDA:236,27,1,0 +BRDA:236,27,2,0 +BRDA:261,28,0,0 +BRDA:261,28,1,0 +BRDA:261,28,2,0 +BRDA:261,28,3,0 +BRDA:261,28,4,0 +BRDA:261,28,5,0 +BRDA:281,29,0,0 +BRDA:281,29,1,0 +BRDA:283,30,0,0 +BRDA:283,30,1,0 +BRF:67 +BRH:0 +end_of_record +TN: +SF:src\controllers\cross-chain-gas.controller.ts +FN:15,(anonymous_4) +FN:36,(anonymous_5) +FN:59,(anonymous_6) +FN:77,(anonymous_7) +FN:106,(anonymous_8) +FNF:5 +FNH:0 +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +DA:1,0 +DA:2,0 +DA:3,0 +DA:9,0 +DA:14,0 +DA:15,0 +DA:36,0 +DA:39,0 +DA:40,0 +DA:43,0 +DA:47,0 +DA:59,0 +DA:60,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:106,0 +DA:110,0 +DA:111,0 +DA:114,0 +LF:20 +LH:0 +BRDA:39,0,0,0 +BRDA:39,0,1,0 +BRDA:110,1,0,0 +BRDA:110,1,1,0 +BRDA:110,2,0,0 +BRDA:110,2,1,0 +BRF:6 +BRH:0 +end_of_record +TN: +SF:src\controllers\failed-transaction.controller.ts +FN:29,(anonymous_4) +FN:44,(anonymous_5) +FN:83,(anonymous_6) +FN:93,(anonymous_7) +FN:93,(anonymous_8) +FN:124,(anonymous_9) +FN:156,(anonymous_10) +FN:207,(anonymous_11) +FN:247,(anonymous_12) +FNF:9 +FNH:0 +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +DA:1,0 +DA:13,0 +DA:20,0 +DA:21,0 +DA:26,0 +DA:27,0 +DA:29,0 +DA:44,0 +DA:47,0 +DA:48,0 +DA:50,0 +DA:51,0 +DA:54,0 +DA:56,0 +DA:57,0 +DA:83,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:92,0 +DA:93,0 +DA:96,0 +DA:98,0 +DA:99,0 +DA:124,0 +DA:128,0 +DA:129,0 +DA:130,0 +DA:133,0 +DA:135,0 +DA:136,0 +DA:156,0 +DA:159,0 +DA:161,0 +DA:162,0 +DA:168,0 +DA:169,0 +DA:172,0 +DA:173,0 +DA:176,0 +DA:178,0 +DA:180,0 +DA:181,0 +DA:207,0 +DA:211,0 +DA:212,0 +DA:213,0 +DA:214,0 +DA:219,0 +DA:229,0 +DA:230,0 +DA:247,0 +DA:248,0 +LF:53 +LH:0 +BRDA:50,0,0,0 +BRDA:50,0,1,0 +BRDA:50,1,0,0 +BRDA:50,1,1,0 +BRDA:88,2,0,0 +BRDA:88,2,1,0 +BRDA:92,3,0,0 +BRDA:92,3,1,0 +BRDA:129,4,0,0 +BRDA:129,4,1,0 +BRDA:161,5,0,0 +BRDA:161,5,1,0 +BRDA:161,6,0,0 +BRDA:161,6,1,0 +BRDA:161,6,2,0 +BRDA:168,7,0,0 +BRDA:168,7,1,0 +BRDA:172,8,0,0 +BRDA:172,8,1,0 +BRDA:213,9,0,0 +BRDA:213,9,1,0 +BRDA:221,10,0,0 +BRDA:221,10,1,0 +BRF:23 +BRH:0 +end_of_record +TN: +SF:src\example\example.controller.ts +FN:14,(anonymous_2) +FNF:1 +FNH:0 +FNDA:0,(anonymous_2) +DA:1,0 +DA:12,0 +DA:14,0 +DA:15,0 +LF:4 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\middleware\error.middleware.ts +FN:10,errorHandler +FN:49,notFoundHandler +FN:60,requestIdHandler +FN:66,rateLimitHandler +FNF:4 +FNH:0 +FNDA:0,errorHandler +FNDA:0,notFoundHandler +FNDA:0,requestIdHandler +FNDA:0,rateLimitHandler +DA:10,0 +DA:16,0 +DA:25,0 +DA:26,0 +DA:28,0 +DA:39,0 +DA:40,0 +DA:46,0 +DA:49,0 +DA:50,0 +DA:60,0 +DA:61,0 +DA:63,0 +DA:66,0 +DA:67,0 +LF:15 +LH:0 +BRDA:25,0,0,0 +BRDA:25,0,1,0 +BRDA:26,1,0,0 +BRDA:26,1,1,0 +BRDA:31,2,0,0 +BRDA:31,2,1,0 +BRDA:34,3,0,0 +BRDA:34,3,1,0 +BRDA:39,4,0,0 +BRDA:39,4,1,0 +BRDA:55,5,0,0 +BRDA:55,5,1,0 +BRDA:61,6,0,0 +BRDA:61,6,1,0 +BRDA:72,7,0,0 +BRDA:72,7,1,0 +BRF:16 +BRH:0 +end_of_record +TN: +SF:src\routes\analysis.routes.ts +FN:5,createAnalysisRoutes +FN:12,(anonymous_1) +FN:17,(anonymous_2) +FN:22,(anonymous_3) +FN:27,(anonymous_4) +FNF:5 +FNH:0 +FNDA:0,createAnalysisRoutes +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +DA:1,0 +DA:2,0 +DA:3,0 +DA:5,0 +DA:6,0 +DA:7,0 +DA:10,0 +DA:12,0 +DA:16,0 +DA:17,0 +DA:21,0 +DA:22,0 +DA:26,0 +DA:27,0 +DA:30,0 +LF:15 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src\services\cross-chain-gas.service.ts +FN:12,(anonymous_1) +FN:65,(anonymous_2) +FN:67,(anonymous_3) +FN:70,(anonymous_4) +FN:83,(anonymous_5) +FN:106,(anonymous_6) +FN:126,(anonymous_7) +FN:127,(anonymous_8) +FN:129,(anonymous_9) +FN:139,(anonymous_10) +FN:140,(anonymous_11) +FN:144,(anonymous_12) +FN:145,(anonymous_13) +FN:157,(anonymous_14) +FN:161,(anonymous_15) +FN:166,(anonymous_16) +FNF:16 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +DA:1,0 +DA:12,0 +DA:13,0 +DA:51,0 +DA:57,0 +DA:66,0 +DA:67,0 +DA:70,0 +DA:71,0 +DA:74,0 +DA:76,0 +DA:85,0 +DA:93,0 +DA:95,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:116,0 +DA:127,0 +DA:129,0 +DA:140,0 +DA:141,0 +DA:145,0 +DA:146,0 +DA:148,0 +DA:149,0 +DA:150,0 +DA:151,0 +DA:153,0 +DA:158,0 +DA:163,0 +DA:168,0 +LF:36 +LH:0 +BRDA:18,0,0,0 +BRDA:18,0,1,0 +BRDA:25,1,0,0 +BRDA:25,1,1,0 +BRDA:32,2,0,0 +BRDA:32,2,1,0 +BRDA:39,3,0,0 +BRDA:39,3,1,0 +BRDA:46,4,0,0 +BRDA:46,4,1,0 +BRDA:93,5,0,0 +BRDA:93,5,1,0 +BRDA:101,6,0,0 +BRDA:101,6,1,0 +BRDA:108,7,0,0 +BRDA:108,7,1,0 +BRDA:109,8,0,0 +BRDA:109,8,1,0 +BRDA:141,9,0,0 +BRDA:141,9,1,0 +BRDA:146,10,0,0 +BRDA:146,10,1,0 +BRDA:148,11,0,0 +BRDA:148,11,1,0 +BRDA:150,12,0,0 +BRDA:150,12,1,0 +BRDA:166,13,0,0 +BRF:27 +BRH:0 +end_of_record +TN: +SF:src\services\failed-transaction.service.ts +FN:11,(anonymous_1) +FN:18,(anonymous_2) +FN:51,(anonymous_3) +FN:55,(anonymous_4) +FN:64,(anonymous_5) +FN:104,(anonymous_6) +FN:120,(anonymous_7) +FN:180,(anonymous_8) +FN:194,(anonymous_9) +FN:199,(anonymous_10) +FN:222,(anonymous_11) +FN:223,(anonymous_12) +FN:232,(anonymous_13) +FN:237,(anonymous_14) +FN:254,(anonymous_15) +FN:258,(anonymous_16) +FN:264,(anonymous_17) +FN:270,(anonymous_18) +FN:282,(anonymous_19) +FN:289,(anonymous_20) +FN:310,(anonymous_21) +FNF:21 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +DA:1,0 +DA:11,0 +DA:12,0 +DA:13,0 +DA:19,0 +DA:20,0 +DA:22,0 +DA:38,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:45,0 +DA:52,0 +DA:54,0 +DA:55,0 +DA:58,0 +DA:65,0 +DA:68,0 +DA:69,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:81,0 +DA:82,0 +DA:84,0 +DA:85,0 +DA:88,0 +DA:89,0 +DA:92,0 +DA:93,0 +DA:96,0 +DA:97,0 +DA:102,0 +DA:103,0 +DA:104,0 +DA:105,0 +DA:109,0 +DA:110,0 +DA:114,0 +DA:124,0 +DA:125,0 +DA:127,0 +DA:129,0 +DA:130,0 +DA:131,0 +DA:136,0 +DA:139,0 +DA:140,0 +DA:141,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:150,0 +DA:153,0 +DA:154,0 +DA:155,0 +DA:158,0 +DA:159,0 +DA:160,0 +DA:163,0 +DA:164,0 +DA:165,0 +DA:166,0 +DA:169,0 +DA:181,0 +DA:183,0 +DA:184,0 +DA:185,0 +DA:188,0 +DA:194,0 +DA:195,0 +DA:199,0 +DA:200,0 +DA:201,0 +DA:204,0 +DA:209,0 +DA:210,0 +DA:212,0 +DA:218,0 +DA:219,0 +DA:221,0 +DA:222,0 +DA:224,0 +DA:225,0 +DA:226,0 +DA:227,0 +DA:228,0 +DA:231,0 +DA:232,0 +DA:237,0 +DA:239,0 +DA:255,0 +DA:259,0 +DA:260,0 +DA:261,0 +DA:265,0 +DA:266,0 +DA:267,0 +DA:272,0 +DA:279,0 +DA:283,0 +DA:284,0 +DA:285,0 +DA:286,0 +DA:291,0 +DA:303,0 +DA:304,0 +DA:305,0 +DA:307,0 +DA:312,0 +DA:313,0 +DA:314,0 +LF:113 +LH:0 +BRDA:28,0,0,0 +BRDA:28,0,1,0 +BRDA:29,1,0,0 +BRDA:29,1,1,0 +BRDA:32,2,0,0 +BRDA:32,2,1,0 +BRDA:33,3,0,0 +BRDA:33,3,1,0 +BRDA:41,4,0,0 +BRDA:41,4,1,0 +BRDA:52,5,0,0 +BRDA:52,5,1,0 +BRDA:54,6,0,0 +BRDA:54,6,1,0 +BRDA:54,7,0,0 +BRDA:54,7,1,0 +BRDA:68,8,0,0 +BRDA:68,8,1,0 +BRDA:68,9,0,0 +BRDA:68,9,1,0 +BRDA:73,10,0,0 +BRDA:73,10,1,0 +BRDA:73,11,0,0 +BRDA:73,11,1,0 +BRDA:75,12,0,0 +BRDA:75,12,1,0 +BRDA:81,13,0,0 +BRDA:81,13,1,0 +BRDA:84,14,0,0 +BRDA:84,14,1,0 +BRDA:84,15,0,0 +BRDA:84,15,1,0 +BRDA:88,16,0,0 +BRDA:88,16,1,0 +BRDA:88,17,0,0 +BRDA:88,17,1,0 +BRDA:92,18,0,0 +BRDA:92,18,1,0 +BRDA:92,19,0,0 +BRDA:92,19,1,0 +BRDA:96,20,0,0 +BRDA:96,20,1,0 +BRDA:96,21,0,0 +BRDA:96,21,1,0 +BRDA:102,22,0,0 +BRDA:102,22,1,0 +BRDA:103,23,0,0 +BRDA:103,23,1,0 +BRDA:105,24,0,0 +BRDA:105,24,1,0 +BRDA:109,25,0,0 +BRDA:109,25,1,0 +BRDA:127,26,0,0 +BRDA:127,26,1,0 +BRDA:127,26,2,0 +BRDA:127,26,3,0 +BRDA:127,26,4,0 +BRDA:141,27,0,0 +BRDA:141,27,1,0 +BRDA:141,28,0,0 +BRDA:141,28,1,0 +BRDA:209,29,0,0 +BRDA:209,29,1,0 +BRDA:225,30,0,0 +BRDA:225,30,1,0 +BRDA:242,31,0,0 +BRDA:242,31,1,0 +BRDA:259,32,0,0 +BRDA:259,32,1,0 +BRDA:260,33,0,0 +BRDA:260,33,1,0 +BRDA:279,34,0,0 +BRDA:279,34,1,0 +BRDA:304,35,0,0 +BRDA:304,35,1,0 +BRDA:305,36,0,0 +BRDA:305,36,1,0 +BRF:77 +BRH:0 +end_of_record +TN: +SF:src\services\mitigation.service.ts +FN:14,(anonymous_1) +FN:29,(anonymous_2) +FN:36,(anonymous_3) +FN:153,(anonymous_4) +FN:189,(anonymous_5) +FN:242,(anonymous_6) +FN:298,(anonymous_7) +FN:303,(anonymous_8) +FN:319,(anonymous_9) +FNF:9 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +DA:1,0 +DA:10,0 +DA:15,0 +DA:18,0 +DA:19,0 +DA:22,0 +DA:25,0 +DA:28,0 +DA:29,0 +DA:40,0 +DA:42,0 +DA:44,0 +DA:57,0 +DA:66,0 +DA:69,0 +DA:82,0 +DA:91,0 +DA:94,0 +DA:107,0 +DA:116,0 +DA:119,0 +DA:132,0 +DA:135,0 +DA:144,0 +DA:147,0 +DA:154,0 +DA:157,0 +DA:158,0 +DA:170,0 +DA:171,0 +DA:172,0 +DA:183,0 +DA:190,0 +DA:192,0 +DA:193,0 +DA:195,0 +DA:196,0 +DA:198,0 +DA:207,0 +DA:210,0 +DA:219,0 +DA:222,0 +DA:231,0 +DA:236,0 +DA:243,0 +DA:245,0 +DA:247,0 +DA:248,0 +DA:261,0 +DA:264,0 +DA:265,0 +DA:277,0 +DA:280,0 +DA:289,0 +DA:292,0 +DA:299,0 +DA:300,0 +DA:305,0 +DA:313,0 +DA:314,0 +DA:316,0 +DA:320,0 +DA:321,0 +DA:324,0 +DA:326,0 +DA:328,0 +DA:329,0 +DA:331,0 +DA:334,0 +LF:69 +LH:0 +BRDA:42,0,0,0 +BRDA:42,0,1,0 +BRDA:42,0,2,0 +BRDA:42,0,3,0 +BRDA:42,0,4,0 +BRDA:157,1,0,0 +BRDA:157,1,1,0 +BRDA:171,2,0,0 +BRDA:171,2,1,0 +BRDA:195,3,0,0 +BRDA:195,3,1,0 +BRDA:196,4,0,0 +BRDA:196,4,1,0 +BRDA:196,4,2,0 +BRDA:245,5,0,0 +BRDA:245,5,1,0 +BRDA:245,5,2,0 +BRDA:300,6,0,0 +BRDA:300,6,1,0 +BRDA:313,7,0,0 +BRDA:313,7,1,0 +BRDA:326,8,0,0 +BRDA:326,8,1,0 +BRDA:329,9,0,0 +BRDA:329,9,1,0 +BRF:25 +BRH:0 +end_of_record +TN: +SF:src\services\transaction-analysis.service.ts +FN:15,(anonymous_2) +FN:23,(anonymous_3) +FN:85,(anonymous_4) +FN:90,(anonymous_5) +FN:91,(anonymous_6) +FN:109,(anonymous_7) +FN:113,(anonymous_8) +FN:117,(anonymous_9) +FN:140,(anonymous_10) +FN:147,(anonymous_11) +FN:150,(anonymous_12) +FN:159,(anonymous_13) +FN:162,(anonymous_14) +FN:169,(anonymous_15) +FN:172,(anonymous_16) +FN:190,(anonymous_17) +FN:191,(anonymous_18) +FN:199,(anonymous_19) +FN:203,(anonymous_20) +FN:213,(anonymous_21) +FN:216,(anonymous_22) +FN:223,(anonymous_23) +FN:233,(anonymous_24) +FN:238,(anonymous_25) +FN:245,(anonymous_26) +FNF:25 +FNH:0 +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +DA:1,0 +DA:2,0 +DA:3,0 +DA:14,0 +DA:16,0 +DA:17,0 +DA:24,0 +DA:27,0 +DA:30,0 +DA:33,0 +DA:36,0 +DA:39,0 +DA:42,0 +DA:59,0 +DA:74,0 +DA:86,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:93,0 +DA:94,0 +DA:97,0 +DA:99,0 +DA:110,0 +DA:111,0 +DA:113,0 +DA:114,0 +DA:117,0 +DA:118,0 +DA:121,0 +DA:141,0 +DA:148,0 +DA:150,0 +DA:151,0 +DA:152,0 +DA:153,0 +DA:155,0 +DA:160,0 +DA:162,0 +DA:163,0 +DA:166,0 +DA:170,0 +DA:172,0 +DA:173,0 +DA:174,0 +DA:182,0 +DA:183,0 +DA:184,0 +DA:190,0 +DA:191,0 +DA:192,0 +DA:193,0 +DA:196,0 +DA:200,0 +DA:201,0 +DA:203,0 +DA:204,0 +DA:205,0 +DA:206,0 +DA:210,0 +DA:214,0 +DA:216,0 +DA:217,0 +DA:220,0 +DA:221,0 +DA:223,0 +DA:224,0 +DA:225,0 +DA:226,0 +DA:230,0 +DA:234,0 +DA:235,0 +DA:238,0 +DA:239,0 +DA:242,0 +DA:246,0 +LF:76 +LH:0 +BRDA:24,0,0,0 +BRDA:42,1,0,0 +BRDA:42,1,1,0 +BRDA:52,2,0,0 +BRDA:52,2,1,0 +BRDA:53,3,0,0 +BRDA:53,3,1,0 +BRDA:68,4,0,0 +BRDA:68,4,1,0 +BRDA:69,5,0,0 +BRDA:69,5,1,0 +BRDA:89,6,0,0 +BRDA:89,6,1,0 +BRDA:93,7,0,0 +BRDA:93,7,1,0 +BRDA:148,8,0,0 +BRDA:148,8,1,0 +BRDA:152,9,0,0 +BRDA:152,9,1,0 +BRDA:153,10,0,0 +BRDA:153,10,1,0 +BRDA:155,11,0,0 +BRDA:155,11,1,0 +BRDA:163,12,0,0 +BRDA:163,12,1,0 +BRDA:173,13,0,0 +BRDA:173,13,1,0 +BRDA:204,14,0,0 +BRDA:204,14,1,0 +BRDA:217,15,0,0 +BRDA:217,15,1,0 +BRDA:224,16,0,0 +BRDA:224,16,1,0 +BRDA:234,17,0,0 +BRDA:234,17,1,0 +BRDA:239,18,0,0 +BRDA:239,18,1,0 +BRF:37 +BRH:0 +end_of_record +TN: +SF:src\validation\analysis.validator.ts +FN:11,(anonymous_0) +FN:70,(anonymous_1) +FN:184,(anonymous_2) +FN:185,(anonymous_3) +FN:197,(anonymous_4) +FN:207,(anonymous_5) +FN:222,(anonymous_6) +FN:237,(anonymous_7) +FNF:8 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +DA:4,0 +DA:5,0 +DA:6,0 +DA:7,0 +DA:8,0 +DA:9,0 +DA:12,0 +DA:13,0 +DA:15,0 +DA:17,0 +DA:18,0 +DA:24,0 +DA:25,0 +DA:33,0 +DA:34,0 +DA:42,0 +DA:43,0 +DA:53,0 +DA:54,0 +DA:60,0 +DA:61,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:73,0 +DA:74,0 +DA:81,0 +DA:82,0 +DA:89,0 +DA:90,0 +DA:98,0 +DA:99,0 +DA:107,0 +DA:110,0 +DA:111,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:131,0 +DA:132,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:153,0 +DA:154,0 +DA:158,0 +DA:159,0 +DA:168,0 +DA:171,0 +DA:173,0 +DA:185,0 +DA:187,0 +DA:188,0 +DA:193,0 +DA:197,0 +DA:198,0 +DA:199,0 +DA:207,0 +DA:208,0 +DA:209,0 +DA:210,0 +DA:212,0 +DA:213,0 +DA:222,0 +DA:223,0 +DA:228,0 +DA:229,0 +DA:238,0 +DA:239,0 +DA:240,0 +DA:242,0 +LF:71 +LH:0 +BRDA:17,0,0,0 +BRDA:17,0,1,0 +BRDA:24,1,0,0 +BRDA:24,1,1,0 +BRDA:24,2,0,0 +BRDA:24,2,1,0 +BRDA:33,3,0,0 +BRDA:33,3,1,0 +BRDA:33,4,0,0 +BRDA:33,4,1,0 +BRDA:42,5,0,0 +BRDA:42,5,1,0 +BRDA:42,6,0,0 +BRDA:42,6,1,0 +BRDA:53,7,0,0 +BRDA:53,7,1,0 +BRDA:53,8,0,0 +BRDA:53,8,1,0 +BRDA:53,8,2,0 +BRDA:60,9,0,0 +BRDA:60,9,1,0 +BRDA:73,10,0,0 +BRDA:73,10,1,0 +BRDA:73,11,0,0 +BRDA:73,11,1,0 +BRDA:81,12,0,0 +BRDA:81,12,1,0 +BRDA:81,13,0,0 +BRDA:81,13,1,0 +BRDA:89,14,0,0 +BRDA:89,14,1,0 +BRDA:89,15,0,0 +BRDA:89,15,1,0 +BRDA:98,16,0,0 +BRDA:98,16,1,0 +BRDA:98,17,0,0 +BRDA:98,17,1,0 +BRDA:107,18,0,0 +BRDA:107,18,1,0 +BRDA:110,19,0,0 +BRDA:110,19,1,0 +BRDA:121,20,0,0 +BRDA:121,20,1,0 +BRDA:122,21,0,0 +BRDA:122,21,1,0 +BRDA:131,22,0,0 +BRDA:131,22,1,0 +BRDA:142,23,0,0 +BRDA:142,23,1,0 +BRDA:143,24,0,0 +BRDA:143,24,1,0 +BRDA:143,25,0,0 +BRDA:143,25,1,0 +BRDA:153,26,0,0 +BRDA:153,26,1,0 +BRDA:158,27,0,0 +BRDA:158,27,1,0 +BRDA:164,28,0,0 +BRDA:164,28,1,0 +BRDA:178,29,0,0 +BRDA:178,29,1,0 +BRDA:187,30,0,0 +BRDA:187,30,1,0 +BRDA:197,31,0,0 +BRDA:197,31,1,0 +BRDA:198,32,0,0 +BRDA:198,32,1,0 +BRDA:208,33,0,0 +BRDA:208,33,1,0 +BRDA:210,34,0,0 +BRDA:210,34,1,0 +BRDA:212,35,0,0 +BRDA:212,35,1,0 +BRDA:223,36,0,0 +BRDA:223,36,1,0 +BRDA:223,36,2,0 +BRDA:228,37,0,0 +BRDA:228,37,1,0 +BRF:78 +BRH:0 +end_of_record diff --git a/apps/api/package-lock.json b/apps/api/package-lock.json index 81f55ec..be10f40 100644 --- a/apps/api/package-lock.json +++ b/apps/api/package-lock.json @@ -9,21 +9,28 @@ "version": "0.1.0", "dependencies": { "@nestjs/common": "^10.3.0", + "@nestjs/config": "^3.1.0", "@nestjs/core": "^10.3.0", + "@nestjs/jwt": "^10.2.0", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.3.0", "@nestjs/swagger": "^7.1.0", "@nestjs/throttler": "^5.1.0", "bullmq": "^5.1.0", "ioredis": "^5.3.0", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "reflect-metadata": "^0.2.1", "rxjs": "^7.8.1" }, "devDependencies": { "@nestjs/cli": "^10.3.0", + "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.4.22", "@types/express": "^4.17.21", "@types/jest": "^30.0.0", "@types/node": "^20.10.0", + "@types/passport-jwt": "^4.0.0", "jest": "^30.2.0", "ts-jest": "^29.0.0", "typescript": "^5.3.0" @@ -220,6 +227,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -314,16 +322,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -857,10 +855,10 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@types/node": { - "version": "20.19.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz", - "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==", + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", "engines": { @@ -1210,39 +1208,6 @@ } } }, - "node_modules/@jest/reporters/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/@jest/schemas": { "version": "30.0.5", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", @@ -1610,6 +1575,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.22.tgz", "integrity": "sha512-fxJ4v85nDHaqT1PmfNCQ37b/jcv2OojtXTaK1P2uAXhzLf9qq6WNUOFvxBrV4fhQek1EQoT1o9oj5xAZmv3NRw==", "license": "MIT", + "peer": true, "dependencies": { "file-type": "20.4.1", "iterare": "1.2.1", @@ -1635,12 +1601,28 @@ } } }, + "node_modules/@nestjs/config": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.3.0.tgz", + "integrity": "sha512-pdGTp8m9d0ZCrjTpjkUbZx6gyf2IKf+7zlkrPNMsJzYZ4bFRRTpXrnj+556/5uiI6AfL5mMrJc2u7dB6bvM+VA==", + "license": "MIT", + "dependencies": { + "dotenv": "16.4.5", + "dotenv-expand": "10.0.0", + "lodash": "4.17.21" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "rxjs": "^7.1.0" + } + }, "node_modules/@nestjs/core": { "version": "10.4.22", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.22.tgz", "integrity": "sha512-6IX9+VwjiKtCjx+mXVPncpkQ5ZjKfmssOZPFexmT+6T9H9wZ3svpYACAo7+9e7Nr9DZSoRZw3pffkJP7Z0UjaA==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", @@ -1673,6 +1655,19 @@ } } }, + "node_modules/@nestjs/jwt": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", + "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "9.0.5", + "jsonwebtoken": "9.0.2" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, "node_modules/@nestjs/mapped-types": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", @@ -1693,11 +1688,22 @@ } } }, + "node_modules/@nestjs/passport": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", + "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "passport": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0" + } + }, "node_modules/@nestjs/platform-express": { "version": "10.4.22", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.22.tgz", "integrity": "sha512-ySSq7Py/DFozzZdNDH67m/vHoeVdphDniWBnl6q5QVoXldDdrZIHLXLRMPayTDh5A95nt7jjJzmD4qpTbNQ6tA==", "license": "MIT", + "peer": true, "dependencies": { "body-parser": "1.20.4", "cors": "2.8.5", @@ -2110,6 +2116,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -2121,12 +2136,43 @@ "version": "20.19.33", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz", "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, + "node_modules/@types/passport": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", + "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" + } + }, + "node_modules/@types/passport-strategy": { + "version": "0.2.38", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", + "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/passport": "*" + } + }, "node_modules/@types/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", @@ -2681,6 +2727,7 @@ "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -3059,6 +3106,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3121,6 +3169,12 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3128,13 +3182,13 @@ "license": "MIT" }, "node_modules/bullmq": { - "version": "5.69.3", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.69.3.tgz", - "integrity": "sha512-P9uLsR7fDvejH/1m6uur6j7U9mqY6nNt+XvhlhStOUe7jdwbZoP/c2oWNtE+8ljOlubw4pRUKymtRqkyvloc4A==", + "version": "5.70.1", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.70.1.tgz", + "integrity": "sha512-HjfGHfICkAClrFL0Y07qNbWcmiOCv1l+nusupXUjrvTPuDEyPEJ23MP0lUwUs/QEy1a3pWt/P/sCsSZ1RjRK+w==", "license": "MIT", "dependencies": { "cron-parser": "4.9.0", - "ioredis": "5.9.2", + "ioredis": "5.9.3", "msgpackr": "1.11.5", "node-abort-controller": "3.1.1", "semver": "7.7.4", @@ -3142,53 +3196,6 @@ "uuid": "11.1.0" } }, - "node_modules/bullmq/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/bullmq/node_modules/ioredis": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.9.2.tgz", - "integrity": "sha512-tAAg/72/VxOUW7RQSX1pIxJVucYKcjFjfvj60L57jrZpYCHC3XN0WCQ3sNYL4Gmvv+7GPvTAjc+KSdeNuE8oWQ==", - "license": "MIT", - "dependencies": { - "@ioredis/commands": "1.5.0", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, - "node_modules/bullmq/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -3278,9 +3285,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001770", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", - "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", + "version": "1.0.30001774", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz", + "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==", "dev": true, "funding": [ { @@ -3814,6 +3821,27 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -3835,6 +3863,15 @@ "dev": true, "license": "MIT" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4015,17 +4052,14 @@ "node": ">=4.0" } }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", - "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=6.0.0" + "node": ">=4.0" } }, "node_modules/etag": { @@ -4293,21 +4327,6 @@ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -4430,10 +4449,6 @@ "node": ">=6.9.0" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001770", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", - "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -4546,24 +4561,37 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -4807,24 +4835,6 @@ "wrappy": "1" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.302", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", - "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", - "dev": true, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -5157,6 +5167,7 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -5356,11 +5367,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "node_modules/jest-environment-node": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", @@ -5405,39 +5411,6 @@ "fsevents": "^2.3.3" } }, - "node_modules/jest-haste-map/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/jest-leak-detector": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", @@ -5600,60 +5573,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runner/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runner/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/jest-runtime": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", @@ -5688,16 +5607,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runtime/node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/jest-snapshot": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", @@ -5814,18 +5723,20 @@ } }, "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "supports-color": "^8.1.1" }, "engines": { - "node": ">= 10.13.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-worker/node_modules/supports-color": { @@ -5923,6 +5834,55 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.2", + "safe-buffer": "^5.0.1" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -5979,12 +5939,48 @@ "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "license": "MIT" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", "license": "MIT" }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -5992,6 +5988,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -6010,11 +6012,14 @@ } }, "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "license": "ISC" + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } }, "node_modules/luxon": { "version": "3.7.2", @@ -6198,9 +6203,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", + "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "license": "ISC", "dependencies": { @@ -6626,6 +6631,43 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "license": "MIT", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6673,6 +6715,13 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/path-to-regexp": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", @@ -6689,6 +6738,11 @@ "node": ">=8" } }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6931,7 +6985,8 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/repeat-string": { "version": "1.6.1", @@ -7082,11 +7137,12 @@ } }, "node_modules/schema-utils/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7119,7 +7175,6 @@ "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -7336,9 +7391,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "license": "MIT", "dependencies": { @@ -7491,13 +7546,13 @@ } }, "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-final-newline": { @@ -7651,6 +7706,21 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", @@ -7671,6 +7741,22 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -7678,6 +7764,27 @@ "dev": true, "license": "MIT" }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -7895,6 +8002,16 @@ "node": ">=10.13.0" } }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -7949,6 +8066,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7999,7 +8117,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/universalify": { @@ -8195,6 +8312,7 @@ "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", diff --git a/apps/api/package.json b/apps/api/package.json index 480a2c6..20c21d3 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -20,6 +20,11 @@ "@nestjs/platform-express": "^10.3.0", "@nestjs/throttler": "^5.1.0", "@nestjs/swagger": "^7.1.0", + "@nestjs/config": "^3.1.0", + "@nestjs/jwt": "^10.2.0", + "@nestjs/passport": "^10.0.3", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "bullmq": "^5.1.0", "ioredis": "^5.3.0", "reflect-metadata": "^0.2.1", @@ -27,11 +32,16 @@ }, "devDependencies": { "@nestjs/cli": "^10.3.0", +<<<<<<< feat/jwtValidationLayer + "@nestjs/schematics": "^10.0.0", +======= "@nestjs/schematics": "^10.3.0", +>>>>>>> main "@nestjs/testing": "^10.4.22", "@types/express": "^4.17.21", "@types/jest": "^30.0.0", "@types/node": "^20.10.0", + "@types/passport-jwt": "^4.0.0", "jest": "^30.2.0", "ts-jest": "^29.0.0", "typescript": "^5.3.0" diff --git a/apps/api/src/__tests__/cross-chain-gas.service.spec.ts b/apps/api/src/__tests__/cross-chain-gas.service.spec.ts index c8b241d..f506357 100644 --- a/apps/api/src/__tests__/cross-chain-gas.service.spec.ts +++ b/apps/api/src/__tests__/cross-chain-gas.service.spec.ts @@ -139,7 +139,7 @@ describe('CrossChainGasService', () => { const ethereumChain = result.chains.find(chain => chain.chainId === 1); // Polygon should be cheaper than Ethereum for transfers - expect(polygonChain.estimatedCostUSD).toBeLessThan(ethereumChain.estimatedCostUSD); + expect(polygonChain!.estimatedCostUSD).toBeLessThan(ethereumChain!.estimatedCostUSD); }); }); }); diff --git a/apps/api/src/app.controller.ts b/apps/api/src/app.controller.ts index aeb9ff1..ee5778c 100644 --- a/apps/api/src/app.controller.ts +++ b/apps/api/src/app.controller.ts @@ -1,10 +1,13 @@ import { Controller, Get } from '@nestjs/common'; +import { Public } from './auth'; @Controller() export class AppController { /** * Root endpoint - API info + * Public endpoint for API discovery */ + @Public() @Get() getRoot(): { name: string; version: string; health: string } { return { @@ -17,7 +20,9 @@ export class AppController { /** * Health check endpoint * Returns a simple status to verify the API is running + * Public endpoint for monitoring and load balancers */ + @Public() @Get('health') getHealth(): { status: string } { return { status: 'ok' }; diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts index c581130..5c9c24a 100644 --- a/apps/api/src/app.module.ts +++ b/apps/api/src/app.module.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; import { APP_GUARD } from '@nestjs/core'; -import { ThrottlerModule } from '@nestjs/throttler'; +import { ConfigModule } from '@nestjs/config'; +import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'; import { AppController } from './app.controller'; import { ExampleController } from './example/example.controller'; import { FailedTransactionController } from './controllers/failed-transaction.controller'; @@ -10,10 +11,15 @@ import { MitigationService } from './services/mitigation.service'; import { TransactionAnalysisService } from './services/transaction-analysis.service'; import { CrossChainGasService } from './services/cross-chain-gas.service'; import { RateLimitingModule, RateLimitGuard } from './rate-limiting'; +import { AuthModule, JwtAuthGuard, RolesGuard } from './auth'; @Module({ imports: [ - // Legacy throttler as backup (disabled by default, can be enabled via env) + // Global configuration module for environment variables + ConfigModule.forRoot({ + isGlobal: true, + envFilePath: ['.env', '.env.local'], + }), ThrottlerModule.forRoot([ { name: 'default', @@ -21,6 +27,8 @@ import { RateLimitingModule, RateLimitGuard } from './rate-limiting'; limit: 100, // 100 requests per TTL window (generous fallback) }, ]), + // JWT Authentication module + AuthModule, // New Redis-based rate limiting module RateLimitingModule.forRoot(), ], @@ -37,6 +45,16 @@ import { RateLimitingModule, RateLimitGuard } from './rate-limiting'; provide: APP_GUARD, useClass: RateLimitGuard, }, + // Apply JWT authentication guard globally to all routes + { + provide: APP_GUARD, + useClass: JwtAuthGuard, + }, + // Apply roles guard globally for RBAC enforcement + { + provide: APP_GUARD, + useClass: RolesGuard, + }, FailedTransactionService, MitigationService, TransactionAnalysisService, diff --git a/apps/api/src/auth/__tests__/auth.module.spec.ts b/apps/api/src/auth/__tests__/auth.module.spec.ts new file mode 100644 index 0000000..c2d7609 --- /dev/null +++ b/apps/api/src/auth/__tests__/auth.module.spec.ts @@ -0,0 +1,123 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { JwtModule } from '@nestjs/jwt'; +import { PassportModule } from '@nestjs/passport'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { AuthModule } from '../auth.module'; +import { JwtStrategy } from '../strategies/jwt.strategy'; +import { JwtAuthGuard } from '../guards/jwt-auth.guard'; +import { RolesGuard } from '../guards/roles.guard'; + +describe('AuthModule', () => { + let module: TestingModule; + + beforeEach(async () => { + module = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [() => ({ + JWT_SECRET: 'test-secret-key-that-is-at-least-32-characters-long', + JWT_ISSUER: 'test-issuer', + JWT_AUDIENCE: 'test-audience', + })], + }), + AuthModule, + ], + }).compile(); + }); + + afterEach(async () => { + await module.close(); + }); + + it('should compile the module', () => { + expect(module).toBeDefined(); + }); + + it('should provide JwtStrategy', () => { + const strategy = module.get(JwtStrategy); + expect(strategy).toBeDefined(); + }); + + it('should provide JwtAuthGuard', () => { + const guard = module.get(JwtAuthGuard); + expect(guard).toBeDefined(); + }); + + it('should provide RolesGuard', () => { + const guard = module.get(RolesGuard); + expect(guard).toBeDefined(); + }); + + it('should export JwtAuthGuard', async () => { + // Test that JwtAuthGuard can be imported from the module + const exportedGuard = module.get(JwtAuthGuard); + expect(exportedGuard).toBeInstanceOf(JwtAuthGuard); + }); + + it('should export RolesGuard', async () => { + const exportedGuard = module.get(RolesGuard); + expect(exportedGuard).toBeInstanceOf(RolesGuard); + }); + + it('should export PassportModule', () => { + const passportModule = module.get(PassportModule); + expect(passportModule).toBeDefined(); + }); + + it('should export JwtModule', () => { + const jwtModule = module.get(JwtModule); + expect(jwtModule).toBeDefined(); + }); + + describe('JWT Configuration', () => { + it('should configure JWT with secret from ConfigService', () => { + const configService = module.get(ConfigService); + const secret = configService.get('JWT_SECRET'); + + expect(secret).toBe('test-secret-key-that-is-at-least-32-characters-long'); + }); + + it('should configure JWT with issuer from ConfigService', () => { + const configService = module.get(ConfigService); + const issuer = configService.get('JWT_ISSUER'); + + expect(issuer).toBe('test-issuer'); + }); + + it('should configure JWT with audience from ConfigService', () => { + const configService = module.get(ConfigService); + const audience = configService.get('JWT_AUDIENCE'); + + expect(audience).toBe('test-audience'); + }); + }); + + describe('Module Metadata', () => { + it('should have PassportModule as import', () => { + const passportModule = module.get(PassportModule); + expect(passportModule).toBeDefined(); + }); + + it('should have JwtModule as import', () => { + const jwtModule = module.get(JwtModule); + expect(jwtModule).toBeDefined(); + }); + }); +}); + +describe('AuthModule without JWT_SECRET', () => { + it('should throw error when JWT_SECRET is not configured', async () => { + await expect( + Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [() => ({})], // Empty config + }), + AuthModule, + ], + }).compile() + ).rejects.toThrow('JWT_SECRET environment variable is required'); + }); +}); diff --git a/apps/api/src/auth/__tests__/decorators.spec.ts b/apps/api/src/auth/__tests__/decorators.spec.ts new file mode 100644 index 0000000..db4c758 --- /dev/null +++ b/apps/api/src/auth/__tests__/decorators.spec.ts @@ -0,0 +1,56 @@ +import { Public, IS_PUBLIC_KEY, Roles, Role, ROLES_KEY } from '../decorators'; +import { CurrentUser } from '../decorators/current-user.decorator'; + +describe('Decorators', () => { + describe('Public decorator', () => { + it('should export IS_PUBLIC_KEY constant', () => { + expect(IS_PUBLIC_KEY).toBe('isPublic'); + }); + + it('should be a function', () => { + expect(typeof Public).toBe('function'); + }); + }); + + describe('Roles decorator', () => { + it('should export ROLES_KEY constant', () => { + expect(ROLES_KEY).toBe('roles'); + }); + + it('should be a function', () => { + expect(typeof Roles).toBe('function'); + }); + }); + + describe('Role constants', () => { + it('should have ADMIN role', () => { + expect(Role.ADMIN).toBe('admin'); + }); + + it('should have ANALYST role', () => { + expect(Role.ANALYST).toBe('analyst'); + }); + + it('should have USER role', () => { + expect(Role.USER).toBe('user'); + }); + + it('should have READONLY role', () => { + expect(Role.READONLY).toBe('readonly'); + }); + + it('should have correct role values', () => { + expect(Role.ADMIN).toBe('admin'); + expect(Role.ANALYST).toBe('analyst'); + expect(Role.USER).toBe('user'); + expect(Role.READONLY).toBe('readonly'); + }); + }); + + describe('CurrentUser decorator', () => { + it('should be defined', () => { + expect(CurrentUser).toBeDefined(); + expect(typeof CurrentUser).toBe('function'); + }); + }); +}); diff --git a/apps/api/src/auth/__tests__/index.ts b/apps/api/src/auth/__tests__/index.ts new file mode 100644 index 0000000..2803b2e --- /dev/null +++ b/apps/api/src/auth/__tests__/index.ts @@ -0,0 +1,6 @@ +// Auth module test exports +export * from './jwt.strategy.spec'; +export * from './jwt-auth.guard.spec'; +export * from './roles.guard.spec'; +export * from './decorators.spec'; +export * from './auth.module.spec'; diff --git a/apps/api/src/auth/__tests__/jwt-auth.guard.spec.ts b/apps/api/src/auth/__tests__/jwt-auth.guard.spec.ts new file mode 100644 index 0000000..ffe359f --- /dev/null +++ b/apps/api/src/auth/__tests__/jwt-auth.guard.spec.ts @@ -0,0 +1,151 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ExecutionContext, UnauthorizedException } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { JwtAuthGuard } from '../guards/jwt-auth.guard'; +import { IS_PUBLIC_KEY } from '../decorators/public.decorator'; + +describe('JwtAuthGuard', () => { + let guard: JwtAuthGuard; + let reflector: Reflector; + + const mockReflector = { + getAllAndOverride: jest.fn(), + }; + + const createMockExecutionContext = (isPublic: boolean = false): ExecutionContext => { + mockReflector.getAllAndOverride.mockImplementation((key: string) => { + if (key === IS_PUBLIC_KEY) return isPublic; + return undefined; + }); + + return { + getHandler: jest.fn(), + getClass: jest.fn(), + switchToHttp: jest.fn().mockReturnValue({ + getRequest: jest.fn().mockReturnValue({}), + getResponse: jest.fn(), + }), + } as unknown as ExecutionContext; + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + JwtAuthGuard, + { + provide: Reflector, + useValue: mockReflector, + }, + ], + }).compile(); + + guard = module.get(JwtAuthGuard); + reflector = module.get(Reflector); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('canActivate', () => { + it('should allow access to public routes without authentication', () => { + const context = createMockExecutionContext(true); + + const result = guard.canActivate(context); + + expect(result).toBe(true); + expect(mockReflector.getAllAndOverride).toHaveBeenCalledWith(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + }); + + it('should require authentication for non-public routes', () => { + const context = createMockExecutionContext(false); + + // Mock the parent canActivate to return true (authenticated) + jest.spyOn(guard, 'canActivate').mockReturnValueOnce(true as any); + + const result = guard.canActivate(context); + + expect(result).toBe(true); + }); + }); + + describe('handleRequest', () => { + it('should return user when authentication is successful', () => { + const user = { userId: 'user-123', roles: ['user'] }; + + const result = guard.handleRequest(null, user, null); + + expect(result).toEqual(user); + }); + + it('should throw UnauthorizedException when user is null', () => { + expect(() => guard.handleRequest(null, null, null)).toThrow(UnauthorizedException); + + try { + guard.handleRequest(null, null, null); + } catch (error) { + expect(error).toBeInstanceOf(UnauthorizedException); + expect(error.response).toMatchObject({ + error: 'Unauthorized', + message: 'Invalid or expired JWT access token.', + }); + expect(error.response.timestamp).toBeDefined(); + } + }); + + it('should throw UnauthorizedException when error is present', () => { + const error = new Error('Some error'); + + expect(() => guard.handleRequest(error, null, null)).toThrow(UnauthorizedException); + }); + + it('should include TokenExpiredError message when token is expired', () => { + const info = { name: 'TokenExpiredError', message: 'jwt expired' }; + + try { + guard.handleRequest(null, null, info); + } catch (error) { + expect(error).toBeInstanceOf(UnauthorizedException); + expect(error.response.message).toBe('JWT access token has expired.'); + } + }); + + it('should include JsonWebTokenError message when token format is invalid', () => { + const info = { name: 'JsonWebTokenError', message: 'invalid token' }; + + try { + guard.handleRequest(null, null, info); + } catch (error) { + expect(error).toBeInstanceOf(UnauthorizedException); + expect(error.response.message).toBe('Invalid JWT access token format.'); + } + }); + + it('should use info message when available', () => { + const info = { message: 'Custom error message' }; + + try { + guard.handleRequest(null, null, info); + } catch (error) { + expect(error).toBeInstanceOf(UnauthorizedException); + expect(error.response.message).toBe('Custom error message'); + } + }); + + it('should include timestamp in error response', () => { + const beforeTime = new Date().getTime(); + + try { + guard.handleRequest(null, null, null); + } catch (error: any) { + const afterTime = new Date().getTime(); + const timestamp = new Date(error.response.timestamp).getTime(); + expect(timestamp).toBeGreaterThanOrEqual(beforeTime); + expect(timestamp).toBeLessThanOrEqual(afterTime); + } + }); + }); +}); diff --git a/apps/api/src/auth/__tests__/jwt.strategy.spec.ts b/apps/api/src/auth/__tests__/jwt.strategy.spec.ts new file mode 100644 index 0000000..01233b3 --- /dev/null +++ b/apps/api/src/auth/__tests__/jwt.strategy.spec.ts @@ -0,0 +1,155 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigService } from '@nestjs/config'; +import { UnauthorizedException } from '@nestjs/common'; +import { JwtStrategy, JwtPayload } from '../strategies/jwt.strategy'; + +describe('JwtStrategy', () => { + let strategy: JwtStrategy; + let configService: ConfigService; + + const mockConfigService = { + get: jest.fn(), + }; + + beforeEach(async () => { + mockConfigService.get.mockImplementation((key: string, defaultValue?: any) => { + const config: Record = { + JWT_SECRET: 'test-secret-key-that-is-at-least-32-characters-long', + JWT_ISSUER: 'test-issuer', + JWT_AUDIENCE: 'test-audience', + }; + return config[key] ?? defaultValue; + }); + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + JwtStrategy, + { + provide: ConfigService, + useValue: mockConfigService, + }, + ], + }).compile(); + + strategy = module.get(JwtStrategy); + configService = module.get(ConfigService); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('constructor', () => { + it('should throw error if JWT_SECRET is not set', () => { + mockConfigService.get.mockReturnValue(undefined); + + expect(() => { + new JwtStrategy(configService); + }).toThrow('JWT_SECRET environment variable is required'); + }); + + it('should use default issuer and audience if not configured', () => { + mockConfigService.get.mockImplementation((key: string, defaultValue?: any) => { + if (key === 'JWT_SECRET') return 'test-secret-key-that-is-at-least-32-characters-long'; + return defaultValue; + }); + + const testStrategy = new JwtStrategy(configService); + expect(testStrategy).toBeDefined(); + }); + }); + + describe('validate', () => { + const validPayload: JwtPayload = { + sub: 'user-123', + iss: 'test-issuer', + aud: 'test-audience', + exp: Math.floor(Date.now() / 1000) + 3600, + iat: Math.floor(Date.now() / 1000), + roles: ['user', 'analyst'], + permissions: ['read:transactions', 'write:transactions'], + }; + + it('should validate a valid payload and return user', async () => { + const result = await strategy.validate(validPayload); + + expect(result).toEqual({ + userId: 'user-123', + roles: ['user', 'analyst'], + permissions: ['read:transactions', 'write:transactions'], + }); + }); + + it('should validate payload with empty roles and permissions', async () => { + const payloadWithoutRoles: JwtPayload = { + sub: 'user-123', + iss: 'test-issuer', + aud: 'test-audience', + exp: Math.floor(Date.now() / 1000) + 3600, + iat: Math.floor(Date.now() / 1000), + }; + + const result = await strategy.validate(payloadWithoutRoles); + + expect(result).toEqual({ + userId: 'user-123', + roles: [], + permissions: [], + }); + }); + + it('should throw UnauthorizedException if sub claim is missing', async () => { + const invalidPayload = { ...validPayload, sub: undefined as any }; + + await expect(strategy.validate(invalidPayload)).rejects.toThrow( + new UnauthorizedException('Invalid token: missing subject claim') + ); + }); + + it('should throw UnauthorizedException if iss claim is missing', async () => { + const invalidPayload = { ...validPayload, iss: undefined as any }; + + await expect(strategy.validate(invalidPayload)).rejects.toThrow( + new UnauthorizedException('Invalid token: missing issuer claim') + ); + }); + + it('should throw UnauthorizedException if aud claim is missing', async () => { + const invalidPayload = { ...validPayload, aud: undefined as any }; + + await expect(strategy.validate(invalidPayload)).rejects.toThrow( + new UnauthorizedException('Invalid token: missing audience claim') + ); + }); + + it('should throw UnauthorizedException if exp claim is missing', async () => { + const invalidPayload = { ...validPayload, exp: undefined as any }; + + await expect(strategy.validate(invalidPayload)).rejects.toThrow( + new UnauthorizedException('Invalid token: missing expiration claim') + ); + }); + + it('should throw UnauthorizedException if token is expired', async () => { + const expiredPayload: JwtPayload = { + ...validPayload, + exp: Math.floor(Date.now() / 1000) - 3600, // 1 hour ago + }; + + await expect(strategy.validate(expiredPayload)).rejects.toThrow( + new UnauthorizedException('Token has expired') + ); + }); + + it('should reject token that expired in the past', async () => { + const expiredPayload: JwtPayload = { + ...validPayload, + exp: Math.floor(Date.now() / 1000) - 1, // 1 second ago + }; + + await expect(strategy.validate(expiredPayload)).rejects.toThrow( + new UnauthorizedException('Token has expired') + ); + }); + }); +}); diff --git a/apps/api/src/auth/__tests__/roles.guard.spec.ts b/apps/api/src/auth/__tests__/roles.guard.spec.ts new file mode 100644 index 0000000..817e550 --- /dev/null +++ b/apps/api/src/auth/__tests__/roles.guard.spec.ts @@ -0,0 +1,193 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ExecutionContext, ForbiddenException } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { RolesGuard } from '../guards/roles.guard'; +import { ROLES_KEY } from '../decorators/roles.decorator'; +import { JwtUser } from '../strategies/jwt.strategy'; + +describe('RolesGuard', () => { + let guard: RolesGuard; + let reflector: Reflector; + + const mockReflector = { + getAllAndOverride: jest.fn(), + }; + + const createMockExecutionContext = (user: JwtUser | null, requiredRoles?: string[]): ExecutionContext => { + mockReflector.getAllAndOverride.mockImplementation((key: string) => { + if (key === ROLES_KEY) return requiredRoles; + return undefined; + }); + + return { + getHandler: jest.fn(), + getClass: jest.fn(), + switchToHttp: jest.fn().mockReturnValue({ + getRequest: jest.fn().mockReturnValue({ user }), + }), + } as unknown as ExecutionContext; + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + RolesGuard, + { + provide: Reflector, + useValue: mockReflector, + }, + ], + }).compile(); + + guard = module.get(RolesGuard); + reflector = module.get(Reflector); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('canActivate', () => { + it('should allow access when no roles are required', () => { + const context = createMockExecutionContext( + { userId: 'user-123', roles: [], permissions: [] }, + [] + ); + + const result = guard.canActivate(context); + + expect(result).toBe(true); + }); + + it('should allow access when roles array is empty', () => { + const context = createMockExecutionContext( + { userId: 'user-123', roles: [], permissions: [] }, + [] + ); + + const result = guard.canActivate(context); + + expect(result).toBe(true); + }); + + it('should allow access when roles is undefined', () => { + const context = createMockExecutionContext( + { userId: 'user-123', roles: [], permissions: [] }, + undefined + ); + + const result = guard.canActivate(context); + + expect(result).toBe(true); + }); + + it('should allow access when user has one of the required roles', () => { + const context = createMockExecutionContext( + { userId: 'user-123', roles: ['user', 'analyst'], permissions: [] }, + ['admin', 'analyst'] + ); + + const result = guard.canActivate(context); + + expect(result).toBe(true); + }); + + it('should allow access when user has exact required role', () => { + const context = createMockExecutionContext( + { userId: 'user-123', roles: ['admin'], permissions: [] }, + ['admin'] + ); + + const result = guard.canActivate(context); + + expect(result).toBe(true); + }); + + it('should deny access when user has none of the required roles', () => { + const context = createMockExecutionContext( + { userId: 'user-123', roles: ['user'], permissions: [] }, + ['admin'] + ); + + expect(() => guard.canActivate(context)).toThrow(ForbiddenException); + + try { + guard.canActivate(context); + } catch (error) { + expect(error).toBeInstanceOf(ForbiddenException); + expect(error.response).toMatchObject({ + error: 'Forbidden', + message: 'Access denied: Required roles are [admin]', + }); + expect(error.response.timestamp).toBeDefined(); + } + }); + + it('should deny access when user roles array is empty', () => { + const context = createMockExecutionContext( + { userId: 'user-123', roles: [], permissions: [] }, + ['admin'] + ); + + expect(() => guard.canActivate(context)).toThrow(ForbiddenException); + }); + + it('should throw ForbiddenException when user is not authenticated', () => { + const context = createMockExecutionContext( + null, + ['admin'] + ); + + expect(() => guard.canActivate(context)).toThrow(ForbiddenException); + + try { + guard.canActivate(context); + } catch (error) { + expect(error).toBeInstanceOf(ForbiddenException); + expect(error.response.message).toBe('Access denied: User not authenticated'); + } + }); + + it('should include all required roles in error message', () => { + const context = createMockExecutionContext( + { userId: 'user-123', roles: ['user'], permissions: [] }, + ['admin', 'analyst', 'manager'] + ); + + try { + guard.canActivate(context); + } catch (error) { + expect(error.response.message).toBe( + 'Access denied: Required roles are [admin, analyst, manager]' + ); + } + }); + + it('should handle user with undefined roles property', () => { + const context = createMockExecutionContext( + { userId: 'user-123', roles: undefined as any, permissions: [] }, + ['admin'] + ); + + expect(() => guard.canActivate(context)).toThrow(ForbiddenException); + }); + + it('should include timestamp in error response', () => { + const context = createMockExecutionContext( + { userId: 'user-123', roles: ['user'], permissions: [] }, + ['admin'] + ); + + const beforeTime = new Date().getTime(); + + try { + guard.canActivate(context); + } catch (error: any) { + const afterTime = new Date().getTime(); + const timestamp = new Date(error.response.timestamp).getTime(); + expect(timestamp).toBeGreaterThanOrEqual(beforeTime); + expect(timestamp).toBeLessThanOrEqual(afterTime); + } + }); + }); +}); diff --git a/apps/api/src/auth/auth.module.ts b/apps/api/src/auth/auth.module.ts new file mode 100644 index 0000000..8fee80e --- /dev/null +++ b/apps/api/src/auth/auth.module.ts @@ -0,0 +1,27 @@ +import { Module } from '@nestjs/common'; +import { JwtModule } from '@nestjs/jwt'; +import { PassportModule } from '@nestjs/passport'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { JwtStrategy } from './strategies/jwt.strategy'; +import { JwtAuthGuard } from './guards/jwt-auth.guard'; +import { RolesGuard } from './guards/roles.guard'; + +@Module({ + imports: [ + PassportModule.register({ defaultStrategy: 'jwt' }), + JwtModule.registerAsync({ + imports: [ConfigModule], + useFactory: async (configService: ConfigService) => ({ + secret: configService.get('JWT_SECRET'), + signOptions: { + issuer: configService.get('JWT_ISSUER', 'gasguard-api'), + audience: configService.get('JWT_AUDIENCE', 'gasguard-client'), + }, + }), + inject: [ConfigService], + }), + ], + providers: [JwtStrategy, JwtAuthGuard, RolesGuard], + exports: [JwtAuthGuard, RolesGuard, PassportModule, JwtModule], +}) +export class AuthModule {} diff --git a/apps/api/src/auth/decorators/current-user.decorator.ts b/apps/api/src/auth/decorators/current-user.decorator.ts new file mode 100644 index 0000000..147a1e8 --- /dev/null +++ b/apps/api/src/auth/decorators/current-user.decorator.ts @@ -0,0 +1,31 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; +import { JwtUser } from '../strategies/jwt.strategy'; + +/** + * Extracts the current authenticated user from the request. + * Returns the full user object or a specific property if key is provided. + * + * @param key - Optional property key to extract from user object + * @returns The user object or the specified property value + * + * @example + * ```typescript + * @Get('profile') + * getProfile(@CurrentUser() user: JwtUser) { ... } + * + * @Get('user-id') + * getUserId(@CurrentUser('userId') userId: string) { ... } + * ``` + */ +export const CurrentUser = createParamDecorator( + (key: keyof JwtUser | undefined, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest<{ user: JwtUser }>(); + const user = request.user; + + if (!user) { + return undefined; + } + + return key ? user[key] : user; + }, +); diff --git a/apps/api/src/auth/decorators/index.ts b/apps/api/src/auth/decorators/index.ts new file mode 100644 index 0000000..85a9f24 --- /dev/null +++ b/apps/api/src/auth/decorators/index.ts @@ -0,0 +1,3 @@ +export { Public, IS_PUBLIC_KEY } from './public.decorator'; +export { Roles, Role, RoleType, ROLES_KEY } from './roles.decorator'; +export { CurrentUser } from './current-user.decorator'; diff --git a/apps/api/src/auth/decorators/public.decorator.ts b/apps/api/src/auth/decorators/public.decorator.ts new file mode 100644 index 0000000..e733b75 --- /dev/null +++ b/apps/api/src/auth/decorators/public.decorator.ts @@ -0,0 +1,16 @@ +import { SetMetadata } from '@nestjs/common'; + +export const IS_PUBLIC_KEY = 'isPublic'; + +/** + * Marks a route or controller as public, bypassing JWT authentication. + * Use this decorator on endpoints that don't require authentication. + * + * @example + * ```typescript + * @Public() + * @Get('health') + * getHealth() { ... } + * ``` + */ +export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); diff --git a/apps/api/src/auth/decorators/roles.decorator.ts b/apps/api/src/auth/decorators/roles.decorator.ts new file mode 100644 index 0000000..b581e1a --- /dev/null +++ b/apps/api/src/auth/decorators/roles.decorator.ts @@ -0,0 +1,30 @@ +import { SetMetadata } from '@nestjs/common'; + +export const ROLES_KEY = 'roles'; + +/** + * Defines the roles required to access a route or controller. + * Must be used in combination with JwtAuthGuard and RolesGuard. + * + * @param roles - Array of role names required for access + * + * @example + * ```typescript + * @Roles('admin', 'analyst') + * @Get('sensitive-data') + * getSensitiveData() { ... } + * ``` + */ +export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles); + +/** + * Predefined role constants for consistent role naming across the application. + */ +export const Role = { + ADMIN: 'admin', + ANALYST: 'analyst', + USER: 'user', + READONLY: 'readonly', +} as const; + +export type RoleType = (typeof Role)[keyof typeof Role]; diff --git a/apps/api/src/auth/guards/index.ts b/apps/api/src/auth/guards/index.ts new file mode 100644 index 0000000..79af697 --- /dev/null +++ b/apps/api/src/auth/guards/index.ts @@ -0,0 +1,2 @@ +export { JwtAuthGuard } from './jwt-auth.guard'; +export { RolesGuard } from './roles.guard'; diff --git a/apps/api/src/auth/guards/jwt-auth.guard.ts b/apps/api/src/auth/guards/jwt-auth.guard.ts new file mode 100644 index 0000000..c7cf63d --- /dev/null +++ b/apps/api/src/auth/guards/jwt-auth.guard.ts @@ -0,0 +1,53 @@ +import { + Injectable, + ExecutionContext, + UnauthorizedException, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { AuthGuard } from '@nestjs/passport'; +import { IS_PUBLIC_KEY } from '../decorators/public.decorator'; + +@Injectable() +export class JwtAuthGuard extends AuthGuard('jwt') { + constructor(private reflector: Reflector) { + super(); + } + + canActivate(context: ExecutionContext) { + // Check if route is marked as public + const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + + if (isPublic) { + return true; + } + + // Otherwise, apply JWT authentication + return super.canActivate(context); + } + + handleRequest(err: any, user: any, info: any) { + // Custom error handling + if (err || !user) { + let message = 'Invalid or expired JWT access token.'; + + if (info?.name === 'TokenExpiredError') { + message = 'JWT access token has expired.'; + } else if (info?.name === 'JsonWebTokenError') { + message = 'Invalid JWT access token format.'; + } else if (info?.message) { + message = info.message; + } + + throw new UnauthorizedException({ + error: 'Unauthorized', + message, + timestamp: new Date().toISOString(), + }); + } + + return user; + } +} diff --git a/apps/api/src/auth/guards/roles.guard.ts b/apps/api/src/auth/guards/roles.guard.ts new file mode 100644 index 0000000..f185ee2 --- /dev/null +++ b/apps/api/src/auth/guards/roles.guard.ts @@ -0,0 +1,51 @@ +import { + Injectable, + CanActivate, + ExecutionContext, + ForbiddenException, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { ROLES_KEY } from '../decorators/roles.decorator'; +import { JwtUser } from '../strategies/jwt.strategy'; + +@Injectable() +export class RolesGuard implements CanActivate { + constructor(private reflector: Reflector) {} + + canActivate(context: ExecutionContext): boolean { + const requiredRoles = this.reflector.getAllAndOverride(ROLES_KEY, [ + context.getHandler(), + context.getClass(), + ]); + + // If no roles are required, allow access + if (!requiredRoles || requiredRoles.length === 0) { + return true; + } + + const { user } = context.switchToHttp().getRequest<{ user: JwtUser }>(); + + if (!user) { + throw new ForbiddenException({ + error: 'Forbidden', + message: 'Access denied: User not authenticated', + timestamp: new Date().toISOString(), + }); + } + + const userRoles = user.roles || []; + + // Check if user has at least one of the required roles + const hasRole = requiredRoles.some((role) => userRoles.includes(role)); + + if (!hasRole) { + throw new ForbiddenException({ + error: 'Forbidden', + message: `Access denied: Required roles are [${requiredRoles.join(', ')}]`, + timestamp: new Date().toISOString(), + }); + } + + return true; + } +} diff --git a/apps/api/src/auth/index.ts b/apps/api/src/auth/index.ts new file mode 100644 index 0000000..e0479c9 --- /dev/null +++ b/apps/api/src/auth/index.ts @@ -0,0 +1,4 @@ +export { AuthModule } from './auth.module'; +export * from './decorators'; +export * from './guards'; +export * from './strategies'; diff --git a/apps/api/src/auth/strategies/index.ts b/apps/api/src/auth/strategies/index.ts new file mode 100644 index 0000000..6527ca9 --- /dev/null +++ b/apps/api/src/auth/strategies/index.ts @@ -0,0 +1 @@ +export { JwtStrategy, JwtPayload, JwtUser } from './jwt.strategy'; diff --git a/apps/api/src/auth/strategies/jwt.strategy.ts b/apps/api/src/auth/strategies/jwt.strategy.ts new file mode 100644 index 0000000..7242f89 --- /dev/null +++ b/apps/api/src/auth/strategies/jwt.strategy.ts @@ -0,0 +1,72 @@ +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; + +export interface JwtPayload { + sub: string; + iss: string; + aud: string; + exp: number; + iat: number; + roles?: string[]; + permissions?: string[]; + [key: string]: any; +} + +export interface JwtUser { + userId: string; + roles: string[]; + permissions: string[]; +} + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor(private readonly configService: ConfigService) { + const secret = configService.get('JWT_SECRET'); + if (!secret) { + throw new Error('JWT_SECRET environment variable is required'); + } + + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: secret, + issuer: configService.get('JWT_ISSUER', 'gasguard-api'), + audience: configService.get('JWT_AUDIENCE', 'gasguard-client'), + algorithms: ['HS256'], + }); + } + + async validate(payload: JwtPayload): Promise { + // Validate required claims + if (!payload.sub) { + throw new UnauthorizedException('Invalid token: missing subject claim'); + } + + if (!payload.iss) { + throw new UnauthorizedException('Invalid token: missing issuer claim'); + } + + if (!payload.aud) { + throw new UnauthorizedException('Invalid token: missing audience claim'); + } + + if (!payload.exp) { + throw new UnauthorizedException('Invalid token: missing expiration claim'); + } + + // Check token expiration (passport-jwt also does this, but double-check) + const now = Math.floor(Date.now() / 1000); + if (payload.exp < now) { + throw new UnauthorizedException('Token has expired'); + } + + // Return user object that will be attached to request + return { + userId: payload.sub, + roles: payload.roles || [], + permissions: payload.permissions || [], + }; + } +} diff --git a/apps/api/src/controllers/cross-chain-gas.controller.ts b/apps/api/src/controllers/cross-chain-gas.controller.ts index ff19886..d49cfa1 100644 --- a/apps/api/src/controllers/cross-chain-gas.controller.ts +++ b/apps/api/src/controllers/cross-chain-gas.controller.ts @@ -1,13 +1,15 @@ -import { Controller, Get, Query, BadRequestException } from '@nestjs/common'; -import { ApiTags, ApiOperation, ApiResponse, ApiQuery } from '@nestjs/swagger'; +import { Controller, Get, Query, BadRequestException, UseGuards } from '@nestjs/common'; +import { ApiTags, ApiOperation, ApiResponse, ApiQuery, ApiBearerAuth } from '@nestjs/swagger'; import { CrossChainGasService } from '../services/cross-chain-gas.service'; import { CrossChainGasRequest, CrossChainGasResponse, SupportedChain } from '../schemas/cross-chain-gas.schema'; +import { Public, Roles, Role, JwtAuthGuard, RolesGuard } from '../auth'; @ApiTags('Cross-Chain Gas Comparison') +@ApiBearerAuth() @Controller() export class CrossChainGasController { constructor(private readonly crossChainGasService: CrossChainGasService) {} @@ -58,15 +60,20 @@ export class CrossChainGasController { return this.crossChainGasService.getSupportedChains(); } + @Roles(Role.ADMIN) @Get('v1/analytics/cross-chain-gas/refresh') @ApiOperation({ summary: 'Refresh gas price data', - description: 'Force refresh of gas price data and native token prices (admin endpoint)' + description: 'Force refresh of gas price data and native token prices (admin endpoint). Requires admin role.' }) @ApiResponse({ status: 200, description: 'Gas price data refreshed successfully' }) + @ApiResponse({ + status: 403, + description: 'Forbidden - Requires admin role' + }) async refreshGasData(): Promise<{ message: string; timestamp: number }> { await this.crossChainGasService.updateNativeTokenPrices(); return { diff --git a/apps/api/src/controllers/failed-transaction.controller.ts b/apps/api/src/controllers/failed-transaction.controller.ts index 32107c2..8863f2b 100644 --- a/apps/api/src/controllers/failed-transaction.controller.ts +++ b/apps/api/src/controllers/failed-transaction.controller.ts @@ -7,9 +7,10 @@ import { Query, HttpException, HttpStatus, - Logger + Logger, + UseGuards, } from '@nestjs/common'; -import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger'; +import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery, ApiBearerAuth } from '@nestjs/swagger'; import { TransactionAnalysisRequest, TransactionAnalysisResponse, @@ -17,8 +18,10 @@ import { FailedTransactionEvent } from '../schemas/failed-transaction.schema'; import { TransactionAnalysisService } from '../services/transaction-analysis.service'; +import { Public, Roles, Role, JwtAuthGuard, RolesGuard } from '../auth'; @ApiTags('failed-transactions') +@ApiBearerAuth() @Controller({ path: 'failed-transactions', version: '1' }) export class FailedTransactionController { private readonly logger = new Logger(FailedTransactionController.name); @@ -231,6 +234,7 @@ export class FailedTransactionController { } } + @Public() @Get('health') @ApiOperation({ summary: 'Health check endpoint', diff --git a/apps/api/src/services/cross-chain-gas.service.ts b/apps/api/src/services/cross-chain-gas.service.ts index 6176ab6..81e35d4 100644 --- a/apps/api/src/services/cross-chain-gas.service.ts +++ b/apps/api/src/services/cross-chain-gas.service.ts @@ -90,7 +90,7 @@ export class CrossChainGasService { 10: { baseFee: '15000000000', priorityFee: '1000000000' } }; - const gasPrice = mockGasPrices[chain.chainId] || { baseFee: '20000000000', priorityFee: '2000000000' }; + const gasPrice = (mockGasPrices as Record)[chain.chainId] || { baseFee: '20000000000', priorityFee: '2000000000' }; return { chainId: chain.chainId, @@ -98,7 +98,7 @@ export class CrossChainGasService { baseFee: gasPrice.baseFee, priorityFee: gasPrice.priorityFee, averageGasUsed: this.averageGasUsage, - nativeTokenPriceUSD: this.nativeTokenPricesUSD[chain.chainId] || 1, + nativeTokenPriceUSD: (this.nativeTokenPricesUSD as Record)[chain.chainId] || 1, averageConfirmationTime: chain.blockTime }; } diff --git a/apps/api/src/services/failed-transaction.service.ts b/apps/api/src/services/failed-transaction.service.ts index 74d1935..d6a54e8 100644 --- a/apps/api/src/services/failed-transaction.service.ts +++ b/apps/api/src/services/failed-transaction.service.ts @@ -65,7 +65,7 @@ export class FailedTransactionService { const { revertReason, metadata, gasUsed, gasPrice } = transactionData; // Check for underpriced gas - if (gasPrice && this.isUnderpriced(gasPrice, transactionData.chainId!)) { + if (gasPrice && await this.isUnderpriced(gasPrice, transactionData.chainId!)) { return 'underpriced_gas'; }