diff --git a/Readme.md b/Readme.md index d043f61..80d5d08 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,3 @@ - - # Contributing to SkillNet: Empowering Exams with Innovation Thank you for your interest in contributing to SkillNet! SkillNet is a cutting-edge platform designed to host secure and efficient online exams. Our backend is built with Node.js and utilizes modern technologies to deliver a seamless testing experience for educators and candidates. @@ -338,4 +336,52 @@ Please adhere to our Code of Conduct to maintain a respectful and inclusive comm - Document new features and updates thoroughly. ## License -This project is licensed under the MIT License - see the LICENSE file for details. \ No newline at end of file +This project is licensed under the MIT License - see the LICENSE file for details. + +## ESM Migration Guide and Test Fixes + +### 1. Update Configuration Files + +#### `package.json`: +```json +{ + "type": "module", + "scripts": { + "test": "NODE_OPTIONS=--experimental-vm-modules jest", + "dev": "nodemon src/server.js", + "start": "node src/server.js" + }, + "jest": { + "transform": {}, + "extensionsToTreatAsEsm": [".js"] + } +} +``` + +#### `jest.config.js`: +```javascript +export default { + testEnvironment: 'node', + transform: {}, + extensionsToTreatAsEsm: ['.js'], + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1' + } +}; +``` + +### 2. Common Issues and Solutions + +| Issue | Solution | +|-------------------------------|------------------------------------| +| "Cannot use import statement" | Ensure "type": "module" is set | +| Mock not working in tests | Use unstable_mockModule for ESM | +| Missing file extensions | Always include .js in imports | +| Async test failures | Add proper await/async handling | + +## ESM Usage + +```javascript +// Importing from this package +import { createExam } from 'skillnet-exam-server' +``` \ No newline at end of file diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..8c792c6 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,3 @@ +export default { + presets: [['@babel/preset-env', { targets: { node: 'current' } }]], +}; \ No newline at end of file diff --git a/coverage/clover.xml b/coverage/clover.xml new file mode 100644 index 0000000..6ba8288 --- /dev/null +++ b/coverage/clover.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/coverage/coverage-final.json b/coverage/coverage-final.json new file mode 100644 index 0000000..b1fa5be --- /dev/null +++ b/coverage/coverage-final.json @@ -0,0 +1,3 @@ +{"/workspaces/skillnet-exam-server/src/controllers/exam.controller.js": {"path":"/workspaces/skillnet-exam-server/src/controllers/exam.controller.js","statementMap":{"0":{"start":{"line":9,"column":17},"end":{"line":28,"column":2}},"1":{"start":{"line":10,"column":23},"end":{"line":10,"column":32}},"2":{"start":{"line":12,"column":22},"end":{"line":12,"column":24}},"3":{"start":{"line":13,"column":2},"end":{"line":15,"column":3}},"4":{"start":{"line":14,"column":4},"end":{"line":14,"column":36}},"5":{"start":{"line":17,"column":16},"end":{"line":25,"column":4}},"6":{"start":{"line":27,"column":2},"end":{"line":27,"column":30}},"7":{"start":{"line":33,"column":20},"end":{"line":54,"column":2}},"8":{"start":{"line":34,"column":15},"end":{"line":46,"column":4}},"9":{"start":{"line":48,"column":2},"end":{"line":51,"column":3}},"10":{"start":{"line":49,"column":4},"end":{"line":49,"column":20}},"11":{"start":{"line":50,"column":4},"end":{"line":50,"column":38}},"12":{"start":{"line":53,"column":2},"end":{"line":53,"column":29}},"13":{"start":{"line":59,"column":27},"end":{"line":73,"column":2}},"14":{"start":{"line":60,"column":23},"end":{"line":60,"column":33}},"15":{"start":{"line":62,"column":16},"end":{"line":70,"column":4}},"16":{"start":{"line":72,"column":2},"end":{"line":72,"column":30}},"17":{"start":{"line":78,"column":19},"end":{"line":124,"column":2}},"18":{"start":{"line":79,"column":2},"end":{"line":123,"column":3}},"19":{"start":{"line":80,"column":19},"end":{"line":80,"column":40}},"20":{"start":{"line":81,"column":4},"end":{"line":83,"column":5}},"21":{"start":{"line":82,"column":6},"end":{"line":82,"column":62}},"22":{"start":{"line":86,"column":4},"end":{"line":90,"column":5}},"23":{"start":{"line":87,"column":6},"end":{"line":87,"column":41}},"24":{"start":{"line":89,"column":6},"end":{"line":89,"column":72}},"25":{"start":{"line":92,"column":4},"end":{"line":114,"column":5}},"26":{"start":{"line":93,"column":6},"end":{"line":108,"column":7}},"27":{"start":{"line":94,"column":8},"end":{"line":107,"column":9}},"28":{"start":{"line":95,"column":34},"end":{"line":98,"column":12}},"29":{"start":{"line":99,"column":10},"end":{"line":106,"column":11}},"30":{"start":{"line":100,"column":12},"end":{"line":105,"column":13}},"31":{"start":{"line":101,"column":14},"end":{"line":104,"column":17}},"32":{"start":{"line":110,"column":6},"end":{"line":112,"column":7}},"33":{"start":{"line":111,"column":8},"end":{"line":111,"column":29}},"34":{"start":{"line":113,"column":6},"end":{"line":113,"column":72}},"35":{"start":{"line":116,"column":24},"end":{"line":118,"column":6}},"36":{"start":{"line":120,"column":4},"end":{"line":120,"column":38}},"37":{"start":{"line":122,"column":4},"end":{"line":122,"column":63}},"38":{"start":{"line":129,"column":19},"end":{"line":179,"column":2}},"39":{"start":{"line":130,"column":15},"end":{"line":130,"column":49}},"40":{"start":{"line":132,"column":2},"end":{"line":135,"column":3}},"41":{"start":{"line":133,"column":4},"end":{"line":133,"column":20}},"42":{"start":{"line":134,"column":4},"end":{"line":134,"column":38}},"43":{"start":{"line":150,"column":6},"end":{"line":150,"column":14}},"44":{"start":{"line":153,"column":2},"end":{"line":166,"column":5}},"45":{"start":{"line":169,"column":22},"end":{"line":176,"column":4}},"46":{"start":{"line":178,"column":2},"end":{"line":178,"column":36}},"47":{"start":{"line":184,"column":19},"end":{"line":205,"column":2}},"48":{"start":{"line":185,"column":15},"end":{"line":185,"column":49}},"49":{"start":{"line":187,"column":2},"end":{"line":190,"column":3}},"50":{"start":{"line":188,"column":4},"end":{"line":188,"column":20}},"51":{"start":{"line":189,"column":4},"end":{"line":189,"column":38}},"52":{"start":{"line":193,"column":20},"end":{"line":193,"column":74}},"53":{"start":{"line":195,"column":2},"end":{"line":197,"column":3}},"54":{"start":{"line":196,"column":4},"end":{"line":196,"column":65}},"55":{"start":{"line":199,"column":2},"end":{"line":199,"column":57}},"56":{"start":{"line":202,"column":2},"end":{"line":202,"column":23}},"57":{"start":{"line":204,"column":2},"end":{"line":204,"column":46}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":9,"column":30},"end":{"line":9,"column":31}},"loc":{"start":{"line":9,"column":50},"end":{"line":28,"column":1}},"line":9},"1":{"name":"(anonymous_1)","decl":{"start":{"line":33,"column":33},"end":{"line":33,"column":34}},"loc":{"start":{"line":33,"column":53},"end":{"line":54,"column":1}},"line":33},"2":{"name":"(anonymous_2)","decl":{"start":{"line":59,"column":40},"end":{"line":59,"column":41}},"loc":{"start":{"line":59,"column":60},"end":{"line":73,"column":1}},"line":59},"3":{"name":"(anonymous_3)","decl":{"start":{"line":78,"column":32},"end":{"line":78,"column":33}},"loc":{"start":{"line":78,"column":58},"end":{"line":124,"column":1}},"line":78},"4":{"name":"(anonymous_4)","decl":{"start":{"line":129,"column":32},"end":{"line":129,"column":33}},"loc":{"start":{"line":129,"column":52},"end":{"line":179,"column":1}},"line":129},"5":{"name":"(anonymous_5)","decl":{"start":{"line":184,"column":32},"end":{"line":184,"column":33}},"loc":{"start":{"line":184,"column":52},"end":{"line":205,"column":1}},"line":184}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":2},"end":{"line":15,"column":3}},"type":"if","locations":[{"start":{"line":13,"column":2},"end":{"line":15,"column":3}},{"start":{},"end":{}}],"line":13},"1":{"loc":{"start":{"line":48,"column":2},"end":{"line":51,"column":3}},"type":"if","locations":[{"start":{"line":48,"column":2},"end":{"line":51,"column":3}},{"start":{},"end":{}}],"line":48},"2":{"loc":{"start":{"line":81,"column":4},"end":{"line":83,"column":5}},"type":"if","locations":[{"start":{"line":81,"column":4},"end":{"line":83,"column":5}},{"start":{},"end":{}}],"line":81},"3":{"loc":{"start":{"line":93,"column":6},"end":{"line":108,"column":7}},"type":"if","locations":[{"start":{"line":93,"column":6},"end":{"line":108,"column":7}},{"start":{},"end":{}}],"line":93},"4":{"loc":{"start":{"line":93,"column":10},"end":{"line":93,"column":98}},"type":"binary-expr","locations":[{"start":{"line":93,"column":10},"end":{"line":93,"column":28}},{"start":{"line":93,"column":32},"end":{"line":93,"column":65}},{"start":{"line":93,"column":69},"end":{"line":93,"column":98}}],"line":93},"5":{"loc":{"start":{"line":99,"column":10},"end":{"line":106,"column":11}},"type":"if","locations":[{"start":{"line":99,"column":10},"end":{"line":106,"column":11}},{"start":{},"end":{}}],"line":99},"6":{"loc":{"start":{"line":99,"column":14},"end":{"line":99,"column":96}},"type":"binary-expr","locations":[{"start":{"line":99,"column":14},"end":{"line":99,"column":30}},{"start":{"line":99,"column":34},"end":{"line":99,"column":65}},{"start":{"line":99,"column":69},"end":{"line":99,"column":96}}],"line":99},"7":{"loc":{"start":{"line":110,"column":6},"end":{"line":112,"column":7}},"type":"if","locations":[{"start":{"line":110,"column":6},"end":{"line":112,"column":7}},{"start":{},"end":{}}],"line":110},"8":{"loc":{"start":{"line":110,"column":10},"end":{"line":110,"column":52}},"type":"binary-expr","locations":[{"start":{"line":110,"column":10},"end":{"line":110,"column":14}},{"start":{"line":110,"column":18},"end":{"line":110,"column":52}}],"line":110},"9":{"loc":{"start":{"line":132,"column":2},"end":{"line":135,"column":3}},"type":"if","locations":[{"start":{"line":132,"column":2},"end":{"line":135,"column":3}},{"start":{},"end":{}}],"line":132},"10":{"loc":{"start":{"line":154,"column":10},"end":{"line":154,"column":27}},"type":"binary-expr","locations":[{"start":{"line":154,"column":10},"end":{"line":154,"column":14}},{"start":{"line":154,"column":18},"end":{"line":154,"column":27}}],"line":154},"11":{"loc":{"start":{"line":155,"column":17},"end":{"line":155,"column":48}},"type":"binary-expr","locations":[{"start":{"line":155,"column":17},"end":{"line":155,"column":28}},{"start":{"line":155,"column":32},"end":{"line":155,"column":48}}],"line":155},"12":{"loc":{"start":{"line":156,"column":14},"end":{"line":156,"column":39}},"type":"binary-expr","locations":[{"start":{"line":156,"column":14},"end":{"line":156,"column":22}},{"start":{"line":156,"column":26},"end":{"line":156,"column":39}}],"line":156},"13":{"loc":{"start":{"line":157,"column":10},"end":{"line":157,"column":27}},"type":"binary-expr","locations":[{"start":{"line":157,"column":10},"end":{"line":157,"column":14}},{"start":{"line":157,"column":18},"end":{"line":157,"column":27}}],"line":157},"14":{"loc":{"start":{"line":158,"column":14},"end":{"line":158,"column":39}},"type":"binary-expr","locations":[{"start":{"line":158,"column":14},"end":{"line":158,"column":22}},{"start":{"line":158,"column":26},"end":{"line":158,"column":39}}],"line":158},"15":{"loc":{"start":{"line":159,"column":19},"end":{"line":159,"column":83}},"type":"cond-expr","locations":[{"start":{"line":159,"column":49},"end":{"line":159,"column":62}},{"start":{"line":159,"column":65},"end":{"line":159,"column":83}}],"line":159},"16":{"loc":{"start":{"line":160,"column":18},"end":{"line":160,"column":51}},"type":"binary-expr","locations":[{"start":{"line":160,"column":18},"end":{"line":160,"column":30}},{"start":{"line":160,"column":34},"end":{"line":160,"column":51}}],"line":160},"17":{"loc":{"start":{"line":161,"column":12},"end":{"line":161,"column":33}},"type":"binary-expr","locations":[{"start":{"line":161,"column":12},"end":{"line":161,"column":18}},{"start":{"line":161,"column":22},"end":{"line":161,"column":33}}],"line":161},"18":{"loc":{"start":{"line":162,"column":19},"end":{"line":162,"column":54}},"type":"binary-expr","locations":[{"start":{"line":162,"column":19},"end":{"line":162,"column":32}},{"start":{"line":162,"column":36},"end":{"line":162,"column":54}}],"line":162},"19":{"loc":{"start":{"line":163,"column":14},"end":{"line":163,"column":39}},"type":"binary-expr","locations":[{"start":{"line":163,"column":14},"end":{"line":163,"column":22}},{"start":{"line":163,"column":26},"end":{"line":163,"column":39}}],"line":163},"20":{"loc":{"start":{"line":164,"column":11},"end":{"line":164,"column":30}},"type":"binary-expr","locations":[{"start":{"line":164,"column":11},"end":{"line":164,"column":16}},{"start":{"line":164,"column":20},"end":{"line":164,"column":30}}],"line":164},"21":{"loc":{"start":{"line":165,"column":18},"end":{"line":165,"column":51}},"type":"binary-expr","locations":[{"start":{"line":165,"column":18},"end":{"line":165,"column":30}},{"start":{"line":165,"column":34},"end":{"line":165,"column":51}}],"line":165},"22":{"loc":{"start":{"line":187,"column":2},"end":{"line":190,"column":3}},"type":"if","locations":[{"start":{"line":187,"column":2},"end":{"line":190,"column":3}},{"start":{},"end":{}}],"line":187}},"s":{"0":1,"1":3,"2":3,"3":3,"4":1,"5":3,"6":3,"7":1,"8":0,"9":0,"10":0,"11":0,"12":0,"13":1,"14":0,"15":0,"16":0,"17":1,"18":6,"19":6,"20":6,"21":1,"22":5,"23":5,"24":1,"25":4,"26":4,"27":2,"28":2,"29":1,"30":1,"31":2,"32":1,"33":1,"34":1,"35":3,"36":3,"37":0,"38":1,"39":3,"40":3,"41":0,"42":0,"43":3,"44":3,"45":3,"46":3,"47":1,"48":2,"49":2,"50":1,"51":1,"52":1,"53":1,"54":1,"55":1,"56":1,"57":1},"f":{"0":3,"1":0,"2":0,"3":6,"4":3,"5":2},"b":{"0":[1,2],"1":[0,0],"2":[1,5],"3":[2,2],"4":[4,3,3],"5":[1,0],"6":[1,1,1],"7":[1,0],"8":[1,1],"9":[0,3],"10":[3,0],"11":[3,3],"12":[3,3],"13":[3,3],"14":[3,3],"15":[0,3],"16":[3,3],"17":[3,3],"18":[3,3],"19":[3,3],"20":[3,3],"21":[3,3],"22":[1,1]},"inputSourceMap":null,"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"43912465b797e1a1dc163834a85422415d611cca"} +,"/workspaces/skillnet-exam-server/src/controllers/user.controller.js": {"path":"/workspaces/skillnet-exam-server/src/controllers/user.controller.js","statementMap":{"0":{"start":{"line":6,"column":23},"end":{"line":17,"column":2}},"1":{"start":{"line":7,"column":15},"end":{"line":9,"column":4}},"2":{"start":{"line":11,"column":2},"end":{"line":14,"column":3}},"3":{"start":{"line":12,"column":4},"end":{"line":12,"column":20}},"4":{"start":{"line":13,"column":4},"end":{"line":13,"column":38}},"5":{"start":{"line":16,"column":2},"end":{"line":16,"column":29}},"6":{"start":{"line":21,"column":26},"end":{"line":53,"column":2}},"7":{"start":{"line":22,"column":2},"end":{"line":52,"column":3}},"8":{"start":{"line":23,"column":4},"end":{"line":26,"column":5}},"9":{"start":{"line":24,"column":6},"end":{"line":24,"column":22}},"10":{"start":{"line":25,"column":6},"end":{"line":25,"column":63}},"11":{"start":{"line":27,"column":17},"end":{"line":27,"column":49}},"12":{"start":{"line":29,"column":4},"end":{"line":32,"column":5}},"13":{"start":{"line":30,"column":6},"end":{"line":30,"column":22}},"14":{"start":{"line":31,"column":6},"end":{"line":31,"column":40}},"15":{"start":{"line":34,"column":32},"end":{"line":34,"column":40}},"16":{"start":{"line":37,"column":4},"end":{"line":37,"column":43}},"17":{"start":{"line":37,"column":18},"end":{"line":37,"column":43}},"18":{"start":{"line":38,"column":4},"end":{"line":38,"column":34}},"19":{"start":{"line":38,"column":15},"end":{"line":38,"column":34}},"20":{"start":{"line":40,"column":4},"end":{"line":40,"column":22}},"21":{"start":{"line":42,"column":4},"end":{"line":48,"column":7}},"22":{"start":{"line":50,"column":4},"end":{"line":50,"column":20}},"23":{"start":{"line":51,"column":4},"end":{"line":51,"column":69}},"24":{"start":{"line":57,"column":24},"end":{"line":69,"column":2}},"25":{"start":{"line":58,"column":15},"end":{"line":61,"column":4}},"26":{"start":{"line":63,"column":2},"end":{"line":66,"column":3}},"27":{"start":{"line":64,"column":4},"end":{"line":64,"column":20}},"28":{"start":{"line":65,"column":4},"end":{"line":65,"column":38}},"29":{"start":{"line":68,"column":2},"end":{"line":68,"column":29}},"30":{"start":{"line":73,"column":17},"end":{"line":79,"column":2}},"31":{"start":{"line":74,"column":16},"end":{"line":76,"column":4}},"32":{"start":{"line":78,"column":2},"end":{"line":78,"column":30}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":36},"end":{"line":6,"column":37}},"loc":{"start":{"line":6,"column":56},"end":{"line":17,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":21,"column":39},"end":{"line":21,"column":40}},"loc":{"start":{"line":21,"column":59},"end":{"line":53,"column":1}},"line":21},"2":{"name":"(anonymous_2)","decl":{"start":{"line":57,"column":37},"end":{"line":57,"column":38}},"loc":{"start":{"line":57,"column":57},"end":{"line":69,"column":1}},"line":57},"3":{"name":"(anonymous_3)","decl":{"start":{"line":73,"column":30},"end":{"line":73,"column":31}},"loc":{"start":{"line":73,"column":50},"end":{"line":79,"column":1}},"line":73}},"branchMap":{"0":{"loc":{"start":{"line":11,"column":2},"end":{"line":14,"column":3}},"type":"if","locations":[{"start":{"line":11,"column":2},"end":{"line":14,"column":3}},{"start":{},"end":{}}],"line":11},"1":{"loc":{"start":{"line":23,"column":4},"end":{"line":26,"column":5}},"type":"if","locations":[{"start":{"line":23,"column":4},"end":{"line":26,"column":5}},{"start":{},"end":{}}],"line":23},"2":{"loc":{"start":{"line":29,"column":4},"end":{"line":32,"column":5}},"type":"if","locations":[{"start":{"line":29,"column":4},"end":{"line":32,"column":5}},{"start":{},"end":{}}],"line":29},"3":{"loc":{"start":{"line":37,"column":4},"end":{"line":37,"column":43}},"type":"if","locations":[{"start":{"line":37,"column":4},"end":{"line":37,"column":43}},{"start":{},"end":{}}],"line":37},"4":{"loc":{"start":{"line":38,"column":4},"end":{"line":38,"column":34}},"type":"if","locations":[{"start":{"line":38,"column":4},"end":{"line":38,"column":34}},{"start":{},"end":{}}],"line":38},"5":{"loc":{"start":{"line":63,"column":2},"end":{"line":66,"column":3}},"type":"if","locations":[{"start":{"line":63,"column":2},"end":{"line":66,"column":3}},{"start":{},"end":{}}],"line":63}},"s":{"0":1,"1":2,"2":2,"3":1,"4":1,"5":1,"6":1,"7":4,"8":4,"9":1,"10":1,"11":3,"12":3,"13":0,"14":0,"15":3,"16":3,"17":3,"18":3,"19":2,"20":3,"21":2,"22":1,"23":1,"24":1,"25":2,"26":2,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1},"f":{"0":2,"1":4,"2":2,"3":1},"b":{"0":[1,1],"1":[1,3],"2":[0,3],"3":[3,0],"4":[2,1],"5":[1,1]},"inputSourceMap":null,"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"695764136b3c0b79fc41073892d4eb6a7dd9465b"} +} diff --git a/coverage/lcov-report/auth.controller.js.html b/coverage/lcov-report/auth.controller.js.html new file mode 100644 index 0000000..5a9fb7a --- /dev/null +++ b/coverage/lcov-report/auth.controller.js.html @@ -0,0 +1,415 @@ + + + + + + Code coverage report for auth.controller.js + + + + + + + + + +
+
+

All files auth.controller.js

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

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import jwt from 'jsonwebtoken';
+import { validationResult } from 'express-validator';
+import { User } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+import { Op } from 'sequelize';
+ 
+// Generate JWT
+const generateToken = (id) => {
+  return jwt.sign({ id }, process.env.JWT_SECRET, {
+    expiresIn: '30d',
+  });
+};
+ 
+// @desc    Register a new user
+// @route   POST /api/auth/register
+// @access  Public
+const registerUser = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  if (!errors.isEmpty()) {
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const { fullName, email, walletAddress, role } = req.body;
+ 
+  // Check if user exists
+  const userExists = await User.findOne({
+    where: {
+      [Op.or]: [
+        { email },
+        ...(walletAddress ? [{ walletAddress }] : [])
+      ]
+    }
+  });
+ 
+  if (userExists) {
+    res.status(400);
+    throw new Error('User already exists');
+  }
+ 
+  // Create user
+  const user = await User.create({
+    fullName,
+    email,
+    walletAddress,
+    role,
+  });
+ 
+  if (user) {
+    res.status(201).json({
+      id: user.id,
+      fullName: user.fullName,
+      email: user.email,
+      walletAddress: user.walletAddress,
+      role: user.role,
+    });
+  } else {
+    res.status(400);
+    throw new Error('Invalid user data');
+  }
+});
+ 
+// @desc    Authenticate a user
+// @route   POST /api/auth/login
+// @access  Public
+const loginUser = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  if (!errors.isEmpty()) {
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const { email, walletAddress } = req.body;
+ 
+  // Check for user email
+  const user = await User.findOne({ where: { email, walletAddress } });
+ 
+  if (user && walletAddress) {
+    res.json({
+      id: user.id,
+      fullName: user.fullName,
+      email: user.email,
+      walletAddress: user.walletAddress,
+      role: user.role,
+      token: generateToken(user.id),
+    });
+  } else {
+    res.status(401);
+    throw new Error('Invalid credentials');
+  }
+});
+ 
+// @desc    Get user profile
+// @route   GET /api/auth/profile
+// @access  Private
+const getUserProfile = asyncHandler(async (req, res) => {
+  const user = await User.findByPk(req.user.id, {
+    attributes: { exclude: ['password'] }
+  });
+ 
+  if (user) {
+    res.json(user);
+  } else {
+    res.status(404);
+    throw new Error('User not found');
+  }
+});
+ 
+export {
+  registerUser,
+  loginUser,
+  getUserProfile,
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/base.css b/coverage/lcov-report/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/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/coverage/lcov-report/block-navigation.js b/coverage/lcov-report/block-navigation.js new file mode 100644 index 0000000..cc12130 --- /dev/null +++ b/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) > ` + + // Selecter 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/coverage/lcov-report/config/db.js.html b/coverage/lcov-report/config/db.js.html new file mode 100644 index 0000000..f0e985c --- /dev/null +++ b/coverage/lcov-report/config/db.js.html @@ -0,0 +1,184 @@ + + + + + + Code coverage report for config/db.js + + + + + + + + + +
+
+

All files / config db.js

+
+ +
+ 25% + Statements + 2/8 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 25% + Lines + 2/8 +
+ + +
+

+ 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  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  + 
import { Sequelize } from 'sequelize';
+import 'dotenv/config';
+import logger from '../utils/logger.js';
+ 
+const sequelize = new Sequelize(
+  process.env.DB_NAME,
+  process.env.DB_USER,
+  process.env.DB_PASSWORD,
+  {
+    host: process.env.DB_HOST,
+    port: process.env.DB_PORT,
+    dialect: 'postgres',
+    logging: msg => logger.debug(msg),
+    pool: {
+      max: 5,
+      min: 0,
+      acquire: 30000,
+      idle: 10000,
+    },
+  }
+);
+ 
+const connectDB = async () => {
+  try {
+    await sequelize.authenticate();
+    logger.info('Database connection has been established successfully.');
+  } catch (error) {
+    logger.error(`Unable to connect to the database: ${error.message}`);
+    process.exit(1);
+  }
+};
+ 
+export { sequelize, connectDB };
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/config/index.html b/coverage/lcov-report/config/index.html new file mode 100644 index 0000000..1018a1a --- /dev/null +++ b/coverage/lcov-report/config/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for config + + + + + + + + + +
+
+

All files config

+
+ +
+ 25% + Statements + 2/8 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 25% + Lines + 2/8 +
+ + +
+

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

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
db.js +
+
25%2/8100%0/00%0/225%2/8
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/controllers/auth.controller.js.html b/coverage/lcov-report/controllers/auth.controller.js.html new file mode 100644 index 0000000..2578c13 --- /dev/null +++ b/coverage/lcov-report/controllers/auth.controller.js.html @@ -0,0 +1,415 @@ + + + + + + Code coverage report for controllers/auth.controller.js + + + + + + + + + +
+
+

All files / controllers auth.controller.js

+
+ +
+ 0% + Statements + 0/32 +
+ + +
+ 0% + Branches + 0/13 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 0% + Lines + 0/32 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import jwt from 'jsonwebtoken';
+import { validationResult } from 'express-validator';
+import { User } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+import { Op } from 'sequelize';
+ 
+// Generate JWT
+const generateToken = (id) => {
+  return jwt.sign({ id }, process.env.JWT_SECRET, {
+    expiresIn: '30d',
+  });
+};
+ 
+// @desc    Register a new user
+// @route   POST /api/auth/register
+// @access  Public
+const registerUser = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  Iif (!errors.isEmpty()) {
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const { fullName, email, walletAddress, role } = req.body;
+ 
+  // Check if user exists
+  const userExists = await User.findOne({
+    where: {
+      [Op.or]: [
+        { email },
+        ...(walletAddress ? [{ walletAddress }] : [])
+      ]
+    }
+  });
+ 
+  Iif (userExists) {
+    res.status(400);
+    throw new Error('User already exists');
+  }
+ 
+  // Create user
+  const user = await User.create({
+    fullName,
+    email,
+    walletAddress,
+    role,
+  });
+ 
+  if (user) {
+    res.status(201).json({
+      id: user.id,
+      fullName: user.fullName,
+      email: user.email,
+      walletAddress: user.walletAddress,
+      role: user.role,
+    });
+  } else {
+    res.status(400);
+    throw new Error('Invalid user data');
+  }
+});
+ 
+// @desc    Authenticate a user
+// @route   POST /api/auth/login
+// @access  Public
+const loginUser = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  Iif (!errors.isEmpty()) {
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const { email, walletAddress } = req.body;
+ 
+  // Check for user email
+  const user = await User.findOne({ where: { email, walletAddress } });
+ 
+  if (user && walletAddress) {
+    res.json({
+      id: user.id,
+      fullName: user.fullName,
+      email: user.email,
+      walletAddress: user.walletAddress,
+      role: user.role,
+      token: generateToken(user.id),
+    });
+  } else {
+    res.status(401);
+    throw new Error('Invalid credentials');
+  }
+});
+ 
+// @desc    Get user profile
+// @route   GET /api/auth/profile
+// @access  Private
+const getUserProfile = asyncHandler(async (req, res) => {
+  const user = await User.findByPk(req.user.id, {
+    attributes: { exclude: ['password'] }
+  });
+ 
+  if (user) {
+    res.json(user);
+  } else {
+    res.status(404);
+    throw new Error('User not found');
+  }
+});
+ 
+export {
+  registerUser,
+  loginUser,
+  getUserProfile,
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/controllers/exam.controller.js.html b/coverage/lcov-report/controllers/exam.controller.js.html new file mode 100644 index 0000000..551ad75 --- /dev/null +++ b/coverage/lcov-report/controllers/exam.controller.js.html @@ -0,0 +1,823 @@ + + + + + + Code coverage report for controllers/exam.controller.js + + + + + + + + + +
+
+

All files / controllers exam.controller.js

+
+ +
+ 82.75% + Statements + 48/58 +
+ + +
+ 88.57% + Branches + 31/35 +
+ + +
+ 66.66% + Functions + 4/6 +
+ + +
+ 82.14% + Lines + 46/56 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +1x +3x +  +3x +3x +1x +  +  +3x +  +  +  +  +  +  +  +  +  +3x +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +6x +6x +6x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +5x +  +  +5x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +4x +2x +2x +2x +  +  +  +  +  +  +1x +1x +2x +2x +  +  +  +  +  +  +  +  +  +  +  +3x +  +  +  +  +  +  +  +  +3x +  +2x +2x +  +  +  +  +  +  +1x +3x +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +  +  +  +  +  +  +  +3x +  +  +  +  +  +1x +2x +  +2x +1x +1x +  +  +  +1x +  +1x +1x +  +  +1x +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  + 
import { Exam, Question, Option } from '../models/index.js';
+import { Op } from 'sequelize';
+import asyncHandler from 'express-async-handler';
+import { validationResult } from 'express-validator';
+ 
+// @desc    Get all exams
+// @route   GET /api/exams
+// @access  Public
+const getExams = asyncHandler(async (req, res) => {
+  const { category } = req.query;
+  
+  const whereClause = {};
+  if (category) {
+    whereClause.category = category;
+  }
+  
+  const exams = await Exam.findAll({
+    where: whereClause,
+    include: [
+      {
+        model: Question,
+        attributes: ['id'], // Only count questions, don't return them
+      },
+    ],
+  });
+  
+  res.status(200).json(exams);
+});
+ 
+// @desc    Get exam by ID
+// @route   GET /api/exams/:id
+// @access  Public
+const getExamById = asyncHandler(async (req, res) => {
+  const exam = await Exam.findByPk(req.params.id, {
+    include: [
+      {
+        model: Question,
+        include: [
+          {
+            model: Option,
+            attributes: ['id', 'text', 'order'], // Don't expose correct answers
+          },
+        ],
+      },
+    ],
+  });
+  
+  Iif (!exam) {
+    res.status(404);
+    throw new Error('Exam not found');
+  }
+  
+  res.status(200).json(exam);
+});
+ 
+// @desc    Get exams by category
+// @route   GET /api/exams/category/:category
+// @access  Public
+const getExamsByCategory = asyncHandler(async (req, res) => {
+  const { category } = req.params;
+  
+  const exams = await Exam.findAll({
+    where: { category },
+    include: [
+      {
+        model: Question,
+        attributes: ['id'], // Only count questions, don't return them
+      },
+    ],
+  });
+  
+  res.status(200).json(exams);
+});
+ 
+// @desc    Create new exam
+// @route   POST /api/exams
+// @access  Private (Admin)
+const createExam = asyncHandler(async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    if (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const {
+      name,
+      description,
+      category,
+      date,
+      duration,
+      certification,
+      passingScore,
+      format,
+      topicsCovered,
+      benefits,
+      price,
+      instructions,
+      questions,
+    } = req.body;
+ 
+    // Create exam
+    const exam = await Exam.create({
+      name,
+      description,
+      category,
+      date,
+      duration,
+      certification,
+      passingScore,
+      format,
+      topicsCovered,
+      benefits,
+      price,
+      instructions,
+    });
+ 
+    // Create questions and options if provided
+    if (questions && questions.length > 0) {
+      for (let i = 0; i < questions.length; i++) {
+        const q = questions[i];
+        const question = await Question.create({
+          examId: exam.id,
+          question: q.question,
+          order: i + 1,
+        });
+ 
+        // Create options for this question
+        if (q.options && q.options.length > 0) {
+          for (let j = 0; j < q.options.length; j++) {
+            const opt = q.options[j];
+            await Option.create({
+              questionId: question.id,
+              text: opt.text,
+              isCorrect: opt.isCorrect,
+              order: String.fromCharCode(65 + j), // A, B, C, D...
+            });
+          }
+        }
+      }
+    }
+ 
+    // Return the created exam with questions
+    const createdExam = await Exam.findByPk(exam.id, {
+      include: [
+        {
+          model: Question,
+          include: [Option],
+        },
+      ],
+    });
+ 
+    res.status(201).json(createdExam);
+  } catch (error) {
+    res.status(500);
+    throw new Error('Error creating exam: ' + error.message);
+  }
+});
+ 
+// @desc    Update exam
+// @route   PUT /api/exams/:id
+// @access  Private (Admin)
+const updateExam = asyncHandler(async (req, res) => {
+  const exam = await Exam.findByPk(req.params.id);
+ 
+  Iif (!exam) {
+    res.status(404);
+    throw new Error('Exam not found');
+  }
+ 
+  const {
+    name,
+    description,
+    category,
+    date,
+    duration,
+    certification,
+    passingScore,
+    format,
+    topicsCovered,
+    benefits,
+    price,
+    instructions,
+  } = req.body;
+ 
+  // Update exam details
+  await exam.update({
+    name: name || exam.name,
+    description: description || exam.description,
+    category: category || exam.category,
+    date: date || exam.date,
+    duration: duration || exam.duration,
+    certification: certification !== undefined ? certification : exam.certification,
+    passingScore: passingScore || exam.passingScore,
+    format: format || exam.format,
+    topicsCovered: topicsCovered || exam.topicsCovered,
+    benefits: benefits || exam.benefits,
+    price: price || exam.price,
+    instructions: instructions || exam.instructions,
+  });
+ 
+  // Get updated exam with questions
+  const updatedExam = await Exam.findByPk(exam.id, {
+    include: [
+      {
+        model: Question,
+        include: [Option],
+      },
+    ],
+  });
+ 
+  res.status(200).json(updatedExam);
+});
+ 
+// @desc    Delete exam
+// @route   DELETE /api/exams/:id
+// @access  Private (Admin)
+const deleteExam = asyncHandler(async (req, res) => {
+  const exam = await Exam.findByPk(req.params.id);
+ 
+  if (!exam) {
+    res.status(404);
+    throw new Error('Exam not found');
+  }
+ 
+  // Delete associated questions and options
+  const questions = await Question.findAll({ where: { examId: exam.id } });
+  
+  for (const question of questions) {
+    await Option.destroy({ where: { questionId: question.id } });
+  }
+  
+  await Question.destroy({ where: { examId: exam.id } });
+  
+  // Delete the exam
+  await exam.destroy();
+ 
+  res.status(200).json({ id: req.params.id });
+});
+ 
+export {
+  getExams,
+  getExamById,
+  getExamsByCategory,
+  createExam,
+  updateExam,
+  deleteExam,
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/controllers/examBanner.controller.js.html b/coverage/lcov-report/controllers/examBanner.controller.js.html new file mode 100644 index 0000000..b8d54e6 --- /dev/null +++ b/coverage/lcov-report/controllers/examBanner.controller.js.html @@ -0,0 +1,1063 @@ + + + + + + Code coverage report for controllers/examBanner.controller.js + + + + + + + + + +
+
+

All files / controllers examBanner.controller.js

+
+ +
+ 0% + Statements + 0/94 +
+ + +
+ 0% + Branches + 0/45 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 0% + Lines + 0/94 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { ExamBanner, Exam } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+import { validationResult } from 'express-validator';
+import logger from '../utils/logger.js';
+ 
+// @desc    Get all exam banners
+// @route   GET /api/exam-banners
+// @access  Public
+const getExamBanners = asyncHandler(async (req, res) => {
+  try {
+    const { active } = req.query;
+ 
+    const whereClause = {};
+    Iif (active === 'true') {
+      const now = new Date();
+      whereClause.isActive = true;
+      whereClause.startDate = { $lte: now }; // Start date is less than or equal to now
+      whereClause.endDate = { $gte: now };   // End date is greater than or equal to now
+    }
+ 
+    logger.info(`Fetching exam banners with filters: ${JSON.stringify(whereClause)}`);
+ 
+    const examBanners = await ExamBanner.findAll({
+      where: whereClause,
+      include: [
+        {
+          model: Exam,
+          as: 'exam',
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+      order: [
+        ['priority', 'DESC'],
+        ['startDate', 'DESC'],
+      ],
+    });
+ 
+    logger.info(`Retrieved ${examBanners.length} exam banners`);
+    res.status(200).json(examBanners);
+  } catch (error) {
+    logger.error(`Error retrieving exam banners: ${error.message}`, { error: error.stack });
+    res.status(500);
+    throw new Error('Error retrieving exam banners');
+  }
+});
+ 
+// @desc    Get exam banner by ID
+// @route   GET /api/exam-banners/:id
+// @access  Public
+const getExamBannerById = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Fetching exam banner with id: ${req.params.id}`);
+ 
+    const examBanner = await ExamBanner.findByPk(req.params.id, {
+      include: [
+        {
+          model: Exam,
+          as: 'exam',
+          attributes: ['id', 'name', 'category', 'description'],
+        },
+      ],
+    });
+ 
+    Iif (!examBanner) {
+      logger.warn(`Exam banner with id ${req.params.id} not found`, { bannerId: req.params.id });
+      res.status(404);
+      throw new Error('Exam banner not found');
+    }
+ 
+    logger.info(`Retrieved exam banner ${examBanner.id}`);
+    res.status(200).json(examBanner);
+  } catch (error) {
+    Iif (error.message === 'Exam banner not found') {
+      throw error;
+    }
+    logger.error(`Error retrieving exam banner ${req.params.id}: ${error.message}`, {
+      bannerId: req.params.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving exam banner');
+  }
+});
+ 
+// @desc    Create new exam banner
+// @route   POST /api/exam-banners
+// @access  Private (Admin)
+const createExamBanner = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  Iif (!errors.isEmpty()) {
+    logger.warn(`Validation errors in creating exam banner: ${JSON.stringify(errors.array())}`, {
+      userId: req.user.id,
+      validationErrors: errors.array()
+    });
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const {
+    title,
+    description,
+    imageUrl,
+    startDate,
+    endDate,
+    isActive,
+    examId,
+    buttonText,
+    buttonLink,
+    priority,
+  } = req.body;
+ 
+  logger.info(`Admin ${req.user.id} creating exam banner: "${title}"`);
+ 
+  try {
+    // Check if exam exists if examId is provided
+    Iif (examId) {
+      const exam = await Exam.findByPk(examId);
+      Iif (!exam) {
+        logger.warn(`Exam with id ${examId} not found while admin ${req.user.id} creating banner`, {
+          examId,
+          adminId: req.user.id
+        });
+        res.status(404);
+        throw new Error('Exam not found');
+      }
+    }
+ 
+    // Create banner
+    const banner = await ExamBanner.create({
+      title,
+      description,
+      imageUrl,
+      startDate,
+      endDate,
+      isActive: isActive !== undefined ? isActive : true,
+      examId,
+      buttonText,
+      buttonLink,
+      priority: priority || 0,
+    });
+ 
+    logger.info(`Created exam banner ${banner.id} by admin ${req.user.id}`, {
+      bannerId: banner.id,
+      adminId: req.user.id,
+      examId: examId || 'none'
+    });
+ 
+    // Return the created banner with exam info
+    const createdBanner = await ExamBanner.findByPk(banner.id, {
+      include: [
+        {
+          model: Exam,
+          as: 'exam',
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+    });
+ 
+    res.status(201).json(createdBanner);
+  } catch (error) {
+    Iif (error.message === 'Exam not found') {
+      throw error;
+    }
+    logger.error(`Error creating exam banner by admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      examId,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error creating exam banner');
+  }
+});
+ 
+// @desc    Update exam banner
+// @route   PUT /api/exam-banners/:id
+// @access  Private (Admin)
+const updateExamBanner = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} updating exam banner ${req.params.id}`);
+ 
+    const banner = await ExamBanner.findByPk(req.params.id);
+ 
+    Iif (!banner) {
+      logger.warn(`Exam banner with id ${req.params.id} not found for update by admin ${req.user.id}`, {
+        bannerId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam banner not found');
+    }
+ 
+    const {
+      title,
+      description,
+      imageUrl,
+      startDate,
+      endDate,
+      isActive,
+      examId,
+      buttonText,
+      buttonLink,
+      priority,
+    } = req.body;
+ 
+    // If examId is being changed, verify the new exam exists
+    Iif (examId && examId !== banner.examId) {
+      const exam = await Exam.findByPk(examId);
+      Iif (!exam) {
+        logger.warn(`Exam with id ${examId} not found while admin ${req.user.id} updating banner ${req.params.id}`, {
+          examId,
+          bannerId: req.params.id,
+          adminId: req.user.id
+        });
+        res.status(404);
+        throw new Error('Exam not found');
+      }
+    }
+ 
+    // Update banner
+    await banner.update({
+      title: title || banner.title,
+      description: description !== undefined ? description : banner.description,
+      imageUrl: imageUrl !== undefined ? imageUrl : banner.imageUrl,
+      startDate: startDate || banner.startDate,
+      endDate: endDate || banner.endDate,
+      isActive: isActive !== undefined ? isActive : banner.isActive,
+      examId: examId || banner.examId,
+      buttonText: buttonText !== undefined ? buttonText : banner.buttonText,
+      buttonLink: buttonLink !== undefined ? buttonLink : banner.buttonLink,
+      priority: priority !== undefined ? priority : banner.priority,
+    });
+ 
+    logger.info(`Updated exam banner ${banner.id} by admin ${req.user.id}`, {
+      bannerId: banner.id,
+      adminId: req.user.id,
+      changes: {
+        title: !!title,
+        description: description !== undefined,
+        imageUrl: imageUrl !== undefined,
+        startDate: !!startDate,
+        endDate: !!endDate,
+        isActive: isActive !== undefined,
+        examId: !!examId && examId !== banner.examId,
+        buttonText: buttonText !== undefined,
+        buttonLink: buttonLink !== undefined,
+        priority: priority !== undefined
+      }
+    });
+ 
+    // Return the updated banner with exam info
+    const updatedBanner = await ExamBanner.findByPk(banner.id, {
+      include: [
+        {
+          model: Exam,
+          as: 'exam',
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+    });
+ 
+    res.status(200).json(updatedBanner);
+  } catch (error) {
+    Iif (error.message === 'Exam banner not found' || error.message === 'Exam not found') {
+      throw error;
+    }
+    logger.error(`Error updating exam banner ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      bannerId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error updating exam banner');
+  }
+});
+ 
+// @desc    Delete exam banner
+// @route   DELETE /api/exam-banners/:id
+// @access  Private (Admin)
+const deleteExamBanner = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} deleting exam banner ${req.params.id}`);
+ 
+    const banner = await ExamBanner.findByPk(req.params.id);
+ 
+    Iif (!banner) {
+      logger.warn(`Exam banner with id ${req.params.id} not found for deletion by admin ${req.user.id}`, {
+        bannerId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam banner not found');
+    }
+ 
+    // Delete banner
+    await banner.destroy();
+ 
+    logger.info(`Deleted exam banner ${req.params.id} by admin ${req.user.id}`, {
+      bannerId: req.params.id,
+      adminId: req.user.id,
+      bannerData: {
+        title: banner.title,
+        examId: banner.examId,
+        isActive: banner.isActive
+      }
+    });
+    res.status(200).json({ id: req.params.id });
+  } catch (error) {
+    Iif (error.message === 'Exam banner not found') {
+      throw error;
+    }
+    logger.error(`Error deleting exam banner ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      bannerId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error deleting exam banner');
+  }
+});
+ 
+export {
+  getExamBanners,
+  getExamBannerById,
+  createExamBanner,
+  updateExamBanner,
+  deleteExamBanner,
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/controllers/examRecording.controller.js.html b/coverage/lcov-report/controllers/examRecording.controller.js.html new file mode 100644 index 0000000..ad861ec --- /dev/null +++ b/coverage/lcov-report/controllers/examRecording.controller.js.html @@ -0,0 +1,1192 @@ + + + + + + Code coverage report for controllers/examRecording.controller.js + + + + + + + + + +
+
+

All files / controllers examRecording.controller.js

+
+ +
+ 0% + Statements + 0/111 +
+ + +
+ 0% + Branches + 0/51 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 0% + Lines + 0/111 +
+ + +
+

+ 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 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { ExamRecording, Exam } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+import { validationResult } from 'express-validator';
+import logger from '../utils/logger.js';
+ 
+// @desc    Get all exam recordings
+// @route   GET /api/exam-recordings
+// @access  Public
+const getExamRecordings = asyncHandler(async (req, res) => {
+  try {
+    const { examId, published } = req.query;
+ 
+    const whereClause = {};
+    Iif (examId) {
+      whereClause.examId = examId;
+    }
+    if (published === 'true') {
+      whereClause.isPublished = true;
+    } else Iif (published === 'false') {
+      whereClause.isPublished = false;
+    }
+ 
+    logger.info(`Fetching exam recordings with filters: ${JSON.stringify(whereClause)}`);
+ 
+    const examRecordings = await ExamRecording.findAll({
+      where: whereClause,
+      include: [
+        {
+          model: Exam,
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+      order: [
+        ['recordedOn', 'DESC'],
+      ],
+    });
+ 
+    logger.info(`Retrieved ${examRecordings.length} exam recordings`);
+    res.status(200).json(examRecordings);
+  } catch (error) {
+    logger.error(`Error retrieving exam recordings: ${error.message}`, { error: error.stack });
+    res.status(500);
+    throw new Error('Error retrieving exam recordings');
+  }
+});
+ 
+// @desc    Get exam recording by ID
+// @route   GET /api/exam-recordings/:id
+// @access  Public
+const getExamRecordingById = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Fetching exam recording with id: ${req.params.id}`);
+ 
+    const examRecording = await ExamRecording.findByPk(req.params.id, {
+      include: [
+        {
+          model: Exam,
+          attributes: ['id', 'name', 'category', 'description'],
+        },
+      ],
+    });
+ 
+    Iif (!examRecording) {
+      logger.warn(`Exam recording with id ${req.params.id} not found`, { recordingId: req.params.id });
+      res.status(404);
+      throw new Error('Exam recording not found');
+    }
+ 
+    logger.info(`Retrieved exam recording ${examRecording.id}`);
+    res.status(200).json(examRecording);
+  } catch (error) {
+    Iif (error.message === 'Exam recording not found') {
+      throw error;
+    }
+    logger.error(`Error retrieving exam recording ${req.params.id}: ${error.message}`, {
+      recordingId: req.params.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving exam recording');
+  }
+});
+ 
+// @desc    Create new exam recording
+// @route   POST /api/exam-recordings
+// @access  Private (Admin)
+const createExamRecording = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  Iif (!errors.isEmpty()) {
+    logger.warn(`Validation errors in creating exam recording: ${JSON.stringify(errors.array())}`, {
+      userId: req.user.id,
+      validationErrors: errors.array()
+    });
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const {
+    title,
+    description,
+    recordingUrl,
+    duration,
+    examId,
+    presenter,
+    recordedOn,
+    isPublished,
+    thumbnailUrl,
+    tags,
+  } = req.body;
+ 
+  logger.info(`Admin ${req.user.id} creating exam recording: "${title}"`);
+ 
+  try {
+    // Check if exam exists
+    const exam = await Exam.findByPk(examId);
+    Iif (!exam) {
+      logger.warn(`Exam with id ${examId} not found while admin ${req.user.id} creating recording`, {
+        examId,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam not found');
+    }
+ 
+    // Create recording
+    const recording = await ExamRecording.create({
+      title,
+      description,
+      recordingUrl,
+      duration,
+      examId,
+      presenter,
+      recordedOn: recordedOn || new Date(),
+      isPublished: isPublished !== undefined ? isPublished : false,
+      thumbnailUrl,
+      tags: tags || [],
+    });
+ 
+    logger.info(`Created exam recording ${recording.id} by admin ${req.user.id}`, {
+      recordingId: recording.id,
+      adminId: req.user.id,
+      examId: examId,
+      isPublished: isPublished !== undefined ? isPublished : false
+    });
+ 
+    // Return the created recording with exam info
+    const createdRecording = await ExamRecording.findByPk(recording.id, {
+      include: [
+        {
+          model: Exam,
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+    });
+ 
+    res.status(201).json(createdRecording);
+  } catch (error) {
+    Iif (error.message === 'Exam not found') {
+      throw error;
+    }
+    logger.error(`Error creating exam recording by admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      examId,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error creating exam recording');
+  }
+});
+ 
+// @desc    Update exam recording
+// @route   PUT /api/exam-recordings/:id
+// @access  Private (Admin)
+const updateExamRecording = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} updating exam recording ${req.params.id}`);
+ 
+    const recording = await ExamRecording.findByPk(req.params.id);
+ 
+    Iif (!recording) {
+      logger.warn(`Exam recording with id ${req.params.id} not found for update by admin ${req.user.id}`, {
+        recordingId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam recording not found');
+    }
+ 
+    const {
+      title,
+      description,
+      recordingUrl,
+      duration,
+      examId,
+      presenter,
+      recordedOn,
+      isPublished,
+      thumbnailUrl,
+      tags,
+    } = req.body;
+ 
+    // If examId is being changed, verify the new exam exists
+    Iif (examId && examId !== recording.examId) {
+      const exam = await Exam.findByPk(examId);
+      Iif (!exam) {
+        logger.warn(`Exam with id ${examId} not found while admin ${req.user.id} updating recording ${req.params.id}`, {
+          examId,
+          recordingId: req.params.id,
+          adminId: req.user.id
+        });
+        res.status(404);
+        throw new Error('Exam not found');
+      }
+    }
+ 
+    // Update recording
+    await recording.update({
+      title: title || recording.title,
+      description: description !== undefined ? description : recording.description,
+      recordingUrl: recordingUrl || recording.recordingUrl,
+      duration: duration !== undefined ? duration : recording.duration,
+      examId: examId || recording.examId,
+      presenter: presenter !== undefined ? presenter : recording.presenter,
+      recordedOn: recordedOn || recording.recordedOn,
+      isPublished: isPublished !== undefined ? isPublished : recording.isPublished,
+      thumbnailUrl: thumbnailUrl !== undefined ? thumbnailUrl : recording.thumbnailUrl,
+      tags: tags || recording.tags,
+    });
+ 
+    logger.info(`Updated exam recording ${recording.id} by admin ${req.user.id}`, {
+      recordingId: recording.id,
+      adminId: req.user.id,
+      changes: {
+        title: !!title,
+        description: description !== undefined,
+        recordingUrl: !!recordingUrl,
+        duration: duration !== undefined,
+        examId: !!examId && examId !== recording.examId,
+        presenter: presenter !== undefined,
+        recordedOn: !!recordedOn,
+        isPublished: isPublished !== undefined,
+        thumbnailUrl: thumbnailUrl !== undefined,
+        tags: !!tags
+      }
+    });
+ 
+    // Return the updated recording with exam info
+    const updatedRecording = await ExamRecording.findByPk(recording.id, {
+      include: [
+        {
+          model: Exam,
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+    });
+ 
+    res.status(200).json(updatedRecording);
+  } catch (error) {
+    Iif (error.message === 'Exam recording not found' || error.message === 'Exam not found') {
+      throw error;
+    }
+    logger.error(`Error updating exam recording ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      recordingId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error updating exam recording');
+  }
+});
+ 
+// @desc    Delete exam recording
+// @route   DELETE /api/exam-recordings/:id
+// @access  Private (Admin)
+const deleteExamRecording = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} deleting exam recording ${req.params.id}`);
+ 
+    const recording = await ExamRecording.findByPk(req.params.id);
+ 
+    Iif (!recording) {
+      logger.warn(`Exam recording with id ${req.params.id} not found for deletion by admin ${req.user.id}`, {
+        recordingId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam recording not found');
+    }
+ 
+    // Delete recording
+    await recording.destroy();
+ 
+    logger.info(`Deleted exam recording ${req.params.id} by admin ${req.user.id}`, {
+      recordingId: req.params.id,
+      adminId: req.user.id,
+      recordingData: {
+        title: recording.title,
+        examId: recording.examId,
+        isPublished: recording.isPublished
+      }
+    });
+    res.status(200).json({ id: req.params.id });
+  } catch (error) {
+    Iif (error.message === 'Exam recording not found') {
+      throw error;
+    }
+    logger.error(`Error deleting exam recording ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      recordingId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error deleting exam recording');
+  }
+});
+ 
+// @desc    Toggle publish status of an exam recording
+// @route   PATCH /api/exam-recordings/:id/publish
+// @access  Private (Admin)
+const togglePublishStatus = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} toggling publish status for exam recording ${req.params.id}`);
+ 
+    const recording = await ExamRecording.findByPk(req.params.id);
+ 
+    Iif (!recording) {
+      logger.warn(`Exam recording with id ${req.params.id} not found for status toggle by admin ${req.user.id}`, {
+        recordingId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam recording not found');
+    }
+ 
+    // Toggle publish status
+    const newStatus = !recording.isPublished;
+    await recording.update({
+      isPublished: newStatus,
+    });
+ 
+    logger.info(`Toggled exam recording ${recording.id} publish status to ${newStatus} by admin ${req.user.id}`, {
+      recordingId: recording.id,
+      adminId: req.user.id,
+      newStatus: newStatus,
+      previousStatus: !newStatus
+    });
+ 
+    res.status(200).json(recording);
+  } catch (error) {
+    Iif (error.message === 'Exam recording not found') {
+      throw error;
+    }
+    logger.error(`Error toggling exam recording ${req.params.id} publish status by admin ${req.user.id}: ${error.message}`, {
+      recordingId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error toggling publish status');
+  }
+});
+ 
+export {
+  getExamRecordings,
+  getExamRecordingById,
+  createExamRecording,
+  updateExamRecording,
+  deleteExamRecording,
+  togglePublishStatus,
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/controllers/index.html b/coverage/lcov-report/controllers/index.html new file mode 100644 index 0000000..6509147 --- /dev/null +++ b/coverage/lcov-report/controllers/index.html @@ -0,0 +1,236 @@ + + + + + + Code coverage report for controllers + + + + + + + + + +
+
+

All files controllers

+
+ +
+ 13.23% + Statements + 79/597 +
+ + +
+ 16.9% + Branches + 36/213 +
+ + +
+ 16% + Functions + 8/50 +
+ + +
+ 12.77% + Lines + 75/587 +
+ + +
+

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

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
auth.controller.js +
+
0%0/320%0/130%0/40%0/32
exam.controller.js +
+
82.75%48/5888.57%31/3566.66%4/682.14%46/56
examBanner.controller.js +
+
0%0/940%0/450%0/50%0/94
examRecording.controller.js +
+
0%0/1110%0/510%0/60%0/111
indexer.controller.js +
+
0%0/810%0/170%0/90%0/78
notification.controller.js +
+
0%0/950%0/300%0/60%0/95
registration.controller.js +
+
0%0/430%0/70%0/40%0/43
result.controller.js +
+
0%0/500%0/90%0/60%0/47
user.controller.js +
+
93.93%31/3383.33%5/6100%4/493.54%29/31
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/controllers/indexer.controller.js.html b/coverage/lcov-report/controllers/indexer.controller.js.html new file mode 100644 index 0000000..8eea35d --- /dev/null +++ b/coverage/lcov-report/controllers/indexer.controller.js.html @@ -0,0 +1,883 @@ + + + + + + Code coverage report for controllers/indexer.controller.js + + + + + + + + + +
+
+

All files / controllers indexer.controller.js

+
+ +
+ 0% + Statements + 0/81 +
+ + +
+ 0% + Branches + 0/17 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 0% + Lines + 0/78 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { ContractEvent } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+import { indexer } from '../indexer/indexer.js';
+import { indexerConfig, apibaraConfig, networks, contracts, eventHandlers, eventSelectors } from '../indexer/config.js';
+import logger from '../utils/logger.js';
+import { Op } from 'sequelize';
+ 
+// @desc    Get indexer status
+// @route   GET /api/indexer/status
+// @access  Private
+const getIndexerStatus = asyncHandler(async (req, res) => {
+  try {
+    logger.info('Fetching indexer status');
+ 
+    // Get the last processed block from DB
+    const lastEvent = await ContractEvent.findOne({
+      order: [['blockNumber', 'DESC']],
+      attributes: ['blockNumber', 'blockTimestamp', 'createdAt']
+    });
+ 
+    const status = {
+      lastProcessedBlock: lastEvent ? Number(lastEvent.blockNumber) : null,
+      lastBlockTimestamp: lastEvent ? new Date(Number(lastEvent.blockTimestamp) * 1000).toISOString() : null,
+      lastProcessingTime: lastEvent ? lastEvent.createdAt : null,
+      network: indexerConfig.network,
+      contractAddress: indexerConfig.contractAddress,
+      isRunning: indexer.isRunning(),
+      startBlock: Number(process.env.INDEXER_START_BLOCK) || 0
+    };
+ 
+    logger.info(`Indexer status retrieved: ${JSON.stringify(status)}`);
+    res.status(200).json(status);
+  } catch (error) {
+    logger.error(`Error retrieving indexer status: ${error.message}`, { error: error.stack });
+    res.status(500);
+    throw new Error('Error retrieving indexer status');
+  }
+});
+ 
+// @desc    Trigger indexer to scan for events
+// @route   POST /api/indexer/scan
+// @access  Private (Admin)
+const triggerScan = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} triggering indexer scan`);
+ 
+    const { fromBlock, toBlock } = req.body;
+ 
+    // Convert 'latest' string to null to use default behavior
+    const from = fromBlock === 'latest' ? null : Number(fromBlock);
+    const to = toBlock === 'latest' ? null : Number(toBlock);
+ 
+    // Trigger a scan
+    const result = await indexer.scanForEvents(from, to);
+ 
+    logger.info(`Manual indexer scan completed by admin ${req.user.id}`, {
+      adminId: req.user.id,
+      eventsProcessed: result.events.length,
+      fromBlock: result.fromBlock,
+      toBlock: result.toBlock
+    });
+ 
+    res.status(200).json({
+      message: 'Scan triggered successfully',
+      eventsProcessed: result.events.length,
+      fromBlock: result.fromBlock,
+      toBlock: result.toBlock
+    });
+  } catch (error) {
+    logger.error(`Error triggering indexer scan by admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error triggering indexer scan: ' + error.message);
+  }
+});
+ 
+// @desc    Get contract events
+// @route   GET /api/indexer/events
+// @access  Private (Admin)
+const getContractEvents = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} retrieving contract events`);
+ 
+    const { eventName, fromBlock, toBlock, limit = 50 } = req.query;
+ 
+    const whereClause = {};
+    Iif (eventName) whereClause.eventName = eventName;
+    Iif (fromBlock) whereClause.blockNumber = { [Op.gte]: fromBlock };
+    Iif (toBlock) whereClause.blockNumber = {
+      ...whereClause.blockNumber,
+      [Op.lte]: toBlock
+    };
+ 
+    const events = await ContractEvent.findAll({
+      where: whereClause,
+      order: [['blockNumber', 'DESC'], ['logIndex', 'DESC']],
+      limit: parseInt(limit)
+    });
+ 
+    logger.info(`Retrieved ${events.length} contract events for admin ${req.user.id}`, {
+      adminId: req.user.id,
+      filter: { eventName, fromBlock, toBlock, limit }
+    });
+ 
+    res.status(200).json(events);
+  } catch (error) {
+    logger.error(`Error retrieving contract events for admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving contract events');
+  }
+});
+ 
+// @desc    Get indexed exams from blockchain
+// @route   GET /api/indexer/exams
+// @access  Private (Admin)
+const getIndexedExams = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} retrieving exams from indexed blockchain events`);
+ 
+    const events = await ContractEvent.findAll({
+      where: {
+        eventName: 'ExamCreated'
+      },
+      order: [['blockNumber', 'DESC']]
+    });
+ 
+    // Process and transform exam creation events
+    const exams = events.map(event => {
+      const { args } = JSON.parse(event.eventData);
+      return {
+        examId: args.examId,
+        name: args.name,
+        category: args.category,
+        creatorAddress: args.creator,
+        price: args.price,
+        blockNumber: event.blockNumber,
+        timestamp: event.blockTimestamp,
+        transactionHash: event.transactionHash
+      };
+    });
+ 
+    logger.info(`Retrieved ${exams.length} indexed exams for admin ${req.user.id}`);
+    res.status(200).json(exams);
+  } catch (error) {
+    logger.error(`Error retrieving indexed exams for admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving indexed exams');
+  }
+});
+ 
+// @desc    Get indexed registrations from blockchain
+// @route   GET /api/indexer/registrations
+// @access  Private (Admin)
+const getIndexedRegistrations = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} retrieving registrations from indexed blockchain events`);
+ 
+    const { examId } = req.query;
+ 
+    const whereClause = { eventName: 'UserRegistered' };
+    Iif (examId) {
+      // Filter by examId in the JSON event data
+      // This is a simplification - in actual implementation, you might need a more
+      // sophisticated query approach depending on your database
+      whereClause.$and = [
+        { eventData: { $like: `%"examId":"${examId}"%` } }
+      ];
+    }
+ 
+    const events = await ContractEvent.findAll({
+      where: whereClause,
+      order: [['blockNumber', 'DESC']]
+    });
+ 
+    // Process and transform registration events
+    const registrations = events.map(event => {
+      const { args } = JSON.parse(event.eventData);
+      return {
+        registrationId: args.registrationId,
+        examId: args.examId,
+        userAddress: args.user,
+        registrationTime: args.timestamp,
+        blockNumber: event.blockNumber,
+        timestamp: event.blockTimestamp,
+        transactionHash: event.transactionHash
+      };
+    });
+ 
+    logger.info(`Retrieved ${registrations.length} indexed registrations for admin ${req.user.id}`, {
+      adminId: req.user.id,
+      filter: { examId }
+    });
+    res.status(200).json(registrations);
+  } catch (error) {
+    logger.error(`Error retrieving indexed registrations for admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving indexed registrations');
+  }
+});
+ 
+// @desc    Get indexed exam results from blockchain
+// @route   GET /api/indexer/results
+// @access  Private (Admin)
+const getIndexedResults = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} retrieving exam results from indexed blockchain events`);
+ 
+    const { examId, userAddress } = req.query;
+ 
+    const whereClause = { eventName: 'ExamCompleted' };
+    // TODO: Implement proper filtering by examId and userAddress in the JSON
+ 
+    const events = await ContractEvent.findAll({
+      where: whereClause,
+      order: [['blockNumber', 'DESC']]
+    });
+ 
+    // Process and transform result events
+    const results = events.map(event => {
+      const { args } = JSON.parse(event.eventData);
+      return {
+        resultId: args.resultId,
+        examId: args.examId,
+        userAddress: args.user,
+        score: args.score,
+        passed: args.passed,
+        blockNumber: event.blockNumber,
+        timestamp: event.blockTimestamp,
+        transactionHash: event.transactionHash
+      };
+    });
+ 
+    logger.info(`Retrieved ${results.length} indexed exam results for admin ${req.user.id}`, {
+      adminId: req.user.id,
+      filter: { examId, userAddress }
+    });
+    res.status(200).json(results);
+  } catch (error) {
+    logger.error(`Error retrieving indexed exam results for admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving indexed exam results');
+  }
+});
+ 
+export {
+  getIndexerStatus,
+  triggerScan,
+  getContractEvents,
+  getIndexedExams,
+  getIndexedRegistrations,
+  getIndexedResults,
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/controllers/notification.controller.js.html b/coverage/lcov-report/controllers/notification.controller.js.html new file mode 100644 index 0000000..6d51c89 --- /dev/null +++ b/coverage/lcov-report/controllers/notification.controller.js.html @@ -0,0 +1,982 @@ + + + + + + Code coverage report for controllers/notification.controller.js + + + + + + + + + +
+
+

All files / controllers notification.controller.js

+
+ +
+ 0% + Statements + 0/95 +
+ + +
+ 0% + Branches + 0/30 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 0% + Lines + 0/95 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Notification, User } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+import { validationResult } from 'express-validator';
+import logger from '../utils/logger.js';
+ 
+// @desc    Get all notifications
+// @route   GET /api/notifications
+// @access  Private
+const getNotifications = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`User ${req.user.id} requesting their notifications`);
+ 
+    const notifications = await Notification.findAll({
+      where: {
+        userId: req.user.id,
+      },
+      order: [['createdAt', 'DESC']],
+    });
+ 
+    logger.info(`Retrieved ${notifications.length} notifications for user ${req.user.id}`);
+    res.status(200).json(notifications);
+  } catch (error) {
+    logger.error(`Error retrieving notifications for user ${req.user.id}: ${error.message}`, {
+      userId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving notifications');
+  }
+});
+ 
+// @desc    Get notification by ID
+// @route   GET /api/notifications/:id
+// @access  Private
+const getNotificationById = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`User ${req.user.id} requesting notification ${req.params.id}`);
+ 
+    const notification = await Notification.findOne({
+      where: {
+        id: req.params.id,
+        userId: req.user.id,
+      },
+    });
+ 
+    Iif (!notification) {
+      logger.warn(`Notification with id ${req.params.id} not found for user ${req.user.id}`, {
+        notificationId: req.params.id,
+        userId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Notification not found');
+    }
+ 
+    logger.info(`Retrieved notification ${notification.id} for user ${req.user.id}`);
+    res.status(200).json(notification);
+  } catch (error) {
+    Iif (error.message === 'Notification not found') {
+      throw error;
+    }
+    logger.error(`Error retrieving notification ${req.params.id} for user ${req.user.id}: ${error.message}`, {
+      notificationId: req.params.id,
+      userId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving notification');
+  }
+});
+ 
+// @desc    Create new notification
+// @route   POST /api/notifications
+// @access  Private (Admin)
+const createNotification = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  Iif (!errors.isEmpty()) {
+    logger.warn(`Validation errors in creating notification by admin ${req.user.id}: ${JSON.stringify(errors.array())}`, {
+      userId: req.user.id,
+      validationErrors: errors.array()
+    });
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const {
+    title,
+    message,
+    type,
+    userId,
+    expiresAt,
+  } = req.body;
+ 
+  logger.info(`Admin ${req.user.id} creating notification: "${title}" for user ${userId || 'all users'}`);
+ 
+  try {
+    // Check if user exists if userId is provided
+    Iif (userId) {
+      const user = await User.findByPk(userId);
+      Iif (!user) {
+        logger.warn(`User with id ${userId} not found while admin ${req.user.id} creating notification`, {
+          targetUserId: userId,
+          adminId: req.user.id
+        });
+        res.status(404);
+        throw new Error('User not found');
+      }
+    }
+ 
+    // Create notification
+    const notification = await Notification.create({
+      title,
+      message,
+      type: type || 'info',
+      userId,
+      expiresAt,
+    });
+ 
+    logger.info(`Created notification ${notification.id} for user ${userId || 'all users'} by admin ${req.user.id}`, {
+      notificationId: notification.id,
+      targetUserId: userId,
+      adminId: req.user.id,
+      notificationType: type || 'info'
+    });
+    res.status(201).json(notification);
+  } catch (error) {
+    Iif (error.message === 'User not found') {
+      throw error;
+    }
+    logger.error(`Error creating notification by admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      targetUserId: userId,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error creating notification');
+  }
+});
+ 
+// @desc    Update notification
+// @route   PUT /api/notifications/:id
+// @access  Private (Admin)
+const updateNotification = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} updating notification ${req.params.id}`);
+ 
+    const notification = await Notification.findByPk(req.params.id);
+ 
+    Iif (!notification) {
+      logger.warn(`Notification with id ${req.params.id} not found for update by admin ${req.user.id}`, {
+        notificationId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Notification not found');
+    }
+ 
+    const {
+      title,
+      message,
+      type,
+      isRead,
+      expiresAt,
+    } = req.body;
+ 
+    // Update notification
+    await notification.update({
+      title: title || notification.title,
+      message: message || notification.message,
+      type: type || notification.type,
+      isRead: isRead !== undefined ? isRead : notification.isRead,
+      expiresAt: expiresAt || notification.expiresAt,
+    });
+ 
+    logger.info(`Updated notification ${notification.id} by admin ${req.user.id}`, {
+      notificationId: notification.id,
+      adminId: req.user.id,
+      changes: {
+        title: !!title,
+        message: !!message,
+        type: !!type,
+        isRead: isRead !== undefined,
+        expiresAt: !!expiresAt
+      }
+    });
+    res.status(200).json(notification);
+  } catch (error) {
+    Iif (error.message === 'Notification not found') {
+      throw error;
+    }
+    logger.error(`Error updating notification ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      notificationId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error updating notification');
+  }
+});
+ 
+// @desc    Mark notification as read
+// @route   PATCH /api/notifications/:id/read
+// @access  Private
+const markAsRead = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`User ${req.user.id} marking notification ${req.params.id} as read`);
+ 
+    const notification = await Notification.findOne({
+      where: {
+        id: req.params.id,
+        userId: req.user.id,
+      },
+    });
+ 
+    Iif (!notification) {
+      logger.warn(`Notification with id ${req.params.id} not found for user ${req.user.id} to mark as read`, {
+        notificationId: req.params.id,
+        userId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Notification not found');
+    }
+ 
+    // Mark as read
+    await notification.update({
+      isRead: true,
+    });
+ 
+    logger.info(`Marked notification ${notification.id} as read for user ${req.user.id}`, {
+      notificationId: notification.id,
+      userId: req.user.id,
+      previousReadStatus: notification.isRead
+    });
+    res.status(200).json(notification);
+  } catch (error) {
+    Iif (error.message === 'Notification not found') {
+      throw error;
+    }
+    logger.error(`Error marking notification ${req.params.id} as read for user ${req.user.id}: ${error.message}`, {
+      notificationId: req.params.id,
+      userId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error marking notification as read');
+  }
+});
+ 
+// @desc    Delete notification
+// @route   DELETE /api/notifications/:id
+// @access  Private (Admin)
+const deleteNotification = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} deleting notification ${req.params.id}`);
+ 
+    const notification = await Notification.findByPk(req.params.id);
+ 
+    Iif (!notification) {
+      logger.warn(`Notification with id ${req.params.id} not found for deletion by admin ${req.user.id}`, {
+        notificationId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Notification not found');
+    }
+ 
+    // Delete notification
+    await notification.destroy();
+ 
+    logger.info(`Deleted notification ${req.params.id} by admin ${req.user.id}`, {
+      notificationId: req.params.id,
+      adminId: req.user.id,
+      notificationData: {
+        title: notification.title,
+        userId: notification.userId,
+        type: notification.type
+      }
+    });
+    res.status(200).json({ id: req.params.id });
+  } catch (error) {
+    Iif (error.message === 'Notification not found') {
+      throw error;
+    }
+    logger.error(`Error deleting notification ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      notificationId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error deleting notification');
+  }
+});
+ 
+export {
+  getNotifications,
+  getNotificationById,
+  createNotification,
+  updateNotification,
+  markAsRead,
+  deleteNotification,
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/controllers/registration.controller.js.html b/coverage/lcov-report/controllers/registration.controller.js.html new file mode 100644 index 0000000..e05742f --- /dev/null +++ b/coverage/lcov-report/controllers/registration.controller.js.html @@ -0,0 +1,481 @@ + + + + + + Code coverage report for controllers/registration.controller.js + + + + + + + + + +
+
+

All files / controllers registration.controller.js

+
+ +
+ 0% + Statements + 0/43 +
+ + +
+ 0% + Branches + 0/7 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 0% + Lines + 0/43 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Registration, User, Exam } from '../models/index.js';
+import { validationResult } from 'express-validator';
+import asyncHandler from 'express-async-handler';
+ 
+// @desc    Register for an exam
+// @access  Private
+const registerForExam = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  Iif (!errors.isEmpty()) {
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const { examId } = req.body;
+  const userId = req.user.id;
+ 
+  // Check if exam exists
+  const exam = await Exam.findByPk(examId);
+  Iif (!exam) {
+    res.status(404);
+    throw new Error('Exam not found');
+  }
+ 
+  // Check if already registered
+  const existingRegistration = await Registration.findOne({
+    where: { userId, examId }
+  });
+ 
+  Iif (existingRegistration) {
+    res.status(400);
+    throw new Error('Already registered for this exam');
+  }
+ 
+  // Create registration
+  const registration = await Registration.create({
+    userId,
+    examId,
+    paymentStatus: 'pending'
+  });
+ 
+  res.status(201).json(registration);
+});
+ 
+// @desc    Update payment status
+// @access  Private
+const updatePaymentStatus = asyncHandler(async (req, res) => {
+  const { paymentStatus } = req.body;
+  const userId = req.user.id;
+ 
+  const registration = await Registration.findOne({
+    where: { id: req.params.id, userId }
+  });
+ 
+  Iif (!registration) {
+    res.status(404);
+    throw new Error('Registration not found');
+  }
+ 
+  registration.paymentStatus = paymentStatus;
+  await registration.save();
+ 
+  res.status(200).json(registration);
+});
+ 
+// @desc    Get user's registrations
+// @access  Private
+const getUserRegistrations = asyncHandler(async (req, res) => {
+  const registrations = await Registration.findAll({
+    where: { userId: req.user.id },
+    include: [
+      {
+        model: Exam,
+        attributes: ['id', 'name', 'category', 'date', 'duration', 'passingScore']
+      }
+    ]
+  });
+ 
+  res.status(200).json(registrations);
+});
+ 
+// @desc    Validate exam code
+// @access  Public
+const validateExamCode = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  Iif (!errors.isEmpty()) {
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const { email, examCode } = req.body;
+ 
+  // Find user by email
+  const user = await User.findOne({ where: { email } });
+  Iif (!user) {
+    res.status(404);
+    throw new Error('User not found');
+  }
+ 
+  // Find registration by exam code and user
+  const registration = await Registration.findOne({
+    where: { 
+      examCode,
+      userId: user.id,
+      paymentStatus: 'completed'
+    },
+    include: [
+      {
+        model: Exam,
+        attributes: { exclude: ['createdAt', 'updatedAt'] }
+      }
+    ]
+  });
+ 
+  Iif (!registration) {
+    res.status(404);
+    throw new Error('Invalid exam code or payment not completed');
+  }
+ 
+  res.status(200).json({
+    registration,
+    exam: registration.Exam,
+    user: {
+      id: user.id,
+      fullName: user.fullName,
+      email: user.email
+    }
+  });
+});
+ 
+export {
+  registerForExam,
+  updatePaymentStatus,
+  getUserRegistrations,
+  validateExamCode
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/controllers/result.controller.js.html b/coverage/lcov-report/controllers/result.controller.js.html new file mode 100644 index 0000000..c746c5c --- /dev/null +++ b/coverage/lcov-report/controllers/result.controller.js.html @@ -0,0 +1,559 @@ + + + + + + Code coverage report for controllers/result.controller.js + + + + + + + + + +
+
+

All files / controllers result.controller.js

+
+ +
+ 0% + Statements + 0/50 +
+ + +
+ 0% + Branches + 0/9 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 0% + Lines + 0/47 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Result, Registration, Exam, Question, Option } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+ 
+// @desc    Submit exam and create result
+// @access  Private
+const submitExam = asyncHandler(async (req, res) => {
+  const { registrationId, answers } = req.body;
+  const userId = req.user.id;
+ 
+  // Find registration
+  const registration = await Registration.findOne({
+    where: { id: registrationId, userId },
+    include: [{ model: Exam }]
+  });
+ 
+  Iif (!registration) {
+    res.status(404);
+    throw new Error('Registration not found');
+  }
+ 
+  Iif (registration.status === 'completed') {
+    res.status(400);
+    throw new Error('Exam already completed');
+  }
+ 
+  // Get all questions for this exam
+  const questions = await Question.findAll({
+    where: { examId: registration.examId },
+    include: [{ model: Option }]
+  });
+ 
+  // Calculate score
+  let correctAnswers = 0;
+  const processedAnswers = [];
+ 
+  for (const answer of answers) {
+    const question = questions.find(q => q.id === answer.questionId);
+    Iif (!question) continue;
+ 
+    const correctOption = question.Options.find(opt => opt.isCorrect);
+    const isCorrect = correctOption && correctOption.id === answer.selectedOption;
+ 
+    Iif (isCorrect) {
+      correctAnswers++;
+    }
+ 
+    processedAnswers.push({
+      questionId: answer.questionId,
+      selectedOption: answer.selectedOption,
+      isCorrect
+    });
+  }
+ 
+  const totalQuestions = questions.length;
+  const score = (correctAnswers / totalQuestions) * 100;
+  const passed = score >= registration.Exam.passingScore;
+ 
+  // Create result
+  const result = await Result.create({
+    registrationId,
+    userId,
+    examId: registration.examId,
+    score,
+    passed,
+    answers: processedAnswers
+  });
+ 
+  // Update registration status
+  registration.status = 'completed';
+  await registration.save();
+ 
+  res.status(201).json(result);
+});
+ 
+// @desc    Get user's results
+// @access  Private
+const getUserResults = asyncHandler(async (req, res) => {
+  const results = await Result.findAll({
+    where: { userId: req.user.id },
+    include: [
+      {
+        model: Exam,
+        attributes: ['id', 'name', 'category', 'passingScore', 'certification']
+      }
+    ]
+  });
+ 
+  res.status(200).json(results);
+});
+ 
+// @desc    Get result by ID
+// @access  Private
+const getResultById = asyncHandler(async (req, res) => {
+  const result = await Result.findOne({
+    where: { id: req.params.id, userId: req.user.id },
+    include: [
+      {
+        model: Exam,
+        attributes: { exclude: ['createdAt', 'updatedAt'] }
+      }
+    ]
+  });
+ 
+  Iif (!result) {
+    res.status(404);
+    throw new Error('Result not found');
+  }
+ 
+  res.status(200).json(result);
+});
+ 
+// @desc    Generate certificate for a result
+// @access  Private
+const generateCertificate = asyncHandler(async (req, res) => {
+  const result = await Result.findOne({
+    where: { id: req.params.id, userId: req.user.id, passed: true },
+    include: [
+      {
+        model: Exam,
+        attributes: ['name', 'category', 'certification']
+      },
+      {
+        model: User,
+        attributes: ['fullName']
+      }
+    ]
+  });
+ 
+  Iif (!result) {
+    res.status(404);
+    throw new Error('Result not found or exam not passed');
+  }
+ 
+  Iif (!result.Exam.certification) {
+    res.status(400);
+    throw new Error('This exam does not provide certification');
+  }
+ 
+  // In a real application, you would generate a PDF certificate here
+  // For now, we'll just return the certificate data
+  const certificateData = {
+    certificateId: `CERT-${result.id}`,
+    candidateName: result.User.fullName,
+    examName: result.Exam.name,
+    category: result.Exam.category,
+    score: result.score,
+    issueDate: new Date(),
+    verificationUrl: `https://skillnet.example.com/verify/${result.id}`
+  };
+ 
+  res.status(200).json(certificateData);
+});
+ 
+export {
+  submitExam,
+  getUserResults,
+  getResultById,
+  generateCertificate
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/controllers/user.controller.js.html b/coverage/lcov-report/controllers/user.controller.js.html new file mode 100644 index 0000000..340d5e6 --- /dev/null +++ b/coverage/lcov-report/controllers/user.controller.js.html @@ -0,0 +1,340 @@ + + + + + + Code coverage report for controllers/user.controller.js + + + + + + + + + +
+
+

All files / controllers user.controller.js

+
+ +
+ 93.93% + Statements + 31/33 +
+ + +
+ 83.33% + Branches + 5/6 +
+ + +
+ 100% + Functions + 4/4 +
+ + +
+ 93.54% + Lines + 29/31 +
+ + +
+

+ 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  +  +  +  +  +1x +2x +  +  +  +2x +1x +1x +  +  +1x +  +  +  +  +1x +4x +4x +1x +1x +  +3x +  +3x +  +  +  +  +3x +  +  +3x +3x +  +3x +  +2x +  +  +  +  +  +  +  +1x +1x +  +  +  +  +  +1x +2x +  +  +  +  +2x +1x +1x +  +  +1x +  +  +  +  +1x +1x +  +  +  +1x +  +  +  +  +  +  +  + 
import { User } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+ 
+// @desc    Get user profile
+// @access  Private
+const getUserProfile = asyncHandler(async (req, res) => {
+  const user = await User.findByPk(req.user.id, {
+    attributes: { exclude: ['password'] }
+  });
+ 
+  if (!user) {
+    res.status(404);
+    throw new Error('User not found');
+  }
+ 
+  res.status(200).json(user);
+});
+ 
+// @desc    Update user profile
+// @access  Private
+const updateUserProfile = asyncHandler(async (req, res) => {
+  try {
+    if (!req.body) {
+      res.status(400);
+      return res.json({ message: 'Request body is required' });
+    }
+    const user = await User.findByPk(req.user.id);
+ 
+    Iif (!user) {
+      res.status(404);
+      throw new Error('User not found');
+    }
+ 
+    const { fullName, email } = req.body;
+ 
+    // Update fields if provided
+    if (fullName) user.fullName = fullName;
+    if (email) user.email = email;
+ 
+    await user.save();
+ 
+    res.status(200).json({
+      id: user.id,
+      fullName: user.fullName,
+      email: user.email,
+      walletAddress: user.walletAddress,
+      role: user.role
+    });
+  } catch (error) {
+    res.status(500);
+    throw new Error('Error updating user profile: ' + error.message);
+  }
+});
+ 
+// @desc    Get user by wallet address
+// @access  Public
+const getUserByWallet = asyncHandler(async (req, res) => {
+  const user = await User.findOne({
+    where: { walletAddress: req.params.address },
+    attributes: { exclude: ['password'] }
+  });
+ 
+  if (!user) {
+    res.status(404);
+    throw new Error('User not found');
+  }
+ 
+  res.status(200).json(user);
+});
+ 
+// @desc    Get all users
+// @access  Private/Admin
+const getUsers = asyncHandler(async (req, res) => {
+  const users = await User.findAll({
+    attributes: { exclude: ['password'] }
+  });
+ 
+  res.status(200).json(users);
+});
+ 
+export {
+  getUserProfile,
+  updateUserProfile,
+  getUserByWallet,
+  getUsers
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/exam.controller.js.html b/coverage/lcov-report/exam.controller.js.html new file mode 100644 index 0000000..39240b0 --- /dev/null +++ b/coverage/lcov-report/exam.controller.js.html @@ -0,0 +1,724 @@ + + + + + + Code coverage report for exam.controller.js + + + + + + + + + +
+
+

All files exam.controller.js

+
+ +
+ 81.03% + Statements + 47/58 +
+ + +
+ 85.41% + Branches + 41/48 +
+ + +
+ 66.66% + Functions + 4/6 +
+ + +
+ 81.03% + Lines + 47/58 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +1x +3x +  +3x +3x +1x +  +  +3x +  +  +  +  +  +  +  +  +  +3x +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +6x +6x +6x +1x +  +  +  +5x +5x +  +1x +  +  +4x +4x +2x +2x +  +  +  +1x +1x +2x +  +  +  +  +  +  +  +  +1x +1x +  +1x +  +  +3x +  +  +  +3x +  +  +  +  +  +  +  +  +1x +3x +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +  +  +  +  +  +  +  +3x +  +  +  +  +  +1x +2x +  +2x +1x +1x +  +  +  +1x +  +1x +1x +  +  +1x +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  + 
import { Exam, Question, Option } from '../models/index.js';
+import { Op } from 'sequelize';
+import asyncHandler from 'express-async-handler';
+import { validationResult } from 'express-validator';
+ 
+// @desc    Get all exams
+// @route   GET /api/exams
+// @access  Public
+const getExams = asyncHandler(async (req, res) => {
+  const { category } = req.query;
+  
+  const whereClause = {};
+  if (category) {
+    whereClause.category = category;
+  }
+  
+  const exams = await Exam.findAll({
+    where: whereClause,
+    include: [
+      {
+        model: Question,
+        attributes: ['id'], // Only count questions, don't return them
+      },
+    ],
+  });
+  
+  res.status(200).json(exams);
+});
+ 
+// @desc    Get exam by ID
+// @route   GET /api/exams/:id
+// @access  Public
+const getExamById = asyncHandler(async (req, res) => {
+  const exam = await Exam.findByPk(req.params.id, {
+    include: [
+      {
+        model: Question,
+        include: [
+          {
+            model: Option,
+            attributes: ['id', 'text', 'order'], // Don't expose correct answers
+          },
+        ],
+      },
+    ],
+  });
+  
+  if (!exam) {
+    res.status(404);
+    throw new Error('Exam not found');
+  }
+  
+  res.status(200).json(exam);
+});
+ 
+// @desc    Get exams by category
+// @route   GET /api/exams/category/:category
+// @access  Public
+const getExamsByCategory = asyncHandler(async (req, res) => {
+  const { category } = req.params;
+  
+  const exams = await Exam.findAll({
+    where: { category },
+    include: [
+      {
+        model: Question,
+        attributes: ['id'], // Only count questions, don't return them
+      },
+    ],
+  });
+  
+  res.status(200).json(exams);
+});
+ 
+// @desc    Create new exam
+// @route   POST /api/exams
+// @access  Private (Admin)
+const createExam = asyncHandler(async (req, res, next) => {
+  try {
+    const errors = validationResult(req);
+    if (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    let exam;
+    try {
+      exam = await Exam.create(req.body);
+    } catch (error) {
+      return next({ message: `Error creating exam: ${error.message}` });
+    }
+ 
+    try {
+      if (req.body.questions && Array.isArray(req.body.questions) && req.body.questions.length > 0) {
+        for (const question of req.body.questions) {
+          const createdQuestion = await Question.create({
+            ...question,
+            examId: exam.id
+          });
+          Eif (question.options && Array.isArray(question.options) && question.options.length > 0) {
+            for (const option of question.options) {
+              await Option.create({
+                ...option,
+                questionId: createdQuestion.id
+              });
+            }
+          }
+        }
+      }
+    } catch (error) {
+      Eif (exam && typeof exam.destroy === 'function') {
+        await exam.destroy();
+      }
+      return next({ message: `Error creating exam: ${error.message}` });
+    }
+ 
+    const createdExam = await Exam.findByPk(exam.id, {
+      include: [{ model: Question, include: [Option] }]
+    });
+ 
+    res.status(201).json(createdExam);
+  } catch (error) {
+    next({ message: `Error creating exam: ${error.message}` });
+  }
+});
+ 
+// @desc    Update exam
+// @route   PUT /api/exams/:id
+// @access  Private (Admin)
+const updateExam = asyncHandler(async (req, res) => {
+  const exam = await Exam.findByPk(req.params.id);
+ 
+  Iif (!exam) {
+    res.status(404);
+    throw new Error('Exam not found');
+  }
+ 
+  const {
+    name,
+    description,
+    category,
+    date,
+    duration,
+    certification,
+    passingScore,
+    format,
+    topicsCovered,
+    benefits,
+    price,
+    instructions,
+  } = req.body;
+ 
+  // Update exam details
+  await exam.update({
+    name: name || exam.name,
+    description: description || exam.description,
+    category: category || exam.category,
+    date: date || exam.date,
+    duration: duration || exam.duration,
+    certification: certification !== undefined ? certification : exam.certification,
+    passingScore: passingScore || exam.passingScore,
+    format: format || exam.format,
+    topicsCovered: topicsCovered || exam.topicsCovered,
+    benefits: benefits || exam.benefits,
+    price: price || exam.price,
+    instructions: instructions || exam.instructions,
+  });
+ 
+  // Get updated exam with questions
+  const updatedExam = await Exam.findByPk(exam.id, {
+    include: [
+      {
+        model: Question,
+        include: [Option],
+      },
+    ],
+  });
+ 
+  res.status(200).json(updatedExam);
+});
+ 
+// @desc    Delete exam
+// @route   DELETE /api/exams/:id
+// @access  Private (Admin)
+const deleteExam = asyncHandler(async (req, res) => {
+  const exam = await Exam.findByPk(req.params.id);
+ 
+  if (!exam) {
+    res.status(404);
+    throw new Error('Exam not found');
+  }
+ 
+  // Delete associated questions and options
+  const questions = await Question.findAll({ where: { examId: exam.id } });
+  
+  for (const question of questions) {
+    await Option.destroy({ where: { questionId: question.id } });
+  }
+  
+  await Question.destroy({ where: { examId: exam.id } });
+  
+  // Delete the exam
+  await exam.destroy();
+ 
+  res.status(200).json({ id: req.params.id });
+});
+ 
+export {
+  getExams,
+  getExamById,
+  getExamsByCategory,
+  createExam,
+  updateExam,
+  deleteExam,
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/examBanner.controller.js.html b/coverage/lcov-report/examBanner.controller.js.html new file mode 100644 index 0000000..b81bb25 --- /dev/null +++ b/coverage/lcov-report/examBanner.controller.js.html @@ -0,0 +1,1063 @@ + + + + + + Code coverage report for examBanner.controller.js + + + + + + + + + +
+
+

All files examBanner.controller.js

+
+ +
+ 0% + Statements + 0/94 +
+ + +
+ 0% + Branches + 0/58 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 0% + Lines + 0/94 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { ExamBanner, Exam } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+import { validationResult } from 'express-validator';
+import logger from '../utils/logger.js';
+ 
+// @desc    Get all exam banners
+// @route   GET /api/exam-banners
+// @access  Public
+const getExamBanners = asyncHandler(async (req, res) => {
+  try {
+    const { active } = req.query;
+ 
+    const whereClause = {};
+    if (active === 'true') {
+      const now = new Date();
+      whereClause.isActive = true;
+      whereClause.startDate = { $lte: now }; // Start date is less than or equal to now
+      whereClause.endDate = { $gte: now };   // End date is greater than or equal to now
+    }
+ 
+    logger.info(`Fetching exam banners with filters: ${JSON.stringify(whereClause)}`);
+ 
+    const examBanners = await ExamBanner.findAll({
+      where: whereClause,
+      include: [
+        {
+          model: Exam,
+          as: 'exam',
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+      order: [
+        ['priority', 'DESC'],
+        ['startDate', 'DESC'],
+      ],
+    });
+ 
+    logger.info(`Retrieved ${examBanners.length} exam banners`);
+    res.status(200).json(examBanners);
+  } catch (error) {
+    logger.error(`Error retrieving exam banners: ${error.message}`, { error: error.stack });
+    res.status(500);
+    throw new Error('Error retrieving exam banners');
+  }
+});
+ 
+// @desc    Get exam banner by ID
+// @route   GET /api/exam-banners/:id
+// @access  Public
+const getExamBannerById = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Fetching exam banner with id: ${req.params.id}`);
+ 
+    const examBanner = await ExamBanner.findByPk(req.params.id, {
+      include: [
+        {
+          model: Exam,
+          as: 'exam',
+          attributes: ['id', 'name', 'category', 'description'],
+        },
+      ],
+    });
+ 
+    if (!examBanner) {
+      logger.warn(`Exam banner with id ${req.params.id} not found`, { bannerId: req.params.id });
+      res.status(404);
+      throw new Error('Exam banner not found');
+    }
+ 
+    logger.info(`Retrieved exam banner ${examBanner.id}`);
+    res.status(200).json(examBanner);
+  } catch (error) {
+    if (error.message === 'Exam banner not found') {
+      throw error;
+    }
+    logger.error(`Error retrieving exam banner ${req.params.id}: ${error.message}`, {
+      bannerId: req.params.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving exam banner');
+  }
+});
+ 
+// @desc    Create new exam banner
+// @route   POST /api/exam-banners
+// @access  Private (Admin)
+const createExamBanner = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  if (!errors.isEmpty()) {
+    logger.warn(`Validation errors in creating exam banner: ${JSON.stringify(errors.array())}`, {
+      userId: req.user.id,
+      validationErrors: errors.array()
+    });
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const {
+    title,
+    description,
+    imageUrl,
+    startDate,
+    endDate,
+    isActive,
+    examId,
+    buttonText,
+    buttonLink,
+    priority,
+  } = req.body;
+ 
+  logger.info(`Admin ${req.user.id} creating exam banner: "${title}"`);
+ 
+  try {
+    // Check if exam exists if examId is provided
+    if (examId) {
+      const exam = await Exam.findByPk(examId);
+      if (!exam) {
+        logger.warn(`Exam with id ${examId} not found while admin ${req.user.id} creating banner`, {
+          examId,
+          adminId: req.user.id
+        });
+        res.status(404);
+        throw new Error('Exam not found');
+      }
+    }
+ 
+    // Create banner
+    const banner = await ExamBanner.create({
+      title,
+      description,
+      imageUrl,
+      startDate,
+      endDate,
+      isActive: isActive !== undefined ? isActive : true,
+      examId,
+      buttonText,
+      buttonLink,
+      priority: priority || 0,
+    });
+ 
+    logger.info(`Created exam banner ${banner.id} by admin ${req.user.id}`, {
+      bannerId: banner.id,
+      adminId: req.user.id,
+      examId: examId || 'none'
+    });
+ 
+    // Return the created banner with exam info
+    const createdBanner = await ExamBanner.findByPk(banner.id, {
+      include: [
+        {
+          model: Exam,
+          as: 'exam',
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+    });
+ 
+    res.status(201).json(createdBanner);
+  } catch (error) {
+    if (error.message === 'Exam not found') {
+      throw error;
+    }
+    logger.error(`Error creating exam banner by admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      examId,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error creating exam banner');
+  }
+});
+ 
+// @desc    Update exam banner
+// @route   PUT /api/exam-banners/:id
+// @access  Private (Admin)
+const updateExamBanner = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} updating exam banner ${req.params.id}`);
+ 
+    const banner = await ExamBanner.findByPk(req.params.id);
+ 
+    if (!banner) {
+      logger.warn(`Exam banner with id ${req.params.id} not found for update by admin ${req.user.id}`, {
+        bannerId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam banner not found');
+    }
+ 
+    const {
+      title,
+      description,
+      imageUrl,
+      startDate,
+      endDate,
+      isActive,
+      examId,
+      buttonText,
+      buttonLink,
+      priority,
+    } = req.body;
+ 
+    // If examId is being changed, verify the new exam exists
+    if (examId && examId !== banner.examId) {
+      const exam = await Exam.findByPk(examId);
+      if (!exam) {
+        logger.warn(`Exam with id ${examId} not found while admin ${req.user.id} updating banner ${req.params.id}`, {
+          examId,
+          bannerId: req.params.id,
+          adminId: req.user.id
+        });
+        res.status(404);
+        throw new Error('Exam not found');
+      }
+    }
+ 
+    // Update banner
+    await banner.update({
+      title: title || banner.title,
+      description: description !== undefined ? description : banner.description,
+      imageUrl: imageUrl !== undefined ? imageUrl : banner.imageUrl,
+      startDate: startDate || banner.startDate,
+      endDate: endDate || banner.endDate,
+      isActive: isActive !== undefined ? isActive : banner.isActive,
+      examId: examId || banner.examId,
+      buttonText: buttonText !== undefined ? buttonText : banner.buttonText,
+      buttonLink: buttonLink !== undefined ? buttonLink : banner.buttonLink,
+      priority: priority !== undefined ? priority : banner.priority,
+    });
+ 
+    logger.info(`Updated exam banner ${banner.id} by admin ${req.user.id}`, {
+      bannerId: banner.id,
+      adminId: req.user.id,
+      changes: {
+        title: !!title,
+        description: description !== undefined,
+        imageUrl: imageUrl !== undefined,
+        startDate: !!startDate,
+        endDate: !!endDate,
+        isActive: isActive !== undefined,
+        examId: !!examId && examId !== banner.examId,
+        buttonText: buttonText !== undefined,
+        buttonLink: buttonLink !== undefined,
+        priority: priority !== undefined
+      }
+    });
+ 
+    // Return the updated banner with exam info
+    const updatedBanner = await ExamBanner.findByPk(banner.id, {
+      include: [
+        {
+          model: Exam,
+          as: 'exam',
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+    });
+ 
+    res.status(200).json(updatedBanner);
+  } catch (error) {
+    if (error.message === 'Exam banner not found' || error.message === 'Exam not found') {
+      throw error;
+    }
+    logger.error(`Error updating exam banner ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      bannerId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error updating exam banner');
+  }
+});
+ 
+// @desc    Delete exam banner
+// @route   DELETE /api/exam-banners/:id
+// @access  Private (Admin)
+const deleteExamBanner = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} deleting exam banner ${req.params.id}`);
+ 
+    const banner = await ExamBanner.findByPk(req.params.id);
+ 
+    if (!banner) {
+      logger.warn(`Exam banner with id ${req.params.id} not found for deletion by admin ${req.user.id}`, {
+        bannerId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam banner not found');
+    }
+ 
+    // Delete banner
+    await banner.destroy();
+ 
+    logger.info(`Deleted exam banner ${req.params.id} by admin ${req.user.id}`, {
+      bannerId: req.params.id,
+      adminId: req.user.id,
+      bannerData: {
+        title: banner.title,
+        examId: banner.examId,
+        isActive: banner.isActive
+      }
+    });
+    res.status(200).json({ id: req.params.id });
+  } catch (error) {
+    if (error.message === 'Exam banner not found') {
+      throw error;
+    }
+    logger.error(`Error deleting exam banner ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      bannerId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error deleting exam banner');
+  }
+});
+ 
+export {
+  getExamBanners,
+  getExamBannerById,
+  createExamBanner,
+  updateExamBanner,
+  deleteExamBanner,
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/examRecording.controller.js.html b/coverage/lcov-report/examRecording.controller.js.html new file mode 100644 index 0000000..97944a7 --- /dev/null +++ b/coverage/lcov-report/examRecording.controller.js.html @@ -0,0 +1,1192 @@ + + + + + + Code coverage report for examRecording.controller.js + + + + + + + + + +
+
+

All files examRecording.controller.js

+
+ +
+ 0% + Statements + 0/111 +
+ + +
+ 0% + Branches + 0/66 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 0% + Lines + 0/111 +
+ + +
+

+ 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 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { ExamRecording, Exam } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+import { validationResult } from 'express-validator';
+import logger from '../utils/logger.js';
+ 
+// @desc    Get all exam recordings
+// @route   GET /api/exam-recordings
+// @access  Public
+const getExamRecordings = asyncHandler(async (req, res) => {
+  try {
+    const { examId, published } = req.query;
+ 
+    const whereClause = {};
+    if (examId) {
+      whereClause.examId = examId;
+    }
+    if (published === 'true') {
+      whereClause.isPublished = true;
+    } else if (published === 'false') {
+      whereClause.isPublished = false;
+    }
+ 
+    logger.info(`Fetching exam recordings with filters: ${JSON.stringify(whereClause)}`);
+ 
+    const examRecordings = await ExamRecording.findAll({
+      where: whereClause,
+      include: [
+        {
+          model: Exam,
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+      order: [
+        ['recordedOn', 'DESC'],
+      ],
+    });
+ 
+    logger.info(`Retrieved ${examRecordings.length} exam recordings`);
+    res.status(200).json(examRecordings);
+  } catch (error) {
+    logger.error(`Error retrieving exam recordings: ${error.message}`, { error: error.stack });
+    res.status(500);
+    throw new Error('Error retrieving exam recordings');
+  }
+});
+ 
+// @desc    Get exam recording by ID
+// @route   GET /api/exam-recordings/:id
+// @access  Public
+const getExamRecordingById = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Fetching exam recording with id: ${req.params.id}`);
+ 
+    const examRecording = await ExamRecording.findByPk(req.params.id, {
+      include: [
+        {
+          model: Exam,
+          attributes: ['id', 'name', 'category', 'description'],
+        },
+      ],
+    });
+ 
+    if (!examRecording) {
+      logger.warn(`Exam recording with id ${req.params.id} not found`, { recordingId: req.params.id });
+      res.status(404);
+      throw new Error('Exam recording not found');
+    }
+ 
+    logger.info(`Retrieved exam recording ${examRecording.id}`);
+    res.status(200).json(examRecording);
+  } catch (error) {
+    if (error.message === 'Exam recording not found') {
+      throw error;
+    }
+    logger.error(`Error retrieving exam recording ${req.params.id}: ${error.message}`, {
+      recordingId: req.params.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving exam recording');
+  }
+});
+ 
+// @desc    Create new exam recording
+// @route   POST /api/exam-recordings
+// @access  Private (Admin)
+const createExamRecording = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  if (!errors.isEmpty()) {
+    logger.warn(`Validation errors in creating exam recording: ${JSON.stringify(errors.array())}`, {
+      userId: req.user.id,
+      validationErrors: errors.array()
+    });
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const {
+    title,
+    description,
+    recordingUrl,
+    duration,
+    examId,
+    presenter,
+    recordedOn,
+    isPublished,
+    thumbnailUrl,
+    tags,
+  } = req.body;
+ 
+  logger.info(`Admin ${req.user.id} creating exam recording: "${title}"`);
+ 
+  try {
+    // Check if exam exists
+    const exam = await Exam.findByPk(examId);
+    if (!exam) {
+      logger.warn(`Exam with id ${examId} not found while admin ${req.user.id} creating recording`, {
+        examId,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam not found');
+    }
+ 
+    // Create recording
+    const recording = await ExamRecording.create({
+      title,
+      description,
+      recordingUrl,
+      duration,
+      examId,
+      presenter,
+      recordedOn: recordedOn || new Date(),
+      isPublished: isPublished !== undefined ? isPublished : false,
+      thumbnailUrl,
+      tags: tags || [],
+    });
+ 
+    logger.info(`Created exam recording ${recording.id} by admin ${req.user.id}`, {
+      recordingId: recording.id,
+      adminId: req.user.id,
+      examId: examId,
+      isPublished: isPublished !== undefined ? isPublished : false
+    });
+ 
+    // Return the created recording with exam info
+    const createdRecording = await ExamRecording.findByPk(recording.id, {
+      include: [
+        {
+          model: Exam,
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+    });
+ 
+    res.status(201).json(createdRecording);
+  } catch (error) {
+    if (error.message === 'Exam not found') {
+      throw error;
+    }
+    logger.error(`Error creating exam recording by admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      examId,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error creating exam recording');
+  }
+});
+ 
+// @desc    Update exam recording
+// @route   PUT /api/exam-recordings/:id
+// @access  Private (Admin)
+const updateExamRecording = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} updating exam recording ${req.params.id}`);
+ 
+    const recording = await ExamRecording.findByPk(req.params.id);
+ 
+    if (!recording) {
+      logger.warn(`Exam recording with id ${req.params.id} not found for update by admin ${req.user.id}`, {
+        recordingId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam recording not found');
+    }
+ 
+    const {
+      title,
+      description,
+      recordingUrl,
+      duration,
+      examId,
+      presenter,
+      recordedOn,
+      isPublished,
+      thumbnailUrl,
+      tags,
+    } = req.body;
+ 
+    // If examId is being changed, verify the new exam exists
+    if (examId && examId !== recording.examId) {
+      const exam = await Exam.findByPk(examId);
+      if (!exam) {
+        logger.warn(`Exam with id ${examId} not found while admin ${req.user.id} updating recording ${req.params.id}`, {
+          examId,
+          recordingId: req.params.id,
+          adminId: req.user.id
+        });
+        res.status(404);
+        throw new Error('Exam not found');
+      }
+    }
+ 
+    // Update recording
+    await recording.update({
+      title: title || recording.title,
+      description: description !== undefined ? description : recording.description,
+      recordingUrl: recordingUrl || recording.recordingUrl,
+      duration: duration !== undefined ? duration : recording.duration,
+      examId: examId || recording.examId,
+      presenter: presenter !== undefined ? presenter : recording.presenter,
+      recordedOn: recordedOn || recording.recordedOn,
+      isPublished: isPublished !== undefined ? isPublished : recording.isPublished,
+      thumbnailUrl: thumbnailUrl !== undefined ? thumbnailUrl : recording.thumbnailUrl,
+      tags: tags || recording.tags,
+    });
+ 
+    logger.info(`Updated exam recording ${recording.id} by admin ${req.user.id}`, {
+      recordingId: recording.id,
+      adminId: req.user.id,
+      changes: {
+        title: !!title,
+        description: description !== undefined,
+        recordingUrl: !!recordingUrl,
+        duration: duration !== undefined,
+        examId: !!examId && examId !== recording.examId,
+        presenter: presenter !== undefined,
+        recordedOn: !!recordedOn,
+        isPublished: isPublished !== undefined,
+        thumbnailUrl: thumbnailUrl !== undefined,
+        tags: !!tags
+      }
+    });
+ 
+    // Return the updated recording with exam info
+    const updatedRecording = await ExamRecording.findByPk(recording.id, {
+      include: [
+        {
+          model: Exam,
+          attributes: ['id', 'name', 'category'],
+        },
+      ],
+    });
+ 
+    res.status(200).json(updatedRecording);
+  } catch (error) {
+    if (error.message === 'Exam recording not found' || error.message === 'Exam not found') {
+      throw error;
+    }
+    logger.error(`Error updating exam recording ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      recordingId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error updating exam recording');
+  }
+});
+ 
+// @desc    Delete exam recording
+// @route   DELETE /api/exam-recordings/:id
+// @access  Private (Admin)
+const deleteExamRecording = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} deleting exam recording ${req.params.id}`);
+ 
+    const recording = await ExamRecording.findByPk(req.params.id);
+ 
+    if (!recording) {
+      logger.warn(`Exam recording with id ${req.params.id} not found for deletion by admin ${req.user.id}`, {
+        recordingId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam recording not found');
+    }
+ 
+    // Delete recording
+    await recording.destroy();
+ 
+    logger.info(`Deleted exam recording ${req.params.id} by admin ${req.user.id}`, {
+      recordingId: req.params.id,
+      adminId: req.user.id,
+      recordingData: {
+        title: recording.title,
+        examId: recording.examId,
+        isPublished: recording.isPublished
+      }
+    });
+    res.status(200).json({ id: req.params.id });
+  } catch (error) {
+    if (error.message === 'Exam recording not found') {
+      throw error;
+    }
+    logger.error(`Error deleting exam recording ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      recordingId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error deleting exam recording');
+  }
+});
+ 
+// @desc    Toggle publish status of an exam recording
+// @route   PATCH /api/exam-recordings/:id/publish
+// @access  Private (Admin)
+const togglePublishStatus = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} toggling publish status for exam recording ${req.params.id}`);
+ 
+    const recording = await ExamRecording.findByPk(req.params.id);
+ 
+    if (!recording) {
+      logger.warn(`Exam recording with id ${req.params.id} not found for status toggle by admin ${req.user.id}`, {
+        recordingId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Exam recording not found');
+    }
+ 
+    // Toggle publish status
+    const newStatus = !recording.isPublished;
+    await recording.update({
+      isPublished: newStatus,
+    });
+ 
+    logger.info(`Toggled exam recording ${recording.id} publish status to ${newStatus} by admin ${req.user.id}`, {
+      recordingId: recording.id,
+      adminId: req.user.id,
+      newStatus: newStatus,
+      previousStatus: !newStatus
+    });
+ 
+    res.status(200).json(recording);
+  } catch (error) {
+    if (error.message === 'Exam recording not found') {
+      throw error;
+    }
+    logger.error(`Error toggling exam recording ${req.params.id} publish status by admin ${req.user.id}: ${error.message}`, {
+      recordingId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error toggling publish status');
+  }
+});
+ 
+export {
+  getExamRecordings,
+  getExamRecordingById,
+  createExamRecording,
+  updateExamRecording,
+  deleteExamRecording,
+  togglePublishStatus,
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/favicon.png b/coverage/lcov-report/favicon.png new file mode 100644 index 0000000..c1525b8 Binary files /dev/null and b/coverage/lcov-report/favicon.png differ diff --git a/coverage/lcov-report/index.html b/coverage/lcov-report/index.html new file mode 100644 index 0000000..999cce4 --- /dev/null +++ b/coverage/lcov-report/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 85.71% + Statements + 78/91 +
+ + +
+ 85% + Branches + 51/60 +
+ + +
+ 80% + Functions + 8/10 +
+ + +
+ 85.39% + Lines + 76/89 +
+ + +
+

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

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
exam.controller.js +
+
81.03%47/5885.41%41/4866.66%4/681.03%47/58
user.controller.js +
+
93.93%31/3383.33%10/12100%4/493.54%29/31
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/indexer.controller.js.html b/coverage/lcov-report/indexer.controller.js.html new file mode 100644 index 0000000..71643db --- /dev/null +++ b/coverage/lcov-report/indexer.controller.js.html @@ -0,0 +1,883 @@ + + + + + + Code coverage report for indexer.controller.js + + + + + + + + + +
+
+

All files indexer.controller.js

+
+ +
+ 0% + Statements + 0/81 +
+ + +
+ 0% + Branches + 0/21 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 0% + Lines + 0/78 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { ContractEvent } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+import { indexer } from '../indexer/indexer.js';
+import { indexerConfig, apibaraConfig, networks, contracts, eventHandlers, eventSelectors } from '../indexer/config.js';
+import logger from '../utils/logger.js';
+import { Op } from 'sequelize';
+ 
+// @desc    Get indexer status
+// @route   GET /api/indexer/status
+// @access  Private
+const getIndexerStatus = asyncHandler(async (req, res) => {
+  try {
+    logger.info('Fetching indexer status');
+ 
+    // Get the last processed block from DB
+    const lastEvent = await ContractEvent.findOne({
+      order: [['blockNumber', 'DESC']],
+      attributes: ['blockNumber', 'blockTimestamp', 'createdAt']
+    });
+ 
+    const status = {
+      lastProcessedBlock: lastEvent ? Number(lastEvent.blockNumber) : null,
+      lastBlockTimestamp: lastEvent ? new Date(Number(lastEvent.blockTimestamp) * 1000).toISOString() : null,
+      lastProcessingTime: lastEvent ? lastEvent.createdAt : null,
+      network: indexerConfig.network,
+      contractAddress: indexerConfig.contractAddress,
+      isRunning: indexer.isRunning(),
+      startBlock: Number(process.env.INDEXER_START_BLOCK) || 0
+    };
+ 
+    logger.info(`Indexer status retrieved: ${JSON.stringify(status)}`);
+    res.status(200).json(status);
+  } catch (error) {
+    logger.error(`Error retrieving indexer status: ${error.message}`, { error: error.stack });
+    res.status(500);
+    throw new Error('Error retrieving indexer status');
+  }
+});
+ 
+// @desc    Trigger indexer to scan for events
+// @route   POST /api/indexer/scan
+// @access  Private (Admin)
+const triggerScan = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} triggering indexer scan`);
+ 
+    const { fromBlock, toBlock } = req.body;
+ 
+    // Convert 'latest' string to null to use default behavior
+    const from = fromBlock === 'latest' ? null : Number(fromBlock);
+    const to = toBlock === 'latest' ? null : Number(toBlock);
+ 
+    // Trigger a scan
+    const result = await indexer.scanForEvents(from, to);
+ 
+    logger.info(`Manual indexer scan completed by admin ${req.user.id}`, {
+      adminId: req.user.id,
+      eventsProcessed: result.events.length,
+      fromBlock: result.fromBlock,
+      toBlock: result.toBlock
+    });
+ 
+    res.status(200).json({
+      message: 'Scan triggered successfully',
+      eventsProcessed: result.events.length,
+      fromBlock: result.fromBlock,
+      toBlock: result.toBlock
+    });
+  } catch (error) {
+    logger.error(`Error triggering indexer scan by admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error triggering indexer scan: ' + error.message);
+  }
+});
+ 
+// @desc    Get contract events
+// @route   GET /api/indexer/events
+// @access  Private (Admin)
+const getContractEvents = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} retrieving contract events`);
+ 
+    const { eventName, fromBlock, toBlock, limit = 50 } = req.query;
+ 
+    const whereClause = {};
+    if (eventName) whereClause.eventName = eventName;
+    if (fromBlock) whereClause.blockNumber = { [Op.gte]: fromBlock };
+    if (toBlock) whereClause.blockNumber = {
+      ...whereClause.blockNumber,
+      [Op.lte]: toBlock
+    };
+ 
+    const events = await ContractEvent.findAll({
+      where: whereClause,
+      order: [['blockNumber', 'DESC'], ['logIndex', 'DESC']],
+      limit: parseInt(limit)
+    });
+ 
+    logger.info(`Retrieved ${events.length} contract events for admin ${req.user.id}`, {
+      adminId: req.user.id,
+      filter: { eventName, fromBlock, toBlock, limit }
+    });
+ 
+    res.status(200).json(events);
+  } catch (error) {
+    logger.error(`Error retrieving contract events for admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving contract events');
+  }
+});
+ 
+// @desc    Get indexed exams from blockchain
+// @route   GET /api/indexer/exams
+// @access  Private (Admin)
+const getIndexedExams = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} retrieving exams from indexed blockchain events`);
+ 
+    const events = await ContractEvent.findAll({
+      where: {
+        eventName: 'ExamCreated'
+      },
+      order: [['blockNumber', 'DESC']]
+    });
+ 
+    // Process and transform exam creation events
+    const exams = events.map(event => {
+      const { args } = JSON.parse(event.eventData);
+      return {
+        examId: args.examId,
+        name: args.name,
+        category: args.category,
+        creatorAddress: args.creator,
+        price: args.price,
+        blockNumber: event.blockNumber,
+        timestamp: event.blockTimestamp,
+        transactionHash: event.transactionHash
+      };
+    });
+ 
+    logger.info(`Retrieved ${exams.length} indexed exams for admin ${req.user.id}`);
+    res.status(200).json(exams);
+  } catch (error) {
+    logger.error(`Error retrieving indexed exams for admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving indexed exams');
+  }
+});
+ 
+// @desc    Get indexed registrations from blockchain
+// @route   GET /api/indexer/registrations
+// @access  Private (Admin)
+const getIndexedRegistrations = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} retrieving registrations from indexed blockchain events`);
+ 
+    const { examId } = req.query;
+ 
+    const whereClause = { eventName: 'UserRegistered' };
+    if (examId) {
+      // Filter by examId in the JSON event data
+      // This is a simplification - in actual implementation, you might need a more
+      // sophisticated query approach depending on your database
+      whereClause.$and = [
+        { eventData: { $like: `%"examId":"${examId}"%` } }
+      ];
+    }
+ 
+    const events = await ContractEvent.findAll({
+      where: whereClause,
+      order: [['blockNumber', 'DESC']]
+    });
+ 
+    // Process and transform registration events
+    const registrations = events.map(event => {
+      const { args } = JSON.parse(event.eventData);
+      return {
+        registrationId: args.registrationId,
+        examId: args.examId,
+        userAddress: args.user,
+        registrationTime: args.timestamp,
+        blockNumber: event.blockNumber,
+        timestamp: event.blockTimestamp,
+        transactionHash: event.transactionHash
+      };
+    });
+ 
+    logger.info(`Retrieved ${registrations.length} indexed registrations for admin ${req.user.id}`, {
+      adminId: req.user.id,
+      filter: { examId }
+    });
+    res.status(200).json(registrations);
+  } catch (error) {
+    logger.error(`Error retrieving indexed registrations for admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving indexed registrations');
+  }
+});
+ 
+// @desc    Get indexed exam results from blockchain
+// @route   GET /api/indexer/results
+// @access  Private (Admin)
+const getIndexedResults = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} retrieving exam results from indexed blockchain events`);
+ 
+    const { examId, userAddress } = req.query;
+ 
+    const whereClause = { eventName: 'ExamCompleted' };
+    // TODO: Implement proper filtering by examId and userAddress in the JSON
+ 
+    const events = await ContractEvent.findAll({
+      where: whereClause,
+      order: [['blockNumber', 'DESC']]
+    });
+ 
+    // Process and transform result events
+    const results = events.map(event => {
+      const { args } = JSON.parse(event.eventData);
+      return {
+        resultId: args.resultId,
+        examId: args.examId,
+        userAddress: args.user,
+        score: args.score,
+        passed: args.passed,
+        blockNumber: event.blockNumber,
+        timestamp: event.blockTimestamp,
+        transactionHash: event.transactionHash
+      };
+    });
+ 
+    logger.info(`Retrieved ${results.length} indexed exam results for admin ${req.user.id}`, {
+      adminId: req.user.id,
+      filter: { examId, userAddress }
+    });
+    res.status(200).json(results);
+  } catch (error) {
+    logger.error(`Error retrieving indexed exam results for admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving indexed exam results');
+  }
+});
+ 
+export {
+  getIndexerStatus,
+  triggerScan,
+  getContractEvents,
+  getIndexedExams,
+  getIndexedRegistrations,
+  getIndexedResults,
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/contractEvent.model.js.html b/coverage/lcov-report/models/contractEvent.model.js.html new file mode 100644 index 0000000..11a92eb --- /dev/null +++ b/coverage/lcov-report/models/contractEvent.model.js.html @@ -0,0 +1,280 @@ + + + + + + Code coverage report for models/contractEvent.model.js + + + + + + + + + +
+
+

All files / models contractEvent.model.js

+
+ +
+ 0% + Statements + 0/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 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 +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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export default (sequelize, DataTypes) => {
+  const ContractEvent = sequelize.define('ContractEvent', {
+    id: {
+      type: DataTypes.UUID,
+      defaultValue: DataTypes.UUIDV4,
+      primaryKey: true,
+    },
+    contractAddress: {
+      type: DataTypes.STRING,
+      allowNull: false,
+    },
+    eventName: {
+      type: DataTypes.STRING,
+      allowNull: false,
+    },
+    transactionHash: {
+      type: DataTypes.STRING,
+      allowNull: false,
+    },
+    blockNumber: {
+      type: DataTypes.BIGINT,
+      allowNull: false,
+    },
+    blockTimestamp: {
+      type: DataTypes.DATE,
+      allowNull: false,
+    },
+    eventData: {
+      type: DataTypes.JSONB,
+      allowNull: false,
+      defaultValue: {},
+    },
+    processed: {
+      type: DataTypes.BOOLEAN,
+      allowNull: false,
+      defaultValue: false,
+    },
+    processedAt: {
+      type: DataTypes.DATE,
+      allowNull: true,
+    }
+  }, {
+    timestamps: true,
+    indexes: [
+      {
+        fields: ['contractAddress'],
+      },
+      {
+        fields: ['eventName'],
+      },
+      {
+        fields: ['blockNumber'],
+      },
+      {
+        fields: ['processed'],
+      },
+      {
+        unique: true,
+        fields: ['transactionHash', 'eventName', 'contractAddress'],
+      },
+    ],
+  });
+ 
+  return ContractEvent;
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/exam.model.js.html b/coverage/lcov-report/models/exam.model.js.html new file mode 100644 index 0000000..324b632 --- /dev/null +++ b/coverage/lcov-report/models/exam.model.js.html @@ -0,0 +1,295 @@ + + + + + + Code coverage report for models/exam.model.js + + + + + + + + + +
+
+

All files / models exam.model.js

+
+ +
+ 0% + Statements + 0/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 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 +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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export default (sequelize, DataTypes) => {
+    const Exam = sequelize.define('Exam', {
+      id: {
+        type: DataTypes.UUID,
+        defaultValue: DataTypes.UUIDV4,
+        primaryKey: true,
+      },
+      name: {
+        type: DataTypes.STRING,
+        allowNull: false,
+      },
+      description: {
+        type: DataTypes.TEXT,
+        allowNull: false,
+      },
+      category: {
+        type: DataTypes.ENUM(
+          'Javascript',
+          'Data Science',
+          'AI Development',
+          'Frontend',
+          'Cairo',
+          'Solidity',
+          'NextJS',
+          'Others',
+          // Add more categories as needed
+        ),
+        allowNull: false,
+      },
+      date: {
+        type: DataTypes.DATE,
+        allowNull: false,
+      },
+      duration: {
+        type: DataTypes.INTEGER, // in minutes
+        allowNull: false,
+      },
+      certification: {
+        type: DataTypes.BOOLEAN,
+        defaultValue: true,
+      },
+      passingScore: {
+        type: DataTypes.FLOAT,
+        allowNull: false,
+      },
+      format: {
+        type: DataTypes.STRING,
+        defaultValue: 'Multichoice',
+      },
+      topicsCovered: {
+        type: DataTypes.ARRAY(DataTypes.STRING),
+        defaultValue: [],
+      },
+      benefits: {
+        type: DataTypes.ARRAY(DataTypes.STRING),
+        defaultValue: [],
+      },
+      price: {
+        type: DataTypes.FLOAT,
+        allowNull: false,
+      },
+      instructions: {
+        type: DataTypes.ARRAY(DataTypes.STRING),
+        defaultValue: [],
+      },
+    }, {
+      timestamps: true,
+    });
+  
+    return Exam;
+  };
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/examBanner.model.js.html b/coverage/lcov-report/models/examBanner.model.js.html new file mode 100644 index 0000000..983e9ee --- /dev/null +++ b/coverage/lcov-report/models/examBanner.model.js.html @@ -0,0 +1,277 @@ + + + + + + Code coverage report for models/examBanner.model.js + + + + + + + + + +
+
+

All files / models examBanner.model.js

+
+ +
+ 0% + Statements + 0/4 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 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 +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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export default (sequelize, DataTypes) => {
+  const ExamBanner = sequelize.define('ExamBanner', {
+    id: {
+      type: DataTypes.UUID,
+      defaultValue: DataTypes.UUIDV4,
+      primaryKey: true,
+    },
+    title: {
+      type: DataTypes.STRING,
+      allowNull: false,
+    },
+    description: {
+      type: DataTypes.TEXT,
+      allowNull: true,
+    },
+    imageUrl: {
+      type: DataTypes.STRING,
+      allowNull: true,
+    },
+    startDate: {
+      type: DataTypes.DATE,
+      allowNull: false,
+    },
+    endDate: {
+      type: DataTypes.DATE,
+      allowNull: false,
+    },
+    isActive: {
+      type: DataTypes.BOOLEAN,
+      defaultValue: true,
+    },
+    examId: {
+      type: DataTypes.UUID,
+      allowNull: true,
+      references: {
+        model: 'Exams',
+        key: 'id',
+      },
+    },
+    buttonText: {
+      type: DataTypes.STRING,
+      defaultValue: 'Learn More',
+    },
+    buttonLink: {
+      type: DataTypes.STRING,
+      allowNull: true,
+    },
+    priority: {
+      type: DataTypes.INTEGER,
+      defaultValue: 1,
+    }
+  }, {
+    timestamps: true,
+  });
+ 
+  ExamBanner.associate = (models) => {
+    ExamBanner.belongsTo(models.Exam, {
+      foreignKey: 'examId',
+      as: 'exam',
+    });
+  };
+ 
+  return ExamBanner;
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/examRecording.model.js.html b/coverage/lcov-report/models/examRecording.model.js.html new file mode 100644 index 0000000..5d44149 --- /dev/null +++ b/coverage/lcov-report/models/examRecording.model.js.html @@ -0,0 +1,277 @@ + + + + + + Code coverage report for models/examRecording.model.js + + + + + + + + + +
+
+

All files / models examRecording.model.js

+
+ +
+ 0% + Statements + 0/4 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 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 +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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export default (sequelize, DataTypes) => {
+  const ExamRecording = sequelize.define('ExamRecording', {
+    id: {
+      type: DataTypes.UUID,
+      defaultValue: DataTypes.UUIDV4,
+      primaryKey: true,
+    },
+    title: {
+      type: DataTypes.STRING,
+      allowNull: false,
+    },
+    description: {
+      type: DataTypes.TEXT,
+      allowNull: true,
+    },
+    recordingUrl: {
+      type: DataTypes.STRING,
+      allowNull: false,
+    },
+    duration: {
+      type: DataTypes.INTEGER, // in seconds
+      allowNull: true,
+    },
+    examId: {
+      type: DataTypes.UUID,
+      allowNull: false,
+      references: {
+        model: 'Exams',
+        key: 'id',
+      },
+    },
+    presenter: {
+      type: DataTypes.STRING,
+      allowNull: true,
+    },
+    recordedOn: {
+      type: DataTypes.DATE,
+      allowNull: true,
+    },
+    isPublished: {
+      type: DataTypes.BOOLEAN,
+      defaultValue: false,
+    },
+    thumbnailUrl: {
+      type: DataTypes.STRING,
+      allowNull: true,
+    },
+    tags: {
+      type: DataTypes.ARRAY(DataTypes.STRING),
+      defaultValue: [],
+    }
+  }, {
+    timestamps: true,
+  });
+ 
+  ExamRecording.associate = (models) => {
+    ExamRecording.belongsTo(models.Exam, {
+      foreignKey: 'examId',
+      as: 'exam',
+    });
+  };
+ 
+  return ExamRecording;
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/index.html b/coverage/lcov-report/models/index.html new file mode 100644 index 0000000..7e5801b --- /dev/null +++ b/coverage/lcov-report/models/index.html @@ -0,0 +1,266 @@ + + + + + + Code coverage report for models + + + + + + + + + +
+
+

All files models

+
+ +
+ 0% + Statements + 0/41 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/16 +
+ + +
+ 0% + Lines + 0/41 +
+ + +
+

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

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
contractEvent.model.js +
+
0%0/2100%0/00%0/10%0/2
exam.model.js +
+
0%0/2100%0/00%0/10%0/2
examBanner.model.js +
+
0%0/4100%0/00%0/20%0/4
examRecording.model.js +
+
0%0/4100%0/00%0/20%0/4
index.js +
+
0%0/11100%0/00%0/10%0/11
notification.model.js +
+
0%0/4100%0/00%0/20%0/4
option.model.js +
+
0%0/4100%0/00%0/20%0/4
question.model.js +
+
0%0/2100%0/00%0/10%0/2
registration.model.js +
+
0%0/40%0/10%0/20%0/4
result.model.js +
+
0%0/2100%0/00%0/10%0/2
user.model.js +
+
0%0/2100%0/00%0/10%0/2
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/index.js.html b/coverage/lcov-report/models/index.js.html new file mode 100644 index 0000000..455e71d --- /dev/null +++ b/coverage/lcov-report/models/index.js.html @@ -0,0 +1,166 @@ + + + + + + Code coverage report for models/index.js + + + + + + + + + +
+
+

All files / models index.js

+
+ +
+ 0% + Statements + 0/11 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 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 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import userModel from './user.model.js';
+import examModel from './exam.model.js';
+import registrationModel from './registration.model.js';
+import resultModel from './result.model.js';
+import questionModel from './question.model.js';
+import optionModel from './option.model.js';
+import notificationModel from './notification.model.js';
+import examBannerModel from './examBanner.model.js';
+import examRecordingModel from './examRecording.model.js';
+import contractEventModel from './contractEvent.model.js';
+ 
+// Export model factories as named exports for mocking and test compatibility
+export const User = userModel;
+export const Exam = examModel;
+export const Registration = registrationModel;
+export const Result = resultModel;
+export const Question = questionModel;
+export const Option = optionModel;
+export const Notification = notificationModel;
+export const ExamBanner = examBannerModel;
+export const ExamRecording = examRecordingModel;
+export const ContractEvent = contractEventModel;
+ 
+export async function syncDatabase() {
+  // Dummy implementation for compatibility
+  return Promise.resolve();
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/notification.model.js.html b/coverage/lcov-report/models/notification.model.js.html new file mode 100644 index 0000000..6877333 --- /dev/null +++ b/coverage/lcov-report/models/notification.model.js.html @@ -0,0 +1,247 @@ + + + + + + Code coverage report for models/notification.model.js + + + + + + + + + +
+
+

All files / models notification.model.js

+
+ +
+ 0% + Statements + 0/4 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 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 +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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export default (sequelize, DataTypes) => {
+  const Notification = sequelize.define('Notification', {
+    id: {
+      type: DataTypes.UUID,
+      defaultValue: DataTypes.UUIDV4,
+      primaryKey: true,
+    },
+    title: {
+      type: DataTypes.STRING,
+      allowNull: false,
+    },
+    message: {
+      type: DataTypes.TEXT,
+      allowNull: false,
+    },
+    type: {
+      type: DataTypes.ENUM(
+        'info',
+        'success',
+        'warning',
+        'error'
+      ),
+      allowNull: false,
+      defaultValue: 'info',
+    },
+    isRead: {
+      type: DataTypes.BOOLEAN,
+      defaultValue: false,
+    },
+    userId: {
+      type: DataTypes.UUID,
+      allowNull: true,
+      references: {
+        model: 'Users',
+        key: 'id',
+      },
+    },
+    expiresAt: {
+      type: DataTypes.DATE,
+      allowNull: true,
+    }
+  }, {
+    timestamps: true,
+  });
+ 
+  Notification.associate = (models) => {
+    Notification.belongsTo(models.User, {
+      foreignKey: 'userId',
+      as: 'user',
+    });
+  };
+ 
+  return Notification;
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/option.model.js.html b/coverage/lcov-report/models/option.model.js.html new file mode 100644 index 0000000..249a8a4 --- /dev/null +++ b/coverage/lcov-report/models/option.model.js.html @@ -0,0 +1,184 @@ + + + + + + Code coverage report for models/option.model.js + + + + + + + + + +
+
+

All files / models option.model.js

+
+ +
+ 0% + Statements + 0/4 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 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 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export default (sequelize, DataTypes) => {
+    const Option = sequelize.define('Option', {
+      id: {
+        type: DataTypes.UUID,
+        defaultValue: DataTypes.UUIDV4,
+        primaryKey: true,
+      },
+      questionId: {
+        type: DataTypes.UUID,
+        allowNull: false,
+      },
+      text: {
+        type: DataTypes.TEXT,
+        allowNull: false,
+      },
+      isCorrect: {
+        type: DataTypes.BOOLEAN,
+        allowNull: false,
+        defaultValue: false,
+      },
+      order: {
+        type: DataTypes.STRING(1), // A, B, C, D
+        allowNull: false,
+      },
+    }, {
+      timestamps: true,
+    });
+  
+    Option.associate = function(models) {
+      Option.belongsTo(models.Question);
+    };
+  
+    return Option;
+  };
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/question.model.js.html b/coverage/lcov-report/models/question.model.js.html new file mode 100644 index 0000000..d1898a2 --- /dev/null +++ b/coverage/lcov-report/models/question.model.js.html @@ -0,0 +1,157 @@ + + + + + + Code coverage report for models/question.model.js + + + + + + + + + +
+
+

All files / models question.model.js

+
+ +
+ 0% + Statements + 0/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 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 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export default (sequelize, DataTypes) => {
+    const Question = sequelize.define('Question', {
+      id: {
+        type: DataTypes.UUID,
+        defaultValue: DataTypes.UUIDV4,
+        primaryKey: true,
+      },
+      examId: {
+        type: DataTypes.UUID,
+        allowNull: false,
+      },
+      question: {
+        type: DataTypes.TEXT,
+        allowNull: false,
+      },
+      order: {
+        type: DataTypes.INTEGER,
+        allowNull: false,
+      },
+    }, {
+      timestamps: true,
+    });
+  
+    return Question;
+  };
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/registration.model.js.html b/coverage/lcov-report/models/registration.model.js.html new file mode 100644 index 0000000..9e4b7fb --- /dev/null +++ b/coverage/lcov-report/models/registration.model.js.html @@ -0,0 +1,208 @@ + + + + + + Code coverage report for models/registration.model.js + + + + + + + + + +
+
+

All files / models registration.model.js

+
+ +
+ 0% + Statements + 0/4 +
+ + +
+ 0% + Branches + 0/1 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 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 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import crypto from 'crypto';
+ 
+export default (sequelize, DataTypes) => {
+  const Registration = sequelize.define('Registration', {
+    id: {
+      type: DataTypes.UUID,
+      defaultValue: DataTypes.UUIDV4,
+      primaryKey: true,
+    },
+    userId: {
+      type: DataTypes.UUID,
+      allowNull: false,
+    },
+    examId: {
+      type: DataTypes.UUID,
+      allowNull: false,
+    },
+    paymentStatus: {
+      type: DataTypes.ENUM('pending', 'completed', 'failed'),
+      defaultValue: 'pending',
+    },
+    examCode: {
+      type: DataTypes.STRING,
+      unique: true,
+    },
+    status: {
+      type: DataTypes.ENUM('registered', 'completed'),
+      defaultValue: 'registered',
+    },
+  }, {
+    timestamps: true,
+    hooks: {
+      beforeCreate: (registration) => {
+        Iif (!registration.examCode) {
+          registration.examCode = crypto.randomBytes(6).toString('hex').toUpperCase();
+        }
+      }
+    }
+  });
+ 
+  return Registration;
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/result.model.js.html b/coverage/lcov-report/models/result.model.js.html new file mode 100644 index 0000000..a8f1543 --- /dev/null +++ b/coverage/lcov-report/models/result.model.js.html @@ -0,0 +1,208 @@ + + + + + + Code coverage report for models/result.model.js + + + + + + + + + +
+
+

All files / models result.model.js

+
+ +
+ 0% + Statements + 0/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 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 +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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export default (sequelize, DataTypes) => {
+    const Result = sequelize.define('Result', {
+      id: {
+        type: DataTypes.UUID,
+        defaultValue: DataTypes.UUIDV4,
+        primaryKey: true,
+      },
+      registrationId: {
+        type: DataTypes.UUID,
+        allowNull: false,
+        unique: true,
+      },
+      userId: {
+        type: DataTypes.UUID,
+        allowNull: false,
+      },
+      examId: {
+        type: DataTypes.UUID,
+        allowNull: false,
+      },
+      score: {
+        type: DataTypes.FLOAT,
+        allowNull: false,
+      },
+      passed: {
+        type: DataTypes.BOOLEAN,
+        allowNull: false,
+      },
+      answers: {
+        type: DataTypes.JSONB,
+        defaultValue: [],
+      },
+      completedAt: {
+        type: DataTypes.DATE,
+        defaultValue: DataTypes.NOW,
+      },
+    }, {
+      timestamps: true,
+    });
+  
+    return Result;
+  };
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/models/user.model.js.html b/coverage/lcov-report/models/user.model.js.html new file mode 100644 index 0000000..1e01613 --- /dev/null +++ b/coverage/lcov-report/models/user.model.js.html @@ -0,0 +1,181 @@ + + + + + + Code coverage report for models/user.model.js + + + + + + + + + +
+
+

All files / models user.model.js

+
+ +
+ 0% + Statements + 0/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 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 +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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export default (sequelize, DataTypes) => {
+    const User = sequelize.define('User', {
+      id: {
+        type: DataTypes.UUID,
+        defaultValue: DataTypes.UUIDV4,
+        primaryKey: true,
+      },
+      fullName: {
+        type: DataTypes.STRING,
+        allowNull: false,
+      },
+      email: {
+        type: DataTypes.STRING,
+        allowNull: false,
+        unique: true,
+        validate: {
+          isEmail: true,
+        },
+      },
+      walletAddress: {
+        type: DataTypes.STRING,
+        allowNull: true,
+        unique: true,
+      },
+      role: {
+        type: DataTypes.ENUM('user', 'admin'),
+        defaultValue: 'user',
+      },
+    }, {
+      timestamps: true,
+    });
+    return User;
+  };
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/notification.controller.js.html b/coverage/lcov-report/notification.controller.js.html new file mode 100644 index 0000000..0bae024 --- /dev/null +++ b/coverage/lcov-report/notification.controller.js.html @@ -0,0 +1,982 @@ + + + + + + Code coverage report for notification.controller.js + + + + + + + + + +
+
+

All files notification.controller.js

+
+ +
+ 0% + Statements + 0/95 +
+ + +
+ 0% + Branches + 0/42 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 0% + Lines + 0/95 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Notification, User } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+import { validationResult } from 'express-validator';
+import logger from '../utils/logger.js';
+ 
+// @desc    Get all notifications
+// @route   GET /api/notifications
+// @access  Private
+const getNotifications = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`User ${req.user.id} requesting their notifications`);
+ 
+    const notifications = await Notification.findAll({
+      where: {
+        userId: req.user.id,
+      },
+      order: [['createdAt', 'DESC']],
+    });
+ 
+    logger.info(`Retrieved ${notifications.length} notifications for user ${req.user.id}`);
+    res.status(200).json(notifications);
+  } catch (error) {
+    logger.error(`Error retrieving notifications for user ${req.user.id}: ${error.message}`, {
+      userId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving notifications');
+  }
+});
+ 
+// @desc    Get notification by ID
+// @route   GET /api/notifications/:id
+// @access  Private
+const getNotificationById = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`User ${req.user.id} requesting notification ${req.params.id}`);
+ 
+    const notification = await Notification.findOne({
+      where: {
+        id: req.params.id,
+        userId: req.user.id,
+      },
+    });
+ 
+    if (!notification) {
+      logger.warn(`Notification with id ${req.params.id} not found for user ${req.user.id}`, {
+        notificationId: req.params.id,
+        userId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Notification not found');
+    }
+ 
+    logger.info(`Retrieved notification ${notification.id} for user ${req.user.id}`);
+    res.status(200).json(notification);
+  } catch (error) {
+    if (error.message === 'Notification not found') {
+      throw error;
+    }
+    logger.error(`Error retrieving notification ${req.params.id} for user ${req.user.id}: ${error.message}`, {
+      notificationId: req.params.id,
+      userId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error retrieving notification');
+  }
+});
+ 
+// @desc    Create new notification
+// @route   POST /api/notifications
+// @access  Private (Admin)
+const createNotification = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  if (!errors.isEmpty()) {
+    logger.warn(`Validation errors in creating notification by admin ${req.user.id}: ${JSON.stringify(errors.array())}`, {
+      userId: req.user.id,
+      validationErrors: errors.array()
+    });
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const {
+    title,
+    message,
+    type,
+    userId,
+    expiresAt,
+  } = req.body;
+ 
+  logger.info(`Admin ${req.user.id} creating notification: "${title}" for user ${userId || 'all users'}`);
+ 
+  try {
+    // Check if user exists if userId is provided
+    if (userId) {
+      const user = await User.findByPk(userId);
+      if (!user) {
+        logger.warn(`User with id ${userId} not found while admin ${req.user.id} creating notification`, {
+          targetUserId: userId,
+          adminId: req.user.id
+        });
+        res.status(404);
+        throw new Error('User not found');
+      }
+    }
+ 
+    // Create notification
+    const notification = await Notification.create({
+      title,
+      message,
+      type: type || 'info',
+      userId,
+      expiresAt,
+    });
+ 
+    logger.info(`Created notification ${notification.id} for user ${userId || 'all users'} by admin ${req.user.id}`, {
+      notificationId: notification.id,
+      targetUserId: userId,
+      adminId: req.user.id,
+      notificationType: type || 'info'
+    });
+    res.status(201).json(notification);
+  } catch (error) {
+    if (error.message === 'User not found') {
+      throw error;
+    }
+    logger.error(`Error creating notification by admin ${req.user.id}: ${error.message}`, {
+      adminId: req.user.id,
+      targetUserId: userId,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error creating notification');
+  }
+});
+ 
+// @desc    Update notification
+// @route   PUT /api/notifications/:id
+// @access  Private (Admin)
+const updateNotification = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} updating notification ${req.params.id}`);
+ 
+    const notification = await Notification.findByPk(req.params.id);
+ 
+    if (!notification) {
+      logger.warn(`Notification with id ${req.params.id} not found for update by admin ${req.user.id}`, {
+        notificationId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Notification not found');
+    }
+ 
+    const {
+      title,
+      message,
+      type,
+      isRead,
+      expiresAt,
+    } = req.body;
+ 
+    // Update notification
+    await notification.update({
+      title: title || notification.title,
+      message: message || notification.message,
+      type: type || notification.type,
+      isRead: isRead !== undefined ? isRead : notification.isRead,
+      expiresAt: expiresAt || notification.expiresAt,
+    });
+ 
+    logger.info(`Updated notification ${notification.id} by admin ${req.user.id}`, {
+      notificationId: notification.id,
+      adminId: req.user.id,
+      changes: {
+        title: !!title,
+        message: !!message,
+        type: !!type,
+        isRead: isRead !== undefined,
+        expiresAt: !!expiresAt
+      }
+    });
+    res.status(200).json(notification);
+  } catch (error) {
+    if (error.message === 'Notification not found') {
+      throw error;
+    }
+    logger.error(`Error updating notification ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      notificationId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error updating notification');
+  }
+});
+ 
+// @desc    Mark notification as read
+// @route   PATCH /api/notifications/:id/read
+// @access  Private
+const markAsRead = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`User ${req.user.id} marking notification ${req.params.id} as read`);
+ 
+    const notification = await Notification.findOne({
+      where: {
+        id: req.params.id,
+        userId: req.user.id,
+      },
+    });
+ 
+    if (!notification) {
+      logger.warn(`Notification with id ${req.params.id} not found for user ${req.user.id} to mark as read`, {
+        notificationId: req.params.id,
+        userId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Notification not found');
+    }
+ 
+    // Mark as read
+    await notification.update({
+      isRead: true,
+    });
+ 
+    logger.info(`Marked notification ${notification.id} as read for user ${req.user.id}`, {
+      notificationId: notification.id,
+      userId: req.user.id,
+      previousReadStatus: notification.isRead
+    });
+    res.status(200).json(notification);
+  } catch (error) {
+    if (error.message === 'Notification not found') {
+      throw error;
+    }
+    logger.error(`Error marking notification ${req.params.id} as read for user ${req.user.id}: ${error.message}`, {
+      notificationId: req.params.id,
+      userId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error marking notification as read');
+  }
+});
+ 
+// @desc    Delete notification
+// @route   DELETE /api/notifications/:id
+// @access  Private (Admin)
+const deleteNotification = asyncHandler(async (req, res) => {
+  try {
+    logger.info(`Admin ${req.user.id} deleting notification ${req.params.id}`);
+ 
+    const notification = await Notification.findByPk(req.params.id);
+ 
+    if (!notification) {
+      logger.warn(`Notification with id ${req.params.id} not found for deletion by admin ${req.user.id}`, {
+        notificationId: req.params.id,
+        adminId: req.user.id
+      });
+      res.status(404);
+      throw new Error('Notification not found');
+    }
+ 
+    // Delete notification
+    await notification.destroy();
+ 
+    logger.info(`Deleted notification ${req.params.id} by admin ${req.user.id}`, {
+      notificationId: req.params.id,
+      adminId: req.user.id,
+      notificationData: {
+        title: notification.title,
+        userId: notification.userId,
+        type: notification.type
+      }
+    });
+    res.status(200).json({ id: req.params.id });
+  } catch (error) {
+    if (error.message === 'Notification not found') {
+      throw error;
+    }
+    logger.error(`Error deleting notification ${req.params.id} by admin ${req.user.id}: ${error.message}`, {
+      notificationId: req.params.id,
+      adminId: req.user.id,
+      error: error.stack
+    });
+    res.status(500);
+    throw new Error('Error deleting notification');
+  }
+});
+ 
+export {
+  getNotifications,
+  getNotificationById,
+  createNotification,
+  updateNotification,
+  markAsRead,
+  deleteNotification,
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/prettify.css b/coverage/lcov-report/prettify.css new file mode 100644 index 0000000..b317a7c --- /dev/null +++ b/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/coverage/lcov-report/prettify.js b/coverage/lcov-report/prettify.js new file mode 100644 index 0000000..b322523 --- /dev/null +++ b/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/coverage/lcov-report/registration.controller.js.html b/coverage/lcov-report/registration.controller.js.html new file mode 100644 index 0000000..8352b82 --- /dev/null +++ b/coverage/lcov-report/registration.controller.js.html @@ -0,0 +1,481 @@ + + + + + + Code coverage report for registration.controller.js + + + + + + + + + +
+
+

All files registration.controller.js

+
+ +
+ 0% + Statements + 0/43 +
+ + +
+ 0% + Branches + 0/14 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 0% + Lines + 0/43 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Registration, User, Exam } from '../models/index.js';
+import { validationResult } from 'express-validator';
+import asyncHandler from 'express-async-handler';
+ 
+// @desc    Register for an exam
+// @access  Private
+const registerForExam = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  if (!errors.isEmpty()) {
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const { examId } = req.body;
+  const userId = req.user.id;
+ 
+  // Check if exam exists
+  const exam = await Exam.findByPk(examId);
+  if (!exam) {
+    res.status(404);
+    throw new Error('Exam not found');
+  }
+ 
+  // Check if already registered
+  const existingRegistration = await Registration.findOne({
+    where: { userId, examId }
+  });
+ 
+  if (existingRegistration) {
+    res.status(400);
+    throw new Error('Already registered for this exam');
+  }
+ 
+  // Create registration
+  const registration = await Registration.create({
+    userId,
+    examId,
+    paymentStatus: 'pending'
+  });
+ 
+  res.status(201).json(registration);
+});
+ 
+// @desc    Update payment status
+// @access  Private
+const updatePaymentStatus = asyncHandler(async (req, res) => {
+  const { paymentStatus } = req.body;
+  const userId = req.user.id;
+ 
+  const registration = await Registration.findOne({
+    where: { id: req.params.id, userId }
+  });
+ 
+  if (!registration) {
+    res.status(404);
+    throw new Error('Registration not found');
+  }
+ 
+  registration.paymentStatus = paymentStatus;
+  await registration.save();
+ 
+  res.status(200).json(registration);
+});
+ 
+// @desc    Get user's registrations
+// @access  Private
+const getUserRegistrations = asyncHandler(async (req, res) => {
+  const registrations = await Registration.findAll({
+    where: { userId: req.user.id },
+    include: [
+      {
+        model: Exam,
+        attributes: ['id', 'name', 'category', 'date', 'duration', 'passingScore']
+      }
+    ]
+  });
+ 
+  res.status(200).json(registrations);
+});
+ 
+// @desc    Validate exam code
+// @access  Public
+const validateExamCode = asyncHandler(async (req, res) => {
+  const errors = validationResult(req);
+  if (!errors.isEmpty()) {
+    return res.status(400).json({ errors: errors.array() });
+  }
+ 
+  const { email, examCode } = req.body;
+ 
+  // Find user by email
+  const user = await User.findOne({ where: { email } });
+  if (!user) {
+    res.status(404);
+    throw new Error('User not found');
+  }
+ 
+  // Find registration by exam code and user
+  const registration = await Registration.findOne({
+    where: { 
+      examCode,
+      userId: user.id,
+      paymentStatus: 'completed'
+    },
+    include: [
+      {
+        model: Exam,
+        attributes: { exclude: ['createdAt', 'updatedAt'] }
+      }
+    ]
+  });
+ 
+  if (!registration) {
+    res.status(404);
+    throw new Error('Invalid exam code or payment not completed');
+  }
+ 
+  res.status(200).json({
+    registration,
+    exam: registration.Exam,
+    user: {
+      id: user.id,
+      fullName: user.fullName,
+      email: user.email
+    }
+  });
+});
+ 
+export {
+  registerForExam,
+  updatePaymentStatus,
+  getUserRegistrations,
+  validateExamCode
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/result.controller.js.html b/coverage/lcov-report/result.controller.js.html new file mode 100644 index 0000000..15d2e33 --- /dev/null +++ b/coverage/lcov-report/result.controller.js.html @@ -0,0 +1,559 @@ + + + + + + Code coverage report for result.controller.js + + + + + + + + + +
+
+

All files result.controller.js

+
+ +
+ 0% + Statements + 0/50 +
+ + +
+ 0% + Branches + 0/16 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 0% + Lines + 0/47 +
+ + +
+

+ 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  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Result, Registration, Exam, Question, Option } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+ 
+// @desc    Submit exam and create result
+// @access  Private
+const submitExam = asyncHandler(async (req, res) => {
+  const { registrationId, answers } = req.body;
+  const userId = req.user.id;
+ 
+  // Find registration
+  const registration = await Registration.findOne({
+    where: { id: registrationId, userId },
+    include: [{ model: Exam }]
+  });
+ 
+  if (!registration) {
+    res.status(404);
+    throw new Error('Registration not found');
+  }
+ 
+  if (registration.status === 'completed') {
+    res.status(400);
+    throw new Error('Exam already completed');
+  }
+ 
+  // Get all questions for this exam
+  const questions = await Question.findAll({
+    where: { examId: registration.examId },
+    include: [{ model: Option }]
+  });
+ 
+  // Calculate score
+  let correctAnswers = 0;
+  const processedAnswers = [];
+ 
+  for (const answer of answers) {
+    const question = questions.find(q => q.id === answer.questionId);
+    if (!question) continue;
+ 
+    const correctOption = question.Options.find(opt => opt.isCorrect);
+    const isCorrect = correctOption && correctOption.id === answer.selectedOption;
+ 
+    if (isCorrect) {
+      correctAnswers++;
+    }
+ 
+    processedAnswers.push({
+      questionId: answer.questionId,
+      selectedOption: answer.selectedOption,
+      isCorrect
+    });
+  }
+ 
+  const totalQuestions = questions.length;
+  const score = (correctAnswers / totalQuestions) * 100;
+  const passed = score >= registration.Exam.passingScore;
+ 
+  // Create result
+  const result = await Result.create({
+    registrationId,
+    userId,
+    examId: registration.examId,
+    score,
+    passed,
+    answers: processedAnswers
+  });
+ 
+  // Update registration status
+  registration.status = 'completed';
+  await registration.save();
+ 
+  res.status(201).json(result);
+});
+ 
+// @desc    Get user's results
+// @access  Private
+const getUserResults = asyncHandler(async (req, res) => {
+  const results = await Result.findAll({
+    where: { userId: req.user.id },
+    include: [
+      {
+        model: Exam,
+        attributes: ['id', 'name', 'category', 'passingScore', 'certification']
+      }
+    ]
+  });
+ 
+  res.status(200).json(results);
+});
+ 
+// @desc    Get result by ID
+// @access  Private
+const getResultById = asyncHandler(async (req, res) => {
+  const result = await Result.findOne({
+    where: { id: req.params.id, userId: req.user.id },
+    include: [
+      {
+        model: Exam,
+        attributes: { exclude: ['createdAt', 'updatedAt'] }
+      }
+    ]
+  });
+ 
+  if (!result) {
+    res.status(404);
+    throw new Error('Result not found');
+  }
+ 
+  res.status(200).json(result);
+});
+ 
+// @desc    Generate certificate for a result
+// @access  Private
+const generateCertificate = asyncHandler(async (req, res) => {
+  const result = await Result.findOne({
+    where: { id: req.params.id, userId: req.user.id, passed: true },
+    include: [
+      {
+        model: Exam,
+        attributes: ['name', 'category', 'certification']
+      },
+      {
+        model: User,
+        attributes: ['fullName']
+      }
+    ]
+  });
+ 
+  if (!result) {
+    res.status(404);
+    throw new Error('Result not found or exam not passed');
+  }
+ 
+  if (!result.Exam.certification) {
+    res.status(400);
+    throw new Error('This exam does not provide certification');
+  }
+ 
+  // In a real application, you would generate a PDF certificate here
+  // For now, we'll just return the certificate data
+  const certificateData = {
+    certificateId: `CERT-${result.id}`,
+    candidateName: result.User.fullName,
+    examName: result.Exam.name,
+    category: result.Exam.category,
+    score: result.score,
+    issueDate: new Date(),
+    verificationUrl: `https://skillnet.example.com/verify/${result.id}`
+  };
+ 
+  res.status(200).json(certificateData);
+});
+ 
+export {
+  submitExam,
+  getUserResults,
+  getResultById,
+  generateCertificate
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/sort-arrow-sprite.png b/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000..6ed6831 Binary files /dev/null and b/coverage/lcov-report/sort-arrow-sprite.png differ diff --git a/coverage/lcov-report/sorter.js b/coverage/lcov-report/sorter.js new file mode 100644 index 0000000..2bb296a --- /dev/null +++ b/coverage/lcov-report/sorter.js @@ -0,0 +1,196 @@ +/* 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; + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + if ( + row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()) + ) { + row.style.display = ''; + } else { + row.style.display = '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/coverage/lcov-report/user.controller.js.html b/coverage/lcov-report/user.controller.js.html new file mode 100644 index 0000000..674555f --- /dev/null +++ b/coverage/lcov-report/user.controller.js.html @@ -0,0 +1,340 @@ + + + + + + Code coverage report for user.controller.js + + + + + + + + + +
+
+

All files user.controller.js

+
+ +
+ 93.93% + Statements + 31/33 +
+ + +
+ 83.33% + Branches + 10/12 +
+ + +
+ 100% + Functions + 4/4 +
+ + +
+ 93.54% + Lines + 29/31 +
+ + +
+

+ 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  +  +  +  +  +1x +2x +  +  +  +2x +1x +1x +  +  +1x +  +  +  +  +1x +4x +4x +1x +1x +  +3x +  +3x +  +  +  +  +3x +  +  +3x +3x +  +3x +  +2x +  +  +  +  +  +  +  +1x +1x +  +  +  +  +  +1x +2x +  +  +  +  +2x +1x +1x +  +  +1x +  +  +  +  +1x +1x +  +  +  +1x +  +  +  +  +  +  +  + 
import { User } from '../models/index.js';
+import asyncHandler from 'express-async-handler';
+ 
+// @desc    Get user profile
+// @access  Private
+const getUserProfile = asyncHandler(async (req, res) => {
+  const user = await User.findByPk(req.user.id, {
+    attributes: { exclude: ['password'] }
+  });
+ 
+  if (!user) {
+    res.status(404);
+    throw new Error('User not found');
+  }
+ 
+  res.status(200).json(user);
+});
+ 
+// @desc    Update user profile
+// @access  Private
+const updateUserProfile = asyncHandler(async (req, res) => {
+  try {
+    if (!req.body) {
+      res.status(400);
+      return res.json({ message: 'Request body is required' });
+    }
+    const user = await User.findByPk(req.user.id);
+ 
+    Iif (!user) {
+      res.status(404);
+      throw new Error('User not found');
+    }
+ 
+    const { fullName, email } = req.body;
+ 
+    // Update fields if provided
+    Eif (fullName) user.fullName = fullName;
+    if (email) user.email = email;
+ 
+    await user.save();
+ 
+    res.status(200).json({
+      id: user.id,
+      fullName: user.fullName,
+      email: user.email,
+      walletAddress: user.walletAddress,
+      role: user.role
+    });
+  } catch (error) {
+    res.status(500);
+    throw new Error('Error updating user profile: ' + error.message);
+  }
+});
+ 
+// @desc    Get user by wallet address
+// @access  Public
+const getUserByWallet = asyncHandler(async (req, res) => {
+  const user = await User.findOne({
+    where: { walletAddress: req.params.address },
+    attributes: { exclude: ['password'] }
+  });
+ 
+  if (!user) {
+    res.status(404);
+    throw new Error('User not found');
+  }
+ 
+  res.status(200).json(user);
+});
+ 
+// @desc    Get all users
+// @access  Private/Admin
+const getUsers = asyncHandler(async (req, res) => {
+  const users = await User.findAll({
+    attributes: { exclude: ['password'] }
+  });
+ 
+  res.status(200).json(users);
+});
+ 
+export {
+  getUserProfile,
+  updateUserProfile,
+  getUserByWallet,
+  getUsers
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/utils/index.html b/coverage/lcov-report/utils/index.html new file mode 100644 index 0000000..61fce32 --- /dev/null +++ b/coverage/lcov-report/utils/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for utils + + + + + + + + + +
+
+

All files utils

+
+ +
+ 33.33% + Statements + 2/6 +
+ + +
+ 50% + Branches + 2/4 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 33.33% + Lines + 2/6 +
+ + +
+

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

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
logger.js +
+
33.33%2/650%2/40%0/133.33%2/6
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/utils/logger.js.html b/coverage/lcov-report/utils/logger.js.html new file mode 100644 index 0000000..b15d544 --- /dev/null +++ b/coverage/lcov-report/utils/logger.js.html @@ -0,0 +1,241 @@ + + + + + + Code coverage report for utils/logger.js + + + + + + + + + +
+
+

All files / utils logger.js

+
+ +
+ 33.33% + Statements + 2/6 +
+ + +
+ 50% + Branches + 2/4 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 33.33% + Lines + 2/6 +
+ + +
+

+ 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  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import winston from 'winston';
+import path from 'path';
+ 
+// Define the custom format with color
+const logFormat = winston.format.combine(
+  winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
+  winston.format.errors({ stack: true }),
+  winston.format.splat(),
+  winston.format.printf(
+    ({ level, message, timestamp, ...metadata }) => {
+      let msg = `${timestamp} [${level}]: ${message} `;
+      if (Object.keys(metadata).length > 0) {
+        msg += JSON.stringify(metadata);
+      }
+      return msg;
+    }
+  )
+);
+ 
+// Create the logger
+const logger = winston.createLogger({
+  level: process.env.LOG_LEVEL || 'info',
+  format: logFormat,
+  defaultMeta: { service: 'skillnet-exam-server' },
+  transports: [
+    // Write all logs to the console
+    new winston.transports.Console({
+      format: winston.format.combine(
+        winston.format.colorize(),
+        logFormat
+      )
+    }),
+ 
+    // Write all logs to files
+    new winston.transports.File({
+      filename: path.join('logs', 'error.log'),
+      level: 'error'
+    }),
+    new winston.transports.File({
+      filename: path.join('logs', 'combined.log')
+    })
+  ],
+  exceptionHandlers: [
+    new winston.transports.File({
+      filename: path.join('logs', 'exceptions.log')
+    })
+  ]
+});
+ 
+// Ensure logs directory exists (this can be done with fs if needed)
+ 
+export default logger;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov.info b/coverage/lcov.info new file mode 100644 index 0000000..880b795 --- /dev/null +++ b/coverage/lcov.info @@ -0,0 +1,187 @@ +TN: +SF:src/controllers/exam.controller.js +FN:9,(anonymous_0) +FN:33,(anonymous_1) +FN:59,(anonymous_2) +FN:78,(anonymous_3) +FN:129,(anonymous_4) +FN:184,(anonymous_5) +FNF:6 +FNH:4 +FNDA:3,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:6,(anonymous_3) +FNDA:3,(anonymous_4) +FNDA:2,(anonymous_5) +DA:9,1 +DA:10,3 +DA:12,3 +DA:13,3 +DA:14,1 +DA:17,3 +DA:27,3 +DA:33,1 +DA:34,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:53,0 +DA:59,1 +DA:60,0 +DA:62,0 +DA:72,0 +DA:78,1 +DA:79,6 +DA:80,6 +DA:81,6 +DA:82,1 +DA:86,5 +DA:87,5 +DA:89,1 +DA:92,4 +DA:93,4 +DA:94,2 +DA:95,2 +DA:99,1 +DA:100,1 +DA:101,2 +DA:110,1 +DA:111,1 +DA:113,1 +DA:116,3 +DA:120,3 +DA:122,0 +DA:129,1 +DA:130,3 +DA:132,3 +DA:133,0 +DA:134,0 +DA:150,3 +DA:153,3 +DA:169,3 +DA:178,3 +DA:184,1 +DA:185,2 +DA:187,2 +DA:188,1 +DA:189,1 +DA:193,1 +DA:195,1 +DA:196,1 +DA:199,1 +DA:202,1 +DA:204,1 +LF:58 +LH:47 +BRDA:13,0,0,1 +BRDA:13,0,1,2 +BRDA:48,1,0,0 +BRDA:48,1,1,0 +BRDA:81,2,0,1 +BRDA:81,2,1,5 +BRDA:93,3,0,2 +BRDA:93,3,1,2 +BRDA:93,4,0,4 +BRDA:93,4,1,3 +BRDA:93,4,2,3 +BRDA:99,5,0,1 +BRDA:99,5,1,0 +BRDA:99,6,0,1 +BRDA:99,6,1,1 +BRDA:99,6,2,1 +BRDA:110,7,0,1 +BRDA:110,7,1,0 +BRDA:110,8,0,1 +BRDA:110,8,1,1 +BRDA:132,9,0,0 +BRDA:132,9,1,3 +BRDA:154,10,0,3 +BRDA:154,10,1,0 +BRDA:155,11,0,3 +BRDA:155,11,1,3 +BRDA:156,12,0,3 +BRDA:156,12,1,3 +BRDA:157,13,0,3 +BRDA:157,13,1,3 +BRDA:158,14,0,3 +BRDA:158,14,1,3 +BRDA:159,15,0,0 +BRDA:159,15,1,3 +BRDA:160,16,0,3 +BRDA:160,16,1,3 +BRDA:161,17,0,3 +BRDA:161,17,1,3 +BRDA:162,18,0,3 +BRDA:162,18,1,3 +BRDA:163,19,0,3 +BRDA:163,19,1,3 +BRDA:164,20,0,3 +BRDA:164,20,1,3 +BRDA:165,21,0,3 +BRDA:165,21,1,3 +BRDA:187,22,0,1 +BRDA:187,22,1,1 +BRF:48 +BRH:41 +end_of_record +TN: +SF:src/controllers/user.controller.js +FN:6,(anonymous_0) +FN:21,(anonymous_1) +FN:57,(anonymous_2) +FN:73,(anonymous_3) +FNF:4 +FNH:4 +FNDA:2,(anonymous_0) +FNDA:4,(anonymous_1) +FNDA:2,(anonymous_2) +FNDA:1,(anonymous_3) +DA:6,1 +DA:7,2 +DA:11,2 +DA:12,1 +DA:13,1 +DA:16,1 +DA:21,1 +DA:22,4 +DA:23,4 +DA:24,1 +DA:25,1 +DA:27,3 +DA:29,3 +DA:30,0 +DA:31,0 +DA:34,3 +DA:37,3 +DA:38,3 +DA:40,3 +DA:42,2 +DA:50,1 +DA:51,1 +DA:57,1 +DA:58,2 +DA:63,2 +DA:64,1 +DA:65,1 +DA:68,1 +DA:73,1 +DA:74,1 +DA:78,1 +LF:31 +LH:29 +BRDA:11,0,0,1 +BRDA:11,0,1,1 +BRDA:23,1,0,1 +BRDA:23,1,1,3 +BRDA:29,2,0,0 +BRDA:29,2,1,3 +BRDA:37,3,0,3 +BRDA:37,3,1,0 +BRDA:38,4,0,2 +BRDA:38,4,1,1 +BRDA:63,5,0,1 +BRDA:63,5,1,1 +BRF:12 +BRH:10 +end_of_record diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..d74ece7 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,16 @@ +export default { + extensionsToTreatAsEsm: ['.ts'], + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + useESM: true, + }, + ], + }, + preset: 'ts-jest/presets/default-esm', + testEnvironment: 'node' +}; diff --git a/logs/combined.log b/logs/combined.log index 79185aa..50c903d 100644 --- a/logs/combined.log +++ b/logs/combined.log @@ -901,3 +901,13 @@ 2025-04-29 02:58:37 [error]: Error retrieving contract events for admin 5c2d2543-8f3c-445a-a96a-707f4747f24d: column ContractEvent.logIndex does not exist {"service":"skillnet-exam-server","adminId":"5c2d2543-8f3c-445a-a96a-707f4747f24d","error":"Error\n at Query.run (/Users/bene/Desktop/OnlyDust/skillnet-exam-server/node_modules/sequelize/lib/dialects/postgres/query.js:50:25)\n at /Users/bene/Desktop/OnlyDust/skillnet-exam-server/node_modules/sequelize/lib/sequelize.js:315:28\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async PostgresQueryInterface.select (/Users/bene/Desktop/OnlyDust/skillnet-exam-server/node_modules/sequelize/lib/dialects/abstract/query-interface.js:407:12)\n at async ContractEvent.findAll (/Users/bene/Desktop/OnlyDust/skillnet-exam-server/node_modules/sequelize/lib/model.js:1140:21)\n at async /Users/bene/Desktop/OnlyDust/skillnet-exam-server/src/controllers/indexer.controller.js:99:20"} 2025-04-29 02:58:37 [error]: Error retrieving contract events - /api/indexer/events?eventName=ExamCreated - GET - ::ffff:127.0.0.1 {"service":"skillnet-exam-server"} 2025-04-29 02:58:37 [info]: ::ffff:127.0.0.1 - - [29/Apr/2025:01:58:37 +0000] "GET /api/indexer/events?eventName=ExamCreated HTTP/1.1" 500 283 "-" "vscode-restclient" {"service":"skillnet-exam-server"} +2025-06-03 18:33:28 [info]: Starknet indexer configured for network: sepolia {"service":"skillnet-exam-server"} +2025-06-03 18:33:28 [info]: Monitoring 1 Starknet contracts {"service":"skillnet-exam-server"} +2025-06-03 18:33:28 [info]: Apibara Starknet indexer initialized {"service":"skillnet-exam-server"} +2025-06-03 18:33:28 [info]: Starting server initialization... {"service":"skillnet-exam-server"} +2025-06-03 18:33:28 [info]: Attempting to connect to database... {"service":"skillnet-exam-server"} +2025-06-03 21:25:12 [info]: Starknet indexer configured for network: sepolia {"service":"skillnet-exam-server"} +2025-06-03 21:25:12 [info]: Monitoring 1 Starknet contracts {"service":"skillnet-exam-server"} +2025-06-03 21:25:12 [info]: Apibara Starknet indexer initialized {"service":"skillnet-exam-server"} +2025-06-03 21:25:12 [info]: Starting server initialization... {"service":"skillnet-exam-server"} +2025-06-03 21:25:12 [info]: Attempting to connect to database... {"service":"skillnet-exam-server"} diff --git a/logs/error.log b/logs/error.log index 393c7e5..ce8f82c 100644 --- a/logs/error.log +++ b/logs/error.log @@ -156,3 +156,5 @@ 2025-04-29 02:58:18 [error]: Error retrieving indexed registrations - /api/indexer/registrations?examId=%7B%7BcreateExam.response.body.id%7D%7D - GET - ::ffff:127.0.0.1 {"service":"skillnet-exam-server"} 2025-04-29 02:58:37 [error]: Error retrieving contract events for admin 5c2d2543-8f3c-445a-a96a-707f4747f24d: column ContractEvent.logIndex does not exist {"service":"skillnet-exam-server","adminId":"5c2d2543-8f3c-445a-a96a-707f4747f24d","error":"Error\n at Query.run (/Users/bene/Desktop/OnlyDust/skillnet-exam-server/node_modules/sequelize/lib/dialects/postgres/query.js:50:25)\n at /Users/bene/Desktop/OnlyDust/skillnet-exam-server/node_modules/sequelize/lib/sequelize.js:315:28\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async PostgresQueryInterface.select (/Users/bene/Desktop/OnlyDust/skillnet-exam-server/node_modules/sequelize/lib/dialects/abstract/query-interface.js:407:12)\n at async ContractEvent.findAll (/Users/bene/Desktop/OnlyDust/skillnet-exam-server/node_modules/sequelize/lib/model.js:1140:21)\n at async /Users/bene/Desktop/OnlyDust/skillnet-exam-server/src/controllers/indexer.controller.js:99:20"} 2025-04-29 02:58:37 [error]: Error retrieving contract events - /api/indexer/events?eventName=ExamCreated - GET - ::ffff:127.0.0.1 {"service":"skillnet-exam-server"} +2025-06-03 18:33:28 [error]: Unable to connect to the database: {"service":"skillnet-exam-server"} +2025-06-03 21:25:12 [error]: Unable to connect to the database: {"service":"skillnet-exam-server"} diff --git a/package-lock.json b/package-lock.json index 82bf5b9..224f992 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,17 +21,19 @@ "cross-env": "^7.0.3", "crypto": "^1.0.1", "dotenv": "^16.4.7", - "express": "^4.21.2", + "express": "4.18", "express-async-handler": "^1.2.0", "express-rate-limit": "^7.5.0", "express-validator": "^7.2.1", + "got": "^12.6.1", "helmet": "^8.1.0", "jest": "^29.7.0", "joi": "^17.13.3", "jsonwebtoken": "^9.0.2", + "lodash-es": "^4.17.21", "moment": "^2.30.1", "moment-timezone": "^0.5.47", - "mongoose": "^8.12.1", + "mongoose": "^6.13.8", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "multer-storage-cloudinary": "^4.0.0", @@ -49,11 +51,18 @@ "winston": "^3.17.0" }, "devDependencies": { + "@babel/core": "^7.27.4", + "@babel/preset-env": "^7.27.2", "@eslint/js": "^9.18.0", + "@jest/globals": "^30.0.0-beta.3", + "@types/jest": "^29.5.14", + "babel-jest": "^30.0.0-beta.3", "eslint": "^9.18.0", "globals": "^15.14.0", "jest": "^29.7.0", - "nodemon": "^3.1.9" + "jest-environment-node": "^30.0.0-beta.3", + "nodemon": "^3.1.9", + "ts-jest": "^29.3.4" } }, "node_modules/@ampproject/remapping": { @@ -154,527 +163,748 @@ "openapi-types": ">=7" } }, - "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "node": ">=14.0.0" } }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "ms": "^2.1.3" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=16.0.0" } }, - "node_modules/@babel/core/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==", - "dev": true, - "license": "MIT" + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/generator": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", - "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/parser": "^7.26.10", - "@babel/types": "^7.26.10", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", - "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/compat-data": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.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", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "optional": true, "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", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.821.0.tgz", + "integrity": "sha512-c6TpvrRAb4hVcbGMCPjTWU2IRNBzfEz2qZ1v6DGViW0i8vN4+zXY/DcVOL2P3ZA9MDXjFRiiA8RdIy1/zsi3YQ==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.821.0", + "@aws-sdk/credential-provider-node": "3.821.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.821.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.821.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.1", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.9", + "@smithy/middleware-retry": "^4.1.10", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.17", + "@smithy/util-defaults-mode-node": "^4.0.17", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/client-sso": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.821.0.tgz", + "integrity": "sha512-aDEBZUKUd/+Tvudi0d9KQlqt2OW2P27LATZX0jkNC8yVk4145bAPS04EYoqdKLuyUn/U33DibEOgKUpxZB12jQ==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.821.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.821.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.821.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.1", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.9", + "@smithy/middleware-retry": "^4.1.10", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.17", + "@smithy/util-defaults-mode-node": "^4.0.17", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/core": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.821.0.tgz", + "integrity": "sha512-8eB3wKbmfciQFmxFq7hAjy7mXdUs7vBOR5SwT0ZtQBg0Txc18Lc9tMViqqdO6/KU7OukA6ib2IAVSjIJJEN7FQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/core": "^3.5.1", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.821.0.tgz", + "integrity": "sha512-8ZdFwmSxvQv8QindA0DJ3YUT9FD8T9sA5hQWp3B9+Znkze29IiIadnsXY0Heo2/FOFygxh8jRXiCWEie7/YpzA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.821.0.tgz", + "integrity": "sha512-C+s/A72pd7CXwEsJj9+Uq9T726iIfIF18hGRY8o82xcIEfOyakiPnlisku8zZOaAu+jm0CihbbYN4NyYNQ+HZQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/core": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.821.0.tgz", + "integrity": "sha512-gIRzTLnAsRfRSNarCag7G7rhcHagz4x5nNTWRihQs5cwTOghEExDy7Tj5m4TEkv3dcTAsNn+l4tnR4nZXo6R+Q==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@aws-sdk/core": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/helpers": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", - "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.821.0.tgz", + "integrity": "sha512-VRTrmsca8kBHtY1tTek1ce+XkK/H0fzodBKcilM/qXjTyumMHPAzVAxKZfSvGC+28/pXyQzhOEyxZfw7giCiWA==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.10" + "@aws-sdk/core": "3.821.0", + "@aws-sdk/credential-provider-env": "3.821.0", + "@aws-sdk/credential-provider-http": "3.821.0", + "@aws-sdk/credential-provider-process": "3.821.0", + "@aws-sdk/credential-provider-sso": "3.821.0", + "@aws-sdk/credential-provider-web-identity": "3.821.0", + "@aws-sdk/nested-clients": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/parser": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", - "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.821.0.tgz", + "integrity": "sha512-oBgbcgOXWMgknAfhIdTeHSSVIv+k2LXN9oTbxu1r++o4WWBWrEQ8mHU0Zo9dfr7Uaoqi3pezYZznsBkXnMLEOg==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/types": "^7.26.10" - }, - "bin": { - "parser": "bin/babel-parser.js" + "@aws-sdk/credential-provider-env": "3.821.0", + "@aws-sdk/credential-provider-http": "3.821.0", + "@aws-sdk/credential-provider-ini": "3.821.0", + "@aws-sdk/credential-provider-process": "3.821.0", + "@aws-sdk/credential-provider-sso": "3.821.0", + "@aws-sdk/credential-provider-web-identity": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.821.0.tgz", + "integrity": "sha512-e18ucfqKB3ICNj5RP/FEdvUfhVK6E9MALOsl8pKP13mwegug46p/1BsZWACD5n+Zf9ViiiHxIO7td03zQixfwA==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/core": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.821.0.tgz", + "integrity": "sha512-Dt+pheBLom4O/egO4L75/72k9C1qtUOLl0F0h6lmqZe4Mvhz+wDtjoO/MdGC/P1q0kcIX/bBKr0NQ3cIvAH8pA==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/client-sso": "3.821.0", + "@aws-sdk/core": "3.821.0", + "@aws-sdk/token-providers": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.821.0.tgz", + "integrity": "sha512-FF5wnRJkxSQaCVVvWNv53K1MhTMgH8d+O+MHTbkv51gVIgVATrtfFQMKBLcEAxzXrgAliIO3LiNv+1TqqBZ+BA==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@aws-sdk/core": "3.821.0", + "@aws-sdk/nested-clients": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-providers": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.821.0.tgz", + "integrity": "sha512-ZkV7KlKD+rSW/AP5zjSgMi+0xJ5TL5J6XVaP3IG5qyqBYTREJ8DbB/9YVUpYt2qtzpWUh/K43nmDEyfLd2YJog==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@aws-sdk/client-cognito-identity": "3.821.0", + "@aws-sdk/core": "3.821.0", + "@aws-sdk/credential-provider-cognito-identity": "3.821.0", + "@aws-sdk/credential-provider-env": "3.821.0", + "@aws-sdk/credential-provider-http": "3.821.0", + "@aws-sdk/credential-provider-ini": "3.821.0", + "@aws-sdk/credential-provider-node": "3.821.0", + "@aws-sdk/credential-provider-process": "3.821.0", + "@aws-sdk/credential-provider-sso": "3.821.0", + "@aws-sdk/credential-provider-web-identity": "3.821.0", + "@aws-sdk/nested-clients": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.1", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", + "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", + "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", + "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.821.0.tgz", + "integrity": "sha512-rw8q3TxygMg3VrofN04QyWVCCyGwz3bVthYmBZZseENPWG3Krz1OCKcyqjkTcAxMQlEywOske+GIiOasGKnJ3w==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@aws-sdk/core": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@smithy/core": "^3.5.1", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/nested-clients": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.821.0.tgz", + "integrity": "sha512-2IuHcUsWw44ftSEDYU4dvktTEqgyDvkOcfpoGC/UmT4Qo6TVCP3U5tWEGpNK9nN+7nLvekruxxG/jaMt5/oWVw==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.821.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.821.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.821.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.1", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.9", + "@smithy/middleware-retry": "^4.1.10", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.17", + "@smithy/util-defaults-mode-node": "^4.0.17", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", + "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/token-providers": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.821.0.tgz", + "integrity": "sha512-qJ7wgKhdxGbPg718zWXbCYKDuSWZNU3TSw64hPRW6FtbZrIyZxObpiTKC6DKwfsVoZZhHEoP/imGykN1OdOTJA==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/core": "3.821.0", + "@aws-sdk/nested-clients": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/types": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", + "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.821.0.tgz", + "integrity": "sha512-Uknt/zUZnLE76zaAAPEayOeF5/4IZ2puTFXvcSCWHsi9m3tqbb9UozlnlVqvCZLCRWfQryZQoG2W4XSS3qgk5A==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "@smithy/util-endpoints": "^3.0.6", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", + "integrity": "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", + "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.821.0.tgz", + "integrity": "sha512-YwMXc9EvuzJgnLBTyiQly2juPujXwDgcMHB0iSN92tHe7Dd1jJ1feBmTgdClaaqCeHFUaFpw+3JU/ZUJ6LjR+A==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@aws-sdk/middleware-user-agent": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", - "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "node_modules/@babel/compat-data": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" - }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.10.tgz", - "integrity": "sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==", + "node_modules/@babel/core": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.10", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/traverse/node_modules/debug": { + "node_modules/@babel/core/node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", @@ -692,136 +922,171 @@ } } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/traverse/node_modules/ms": { + "node_modules/@babel/core/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==", "dev": true, "license": "MIT" }, - "node_modules/@babel/types": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", - "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "dev": true, - "license": "MIT" - }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, "engines": { - "node": ">=0.1.90" + "node": ">=6.9.0" } }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, "license": "MIT", "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", - "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "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": "MIT", + "license": "ISC", "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "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", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", + "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.27.1", + "semver": "^6.3.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=6.9.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", + "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@eslint/config-array/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -836,1667 +1101,4564 @@ } } }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-array/node_modules/ms": { + "node_modules/@babel/helper-define-polyfill-provider/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==", "dev": true, "license": "MIT" }, - "node_modules/@eslint/config-helpers": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", - "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.15" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, "engines": { - "node": ">=18" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { - "node": "*" + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc/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==", + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@eslint/js": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", - "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", - "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "node_modules/@babel/helper-wrap-function": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", + "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@eslint/core": "^0.12.0", - "levn": "^0.4.1" + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" } }, - "node_modules/@grpc/grpc-js": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.0.tgz", - "integrity": "sha512-pMuxInZjUnUkgMT2QLZclRqwk2ykJbIU05aZgPgJYXEpN9+2I7z7aNwcjWZSycRPl232FfhPszyBFJyOxTHNog==", - "license": "Apache-2.0", + "node_modules/@babel/helpers": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.4.tgz", + "integrity": "sha512-Y+bO6U+I7ZKaM5G5rDUZiYfUvQPUibYmAFe7EnKdnKBbVXDZxvp+MWOH5gYciY0EPk4EScsuFMQBbEfpdRKSCQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@grpc/proto-loader": "^0.7.13", - "@js-sdsl/ordered-map": "^4.4.2" + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3" }, "engines": { - "node": ">=12.10.0" + "node": ">=6.9.0" } }, - "node_modules/@grpc/proto-loader": { - "version": "0.7.13", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", - "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", - "license": "Apache-2.0", + "node_modules/@babel/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "dev": true, + "license": "MIT", "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.5", - "yargs": "^17.7.2" + "@babel/types": "^7.27.3" }, "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=6" - } - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" + "node": ">=6.0.0" } }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", + "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" + "node": ">=6.9.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=12.22" + "node": ">=6.9.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=18.18" + "node": ">=6.9.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "dev": true, + "license": "MIT", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "engines": { - "node": ">=12" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", + "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/core/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/core/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, "license": "MIT", "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "license": "MIT", "dependencies": { - "jest-get-type": "^29.6.3" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "license": "MIT", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/reporters/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/reporters/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": "*" + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": "*" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/reporters/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.27.8" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.27.1.tgz", + "integrity": "sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.5.tgz", + "integrity": "sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", + "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "node_modules/@babel/plugin-transform-classes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.27.1.tgz", + "integrity": "sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.27.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "node_modules/@babel/plugin-transform-classes/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" + "engines": { + "node": ">=4" } }, - "node_modules/@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", - "license": "MIT" - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "license": "BSD-3-Clause", + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "dev": true, + "license": "MIT", "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.3.tgz", + "integrity": "sha512-s4Jrok82JpiaIprtY2nHsYmrThKvvwgHwjgd7UMiYhZaN0asdXNLr0y+NjTfkA7SyQE5i2Fb7eawUOZmLvyqOA==", + "dev": true, "license": "MIT", "dependencies": { - "semver": "^6.0.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@mongodb-js/saslprep": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.0.tgz", - "integrity": "sha512-+ywrb0AqkfaYuhHs6LxKWgqbh3I72EpEgESCw37o+9qPx9WTCkgDm2B+eMrwehGtHBWHFU4GXvnSCNiFhhausg==", + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "dev": true, "license": "MIT", "dependencies": { - "sparse-bitfield": "^3.0.3" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@noble/curves": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz", - "integrity": "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==", + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "dev": true, "license": "MIT", "dependencies": { - "@noble/hashes": "1.6.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": "^14.21.3 || >=16" + "node": ">=6.9.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@noble/hashes": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz", - "integrity": "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==", + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": "^14.21.3 || >=16" + "node": ">=6.9.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=8.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/api-logs": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz", - "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", - "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/core": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", - "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/semantic-conventions": "1.28.0" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", - "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, "engines": { - "node": ">=14" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", - "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/api-logs": "0.57.2", - "@types/shimmer": "^1.2.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.2", - "shimmer": "^1.2.1" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.46.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.1.tgz", - "integrity": "sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.43.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.43.1.tgz", - "integrity": "sha512-ht7YGWQuV5BopMcw5Q2hXn3I8eG8TH0J/kc/GMcW4CuNTgiP6wCu44BOnucJWL3CmFWaRHI//vWyAhaC8BwePw==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/connect": "3.4.38" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.16.1.tgz", - "integrity": "sha512-K/qU4CjnzOpNkkKO4DfCLSQshejRNAJtd4esgigo/50nxCB6XCyi1dhAblUHM9jG5dRm8eu0FB+t87nIo99LYQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.47.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.47.1.tgz", - "integrity": "sha512-QNXPTWteDclR2B4pDFpz0TNghgB33UMjUt14B+BZPmtH1MwUFAfLHBaP5If0Z5NZC+jaH8oF2glgYjrmhZWmSw==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-fastify": { - "version": "0.44.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.44.2.tgz", - "integrity": "sha512-arSp97Y4D2NWogoXRb8CzFK3W2ooVdvqRRtQDljFt9uC3zI6OuShgey6CVFC0JxT1iGjkAr1r4PDz23mWrFULQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.19.1.tgz", - "integrity": "sha512-6g0FhB3B9UobAR60BGTcXg4IHZ6aaYJzp0Ki5FhnxyAPt8Ns+9SSvgcrnsN2eGmk3RWG5vYycUGOEApycQL24A==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.57.1" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.43.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.43.1.tgz", - "integrity": "sha512-M6qGYsp1cURtvVLGDrPPZemMFEbuMmCXgQYTReC/IbimV5sGrLBjB+/hANUpRZjX67nGLdKSVLZuQQAiNz+sww==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.47.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.47.1.tgz", - "integrity": "sha512-EGQRWMGqwiuVma8ZLAZnExQ7sBvbOx0N/AE/nlafISPs8S+QtXX+Viy6dcQwVWwYHQPAcuY3bFt3xgoAwb4ZNQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.45.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.45.2.tgz", - "integrity": "sha512-7Ehow/7Wp3aoyCrZwQpU7a2CnoMq0XhIcioFuKjBb0PLYfBfmTsFTUyatlHu0fRxhwcRsSQRTvEhmZu8CppBpQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.2.tgz", - "integrity": "sha512-1Uz5iJ9ZAlFOiPuwYg29Bf7bJJc/GeoeJIFKJYQf67nTVKFe8RHbEtxgkOmK4UGZNHKXcpW4P8cWBYzBn1USpg==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/instrumentation": "0.57.2", - "@opentelemetry/semantic-conventions": "1.28.0", - "forwarded-parse": "2.1.2", - "semver": "^7.5.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", - "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.47.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.47.1.tgz", - "integrity": "sha512-OtFGSN+kgk/aoKgdkKQnBsQFDiG8WdCxu+UrHr0bXScdAmtSzLSraLo7wFIb25RVHfRWvzI5kZomqJYEg/l1iA==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/redis-common": "^0.36.2", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.7.1.tgz", - "integrity": "sha512-OtjaKs8H7oysfErajdYr1yuWSjMAectT7Dwr+axIoZqT9lmEOkD/H/3rgAs8h/NIuEi2imSXD+vL4MZtOuJfqQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.27.3.tgz", + "integrity": "sha512-7ZZtznF9g4l2JCImCo5LNKFHB5eXnN39lLtLY5Tg+VkR0jwOt7TBciMckuiQIOIW7L5tkQOCh3bVGYeXgMx52Q==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.27.3", + "@babel/plugin-transform-parameters": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.44.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.44.1.tgz", - "integrity": "sha512-U4dQxkNhvPexffjEmGwCq68FuftFK15JgUF05y/HlK3M6W/G2iEaACIfXdSnwVNe9Qh0sPfw8LbOPxrWzGWGMQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.47.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.47.1.tgz", - "integrity": "sha512-l/c+Z9F86cOiPJUllUCt09v+kICKvT+Vg1vOAJHtHPsJIzurGayucfCMq2acd/A/yxeNWunl9d9eqZ0G+XiI6A==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.44.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.44.1.tgz", - "integrity": "sha512-5MPkYCvG2yw7WONEjYj5lr5JFehTobW7wX+ZUFy81oF2lr9IPfZk9qO+FTaM0bGEiymwfLwKe6jE15nHn1nmHg==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.52.0.tgz", - "integrity": "sha512-1xmAqOtRUQGR7QfJFfGV/M2kC7wmI2WgZdpru8hJl3S0r4hW0n3OQpEHlSGXJAaNFyvT+ilnwkT+g5L4ljHR6g==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz", + "integrity": "sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.46.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.46.1.tgz", - "integrity": "sha512-3kINtW1LUTPkiXFRSSBmva1SXzS/72we/jL22N+BnF3DFcoewkdkHPYOIdAAk9gSicJ4d5Ojtt1/HeibEc5OQg==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.45.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.45.1.tgz", - "integrity": "sha512-TKp4hQ8iKQsY7vnp/j0yJJ4ZsP109Ht6l4RHTj0lNEG1TfgTrIH5vJMbgmoYXWzNHAqBH2e7fncN12p3BP8LFg==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/mysql": "2.15.26" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.45.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.45.2.tgz", - "integrity": "sha512-h6Ad60FjCYdJZ5DTz1Lk2VmQsShiViKe0G7sYikb0GHI0NVvApp2XQNRHNjEMz87roFttGPLHOYVPlfy+yVIhQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@opentelemetry/sql-common": "^0.40.1" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.51.1.tgz", - "integrity": "sha512-QxgjSrxyWZc7Vk+qGSfsejPVFL1AgAJdSBMYZdDUbwg730D09ub3PXScB9d04vIqPriZ+0dqzjmQx0yWKiCi2Q==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.5.tgz", + "integrity": "sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "^1.26.0", - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@opentelemetry/sql-common": "^0.40.1", - "@types/pg": "8.6.1", - "@types/pg-pool": "2.0.6" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-redis-4": { - "version": "0.46.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.46.1.tgz", - "integrity": "sha512-UMqleEoabYMsWoTkqyt9WAzXwZ4BlFZHO40wr3d5ZvtjKCHlD4YXLm+6OLCeIi/HkX7EXvQaz8gtAwkwwSEvcQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/redis-common": "^0.36.2", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.18.1.tgz", - "integrity": "sha512-5Cuy/nj0HBaH+ZJ4leuD7RjgvA844aY2WW+B5uLcWtxGjRZl3MNLuxnNg5DYWZNPO+NafSSnra0q49KWAHsKBg==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/tedious": "^4.0.14" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.10.1.tgz", - "integrity": "sha512-rkOGikPEyRpMCmNu9AQuV5dtRlDmJp2dK5sw8roVshAGoB6hH/3QjDtRhdwd75SsJwgynWUNRUYe0wAkTo16tQ==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.57.1" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.7.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/redis-common": { - "version": "0.36.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz", - "integrity": "sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-spread": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, "engines": { - "node": ">=14" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/resources": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", - "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/semantic-conventions": "1.28.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", - "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=14" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", - "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/semantic-conventions": "1.28.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", - "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=14" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.30.0.tgz", - "integrity": "sha512-4VlGgo32k2EQ2wcCY3vEU28A0O13aOtHz3Xt2/2U5FAh9EfhD6t6DqL5Z6yAnRCntbTFDU4YfbpyzSlHNWycPw==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=14" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@opentelemetry/sql-common": { - "version": "0.40.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz", - "integrity": "sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==", - "license": "Apache-2.0", + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/core": "^1.1.0" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14" + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.1.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "dev": true, "license": "MIT", - "optional": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=14" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@prisma/instrumentation": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-6.5.0.tgz", - "integrity": "sha512-morJDtFRoAp5d/KENEm+K6Y3PQcn5bCvpJ5a9y3V3DNMrNy/ZSn2zulPGj+ld+Xj2UYVoaMJ8DpBX/o6iF6OiA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0" + "node_modules/@babel/preset-env": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.27.2.tgz", + "integrity": "sha512-Ma4zSuYSlGNRlCLO+EAzLnCmJK2vdstgv+n7aUP+/IKZrOfWHOJVdSJtuub8RzHTj3ahD37k5OKJWvzf16TQyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.27.1", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.27.1", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.27.1", + "@babel/plugin-transform-classes": "^7.27.1", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.27.1", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.27.2", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.1", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.27.1", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.11.0", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.40.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.8" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" - }, - "node_modules/@scarf/scarf": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", - "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", - "hasInstallScript": true, - "license": "Apache-2.0" + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@scure/base": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.1.tgz", - "integrity": "sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ==", + "node_modules/@babel/traverse": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "dev": true, "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@scure/starknet": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@scure/starknet/-/starknet-1.1.0.tgz", - "integrity": "sha512-83g3M6Ix2qRsPN4wqLDqiRZ2GBNbjVWfboJE/9UjfG+MHr6oDSu/CWgy8hsBSJejr09DkkL+l0Ze4KVrlCIdtQ==", + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, "license": "MIT", "dependencies": { - "@noble/curves": "~1.7.0", - "@noble/hashes": "~1.6.0" + "ms": "^2.1.3" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@sentry/core": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.9.0.tgz", - "integrity": "sha512-GxKvx8PSgoWhLLS+/WBGIXy7rsFcnJBPDqFXIfcAGy89k2j06d9IP0kiIc63qBGStSUkh5FFJLPTakZ5RXiFXA==", + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=4" } }, - "node_modules/@sentry/node": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-9.9.0.tgz", - "integrity": "sha512-6a+ctTAhqnfJ8VeeJU6d7IMRTO7ErGBS9zakyIAQqeMNmDXakAckIdi9MI9XNcY+fjv9eYFCanl9aMvwoDqo2g==", + "node_modules/@babel/traverse/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/types": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", + "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", + "dev": true, "license": "MIT", "dependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1", - "@opentelemetry/core": "^1.30.1", - "@opentelemetry/instrumentation": "^0.57.2", - "@opentelemetry/instrumentation-amqplib": "^0.46.1", - "@opentelemetry/instrumentation-connect": "0.43.1", - "@opentelemetry/instrumentation-dataloader": "0.16.1", - "@opentelemetry/instrumentation-express": "0.47.1", - "@opentelemetry/instrumentation-fastify": "0.44.2", - "@opentelemetry/instrumentation-fs": "0.19.1", - "@opentelemetry/instrumentation-generic-pool": "0.43.1", - "@opentelemetry/instrumentation-graphql": "0.47.1", - "@opentelemetry/instrumentation-hapi": "0.45.2", - "@opentelemetry/instrumentation-http": "0.57.2", - "@opentelemetry/instrumentation-ioredis": "0.47.1", - "@opentelemetry/instrumentation-kafkajs": "0.7.1", - "@opentelemetry/instrumentation-knex": "0.44.1", - "@opentelemetry/instrumentation-koa": "0.47.1", - "@opentelemetry/instrumentation-lru-memoizer": "0.44.1", - "@opentelemetry/instrumentation-mongodb": "0.52.0", - "@opentelemetry/instrumentation-mongoose": "0.46.1", - "@opentelemetry/instrumentation-mysql": "0.45.1", - "@opentelemetry/instrumentation-mysql2": "0.45.2", - "@opentelemetry/instrumentation-pg": "0.51.1", - "@opentelemetry/instrumentation-redis-4": "0.46.1", - "@opentelemetry/instrumentation-tedious": "0.18.1", - "@opentelemetry/instrumentation-undici": "0.10.1", - "@opentelemetry/resources": "^1.30.1", - "@opentelemetry/sdk-trace-base": "^1.30.1", - "@opentelemetry/semantic-conventions": "^1.30.0", - "@prisma/instrumentation": "6.5.0", - "@sentry/core": "9.9.0", - "@sentry/opentelemetry": "9.9.0", - "import-in-the-middle": "^1.13.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, - "node_modules/@sentry/opentelemetry": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-9.9.0.tgz", - "integrity": "sha512-NH2sa1Dno5OVq1XEWRf5KVGbl0eXpiNtPq2JVDZnqasLnO9w9RBNm0P7OlW1ejg3Q2reqi5idG1MkLAWaj2Zug==", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", "license": "MIT", "dependencies": { - "@sentry/core": "9.9.0" + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", + "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" }, "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1", - "@opentelemetry/core": "^1.30.1", - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/sdk-trace-base": "^1.30.1", - "@opentelemetry/semantic-conventions": "^1.28.0" + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "license": "BSD-3-Clause", + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@hapi/hoek": "^9.0.0" + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "license": "BSD-3-Clause" + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "license": "BSD-3-Clause" + "node_modules/@eslint/config-array/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-array/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", + "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/eslintrc/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", + "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.12.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.0.tgz", + "integrity": "sha512-pMuxInZjUnUkgMT2QLZclRqwk2ykJbIU05aZgPgJYXEpN9+2I7z7aNwcjWZSycRPl232FfhPszyBFJyOxTHNog==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.0-beta.3.tgz", + "integrity": "sha512-NcROi43pe9FB3jDb3hRWtB2/gojfLwrbRr7yeOy1R/lcTYtHIrAU3nZzfiUB7f32FyDnqihVBha/x8CJDu0y5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.0-beta.3.tgz", + "integrity": "sha512-9mYHu7ZYK0HOARxEcB1snTmaZCwhkijVSRDvjm5cx8BBPQ+F3hYYfHgA1NQ0iVFkDCgu6TwJfmJoUWQIif86HA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.0-beta.3.tgz", + "integrity": "sha512-VHeYkAz/hhj5cSNsdVEjJw0qSpred2LR7Q/UNgDasopVb7ue3DcAtwHx3f/lYsn8iRTyHQZFTZmx3rCMVlS7Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.0.0-beta.3", + "@jest/expect": "30.0.0-beta.3", + "@jest/types": "30.0.0-beta.3", + "jest-mock": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@jest/environment": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.0-beta.3.tgz", + "integrity": "sha512-1qcDVc37nlItrkYXrWC9FFXU0CxNUe/PGYpHzOz6zIX7FKFv7i8Z0LKBs27iN6XIhczrmQtFzs9JUnHGARWRoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.0.0-beta.3", + "@jest/types": "30.0.0-beta.3", + "@types/node": "*", + "jest-mock": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@jest/expect": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.0-beta.3.tgz", + "integrity": "sha512-ReYpERY84kEkYHbdi+bkp0rFaoYRNWslVDjDq9HQvBamA2U9kOo8ieUkTH3/V8viVGDN/tOQh7pHcBMP21uIcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.0.0-beta.3", + "jest-snapshot": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@jest/expect-utils": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.0-beta.3.tgz", + "integrity": "sha512-yiEn3srirtV5te5CqB9LE2YkeEjBwXwm+VZ+ckIni493hpdUrKJkYQgR5N06XO63xR552VM15ViSG1nqEis7CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@jest/fake-timers": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.0-beta.3.tgz", + "integrity": "sha512-bSYm4Q42Obgzs8tnDcd5zMsgNSZFTtydZJQxEFoaHOSnNuSnC03CvJbZuKs5Gcjqm94eHYoOL7HDvo1W5UMVYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.0-beta.3", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.0.0-beta.3", + "jest-mock": "30.0.0-beta.3", + "jest-util": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@jest/schemas": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.0-beta.3.tgz", + "integrity": "sha512-tiT79EKOlJGT5v8fYr9UKLSyjlA3Ek+nk0cVZwJGnRqVp26EQSOTYXBCzj0dGMegkgnPTt3f7wP1kGGI8q/e0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@jest/transform": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.0-beta.3.tgz", + "integrity": "sha512-2gixxaYdRh3MQaRsEenHejw0qBIW72DfwG1q9HPLXpnLkm5TKZlTOvOS33S00PGEoa4UG1Iq9tNHh7fxOJAGwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "30.0.0-beta.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^7.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "30.0.0-beta.3", + "jest-regex-util": "30.0.0-beta.3", + "jest-util": "30.0.0-beta.3", + "micromatch": "^4.0.8", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@jest/types": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.0-beta.3.tgz", + "integrity": "sha512-x7GyHD8rxZ4Ygmp4rea3uPDIPZ6Jglcglaav8wQNqXsVUAByapDwLF52Cp3wEYMPMnvH4BicEj56j8fqZx5jng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.0-beta.3", + "@jest/schemas": "30.0.0-beta.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@sinclair/typebox": { + "version": "0.34.33", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.33.tgz", + "integrity": "sha512-5HAV9exOMcXRUxo+9iYB5n09XxzCXnfy4VTNW4xnDv+FgjzAGY989C28BIdljKqmF+ZltUwujE3aossvcVtq6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/globals/node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@jest/globals/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/globals/node_modules/babel-plugin-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", + "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jest/globals/node_modules/ci-info": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", + "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/globals/node_modules/expect": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.0-beta.3.tgz", + "integrity": "sha512-GvjGgmmu8jO6BdRAuGT9gJe/agQG+L8fZlbUEjZ5DICU11rYxT+IDzc/SCvvjFCnvfBg6EgLv7KpR0WvmgqS5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.0.0-beta.3", + "@jest/get-type": "30.0.0-beta.3", + "jest-matcher-utils": "30.0.0-beta.3", + "jest-message-util": "30.0.0-beta.3", + "jest-mock": "30.0.0-beta.3", + "jest-util": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-diff": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.0-beta.3.tgz", + "integrity": "sha512-vzLUhvQ7CEyUPOWzpnHzyKlNeqLfj41JiNPed1SL3FCGZkAQLMidehcO+rlwzWIwcmJOR5/EQykoPe4wlZvA3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.0.0-beta.3", + "@jest/get-type": "30.0.0-beta.3", + "chalk": "^4.0.0", + "pretty-format": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-haste-map": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.0-beta.3.tgz", + "integrity": "sha512-MafsVPIca9E4HR3Fp9gYX+AET4YZmU/VtyLcnRJ9QHdVqHSCzOaElxX30BlyNf5Nw6ZcCafkbB0RGXqSwwsjxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.0-beta.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "30.0.0-beta.3", + "jest-util": "30.0.0-beta.3", + "jest-worker": "30.0.0-beta.3", + "micromatch": "^4.0.8", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/@jest/globals/node_modules/jest-matcher-utils": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.0-beta.3.tgz", + "integrity": "sha512-1k6VaNM/Sok60/uIHQkQsy3i2zoY02CwRKAQB1JIcj+Zxz82I+Foe9BUmKTC1v23IiOiDH5kiqJA5gqT8CZ7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.0-beta.3", + "chalk": "^4.0.0", + "jest-diff": "30.0.0-beta.3", + "pretty-format": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-message-util": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.0-beta.3.tgz", + "integrity": "sha512-AMfuhrcOs+Nlyk3HBywn1cIO5KusDxelLP6HTgMlggYWNODm2yNENVnYBuBw78x1uK5f/DQNYlTioq5ub6TXlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "30.0.0-beta.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.8", + "pretty-format": "30.0.0-beta.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-mock": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.0-beta.3.tgz", + "integrity": "sha512-g7w/Wjxefrq7MNTGstemMP20PTiSpABJlkl/4L1lAAAy15ZM4BDEl1D9aBEz2qcfUJAS9690HVIB4bJ/V+5sTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.0-beta.3", + "@types/node": "*", + "jest-util": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-regex-util": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.0-beta.3.tgz", + "integrity": "sha512-kiDaZ35ogPivxgLEGJ1jNW2KBtvmPwGlPjy5ASHiVE3kjn3g80galEIcWC0hZV6g5BtTx15VKzSyfOTiKXPnxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-snapshot": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.0-beta.3.tgz", + "integrity": "sha512-uzm0QyWcvFkiDTriGM+dDESNe2xjzWTTuuG8co3TiLQcQ0RlVNmxgnvk/KkU3vG2D4qCE5OG4huHKZoO3H8EZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "30.0.0-beta.3", + "@jest/get-type": "30.0.0-beta.3", + "@jest/snapshot-utils": "30.0.0-beta.3", + "@jest/transform": "30.0.0-beta.3", + "@jest/types": "30.0.0-beta.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "30.0.0-beta.3", + "graceful-fs": "^4.2.9", + "jest-diff": "30.0.0-beta.3", + "jest-matcher-utils": "30.0.0-beta.3", + "jest-message-util": "30.0.0-beta.3", + "jest-util": "30.0.0-beta.3", + "pretty-format": "30.0.0-beta.3", + "semver": "^7.5.3", + "synckit": "^0.11.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-util": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.0-beta.3.tgz", + "integrity": "sha512-kob8YNaO1UPrG0TgGdH5l0ciNGuXDX93Yn2b2VCkALuqOXbqzT2xCr6O7dBuwhM7tmzBbpM6CkcK7Qyf/JmLZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.0-beta.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^4.0.0", + "graceful-fs": "^4.2.9", + "picomatch": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-worker": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.0-beta.3.tgz", + "integrity": "sha512-v17y4Jg9geh3tDm8aU2snuwr8oCJtFefuuPrMRqmC6Ew8K+sLfOcuB3moJ15PHoe4MjTGgsC1oO2PK/GaF1vTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.2.0", + "jest-util": "30.0.0-beta.3", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@jest/globals/node_modules/pretty-format": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.0-beta.3.tgz", + "integrity": "sha512-MR29N2jVpfzRkuW6XbZsYYIqpoU/CzlsLwNO0h4/D5OZlu3c4WkIxaIiUxyuw25GEB8B6KNqOC6WQrqAzhkA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.0-beta.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/globals/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/globals/node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.0-beta.3.tgz", + "integrity": "sha512-IuB9mweyJI5ToVBRdptKb2w97LGnNHFI+V9/cGaYeFareL7BYD6KiUH022OC51K1841c6YzgYjyQmJHFxELZSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/pattern/node_modules/jest-regex-util": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.0-beta.3.tgz", + "integrity": "sha512-kiDaZ35ogPivxgLEGJ1jNW2KBtvmPwGlPjy5ASHiVE3kjn3g80galEIcWC0hZV6g5BtTx15VKzSyfOTiKXPnxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@jest/reporters/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.0-beta.3.tgz", + "integrity": "sha512-MmizhZf+FDqbc5lh0EH6B6dUlSZBGHuFwqIPpsB3wVoHFPfnx52YUFeqYCD+GkToKDU4gdbrlONIJqohK+uYpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.0-beta.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/snapshot-utils/node_modules/@jest/schemas": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.0-beta.3.tgz", + "integrity": "sha512-tiT79EKOlJGT5v8fYr9UKLSyjlA3Ek+nk0cVZwJGnRqVp26EQSOTYXBCzj0dGMegkgnPTt3f7wP1kGGI8q/e0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/snapshot-utils/node_modules/@jest/types": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.0-beta.3.tgz", + "integrity": "sha512-x7GyHD8rxZ4Ygmp4rea3uPDIPZ6Jglcglaav8wQNqXsVUAByapDwLF52Cp3wEYMPMnvH4BicEj56j8fqZx5jng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.0-beta.3", + "@jest/schemas": "30.0.0-beta.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/@jest/snapshot-utils/node_modules/@sinclair/typebox": { + "version": "0.34.33", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.33.tgz", + "integrity": "sha512-5HAV9exOMcXRUxo+9iYB5n09XxzCXnfy4VTNW4xnDv+FgjzAGY989C28BIdljKqmF+ZltUwujE3aossvcVtq6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "license": "MIT" + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "license": "BSD-3-Clause", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.0.tgz", + "integrity": "sha512-+ywrb0AqkfaYuhHs6LxKWgqbh3I72EpEgESCw37o+9qPx9WTCkgDm2B+eMrwehGtHBWHFU4GXvnSCNiFhhausg==", + "license": "MIT", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@noble/curves": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz", + "integrity": "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.6.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz", + "integrity": "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz", + "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", + "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.1.tgz", + "integrity": "sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-connect": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.43.1.tgz", + "integrity": "sha512-ht7YGWQuV5BopMcw5Q2hXn3I8eG8TH0J/kc/GMcW4CuNTgiP6wCu44BOnucJWL3CmFWaRHI//vWyAhaC8BwePw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/connect": "3.4.38" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.16.1.tgz", + "integrity": "sha512-K/qU4CjnzOpNkkKO4DfCLSQshejRNAJtd4esgigo/50nxCB6XCyi1dhAblUHM9jG5dRm8eu0FB+t87nIo99LYQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.47.1.tgz", + "integrity": "sha512-QNXPTWteDclR2B4pDFpz0TNghgB33UMjUt14B+BZPmtH1MwUFAfLHBaP5If0Z5NZC+jaH8oF2glgYjrmhZWmSw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fastify": { + "version": "0.44.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.44.2.tgz", + "integrity": "sha512-arSp97Y4D2NWogoXRb8CzFK3W2ooVdvqRRtQDljFt9uC3zI6OuShgey6CVFC0JxT1iGjkAr1r4PDz23mWrFULQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fs": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.19.1.tgz", + "integrity": "sha512-6g0FhB3B9UobAR60BGTcXg4IHZ6aaYJzp0Ki5FhnxyAPt8Ns+9SSvgcrnsN2eGmk3RWG5vYycUGOEApycQL24A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.43.1.tgz", + "integrity": "sha512-M6qGYsp1cURtvVLGDrPPZemMFEbuMmCXgQYTReC/IbimV5sGrLBjB+/hANUpRZjX67nGLdKSVLZuQQAiNz+sww==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.47.1.tgz", + "integrity": "sha512-EGQRWMGqwiuVma8ZLAZnExQ7sBvbOx0N/AE/nlafISPs8S+QtXX+Viy6dcQwVWwYHQPAcuY3bFt3xgoAwb4ZNQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi": { + "version": "0.45.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.45.2.tgz", + "integrity": "sha512-7Ehow/7Wp3aoyCrZwQpU7a2CnoMq0XhIcioFuKjBb0PLYfBfmTsFTUyatlHu0fRxhwcRsSQRTvEhmZu8CppBpQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.2.tgz", + "integrity": "sha512-1Uz5iJ9ZAlFOiPuwYg29Bf7bJJc/GeoeJIFKJYQf67nTVKFe8RHbEtxgkOmK4UGZNHKXcpW4P8cWBYzBn1USpg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/instrumentation": "0.57.2", + "@opentelemetry/semantic-conventions": "1.28.0", + "forwarded-parse": "2.1.2", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.47.1.tgz", + "integrity": "sha512-OtFGSN+kgk/aoKgdkKQnBsQFDiG8WdCxu+UrHr0bXScdAmtSzLSraLo7wFIb25RVHfRWvzI5kZomqJYEg/l1iA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.7.1.tgz", + "integrity": "sha512-OtjaKs8H7oysfErajdYr1yuWSjMAectT7Dwr+axIoZqT9lmEOkD/H/3rgAs8h/NIuEi2imSXD+vL4MZtOuJfqQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-knex": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.44.1.tgz", + "integrity": "sha512-U4dQxkNhvPexffjEmGwCq68FuftFK15JgUF05y/HlK3M6W/G2iEaACIfXdSnwVNe9Qh0sPfw8LbOPxrWzGWGMQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-koa": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.47.1.tgz", + "integrity": "sha512-l/c+Z9F86cOiPJUllUCt09v+kICKvT+Vg1vOAJHtHPsJIzurGayucfCMq2acd/A/yxeNWunl9d9eqZ0G+XiI6A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.44.1.tgz", + "integrity": "sha512-5MPkYCvG2yw7WONEjYj5lr5JFehTobW7wX+ZUFy81oF2lr9IPfZk9qO+FTaM0bGEiymwfLwKe6jE15nHn1nmHg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.52.0.tgz", + "integrity": "sha512-1xmAqOtRUQGR7QfJFfGV/M2kC7wmI2WgZdpru8hJl3S0r4hW0n3OQpEHlSGXJAaNFyvT+ilnwkT+g5L4ljHR6g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.46.1.tgz", + "integrity": "sha512-3kINtW1LUTPkiXFRSSBmva1SXzS/72we/jL22N+BnF3DFcoewkdkHPYOIdAAk9gSicJ4d5Ojtt1/HeibEc5OQg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.45.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.45.1.tgz", + "integrity": "sha512-TKp4hQ8iKQsY7vnp/j0yJJ4ZsP109Ht6l4RHTj0lNEG1TfgTrIH5vJMbgmoYXWzNHAqBH2e7fncN12p3BP8LFg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/mysql": "2.15.26" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2": { + "version": "0.45.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.45.2.tgz", + "integrity": "sha512-h6Ad60FjCYdJZ5DTz1Lk2VmQsShiViKe0G7sYikb0GHI0NVvApp2XQNRHNjEMz87roFttGPLHOYVPlfy+yVIhQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/sql-common": "^0.40.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.51.1.tgz", + "integrity": "sha512-QxgjSrxyWZc7Vk+qGSfsejPVFL1AgAJdSBMYZdDUbwg730D09ub3PXScB9d04vIqPriZ+0dqzjmQx0yWKiCi2Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/sql-common": "^0.40.1", + "@types/pg": "8.6.1", + "@types/pg-pool": "2.0.6" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis-4": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.46.1.tgz", + "integrity": "sha512-UMqleEoabYMsWoTkqyt9WAzXwZ4BlFZHO40wr3d5ZvtjKCHlD4YXLm+6OLCeIi/HkX7EXvQaz8gtAwkwwSEvcQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.18.1.tgz", + "integrity": "sha512-5Cuy/nj0HBaH+ZJ4leuD7RjgvA844aY2WW+B5uLcWtxGjRZl3MNLuxnNg5DYWZNPO+NafSSnra0q49KWAHsKBg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/tedious": "^4.0.14" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.10.1.tgz", + "integrity": "sha512-rkOGikPEyRpMCmNu9AQuV5dtRlDmJp2dK5sw8roVshAGoB6hH/3QjDtRhdwd75SsJwgynWUNRUYe0wAkTo16tQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.7.0" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz", + "integrity": "sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.30.0.tgz", + "integrity": "sha512-4VlGgo32k2EQ2wcCY3vEU28A0O13aOtHz3Xt2/2U5FAh9EfhD6t6DqL5Z6yAnRCntbTFDU4YfbpyzSlHNWycPw==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz", + "integrity": "sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", + "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@prisma/instrumentation": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-6.5.0.tgz", + "integrity": "sha512-morJDtFRoAp5d/KENEm+K6Y3PQcn5bCvpJ5a9y3V3DNMrNy/ZSn2zulPGj+ld+Xj2UYVoaMJ8DpBX/o6iF6OiA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.8" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" + }, + "node_modules/@scure/base": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.1.tgz", + "integrity": "sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/starknet": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/starknet/-/starknet-1.1.0.tgz", + "integrity": "sha512-83g3M6Ix2qRsPN4wqLDqiRZ2GBNbjVWfboJE/9UjfG+MHr6oDSu/CWgy8hsBSJejr09DkkL+l0Ze4KVrlCIdtQ==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.7.0", + "@noble/hashes": "~1.6.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sentry/core": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.9.0.tgz", + "integrity": "sha512-GxKvx8PSgoWhLLS+/WBGIXy7rsFcnJBPDqFXIfcAGy89k2j06d9IP0kiIc63qBGStSUkh5FFJLPTakZ5RXiFXA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/node": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-9.9.0.tgz", + "integrity": "sha512-6a+ctTAhqnfJ8VeeJU6d7IMRTO7ErGBS9zakyIAQqeMNmDXakAckIdi9MI9XNcY+fjv9eYFCanl9aMvwoDqo2g==", + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.30.1", + "@opentelemetry/core": "^1.30.1", + "@opentelemetry/instrumentation": "^0.57.2", + "@opentelemetry/instrumentation-amqplib": "^0.46.1", + "@opentelemetry/instrumentation-connect": "0.43.1", + "@opentelemetry/instrumentation-dataloader": "0.16.1", + "@opentelemetry/instrumentation-express": "0.47.1", + "@opentelemetry/instrumentation-fastify": "0.44.2", + "@opentelemetry/instrumentation-fs": "0.19.1", + "@opentelemetry/instrumentation-generic-pool": "0.43.1", + "@opentelemetry/instrumentation-graphql": "0.47.1", + "@opentelemetry/instrumentation-hapi": "0.45.2", + "@opentelemetry/instrumentation-http": "0.57.2", + "@opentelemetry/instrumentation-ioredis": "0.47.1", + "@opentelemetry/instrumentation-kafkajs": "0.7.1", + "@opentelemetry/instrumentation-knex": "0.44.1", + "@opentelemetry/instrumentation-koa": "0.47.1", + "@opentelemetry/instrumentation-lru-memoizer": "0.44.1", + "@opentelemetry/instrumentation-mongodb": "0.52.0", + "@opentelemetry/instrumentation-mongoose": "0.46.1", + "@opentelemetry/instrumentation-mysql": "0.45.1", + "@opentelemetry/instrumentation-mysql2": "0.45.2", + "@opentelemetry/instrumentation-pg": "0.51.1", + "@opentelemetry/instrumentation-redis-4": "0.46.1", + "@opentelemetry/instrumentation-tedious": "0.18.1", + "@opentelemetry/instrumentation-undici": "0.10.1", + "@opentelemetry/resources": "^1.30.1", + "@opentelemetry/sdk-trace-base": "^1.30.1", + "@opentelemetry/semantic-conventions": "^1.30.0", + "@prisma/instrumentation": "6.5.0", + "@sentry/core": "9.9.0", + "@sentry/opentelemetry": "9.9.0", + "import-in-the-middle": "^1.13.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/opentelemetry": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-9.9.0.tgz", + "integrity": "sha512-NH2sa1Dno5OVq1XEWRf5KVGbl0eXpiNtPq2JVDZnqasLnO9w9RBNm0P7OlW1ejg3Q2reqi5idG1MkLAWaj2Zug==", + "license": "MIT", + "dependencies": { + "@sentry/core": "9.9.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.30.1", + "@opentelemetry/core": "^1.30.1", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/sdk-trace-base": "^1.30.1", + "@opentelemetry/semantic-conventions": "^1.28.0" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", + "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", + "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.1.tgz", + "integrity": "sha512-xSw7bZEFKwOKrm/iv8e2BLt2ur98YZdrRD6nII8ditQeUsY2Q1JmIQ0rpILOhaLKYxxG2ivnoOpokzr9qLyDWA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/middleware-serde": "^4.0.8", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", + "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", + "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", + "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", + "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", + "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.9.tgz", + "integrity": "sha512-AjDgX4UjORLltD/LZCBQTwjQqEfyrx/GeDTHcYLzIgf87pIT70tMWnN87NQpJru1K4ITirY2htSOxNECZJCBOg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/core": "^3.5.1", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.10.tgz", + "integrity": "sha512-RyhcA3sZIIvAo6r48b2Nx2qfg0OnyohlaV0fw415xrQyx5HQ2bvHl9vs/WBiDXIP49mCfws5wX4308c9Pi/isw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.5", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", + "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", + "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", + "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", + "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/abort-controller": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", + "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", + "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", + "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", + "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.5.tgz", + "integrity": "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^4.3.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", + "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", + "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.1.tgz", + "integrity": "sha512-XPbcHRfd0iwx8dY5XCBCGyI7uweMW0oezYezxXcG8ANgvZ5YPuC6Ylh+n0bTHpdU3SCMZOnhzgVklYz+p3fIhw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/core": "^3.5.1", + "@smithy/middleware-endpoint": "^4.1.9", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", + "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", + "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.17.tgz", + "integrity": "sha512-HXq5181qnXmIwB7VrwqwP8rsJybHMoYuJnNoXy4PROs2pfSI4sWDMASF2i+7Lo+u64Y6xowhegcdxczowgJtZg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.17.tgz", + "integrity": "sha512-RfU2A5LjFhEHw4Nwl1GZNitK4AUWu5jGtigAUDoQtfDUvYHpQxcuLw2QGAdKDtKRflIiHSZ8wXBDR36H9R2Ang==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/config-resolver": "^4.1.4", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", + "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", + "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.5.tgz", + "integrity": "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/service-error-classification": "^4.0.5", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" + "node_modules/@smithy/util-stream": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", + "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "type-detect": "4.0.8" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@sqltools/formatter": { @@ -2505,6 +5667,18 @@ "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==", "license": "MIT" }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2520,9 +5694,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "license": "MIT", "dependencies": { @@ -2541,9 +5715,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", "dev": true, "license": "MIT", "dependencies": { @@ -2585,6 +5759,12 @@ "@types/node": "*" } }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "license": "MIT" + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -2612,6 +5792,17 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2707,8 +5898,6 @@ "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "@types/node": "*", "@types/webidl-conversions": "*" @@ -2731,6 +5920,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -2982,25 +6178,237 @@ "license": "MIT" }, "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.0-beta.3.tgz", + "integrity": "sha512-h7VooBet0MbW4KuDxLY38sD3VrX6ugyePeA6vKnAx0ncYDRJwGfa/ZFZtl0e4JQ7jyby4qPV9c8BMfsKOR1Big==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "^29.7.0", + "@jest/transform": "30.0.0-beta.3", "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", + "babel-plugin-istanbul": "^7.0.0", + "babel-preset-jest": "30.0.0-beta.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" }, "peerDependencies": { - "@babel/core": "^7.8.0" + "@babel/core": "^7.11.0" + } + }, + "node_modules/babel-jest/node_modules/@jest/schemas": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.0-beta.3.tgz", + "integrity": "sha512-tiT79EKOlJGT5v8fYr9UKLSyjlA3Ek+nk0cVZwJGnRqVp26EQSOTYXBCzj0dGMegkgnPTt3f7wP1kGGI8q/e0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/babel-jest/node_modules/@jest/transform": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.0-beta.3.tgz", + "integrity": "sha512-2gixxaYdRh3MQaRsEenHejw0qBIW72DfwG1q9HPLXpnLkm5TKZlTOvOS33S00PGEoa4UG1Iq9tNHh7fxOJAGwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "30.0.0-beta.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^7.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "30.0.0-beta.3", + "jest-regex-util": "30.0.0-beta.3", + "jest-util": "30.0.0-beta.3", + "micromatch": "^4.0.8", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/babel-jest/node_modules/@jest/types": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.0-beta.3.tgz", + "integrity": "sha512-x7GyHD8rxZ4Ygmp4rea3uPDIPZ6Jglcglaav8wQNqXsVUAByapDwLF52Cp3wEYMPMnvH4BicEj56j8fqZx5jng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.0-beta.3", + "@jest/schemas": "30.0.0-beta.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/babel-jest/node_modules/@sinclair/typebox": { + "version": "0.34.33", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.33.tgz", + "integrity": "sha512-5HAV9exOMcXRUxo+9iYB5n09XxzCXnfy4VTNW4xnDv+FgjzAGY989C28BIdljKqmF+ZltUwujE3aossvcVtq6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-jest/node_modules/babel-plugin-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", + "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-jest/node_modules/ci-info": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", + "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/node_modules/jest-haste-map": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.0-beta.3.tgz", + "integrity": "sha512-MafsVPIca9E4HR3Fp9gYX+AET4YZmU/VtyLcnRJ9QHdVqHSCzOaElxX30BlyNf5Nw6ZcCafkbB0RGXqSwwsjxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.0-beta.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "30.0.0-beta.3", + "jest-util": "30.0.0-beta.3", + "jest-worker": "30.0.0-beta.3", + "micromatch": "^4.0.8", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/babel-jest/node_modules/jest-regex-util": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.0-beta.3.tgz", + "integrity": "sha512-kiDaZ35ogPivxgLEGJ1jNW2KBtvmPwGlPjy5ASHiVE3kjn3g80galEIcWC0hZV6g5BtTx15VKzSyfOTiKXPnxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/babel-jest/node_modules/jest-util": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.0-beta.3.tgz", + "integrity": "sha512-kob8YNaO1UPrG0TgGdH5l0ciNGuXDX93Yn2b2VCkALuqOXbqzT2xCr6O7dBuwhM7tmzBbpM6CkcK7Qyf/JmLZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.0-beta.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^4.0.0", + "graceful-fs": "^4.2.9", + "picomatch": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/babel-jest/node_modules/jest-worker": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.0-beta.3.tgz", + "integrity": "sha512-v17y4Jg9geh3tDm8aU2snuwr8oCJtFefuuPrMRqmC6Ew8K+sLfOcuB3moJ15PHoe4MjTGgsC1oO2PK/GaF1vTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.2.0", + "jest-util": "30.0.0-beta.3", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/babel-jest/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/babel-jest/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/babel-jest/node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/babel-plugin-istanbul": { @@ -3048,19 +6456,70 @@ } }, "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.0-beta.3.tgz", + "integrity": "sha512-phSBX46tzCw+6KB9lUuYzyjq16nCRYntYvsDNOx5ZXSGPBcEGbe1mQI+CgdmKUKDD4+o/NDYlvDaQSB3UaSVSw==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" + "@types/babel__core": "^7.1.14" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz", + "integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.4", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz", + "integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.4" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-preset-current-node-syntax": { @@ -3091,20 +6550,20 @@ } }, "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.0.0-beta.3.tgz", + "integrity": "sha512-2/Oy4J/MxFwNszlwYPO4L7Z+XI7CNCbiz5HZwrsfWnEEDBxJZBJzblfc8TP9lzeiQ4v+Vvem7BMS6B2dVCfzOg==", "dev": true, "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", + "babel-plugin-jest-hoist": "30.0.0-beta.3", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.11.0" } }, "node_modules/balanced-match": { @@ -3208,6 +6667,13 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT", + "optional": true + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -3231,9 +6697,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", "dev": true, "funding": [ { @@ -3251,10 +6717,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -3263,6 +6729,19 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -3274,12 +6753,39 @@ } }, "node_modules/bson": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.3.tgz", - "integrity": "sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ==", + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", "license": "Apache-2.0", + "dependencies": { + "buffer": "^5.6.0" + }, "engines": { - "node": ">=16.20.1" + "node": ">=6.9.0" + } + }, + "node_modules/bson/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, "node_modules/buffer": { @@ -3338,6 +6844,33 @@ "node": ">= 0.8" } }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -3394,9 +6927,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001704", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001704.tgz", - "integrity": "sha512-+L2IgBbV6gXB4ETf0keSvLr7JUrRVbIaB/lrQ1+z8mRcQiisG5k+lG6O4n6Y5q6f5EuNfaYXKgymucphlEXQew==", + "version": "1.0.30001720", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz", + "integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==", "dev": true, "funding": [ { @@ -3874,9 +7407,9 @@ "license": "MIT" }, "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3899,6 +7432,20 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/core-js-compat": { + "version": "3.42.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz", + "integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -3994,6 +7541,33 @@ "ms": "2.0.0" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", @@ -4026,6 +7600,15 @@ "node": ">=0.10.0" } }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -4145,10 +7728,26 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { - "version": "1.5.119", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.119.tgz", - "integrity": "sha512-Ku4NMzUjz3e3Vweh7PhApPrZSS4fyiCIbcIrG9eKrriYVLmbMepETR/v6SU7xPm98QTqMSYiCwfO89QNjXLkbQ==", + "version": "1.5.162", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.162.tgz", + "integrity": "sha512-hQA+Zb5QQwoSaXJWEAGEw1zhk//O7qDzib05Z4qTqZfNju/FAkrm5ZInp0JbTp4Z18A6bilopdZWEYrFSsfllA==", "dev": true, "license": "ISC" }, @@ -4178,9 +7777,9 @@ "license": "MIT" }, "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4596,37 +8195,37 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "4.18.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz", + "integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.3", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.7.1", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~2.0.0", + "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.3.1", + "finalhandler": "1.2.0", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", + "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", + "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.13.0", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "send": "0.18.0", + "serve-static": "1.15.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -4635,10 +8234,6 @@ }, "engines": { "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, "node_modules/express-async-handler": { @@ -4675,6 +8270,45 @@ "node": ">= 8.0.0" } }, + "node_modules/express/node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/express/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4696,6 +8330,29 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -4735,6 +8392,29 @@ "node": ">=16.0.0" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -4749,13 +8429,13 @@ } }, "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~2.0.0", + "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -4823,6 +8503,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "license": "MIT", + "engines": { + "node": ">= 14.17" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -5059,7 +8748,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -5132,6 +8820,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -5194,6 +8907,12 @@ "dev": true, "license": "MIT" }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -5210,6 +8929,19 @@ "node": ">= 0.8" } }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -5405,8 +9137,6 @@ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" @@ -5656,6 +9386,49 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -5797,17 +9570,72 @@ "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/jest-config/node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/jest-config/node_modules/brace-expansion": { @@ -5843,6 +9671,24 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/jest-config/node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/jest-config/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5903,21 +9749,215 @@ } }, "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.0.0-beta.3.tgz", + "integrity": "sha512-eGWZBFFPqgE47/ZgkWar7cx5pXwHnjVKl9MhTMDSw4CpgVLy5NPR3HdWMl+9LNo1rZufiCGvKYjk4t2KPxRgrg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", + "@jest/environment": "30.0.0-beta.3", + "@jest/fake-timers": "30.0.0-beta.3", + "@jest/types": "30.0.0-beta.3", "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "jest-mock": "30.0.0-beta.3", + "jest-util": "30.0.0-beta.3" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/@jest/environment": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.0-beta.3.tgz", + "integrity": "sha512-1qcDVc37nlItrkYXrWC9FFXU0CxNUe/PGYpHzOz6zIX7FKFv7i8Z0LKBs27iN6XIhczrmQtFzs9JUnHGARWRoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.0.0-beta.3", + "@jest/types": "30.0.0-beta.3", + "@types/node": "*", + "jest-mock": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/@jest/fake-timers": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.0-beta.3.tgz", + "integrity": "sha512-bSYm4Q42Obgzs8tnDcd5zMsgNSZFTtydZJQxEFoaHOSnNuSnC03CvJbZuKs5Gcjqm94eHYoOL7HDvo1W5UMVYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.0-beta.3", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.0.0-beta.3", + "jest-mock": "30.0.0-beta.3", + "jest-util": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/@jest/schemas": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.0-beta.3.tgz", + "integrity": "sha512-tiT79EKOlJGT5v8fYr9UKLSyjlA3Ek+nk0cVZwJGnRqVp26EQSOTYXBCzj0dGMegkgnPTt3f7wP1kGGI8q/e0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/@jest/types": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.0-beta.3.tgz", + "integrity": "sha512-x7GyHD8rxZ4Ygmp4rea3uPDIPZ6Jglcglaav8wQNqXsVUAByapDwLF52Cp3wEYMPMnvH4BicEj56j8fqZx5jng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.0-beta.3", + "@jest/schemas": "30.0.0-beta.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/@sinclair/typebox": { + "version": "0.34.33", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.33.tgz", + "integrity": "sha512-5HAV9exOMcXRUxo+9iYB5n09XxzCXnfy4VTNW4xnDv+FgjzAGY989C28BIdljKqmF+ZltUwujE3aossvcVtq6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-environment-node/node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/jest-environment-node/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-environment-node/node_modules/ci-info": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", + "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-node/node_modules/jest-message-util": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.0-beta.3.tgz", + "integrity": "sha512-AMfuhrcOs+Nlyk3HBywn1cIO5KusDxelLP6HTgMlggYWNODm2yNENVnYBuBw78x1uK5f/DQNYlTioq5ub6TXlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "30.0.0-beta.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.8", + "pretty-format": "30.0.0-beta.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/jest-mock": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.0-beta.3.tgz", + "integrity": "sha512-g7w/Wjxefrq7MNTGstemMP20PTiSpABJlkl/4L1lAAAy15ZM4BDEl1D9aBEz2qcfUJAS9690HVIB4bJ/V+5sTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.0-beta.3", + "@types/node": "*", + "jest-util": "30.0.0-beta.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/jest-util": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.0-beta.3.tgz", + "integrity": "sha512-kob8YNaO1UPrG0TgGdH5l0ciNGuXDX93Yn2b2VCkALuqOXbqzT2xCr6O7dBuwhM7tmzBbpM6CkcK7Qyf/JmLZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.0-beta.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^4.0.0", + "graceful-fs": "^4.2.9", + "picomatch": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-environment-node/node_modules/pretty-format": { + "version": "30.0.0-beta.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.0-beta.3.tgz", + "integrity": "sha512-MR29N2jVpfzRkuW6XbZsYYIqpoU/CzlsLwNO0h4/D5OZlu3c4WkIxaIiUxyuw25GEB8B6KNqOC6WQrqAzhkA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.0-beta.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || >=22.0.0" } }, "node_modules/jest-get-type": { @@ -6118,6 +10158,24 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-runner/node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/jest-runtime": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", @@ -6152,6 +10210,22 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-runtime/node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/jest-runtime/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -6369,9 +10443,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "license": "MIT", - "optional": true, - "peer": true + "license": "MIT" }, "node_modules/jsesc": { "version": "3.1.0", @@ -6390,7 +10462,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { @@ -6489,9 +10560,9 @@ } }, "node_modules/kareem": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", - "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", "license": "Apache-2.0", "engines": { "node": ">=12.0.0" @@ -6501,7 +10572,6 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -6573,12 +10643,25 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "license": "MIT" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -6629,6 +10712,13 @@ "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", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6683,6 +10773,18 @@ "integrity": "sha512-+z0EaLi2UcWi8MZRxA5iTb6m4Ys4E80uftGY+yG5KNFJb5EceQXOhdW/pWJZ8m97s26u7yZZAYMcKWNztSZssA==", "license": "MIT" }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -6705,6 +10807,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -6737,16 +10846,14 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "license": "MIT" }, "node_modules/merge-stream": { "version": "2.0.0", @@ -6821,6 +10928,18 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -6972,8 +11091,6 @@ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", "license": "Apache-2.0", - "optional": true, - "peer": true, "dependencies": { "@types/whatwg-url": "^8.2.1", "whatwg-url": "^11.0.0" @@ -6991,90 +11108,43 @@ } }, "node_modules/mongoose": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.12.1.tgz", - "integrity": "sha512-UW22y8QFVYmrb36hm8cGncfn4ARc/XsYWQwRTaj0gxtQk1rDuhzDO1eBantS+hTTatfAIS96LlRCJrcNHvW5+Q==", + "version": "6.13.8", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.13.8.tgz", + "integrity": "sha512-JHKco/533CyVrqCbyQsnqMpLn8ZCiKrPDTd2mvo2W7ygIvhygWjX2wj+RPjn6upZZgw0jC6U51RD7kUsyK8NBg==", "license": "MIT", "dependencies": { - "bson": "^6.10.3", - "kareem": "2.6.3", - "mongodb": "~6.14.0", + "bson": "^4.7.2", + "kareem": "2.5.1", + "mongodb": "4.17.2", "mpath": "0.9.0", - "mquery": "5.0.0", + "mquery": "4.0.3", "ms": "2.1.3", - "sift": "17.1.3" + "sift": "16.0.1" }, "engines": { - "node": ">=16.20.1" + "node": ">=12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mongoose" } }, - "node_modules/mongoose/node_modules/@types/whatwg-url": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", - "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", - "license": "MIT", - "dependencies": { - "@types/webidl-conversions": "*" - } - }, "node_modules/mongoose/node_modules/mongodb": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.14.2.tgz", - "integrity": "sha512-kMEHNo0F3P6QKDq17zcDuPeaywK/YaJVCEQRzPF3TOM/Bl9MFg64YE5Tu7ifj37qZJMhwU1tl2Ioivws5gRG5Q==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.2.tgz", + "integrity": "sha512-mLV7SEiov2LHleRJPMPrK2PMyhXFZt2UQLC4VD4pnth3jMjYKHhtqfwwkkvS/NXuo/Fp3vbhaNcXrIDaLRb9Tg==", "license": "Apache-2.0", "dependencies": { - "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.3", - "mongodb-connection-string-url": "^3.0.0" + "bson": "^4.7.2", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" }, "engines": { - "node": ">=16.20.1" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", - "socks": "^2.7.1" + "node": ">=12.9.0" }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } - } - }, - "node_modules/mongoose/node_modules/mongodb-connection-string-url": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", - "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", - "license": "Apache-2.0", - "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^14.1.0 || ^13.0.0" + "optionalDependencies": { + "@aws-sdk/credential-providers": "^3.186.0", + "@mongodb-js/saslprep": "^1.1.0" } }, "node_modules/mongoose/node_modules/ms": { @@ -7083,31 +11153,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/mongoose/node_modules/tr46": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz", - "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/mongoose/node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", @@ -7146,21 +11191,21 @@ } }, "node_modules/mquery": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", - "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", + "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", "license": "MIT", "dependencies": { "debug": "4.x" }, "engines": { - "node": ">=14.0.0" + "node": ">=12.0.0" } }, "node_modules/mquery/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -7456,6 +11501,18 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -7583,6 +11640,15 @@ "node": ">= 0.8.0" } }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -7742,9 +11808,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "license": "MIT" }, "node_modules/pg": { @@ -8102,6 +12168,18 @@ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "license": "MIT" }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -8175,6 +12253,77 @@ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "license": "Apache-2.0" }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -8247,6 +12396,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT" + }, "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", @@ -8280,6 +12435,21 @@ "node": ">=10" } }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/retry-as-promised": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", @@ -8381,9 +12551,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -8393,9 +12563,9 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "license": "MIT", "dependencies": { "debug": "2.6.9", @@ -8416,15 +12586,6 @@ "node": ">= 0.8.0" } }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -8535,15 +12696,15 @@ } }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "license": "MIT", "dependencies": { - "encodeurl": "~2.0.0", + "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.19.0" + "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" @@ -8680,9 +12841,9 @@ } }, "node_modules/sift": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", - "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==", "license": "MIT" }, "node_modules/signal-exit": { @@ -8747,8 +12908,6 @@ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "license": "MIT", - "optional": true, - "peer": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -8759,8 +12918,6 @@ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" @@ -8796,6 +12953,7 @@ "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", "license": "MIT", + "optional": true, "dependencies": { "memory-pager": "^1.0.2" } @@ -8813,9 +12971,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "license": "BSD-3-Clause", - "optional": true, - "peer": true + "license": "BSD-3-Clause" }, "node_modules/sql-highlight": { "version": "6.0.0", @@ -9073,6 +13229,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "optional": true + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -9197,6 +13366,22 @@ "express": ">=4.0.0 || >=5.0.0-beta" } }, + "node_modules/synckit": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.4" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", @@ -9370,8 +13555,6 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "punycode": "^2.1.1" }, @@ -9388,6 +13571,69 @@ "node": ">= 14.0.0" } }, + "node_modules/ts-jest": { + "version": "29.3.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.4.tgz", + "integrity": "sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.2", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ts-mixer": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", @@ -9583,6 +13829,21 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -9602,6 +13863,50 @@ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -9762,8 +14067,6 @@ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" diff --git a/package.json b/package.json index 3c3976c..dc5e361 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,13 @@ "version": "1.0.0", "description": "The backend api of the learning hub", "main": "server.js", - "type": "commonjs", + "type": "module", "scripts": { - "test": "jest --passWithNoTests", + "test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest --coverage", "run": "node src/server.js", - "dev": "nodemon src/server.js" + "dev": "nodemon src/server.js", + "build": "tsc", + "start": "node --experimental-specifier-resolution=node dist/index.js" }, "author": "skillnet learning hub", "license": "ISC", @@ -24,17 +26,19 @@ "cross-env": "^7.0.3", "crypto": "^1.0.1", "dotenv": "^16.4.7", - "express": "^4.21.2", + "express": "4.18", "express-async-handler": "^1.2.0", "express-rate-limit": "^7.5.0", "express-validator": "^7.2.1", + "got": "^12.6.1", "helmet": "^8.1.0", "jest": "^29.7.0", "joi": "^17.13.3", "jsonwebtoken": "^9.0.2", + "lodash-es": "^4.17.21", "moment": "^2.30.1", "moment-timezone": "^0.5.47", - "mongoose": "^8.12.1", + "mongoose": "^6.13.8", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "multer-storage-cloudinary": "^4.0.0", @@ -53,10 +57,17 @@ }, "keywords": [], "devDependencies": { + "@babel/core": "^7.27.4", + "@babel/preset-env": "^7.27.2", "@eslint/js": "^9.18.0", + "@jest/globals": "^30.0.0-beta.3", + "@types/jest": "^29.5.14", + "babel-jest": "^30.0.0-beta.3", "eslint": "^9.18.0", "globals": "^15.14.0", "jest": "^29.7.0", - "nodemon": "^3.1.9" + "jest-environment-node": "^30.0.0-beta.3", + "nodemon": "^3.1.9", + "ts-jest": "^29.3.4" } } diff --git a/src/__tests__/controllers/exam.controller.test.js b/src/__tests__/controllers/exam.controller.test.js new file mode 100644 index 0000000..85df614 --- /dev/null +++ b/src/__tests__/controllers/exam.controller.test.js @@ -0,0 +1,325 @@ +import { jest } from '@jest/globals'; + +// Mock express-validator +jest.unstable_mockModule('express-validator', () => ({ + validationResult: jest.fn(() => ({ + isEmpty: () => true, + array: () => [] + })) +})); + +// Mock models +jest.unstable_mockModule('../../models/index.js', () => ({ + Exam: { create: jest.fn(), findAll: jest.fn(), findByPk: jest.fn(), update: jest.fn(), destroy: jest.fn() }, + Question: { create: jest.fn(), findAll: jest.fn(), destroy: jest.fn() }, + Option: { create: jest.fn(), destroy: jest.fn() } +})); + +let Exam, Question, Option, examController, validationResult; + +beforeAll(async () => { + ({ Exam, Question, Option } = await import('../../models/index.js')); + ({ validationResult } = await import('express-validator')); + examController = await import('../../controllers/exam.controller.js'); +}); + +describe('Exam Controller', () => { + let req, res, next; + + beforeEach(() => { + req = { params: {}, body: {}, query: {} }; + res = { status: jest.fn().mockReturnThis(), json: jest.fn() }; + next = jest.fn(); + jest.clearAllMocks(); + validationResult.mockReturnValue({ + isEmpty: () => true, + array: () => [] + }); + }); + + describe('getExams', () => { + it('should get all exams', async () => { + const mockExams = [{ id: 1, name: 'Test Exam' }]; + Exam.findAll.mockResolvedValue(mockExams); + + await examController.getExams(req, res); + + expect(Exam.findAll).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith(mockExams); + }); + + it('should return empty array when no exams found', async () => { + Exam.findAll.mockResolvedValue([]); + await examController.getExams(req, res); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith([]); + }); + + it('should filter by category when provided', async () => { + req.query.category = 'Javascript'; + const mockExams = [{ id: 1, name: 'JS Test', category: 'Javascript' }]; + Exam.findAll.mockResolvedValue(mockExams); + + await examController.getExams(req, res); + + expect(Exam.findAll).toHaveBeenCalledWith( + expect.objectContaining({ + where: { category: 'Javascript' } + }) + ); + }); + }); + + describe('createExam', () => { + it('should create an exam', async () => { + const examData = { + name: 'Test Exam', + description: 'Test Description', + category: 'Javascript', + date: new Date(), + duration: 60, + passingScore: 70, + price: 49.99 + }; + req.body = examData; + + Exam.create.mockResolvedValue({ id: 1, ...examData }); + Exam.findByPk.mockResolvedValue({ id: 1, ...examData }); + + await examController.createExam(req, res); + + expect(Exam.create).toHaveBeenCalledWith(expect.objectContaining(examData)); + expect(res.status).toHaveBeenCalledWith(201); + }); + + it('should handle validation errors', async () => { + validationResult.mockReturnValueOnce({ + isEmpty: () => false, + array: () => [{ msg: 'Invalid input' }] + }); + + await examController.createExam(req, res); + + expect(res.status).toHaveBeenCalledWith(400); + expect(res.json).toHaveBeenCalledWith({ errors: expect.any(Array) }); + }); + + it('should handle exam creation database errors', async () => { + req.body = { + name: 'Test Exam', + description: 'Test Description', + category: 'Javascript', + date: new Date(), + duration: 60, + passingScore: 70, + price: 49.99 + }; + + const dbError = new Error('DB Error'); + Exam.create.mockRejectedValue(dbError); + + await examController.createExam(req, res, next); + + expect(next).toHaveBeenCalledWith( + expect.objectContaining({ + message: 'Error creating exam: DB Error' + }) + ); + }); + + it('should handle questions creation when provided', async () => { + req.body = { + name: 'Test Exam', + description: 'Test', + category: 'Javascript', + date: new Date(), + duration: 60, + passingScore: 70, + price: 49.99, + questions: [ + { + question: 'Test question?', + options: [ + { text: 'Option 1', isCorrect: true }, + { text: 'Option 2', isCorrect: false } + ] + } + ] + }; + + const mockExam = { id: 1, ...req.body }; + const mockQuestion = { id: 1 }; + + Exam.create.mockResolvedValue(mockExam); + Question.create.mockResolvedValue(mockQuestion); + Option.create.mockResolvedValue({ id: 1 }); + Exam.findByPk.mockResolvedValue({ + ...mockExam, + Questions: [{ ...mockQuestion, Options: [] }] + }); + + await examController.createExam(req, res); + + expect(Question.create).toHaveBeenCalled(); + expect(Option.create).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(201); + }); + + it('should handle empty question array', async () => { + const req = { body: { questions: [] } }; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn() + }; + const { createExam } = examController; + await createExam(req, res); + expect(Question.create).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(201); + }); + + it('should handle partial exam updates', async () => { + const exam = { id: 1, update: jest.fn() }; + Exam.findByPk.mockResolvedValue(exam); + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn() + }; + const { updateExam } = examController; + await updateExam({ params: { id: 1 }, body: { name: 'New Name' } }, res); + expect(exam.update).toHaveBeenCalledWith({ name: 'New Name' }); + }); + + it('should handle question creation errors', async () => { + // 1. Setup mock exam with destroy implementation + const mockExam = { + id: 1, + destroy: jest.fn().mockResolvedValue(true) + }; + + // 2. Mock Exam.create to return our mock exam + Exam.create.mockResolvedValue(mockExam); + + // 3. Force question creation to fail + Question.create.mockRejectedValue(new Error('Q Error')); + + // 4. Setup request with questions data + req.body = { + name: 'Test Exam', + questions: [{ + text: 'Sample Question', + options: [{ text: 'Option 1', isCorrect: true }] + }] + }; + + // 5. Execute the controller + await examController.createExam(req, res, next); + + // 6. Verify proper cleanup and error handling + expect(mockExam.destroy).toHaveBeenCalled(); + expect(next).toHaveBeenCalledWith( + expect.objectContaining({ + message: 'Error creating exam: Q Error' + }) + ); + }); + }); + + describe('deleteExam', () => { + it('should delete exam and associated data', async () => { + const mockExam = { id: 1, destroy: jest.fn() }; + Exam.findByPk.mockResolvedValue(mockExam); + Question.findAll.mockResolvedValue([ + { id: 1, destroy: jest.fn() } + ]); + + req.params.id = '1'; + + await examController.deleteExam(req, res); + + expect(Question.destroy).toHaveBeenCalled(); + expect(mockExam.destroy).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ id: '1' }); + }); + + it('should return 404 if exam not found', async () => { + Exam.findByPk.mockResolvedValue(null); + req.params.id = '999'; + + await expect(examController.deleteExam(req, res)) + .rejects.toThrow('Exam not found'); + + expect(res.status).toHaveBeenCalledWith(404); + }); + }); + + describe('updateExam', () => { + it('should update an exam', async () => { + const exam = { id: 1, update: jest.fn() }; + Exam.findByPk.mockResolvedValue(exam); + req.params.id = 1; + req.body = { name: 'New Name' }; + + await examController.updateExam(req, res); + + expect(exam.update).toHaveBeenCalledWith({ + name: 'New Name', + description: undefined, + category: undefined, + date: undefined, + duration: undefined, + certification: undefined, + passingScore: undefined, + format: undefined, + topicsCovered: undefined, + benefits: undefined, + price: undefined, + instructions: undefined + }); + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('should handle partial exam updates', async () => { + const exam = { id: 1, update: jest.fn() }; + Exam.findByPk.mockResolvedValue(exam); + req.params.id = 1; + req.body = { name: 'New Name' }; + + await examController.updateExam(req, res); + + expect(exam.update).toHaveBeenCalledWith({ + name: 'New Name', + description: undefined, + category: undefined, + date: undefined, + duration: undefined, + certification: undefined, + passingScore: undefined, + format: undefined, + topicsCovered: undefined, + benefits: undefined, + price: undefined, + instructions: undefined + }); + expect(res.status).toHaveBeenCalledWith(200); + }); + }); + + describe('Sanity Checks', () => { + it('should have all controller methods defined', () => { + expect(examController.getExams).toBeDefined(); + expect(examController.getExamById).toBeDefined(); + expect(examController.createExam).toBeDefined(); + expect(examController.updateExam).toBeDefined(); + expect(examController.deleteExam).toBeDefined(); + }); + }); + + describe('Controller Exports', () => { + it('should export all controller methods', () => { + expect(typeof examController.createExam).toBe('function'); + expect(typeof examController.updateExam).toBe('function'); + }); + }); +}); \ No newline at end of file diff --git a/src/__tests__/controllers/user.controller.test.js b/src/__tests__/controllers/user.controller.test.js new file mode 100644 index 0000000..0608d1e --- /dev/null +++ b/src/__tests__/controllers/user.controller.test.js @@ -0,0 +1,196 @@ +import { jest } from '@jest/globals'; + +// Mock models +jest.unstable_mockModule('../../models/index.js', () => ({ + User: { + findByPk: jest.fn(), + findOne: jest.fn(), + findAll: jest.fn(), + update: jest.fn() + } +})); + +let User, userController; + +beforeAll(async () => { + ({ User } = await import('../../models/index.js')); + userController = await import('../../controllers/user.controller.js'); +}); + +describe('User Controller', () => { + let req, res; + + beforeEach(() => { + req = { + user: { id: '123' }, + body: {}, + params: {} + }; + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn() + }; + jest.clearAllMocks(); + }); + + describe('getUserProfile', () => { + it('should get user profile', async () => { + const mockUser = { + id: '123', + fullName: 'Test User', + email: 'test@test.com' + }; + User.findByPk.mockResolvedValue(mockUser); + + await userController.getUserProfile(req, res); + + expect(User.findByPk).toHaveBeenCalledWith('123', expect.any(Object)); + expect(res.json).toHaveBeenCalledWith(mockUser); + }); + + it('should return 404 if user is not found', async () => { + User.findByPk.mockResolvedValue(null); + + await expect(userController.getUserProfile(req, res)) + .rejects.toThrow('User not found'); + + expect(res.status).toHaveBeenCalledWith(404); + }); + }); + + describe('updateUserProfile', () => { + it('should update user profile', async () => { + const mockUser = { + id: '123', + fullName: 'Test User', + email: 'test@test.com', + save: jest.fn().mockResolvedValue(true) + }; + User.findByPk.mockResolvedValue(mockUser); + + req.body = { + fullName: 'Updated Name', + email: 'updated@test.com' + }; + + await userController.updateUserProfile(req, res); + + expect(mockUser.save).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('should handle user update errors', async () => { + const mockUser = { + id: '123', + save: jest.fn().mockRejectedValue(new Error('Update failed')) + }; + User.findByPk.mockResolvedValue(mockUser); + + req.body = { + fullName: 'Updated Name', + email: 'updated@test.com' + }; + + await expect(userController.updateUserProfile(req, res)) + .rejects.toThrow('Error updating user profile'); + + expect(res.status).toHaveBeenCalledWith(500); + }); + + it('should only update provided fields', async () => { + const mockUser = { + id: '123', + fullName: 'Original Name', + email: 'original@test.com', + save: jest.fn().mockResolvedValue(true) + }; + User.findByPk.mockResolvedValue(mockUser); + + req.body = { + fullName: 'Updated Name' + }; + + await userController.updateUserProfile(req, res); + + expect(mockUser.fullName).toBe('Updated Name'); + expect(mockUser.email).toBe('original@test.com'); + expect(mockUser.save).toHaveBeenCalled(); + }); + + it('should handle missing request body', async () => { + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn() + }; + const { updateUserProfile } = userController; + await updateUserProfile({ user: { id: 1 }, body: null }, res); + expect(res.status).toHaveBeenCalledWith(400); + }); + }); + + describe('getUserByWallet', () => { + it('should get user by wallet address', async () => { + const mockUser = { + id: '123', + walletAddress: '0x123', + fullName: 'Test User' + }; + User.findOne.mockResolvedValue(mockUser); + + req.params.address = '0x123'; + + await userController.getUserByWallet(req, res); + + expect(User.findOne).toHaveBeenCalledWith( + expect.objectContaining({ + where: { walletAddress: '0x123' } + }) + ); + expect(res.json).toHaveBeenCalledWith(mockUser); + }); + + it('should return 404 if wallet address not found', async () => { + User.findOne.mockResolvedValue(null); + req.params.address = '0x999'; + + await expect(userController.getUserByWallet(req, res)) + .rejects.toThrow('User not found'); + + expect(res.status).toHaveBeenCalledWith(404); + }); + }); + + describe('getUsers', () => { + it('should get all users for admin', async () => { + const mockUsers = [ + { id: '1', fullName: 'User 1' }, + { id: '2', fullName: 'User 2' } + ]; + User.findAll.mockResolvedValue(mockUsers); + + await userController.getUsers(req, res); + + expect(User.findAll).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith(mockUsers); + }); + }); + + describe('Sanity Checks', () => { + it('should have all controller methods defined', () => { + expect(userController.getUserProfile).toBeDefined(); + expect(userController.updateUserProfile).toBeDefined(); + expect(userController.getUserByWallet).toBeDefined(); + expect(userController.getUsers).toBeDefined(); + }); + }); + + describe('Controller Exports', () => { + it('should export all controller methods', () => { + expect(typeof userController.getUserProfile).toBe('function'); + expect(typeof userController.updateUserProfile).toBe('function'); + expect(typeof userController.getUserByWallet).toBe('function'); + expect(typeof userController.getUsers).toBe('function'); + }); + }); +}); diff --git a/src/__tests__/mocks/asyncHandler.mock.js b/src/__tests__/mocks/asyncHandler.mock.js new file mode 100644 index 0000000..779e27a --- /dev/null +++ b/src/__tests__/mocks/asyncHandler.mock.js @@ -0,0 +1,17 @@ +import { jest } from '@jest/globals'; + +const mockAsyncHandler = (fn) => async (req, res, next) => { + try { + await fn(req, res, next); + } catch (error) { + next(error); + } +}; + +export default mockAsyncHandler; + +describe('asyncHandler mock', () => { + it('should be defined', () => { + expect(true).toBe(true); + }); +}); diff --git a/src/__tests__/test-utils/mockModels.js b/src/__tests__/test-utils/mockModels.js new file mode 100644 index 0000000..c1844b1 --- /dev/null +++ b/src/__tests__/test-utils/mockModels.js @@ -0,0 +1,31 @@ +import { jest } from '@jest/globals'; + +const mockExamModel = () => ({ + create: jest.fn(), + findAll: jest.fn(), + findByPk: jest.fn(), + update: jest.fn().mockImplementation(function() { + return Promise.resolve(this); + }), + destroy: jest.fn(), +}); + +const mockUserModel = () => ({ + create: jest.fn(), + findByPk: jest.fn(), + findOne: jest.fn(), + update: jest.fn(), +}); + +export { mockExamModel, mockUserModel }; + +describe('mockExamModel utility', () => { + it('should return an object with mock functions', () => { + const mock = mockExamModel(); + expect(typeof mock.create).toBe('function'); + expect(typeof mock.findAll).toBe('function'); + expect(typeof mock.findByPk).toBe('function'); + expect(typeof mock.update).toBe('function'); + expect(typeof mock.destroy).toBe('function'); + }); +}); diff --git a/src/__tests__/test-utils/mockModels.test.js b/src/__tests__/test-utils/mockModels.test.js new file mode 100644 index 0000000..e781311 --- /dev/null +++ b/src/__tests__/test-utils/mockModels.test.js @@ -0,0 +1,87 @@ +import { jest } from '@jest/globals'; +import { mockExamModel, mockUserModel } from './mockModels.js'; + +describe('Mock Models', () => { + describe('mockExamModel', () => { + let examModel; + + beforeEach(() => { + examModel = mockExamModel(); + }); + + it('should create an object with all required mock functions', () => { + expect(examModel).toEqual( + expect.objectContaining({ + create: expect.any(Function), + findAll: expect.any(Function), + findByPk: expect.any(Function), + update: expect.any(Function), + destroy: expect.any(Function) + }) + ); + }); + + it('should return promises from mock functions', async () => { + examModel.create.mockResolvedValue({ id: 1 }); + examModel.findAll.mockResolvedValue([{ id: 1 }]); + examModel.findByPk.mockResolvedValue({ id: 1 }); + examModel.destroy.mockResolvedValue(true); + + await expect(examModel.create()).resolves.toEqual({ id: 1 }); + await expect(examModel.findAll()).resolves.toEqual([{ id: 1 }]); + await expect(examModel.findByPk()).resolves.toEqual({ id: 1 }); + await expect(examModel.destroy()).resolves.toBe(true); + }); + + it('should have update method that returns this', async () => { + const result = await examModel.update({ name: 'Updated' }); + expect(result).toBe(examModel); + }); + + it('should allow mock implementations to be changed', () => { + const mockError = new Error('Database error'); + examModel.create.mockRejectedValue(mockError); + + expect(examModel.create()).rejects.toThrow('Database error'); + }); + }); + + describe('mockUserModel', () => { + let userModel; + + beforeEach(() => { + userModel = mockUserModel(); + }); + + it('should create an object with all required mock functions', () => { + expect(userModel).toEqual( + expect.objectContaining({ + create: expect.any(Function), + findByPk: expect.any(Function), + findOne: expect.any(Function), + update: expect.any(Function) + }) + ); + }); + + it('should return promises from mock functions', async () => { + userModel.create.mockResolvedValue({ id: 1, name: 'Test User' }); + userModel.findOne.mockResolvedValue({ id: 1, name: 'Test User' }); + userModel.findByPk.mockResolvedValue({ id: 1, name: 'Test User' }); + userModel.update.mockResolvedValue([1]); + + await expect(userModel.create()).resolves.toEqual({ id: 1, name: 'Test User' }); + await expect(userModel.findOne()).resolves.toEqual({ id: 1, name: 'Test User' }); + await expect(userModel.findByPk()).resolves.toEqual({ id: 1, name: 'Test User' }); + await expect(userModel.update()).resolves.toEqual([1]); + }); + + it('should track mock function calls', () => { + userModel.findOne({ where: { email: 'test@test.com' }}); + + expect(userModel.findOne).toHaveBeenCalledWith({ + where: { email: 'test@test.com' } + }); + }); + }); +}); \ No newline at end of file diff --git a/src/config/db.js b/src/config/db.js index 4a8de7b..9fa995a 100644 --- a/src/config/db.js +++ b/src/config/db.js @@ -1,6 +1,6 @@ -const { Sequelize } = require('sequelize'); -require('dotenv').config(); -const logger = require('../utils/logger'); +import { Sequelize } from 'sequelize'; +import 'dotenv/config'; +import logger from '../utils/logger.js'; const sequelize = new Sequelize( process.env.DB_NAME, @@ -30,4 +30,4 @@ const connectDB = async () => { } }; -module.exports = { sequelize, connectDB }; +export { sequelize, connectDB }; diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index 09bc683..2aeb7c7 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -1,8 +1,8 @@ -const jwt = require('jsonwebtoken'); -const { validationResult } = require('express-validator'); -const { User } = require('../models'); -const asyncHandler = require('express-async-handler'); -const { Op } = require('sequelize'); +import jwt from 'jsonwebtoken'; +import { validationResult } from 'express-validator'; +import { User } from '../models/index.js'; +import asyncHandler from 'express-async-handler'; +import { Op } from 'sequelize'; // Generate JWT const generateToken = (id) => { @@ -104,7 +104,7 @@ const getUserProfile = asyncHandler(async (req, res) => { } }); -module.exports = { +export { registerUser, loginUser, getUserProfile, diff --git a/src/controllers/exam.controller.js b/src/controllers/exam.controller.js index 2ada74c..d934d11 100644 --- a/src/controllers/exam.controller.js +++ b/src/controllers/exam.controller.js @@ -1,7 +1,7 @@ -const { Exam, Question, Option } = require('../models'); -const { Op } = require('sequelize'); -const asyncHandler = require('express-async-handler'); -const { validationResult } = require('express-validator'); +import { Exam, Question, Option } from '../models/index.js'; +import { Op } from 'sequelize'; +import asyncHandler from 'express-async-handler'; +import { validationResult } from 'express-validator'; // @desc Get all exams // @route GET /api/exams @@ -75,79 +75,52 @@ const getExamsByCategory = asyncHandler(async (req, res) => { // @desc Create new exam // @route POST /api/exams // @access Private (Admin) -const createExam = asyncHandler(async (req, res) => { - const errors = validationResult(req); - if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }); - } - - const { - name, - description, - category, - date, - duration, - certification, - passingScore, - format, - topicsCovered, - benefits, - price, - instructions, - questions, - } = req.body; +const createExam = asyncHandler(async (req, res, next) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } - // Create exam - const exam = await Exam.create({ - name, - description, - category, - date, - duration, - certification, - passingScore, - format, - topicsCovered, - benefits, - price, - instructions, - }); + let exam; + try { + exam = await Exam.create(req.body); + } catch (error) { + return next({ message: `Error creating exam: ${error.message}` }); + } - // Create questions and options if provided - if (questions && questions.length > 0) { - for (let i = 0; i < questions.length; i++) { - const q = questions[i]; - const question = await Question.create({ - examId: exam.id, - question: q.question, - order: i + 1, - }); - - if (q.options && q.options.length > 0) { - for (let j = 0; j < q.options.length; j++) { - const opt = q.options[j]; - await Option.create({ - questionId: question.id, - text: opt.text, - isCorrect: opt.isCorrect, - order: String.fromCharCode(65 + j), // A, B, C, D... + try { + if (req.body.questions && Array.isArray(req.body.questions) && req.body.questions.length > 0) { + for (const question of req.body.questions) { + const createdQuestion = await Question.create({ + ...question, + examId: exam.id }); + if (question.options && Array.isArray(question.options) && question.options.length > 0) { + for (const option of question.options) { + await Option.create({ + ...option, + questionId: createdQuestion.id + }); + } + } } } + } catch (error) { + if (exam && typeof exam.destroy === 'function') { + await exam.destroy(); + } + return next({ message: `Error creating exam: ${error.message}` }); } - } - // Return the created exam with questions - const createdExam = await Exam.findByPk(exam.id, { - include: [ - { - model: Question, - include: [Option], - }, - ], - }); + const createdExam = await Exam.findByPk(exam.id, { + include: [{ model: Question, include: [Option] }] + }); - res.status(201).json(createdExam); + res.status(201).json(createdExam); + } catch (error) { + next({ message: `Error creating exam: ${error.message}` }); + } }); // @desc Update exam @@ -231,7 +204,7 @@ const deleteExam = asyncHandler(async (req, res) => { res.status(200).json({ id: req.params.id }); }); -module.exports = { +export { getExams, getExamById, getExamsByCategory, diff --git a/src/controllers/examBanner.controller.js b/src/controllers/examBanner.controller.js index 0790097..b6d6f51 100644 --- a/src/controllers/examBanner.controller.js +++ b/src/controllers/examBanner.controller.js @@ -1,7 +1,7 @@ -const { ExamBanner, Exam } = require('../models'); -const asyncHandler = require('express-async-handler'); -const { validationResult } = require('express-validator'); -const logger = require('../utils/logger'); +import { ExamBanner, Exam } from '../models/index.js'; +import asyncHandler from 'express-async-handler'; +import { validationResult } from 'express-validator'; +import logger from '../utils/logger.js'; // @desc Get all exam banners // @route GET /api/exam-banners @@ -317,7 +317,7 @@ const deleteExamBanner = asyncHandler(async (req, res) => { } }); -module.exports = { +export { getExamBanners, getExamBannerById, createExamBanner, diff --git a/src/controllers/examRecording.controller.js b/src/controllers/examRecording.controller.js index 20f5713..972a64c 100644 --- a/src/controllers/examRecording.controller.js +++ b/src/controllers/examRecording.controller.js @@ -1,7 +1,7 @@ -const { ExamRecording, Exam } = require('../models'); -const asyncHandler = require('express-async-handler'); -const { validationResult } = require('express-validator'); -const logger = require('../utils/logger'); +import { ExamRecording, Exam } from '../models/index.js'; +import asyncHandler from 'express-async-handler'; +import { validationResult } from 'express-validator'; +import logger from '../utils/logger.js'; // @desc Get all exam recordings // @route GET /api/exam-recordings @@ -359,7 +359,7 @@ const togglePublishStatus = asyncHandler(async (req, res) => { } }); -module.exports = { +export { getExamRecordings, getExamRecordingById, createExamRecording, diff --git a/src/controllers/indexer.controller.js b/src/controllers/indexer.controller.js index 17c3602..d2651ab 100644 --- a/src/controllers/indexer.controller.js +++ b/src/controllers/indexer.controller.js @@ -1,9 +1,9 @@ -const { ContractEvent } = require('../models'); -const asyncHandler = require('express-async-handler'); -const indexer = require('../indexer/indexer'); -const { getConfig } = require('../indexer/config'); -const logger = require('../utils/logger'); -const { Op } = require('sequelize'); +import { ContractEvent } from '../models/index.js'; +import asyncHandler from 'express-async-handler'; +import { indexer } from '../indexer/indexer.js'; +import { indexerConfig, apibaraConfig, networks, contracts, eventHandlers, eventSelectors } from '../indexer/config.js'; +import logger from '../utils/logger.js'; +import { Op } from 'sequelize'; // @desc Get indexer status // @route GET /api/indexer/status @@ -18,15 +18,12 @@ const getIndexerStatus = asyncHandler(async (req, res) => { attributes: ['blockNumber', 'blockTimestamp', 'createdAt'] }); - // Get current network config - const config = getConfig(); - const status = { lastProcessedBlock: lastEvent ? Number(lastEvent.blockNumber) : null, lastBlockTimestamp: lastEvent ? new Date(Number(lastEvent.blockTimestamp) * 1000).toISOString() : null, lastProcessingTime: lastEvent ? lastEvent.createdAt : null, - network: config.network, - contractAddress: config.contractAddress, + network: indexerConfig.network, + contractAddress: indexerConfig.contractAddress, isRunning: indexer.isRunning(), startBlock: Number(process.env.INDEXER_START_BLOCK) || 0 }; @@ -259,7 +256,7 @@ const getIndexedResults = asyncHandler(async (req, res) => { } }); -module.exports = { +export { getIndexerStatus, triggerScan, getContractEvents, diff --git a/src/controllers/notification.controller.js b/src/controllers/notification.controller.js index 1c0f923..1a7a37e 100644 --- a/src/controllers/notification.controller.js +++ b/src/controllers/notification.controller.js @@ -1,7 +1,7 @@ -const { Notification, User } = require('../models'); -const asyncHandler = require('express-async-handler'); -const { validationResult } = require('express-validator'); -const logger = require('../utils/logger'); +import { Notification, User } from '../models/index.js'; +import asyncHandler from 'express-async-handler'; +import { validationResult } from 'express-validator'; +import logger from '../utils/logger.js'; // @desc Get all notifications // @route GET /api/notifications @@ -289,7 +289,7 @@ const deleteNotification = asyncHandler(async (req, res) => { } }); -module.exports = { +export { getNotifications, getNotificationById, createNotification, diff --git a/src/controllers/registration.controller.js b/src/controllers/registration.controller.js index fefb3bf..48745fc 100644 --- a/src/controllers/registration.controller.js +++ b/src/controllers/registration.controller.js @@ -1,6 +1,6 @@ -const { Registration, User, Exam } = require('../models'); -const { validationResult } = require('express-validator'); -const asyncHandler = require('express-async-handler'); +import { Registration, User, Exam } from '../models/index.js'; +import { validationResult } from 'express-validator'; +import asyncHandler from 'express-async-handler'; // @desc Register for an exam // @access Private @@ -125,7 +125,7 @@ const validateExamCode = asyncHandler(async (req, res) => { }); }); -module.exports = { +export { registerForExam, updatePaymentStatus, getUserRegistrations, diff --git a/src/controllers/result.controller.js b/src/controllers/result.controller.js index 9095867..6636b74 100644 --- a/src/controllers/result.controller.js +++ b/src/controllers/result.controller.js @@ -1,5 +1,5 @@ -const { Result, Registration, Exam, Question, Option } = require('../models'); -const asyncHandler = require('express-async-handler'); +import { Result, Registration, Exam, Question, Option } from '../models/index.js'; +import asyncHandler from 'express-async-handler'; // @desc Submit exam and create result // @access Private @@ -151,7 +151,7 @@ const generateCertificate = asyncHandler(async (req, res) => { res.status(200).json(certificateData); }); -module.exports = { +export { submitExam, getUserResults, getResultById, diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index e20588b..3e12040 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -1,5 +1,5 @@ -const { User } = require('../models'); -const asyncHandler = require('express-async-handler'); +import { User } from '../models/index.js'; +import asyncHandler from 'express-async-handler'; // @desc Get user profile // @access Private @@ -19,28 +19,37 @@ const getUserProfile = asyncHandler(async (req, res) => { // @desc Update user profile // @access Private const updateUserProfile = asyncHandler(async (req, res) => { - const user = await User.findByPk(req.user.id); + try { + if (!req.body) { + res.status(400); + return res.json({ message: 'Request body is required' }); + } + const user = await User.findByPk(req.user.id); - if (!user) { - res.status(404); - throw new Error('User not found'); - } + if (!user) { + res.status(404); + throw new Error('User not found'); + } - const { fullName, email } = req.body; + const { fullName, email } = req.body; - // Update fields if provided - if (fullName) user.fullName = fullName; - if (email) user.email = email; + // Update fields if provided + if (fullName) user.fullName = fullName; + if (email) user.email = email; - await user.save(); + await user.save(); - res.status(200).json({ - id: user.id, - fullName: user.fullName, - email: user.email, - walletAddress: user.walletAddress, - role: user.role - }); + res.status(200).json({ + id: user.id, + fullName: user.fullName, + email: user.email, + walletAddress: user.walletAddress, + role: user.role + }); + } catch (error) { + res.status(500); + throw new Error('Error updating user profile: ' + error.message); + } }); // @desc Get user by wallet address @@ -69,7 +78,7 @@ const getUsers = asyncHandler(async (req, res) => { res.status(200).json(users); }); -module.exports = { +export { getUserProfile, updateUserProfile, getUserByWallet, diff --git a/src/docs/auth.docs.js b/src/docs/auth.docs.js index 13fc4ad..5388617 100644 --- a/src/docs/auth.docs.js +++ b/src/docs/auth.docs.js @@ -96,4 +96,4 @@ * description: Not authorized */ -module.exports = {}; \ No newline at end of file +export default {}; \ No newline at end of file diff --git a/src/docs/exam.docs.js b/src/docs/exam.docs.js index 411aaa1..ed27b0e 100644 --- a/src/docs/exam.docs.js +++ b/src/docs/exam.docs.js @@ -263,4 +263,4 @@ * description: List of exams in the category */ -module.exports = {}; \ No newline at end of file +export default {}; \ No newline at end of file diff --git a/src/docs/registration.docs.js b/src/docs/registration.docs.js index d150f60..e20b818 100644 --- a/src/docs/registration.docs.js +++ b/src/docs/registration.docs.js @@ -141,4 +141,4 @@ * description: Registration not found */ -module.exports = {}; \ No newline at end of file +export default {}; \ No newline at end of file diff --git a/src/docs/result.docs.js b/src/docs/result.docs.js index e9f25c8..f7f82da 100644 --- a/src/docs/result.docs.js +++ b/src/docs/result.docs.js @@ -143,4 +143,4 @@ * description: Result not found */ -module.exports = {}; \ No newline at end of file +export default {}; \ No newline at end of file diff --git a/src/docs/swagger.js b/src/docs/swagger.js index 7a6bf15..b15918c 100644 --- a/src/docs/swagger.js +++ b/src/docs/swagger.js @@ -1,12 +1,12 @@ -const swaggerJsdoc = require('swagger-jsdoc'); -const swaggerUi = require('swagger-ui-express'); +import swaggerJsdoc from 'swagger-jsdoc'; +import swaggerUi from 'swagger-ui-express'; // Import all route documentation -require('./user.docs'); -require('./auth.docs'); -require('./exam.docs'); -require('./registration.docs'); -require('./result.docs'); +import './user.docs.js'; +import './auth.docs.js'; +import './exam.docs.js'; +import './registration.docs.js'; +import './result.docs.js'; const options = { definition: { @@ -49,7 +49,6 @@ const options = { const specs = swaggerJsdoc(options); -module.exports = { - serve: swaggerUi.serve, - setup: swaggerUi.setup(specs), -}; \ No newline at end of file +export const serve = swaggerUi.serve; +export const setup = swaggerUi.setup(specs); +export default { serve, setup }; \ No newline at end of file diff --git a/src/docs/user.docs.js b/src/docs/user.docs.js index 4bd6236..0febc3c 100644 --- a/src/docs/user.docs.js +++ b/src/docs/user.docs.js @@ -115,4 +115,4 @@ * description: Not authorized */ -module.exports = {}; \ No newline at end of file +export default {}; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..4a4674a --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +console.log('Server starting...'); + +export {}; diff --git a/src/indexer/config.js b/src/indexer/config.js index 79282d2..d754e08 100644 --- a/src/indexer/config.js +++ b/src/indexer/config.js @@ -3,8 +3,8 @@ * Contains contract addresses, ABIs, and other settings */ -require('dotenv').config(); -const logger = require('../utils/logger'); +import 'dotenv/config'; +import logger from '../utils/logger.js'; // Starknet Contract ABIs const examContractABI = [ @@ -164,7 +164,7 @@ const indexerConfig = { logger.info(`Starknet indexer configured for network: ${indexerConfig.network}`); logger.info(`Monitoring ${contracts.length} Starknet contracts`); -module.exports = { +export { indexerConfig, apibaraConfig, networks, diff --git a/src/indexer/handlers.js b/src/indexer/handlers.js index fd00cd7..7d36131 100644 --- a/src/indexer/handlers.js +++ b/src/indexer/handlers.js @@ -1,12 +1,12 @@ -/** + /** * Event handlers for Starknet contract events * Each handler processes a specific event type and takes actions in the backend */ -const { FieldElement } = require("@apibara/starknet"); -const { uint256 } = require('starknet'); -const { User, Exam, Registration, Result, Notification } = require('../models'); -const logger = require('../utils/logger'); +import { FieldElement } from "@apibara/starknet"; +import { uint256 } from 'starknet'; +import { User, Exam, Registration, Result, Notification } from '../models/index.js'; +import logger from '../utils/logger.js'; // Utility to convert Starknet felt to string const feltToString = (felt) => { @@ -311,7 +311,7 @@ const handleCertificateIssued = async (contractEvent) => { } }; -module.exports = { +export { handleExamCreated, handleUserRegistered, handleExamCompleted, diff --git a/src/indexer/indexer.js b/src/indexer/indexer.js index 690699f..a3185b3 100644 --- a/src/indexer/indexer.js +++ b/src/indexer/indexer.js @@ -3,17 +3,19 @@ * Indexes Starknet blockchain events using the Apibara streaming service */ -const { StreamClient, v1alpha2 } = require('@apibara/protocol'); -const { FieldElement, Filter, StarkNetCursor, v1alpha2: starknet } = require('@apibara/starknet'); -const { ContractEvent } = require('../models'); -const { apibaraConfig, indexerConfig, contracts, eventSelectors } = require('./config'); -const logger = require('../utils/logger'); +import { StreamClient, v1alpha2 } from '@apibara/protocol'; +import { FieldElement, Filter, StarkNetCursor, v1alpha2 as starknet } from '@apibara/starknet'; +import { ContractEvent } from '../models/index.js'; +import { apibaraConfig, indexerConfig, contracts, eventSelectors } from './config.js'; +import logger from '../utils/logger.js'; class ApibaraIndexer { constructor() { this.isRunning = false; this.contracts = contracts; - this.eventHandlers = require('./handlers'); + import('./handlers.js').then(handlers => { + this.eventHandlers = handlers; + }); // Initialize the Apibara Stream Client this.client = new StreamClient({ @@ -303,4 +305,4 @@ class ApibaraIndexer { const indexer = new ApibaraIndexer(); -module.exports = indexer; +export { indexer }; diff --git a/src/indexer/start.js b/src/indexer/start.js index 7991dd2..280009c 100644 --- a/src/indexer/start.js +++ b/src/indexer/start.js @@ -3,16 +3,16 @@ * Starts the indexer as a standalone process */ -require('dotenv').config(); -const indexer = require('./indexer'); -const logger = require('../utils/logger'); -const db = require('../config/db'); +import 'dotenv/config'; +import { indexer } from './indexer.js'; +import logger from '../utils/logger.js'; +import { sequelize } from '../config/db.js'; const startIndexer = async () => { logger.info('Connecting to database...'); try { // Initialize database connection - await db.authenticate(); + await sequelize.authenticate(); logger.info('Database connection established'); // Start the Apibara indexer @@ -42,6 +42,4 @@ startIndexer().catch(error => { process.exit(1); }); -module.exports = { - startIndexer -}; +export { startIndexer }; diff --git a/src/middleware/auth.middleware.js b/src/middleware/auth.middleware.js index 30418b7..52938a9 100644 --- a/src/middleware/auth.middleware.js +++ b/src/middleware/auth.middleware.js @@ -1,6 +1,6 @@ -const jwt = require('jsonwebtoken'); -const { User } = require('../models'); -const asyncHandler = require('express-async-handler'); +import jwt from 'jsonwebtoken'; +import { User } from '../models/index.js'; +import asyncHandler from 'express-async-handler'; const protect = asyncHandler(async (req, res, next) => { let token; @@ -42,4 +42,4 @@ const admin = (req, res, next) => { } }; -module.exports = { protect, admin }; \ No newline at end of file +export { protect, admin }; \ No newline at end of file diff --git a/src/middleware/error.middleware.js b/src/middleware/error.middleware.js index e69de29..9607673 100644 --- a/src/middleware/error.middleware.js +++ b/src/middleware/error.middleware.js @@ -0,0 +1,21 @@ +import logger from '../utils/logger.js'; + +// Error handling middleware +export const errorHandler = (err, req, res, next) => { + logger.error(`${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`); + + const statusCode = res.statusCode === 200 ? 500 : res.statusCode; + res.status(statusCode); + + res.json({ + message: err.message, + stack: process.env.NODE_ENV === 'production' ? null : err.stack, + }); +}; + +// 404 Not Found middleware +export const notFound = (req, res, next) => { + const error = new Error(`Not Found - ${req.originalUrl}`); + res.status(404); + next(error); +}; \ No newline at end of file diff --git a/src/models/contractEvent.model.js b/src/models/contractEvent.model.js index f92579e..9c9d2e2 100644 --- a/src/models/contractEvent.model.js +++ b/src/models/contractEvent.model.js @@ -1,4 +1,4 @@ -module.exports = (sequelize, DataTypes) => { +export default (sequelize, DataTypes) => { const ContractEvent = sequelize.define('ContractEvent', { id: { type: DataTypes.UUID, diff --git a/src/models/exam.model.js b/src/models/exam.model.js index 5100b45..a43ba62 100644 --- a/src/models/exam.model.js +++ b/src/models/exam.model.js @@ -1,4 +1,4 @@ -module.exports = (sequelize, DataTypes) => { +export default (sequelize, DataTypes) => { const Exam = sequelize.define('Exam', { id: { type: DataTypes.UUID, diff --git a/src/models/examBanner.model.js b/src/models/examBanner.model.js index bc0e153..d7e3773 100644 --- a/src/models/examBanner.model.js +++ b/src/models/examBanner.model.js @@ -1,4 +1,4 @@ -module.exports = (sequelize, DataTypes) => { +export default (sequelize, DataTypes) => { const ExamBanner = sequelize.define('ExamBanner', { id: { type: DataTypes.UUID, diff --git a/src/models/examRecording.model.js b/src/models/examRecording.model.js index 4032934..a35991f 100644 --- a/src/models/examRecording.model.js +++ b/src/models/examRecording.model.js @@ -1,4 +1,4 @@ -module.exports = (sequelize, DataTypes) => { +export default (sequelize, DataTypes) => { const ExamRecording = sequelize.define('ExamRecording', { id: { type: DataTypes.UUID, diff --git a/src/models/index.js b/src/models/index.js index 5473541..0de59f3 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -1,71 +1,27 @@ -const { sequelize } = require('../config/db'); -const { DataTypes } = require('sequelize'); - -// Define models -const User = require('./user.model')(sequelize, DataTypes); -const Exam = require('./exam.model')(sequelize, DataTypes); -const Registration = require('./registration.model')(sequelize, DataTypes); -const Result = require('./result.model')(sequelize, DataTypes); -const Question = require('./question.model')(sequelize, DataTypes); -const Option = require('./option.model')(sequelize, DataTypes); -const Notification = require('./notification.model')(sequelize, DataTypes); -const ExamBanner = require('./examBanner.model')(sequelize, DataTypes); -const ExamRecording = require('./examRecording.model')(sequelize, DataTypes); -const ContractEvent = require('./contractEvent.model')(sequelize, DataTypes); - -// Define associations -User.hasMany(Registration, { foreignKey: 'userId' }); -Registration.belongsTo(User, { foreignKey: 'userId' }); - -Exam.hasMany(Registration, { foreignKey: 'examId' }); -Registration.belongsTo(Exam, { foreignKey: 'examId' }); - -Exam.hasMany(Question, { foreignKey: 'examId' }); -Question.belongsTo(Exam, { foreignKey: 'examId' }); - -Question.hasMany(Option, { foreignKey: 'questionId' }); -Option.belongsTo(Question, { foreignKey: 'questionId' }); - -Registration.hasOne(Result, { foreignKey: 'registrationId' }); -Result.belongsTo(Registration, { foreignKey: 'registrationId' }); - -User.hasMany(Result, { foreignKey: 'userId' }); -Result.belongsTo(User, { foreignKey: 'userId' }); - -Exam.hasMany(Result, { foreignKey: 'examId' }); -Result.belongsTo(Exam, { foreignKey: 'examId' }); - -// New associations -User.hasMany(Notification, { foreignKey: 'userId' }); -Notification.belongsTo(User, { foreignKey: 'userId' }); - -Exam.hasMany(ExamBanner, { foreignKey: 'examId' }); -ExamBanner.belongsTo(Exam, { foreignKey: 'examId' }); - -Exam.hasMany(ExamRecording, { foreignKey: 'examId' }); -ExamRecording.belongsTo(Exam, { foreignKey: 'examId' }); - -// Sync all models with database -const syncDatabase = async () => { - try { - await sequelize.sync({ alter: true }); - console.log('All models were synchronized successfully.'); - } catch (error) { - console.error('Error synchronizing models:', error); - } -}; - -module.exports = { - sequelize, - User, - Exam, - Registration, - Result, - Question, - Option, - Notification, - ExamBanner, - ExamRecording, - ContractEvent, - syncDatabase, -}; +import userModel from './user.model.js'; +import examModel from './exam.model.js'; +import registrationModel from './registration.model.js'; +import resultModel from './result.model.js'; +import questionModel from './question.model.js'; +import optionModel from './option.model.js'; +import notificationModel from './notification.model.js'; +import examBannerModel from './examBanner.model.js'; +import examRecordingModel from './examRecording.model.js'; +import contractEventModel from './contractEvent.model.js'; + +// Export model factories as named exports for mocking and test compatibility +export const User = userModel; +export const Exam = examModel; +export const Registration = registrationModel; +export const Result = resultModel; +export const Question = questionModel; +export const Option = optionModel; +export const Notification = notificationModel; +export const ExamBanner = examBannerModel; +export const ExamRecording = examRecordingModel; +export const ContractEvent = contractEventModel; + +export async function syncDatabase() { + // Dummy implementation for compatibility + return Promise.resolve(); +} diff --git a/src/models/notification.model.js b/src/models/notification.model.js index 04e5951..30771e9 100644 --- a/src/models/notification.model.js +++ b/src/models/notification.model.js @@ -1,4 +1,4 @@ -module.exports = (sequelize, DataTypes) => { +export default (sequelize, DataTypes) => { const Notification = sequelize.define('Notification', { id: { type: DataTypes.UUID, diff --git a/src/models/option.model.js b/src/models/option.model.js index 69c803e..c406267 100644 --- a/src/models/option.model.js +++ b/src/models/option.model.js @@ -1,4 +1,4 @@ -module.exports = (sequelize, DataTypes) => { +export default (sequelize, DataTypes) => { const Option = sequelize.define('Option', { id: { type: DataTypes.UUID, @@ -26,5 +26,9 @@ module.exports = (sequelize, DataTypes) => { timestamps: true, }); + Option.associate = function(models) { + Option.belongsTo(models.Question); + }; + return Option; }; \ No newline at end of file diff --git a/src/models/question.model.js b/src/models/question.model.js index 9d78355..1b7e7be 100644 --- a/src/models/question.model.js +++ b/src/models/question.model.js @@ -1,4 +1,4 @@ -module.exports = (sequelize, DataTypes) => { +export default (sequelize, DataTypes) => { const Question = sequelize.define('Question', { id: { type: DataTypes.UUID, diff --git a/src/models/registration.model.js b/src/models/registration.model.js index f064944..f443b44 100644 --- a/src/models/registration.model.js +++ b/src/models/registration.model.js @@ -1,6 +1,6 @@ -const crypto = require('crypto'); +import crypto from 'crypto'; -module.exports = (sequelize, DataTypes) => { +export default (sequelize, DataTypes) => { const Registration = sequelize.define('Registration', { id: { type: DataTypes.UUID, diff --git a/src/models/result.model.js b/src/models/result.model.js index f088ec8..5861127 100644 --- a/src/models/result.model.js +++ b/src/models/result.model.js @@ -1,4 +1,4 @@ -module.exports = (sequelize, DataTypes) => { +export default (sequelize, DataTypes) => { const Result = sequelize.define('Result', { id: { type: DataTypes.UUID, diff --git a/src/models/user.model.js b/src/models/user.model.js index ce32b6d..dd9712f 100644 --- a/src/models/user.model.js +++ b/src/models/user.model.js @@ -1,4 +1,4 @@ -module.exports = (sequelize, DataTypes) => { +export default (sequelize, DataTypes) => { const User = sequelize.define('User', { id: { type: DataTypes.UUID, diff --git a/src/routes/auth.routes.js b/src/routes/auth.routes.js index 6ec4161..3068774 100644 --- a/src/routes/auth.routes.js +++ b/src/routes/auth.routes.js @@ -1,12 +1,13 @@ -const express = require('express'); -const router = express.Router(); -const { check } = require('express-validator'); -const { +import express from 'express'; +import { check } from 'express-validator'; +import { registerUser, loginUser, getUserProfile, -} = require('../controllers/auth.controller'); -const { protect } = require('../middleware/auth.middleware'); +} from '../controllers/auth.controller.js'; +import { protect } from '../middleware/auth.middleware.js'; + +const router = express.Router(); // Register user router.post( @@ -32,4 +33,4 @@ router.post( // Get user profile router.get('/profile', protect, getUserProfile); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/src/routes/exam.routes.js b/src/routes/exam.routes.js index 2c203dc..abf0f02 100644 --- a/src/routes/exam.routes.js +++ b/src/routes/exam.routes.js @@ -1,8 +1,9 @@ -const express = require('express'); +import express from 'express'; +import { check } from 'express-validator'; +import * as examController from '../controllers/exam.controller.js'; +import { protect, admin } from '../middleware/auth.middleware.js'; + const router = express.Router(); -const { check } = require('express-validator'); -const examController = require('../controllers/exam.controller'); -const { protect, admin } = require('../middleware/auth.middleware'); // @route GET /api/exams // @desc Get all exams @@ -46,4 +47,4 @@ router.put('/:id', [protect, admin], examController.updateExam); // @access Private/Admin router.delete('/:id', [protect, admin], examController.deleteExam); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/src/routes/examBanner.routes.js b/src/routes/examBanner.routes.js index 568fb0e..e899492 100644 --- a/src/routes/examBanner.routes.js +++ b/src/routes/examBanner.routes.js @@ -1,8 +1,9 @@ -const express = require('express'); +import express from 'express'; +import { check } from 'express-validator'; +import * as examBannerController from '../controllers/examBanner.controller.js'; +import { protect, admin } from '../middleware/auth.middleware.js'; + const router = express.Router(); -const { check } = require('express-validator'); -const examBannerController = require('../controllers/examBanner.controller'); -const { protect, admin } = require('../middleware/auth.middleware'); // @route GET /api/exam-banners // @desc Get all exam banners @@ -37,4 +38,4 @@ router.put('/:id', [protect, admin], examBannerController.updateExamBanner); // @access Private/Admin router.delete('/:id', [protect, admin], examBannerController.deleteExamBanner); -module.exports = router; +export default router; diff --git a/src/routes/examRecording.routes.js b/src/routes/examRecording.routes.js index 7f4986f..67e3744 100644 --- a/src/routes/examRecording.routes.js +++ b/src/routes/examRecording.routes.js @@ -1,8 +1,9 @@ -const express = require('express'); +import express from 'express'; +import { check } from 'express-validator'; +import * as examRecordingController from '../controllers/examRecording.controller.js'; +import { protect, admin } from '../middleware/auth.middleware.js'; + const router = express.Router(); -const { check } = require('express-validator'); -const examRecordingController = require('../controllers/examRecording.controller'); -const { protect, admin } = require('../middleware/auth.middleware'); // @route GET /api/exam-recordings // @desc Get all exam recordings @@ -42,4 +43,4 @@ router.patch('/:id/publish', [protect, admin], examRecordingController.togglePub // @access Private/Admin router.delete('/:id', [protect, admin], examRecordingController.deleteExamRecording); -module.exports = router; +export default router; diff --git a/src/routes/indexer.routes.js b/src/routes/indexer.routes.js index 504d10b..4229b83 100644 --- a/src/routes/indexer.routes.js +++ b/src/routes/indexer.routes.js @@ -1,14 +1,15 @@ -const express = require('express'); -const router = express.Router(); -const { +import express from 'express'; +import { getIndexerStatus, triggerScan, getContractEvents, getIndexedExams, getIndexedRegistrations, getIndexedResults -} = require('../controllers/indexer.controller'); -const { protect, admin } = require('../middleware/auth.middleware'); +} from '../controllers/indexer.controller.js'; +import { protect, admin } from '../middleware/auth.middleware.js'; + +const router = express.Router(); // Route for everyone who is authenticated router.get('/status', protect, getIndexerStatus); @@ -20,4 +21,4 @@ router.get('/exams', protect, admin, getIndexedExams); router.get('/registrations', protect, admin, getIndexedRegistrations); router.get('/results', protect, admin, getIndexedResults); -module.exports = router; +export default router; diff --git a/src/routes/notification.routes.js b/src/routes/notification.routes.js index a84fdaf..564160e 100644 --- a/src/routes/notification.routes.js +++ b/src/routes/notification.routes.js @@ -1,8 +1,9 @@ -const express = require('express'); +import express from 'express'; +import { check } from 'express-validator'; +import * as notificationController from '../controllers/notification.controller.js'; +import { protect, admin } from '../middleware/auth.middleware.js'; + const router = express.Router(); -const { check } = require('express-validator'); -const notificationController = require('../controllers/notification.controller'); -const { protect, admin } = require('../middleware/auth.middleware'); // @route GET /api/notifications // @desc Get all notifications for the current user @@ -42,4 +43,4 @@ router.patch('/:id/read', protect, notificationController.markAsRead); // @access Private/Admin router.delete('/:id', [protect, admin], notificationController.deleteNotification); -module.exports = router; +export default router; diff --git a/src/routes/registration.routes.js b/src/routes/registration.routes.js index 05a8537..9e0254e 100644 --- a/src/routes/registration.routes.js +++ b/src/routes/registration.routes.js @@ -1,8 +1,9 @@ -const express = require('express'); +import express from 'express'; +import { check } from 'express-validator'; +import * as registrationController from '../controllers/registration.controller.js'; +import { protect } from '../middleware/auth.middleware.js'; + const router = express.Router(); -const { check } = require('express-validator'); -const registrationController = require('../controllers/registration.controller'); -const { protect } = require('../middleware/auth.middleware'); // @route POST /api/registrations // @desc Register for an exam @@ -32,4 +33,4 @@ router.post('/validate', [ check('examCode', 'Exam code is required').not().isEmpty() ], registrationController.validateExamCode); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/src/routes/result.routes.js b/src/routes/result.routes.js index 565158b..94fac6d 100644 --- a/src/routes/result.routes.js +++ b/src/routes/result.routes.js @@ -1,7 +1,8 @@ -const express = require('express'); +import express from 'express'; +import * as resultController from '../controllers/result.controller.js'; +import { protect } from '../middleware/auth.middleware.js'; + const router = express.Router(); -const resultController = require('../controllers/result.controller'); -const { protect } = require('../middleware/auth.middleware'); // @route POST /api/results // @desc Submit exam and create result @@ -23,4 +24,4 @@ router.get('/:id', protect, resultController.getResultById); // @access Private router.get('/certificate/:id', protect, resultController.generateCertificate); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 72d102d..9092a4a 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -1,8 +1,9 @@ -const express = require('express'); +import express from 'express'; +import { check } from 'express-validator'; +import * as userController from '../controllers/user.controller.js'; +import { protect, admin } from '../middleware/auth.middleware.js'; + const router = express.Router(); -const { check } = require('express-validator'); -const userController = require('../controllers/user.controller'); -const { protect, admin } = require('../middleware/auth.middleware'); // @route GET /api/users/profile // @desc Get user profile @@ -24,4 +25,4 @@ router.get('/wallet/:address', userController.getUserByWallet); // @access Private/Admin router.get('/', [protect, admin], userController.getUsers); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/src/server.js b/src/server.js index 216b459..f27d6dd 100644 --- a/src/server.js +++ b/src/server.js @@ -1,15 +1,26 @@ -const express = require('express'); -const helmet = require('helmet'); -const cors = require('cors'); -const rateLimit = require('express-rate-limit'); -const compression = require('compression'); -const morgan = require('morgan'); -const logger = require('./utils/logger'); -const { connectDB } = require('./config/db'); -const { syncDatabase } = require('./models'); -const swaggerDocs = require('./docs/swagger'); -// const { startIndexer } = require('./indexer/start'); -require('dotenv').config(); +import express from 'express'; +import helmet from 'helmet'; +import cors from 'cors'; +import rateLimit from 'express-rate-limit'; +import compression from 'compression'; +import morgan from 'morgan'; +import logger from './utils/logger.js'; +import { connectDB } from './config/db.js'; +import { syncDatabase } from './models/index.js'; +import swaggerDocs from './docs/swagger.js'; +import 'dotenv/config'; +import { notFound, errorHandler } from './middleware/error.middleware.js'; + +// Import routes +import examRoutes from './routes/exam.routes.js'; +import userRoutes from './routes/user.routes.js'; +import authRoutes from './routes/auth.routes.js'; +import registrationRoutes from './routes/registration.routes.js'; +import resultRoutes from './routes/result.routes.js'; +import notificationRoutes from './routes/notification.routes.js'; +import examBannerRoutes from './routes/examBanner.routes.js'; +import examRecordingRoutes from './routes/examRecording.routes.js'; +import indexerRoutes from './routes/indexer.routes.js'; // Connect to database and sync models const startServer = async () => { @@ -69,15 +80,15 @@ const startServer = async () => { // Routes logger.info('Setting up API routes...'); try { - app.use('/api/exams', require('./routes/exam.routes')); - app.use('/api/users', require('./routes/user.routes')); - app.use('/api/auth', require('./routes/auth.routes')); - app.use('/api/registrations', require('./routes/registration.routes')); - app.use('/api/results', require('./routes/result.routes')); - app.use('/api/notifications', require('./routes/notification.routes')); - app.use('/api/exam-banners', require('./routes/examBanner.routes')); - app.use('/api/exam-recordings', require('./routes/examRecording.routes')); - app.use('/api/indexer', require('./routes/indexer.routes')); // Added indexer routes + app.use('/api/exams', examRoutes); + app.use('/api/users', userRoutes); + app.use('/api/auth', authRoutes); + app.use('/api/registrations', registrationRoutes); + app.use('/api/results', resultRoutes); + app.use('/api/notifications', notificationRoutes); + app.use('/api/exam-banners', examBannerRoutes); + app.use('/api/exam-recordings', examRecordingRoutes); + app.use('/api/indexer', indexerRoutes); // Added indexer routes logger.info('API routes set up successfully'); } catch (routeError) { logger.error(`Route setup failed: ${routeError.message}`); @@ -85,17 +96,8 @@ const startServer = async () => { } // Error handling middleware - app.use((err, req, res, next) => { - logger.error(`${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`); - - const statusCode = res.statusCode ? res.statusCode : 500; - res.status(statusCode); - - res.json({ - message: err.message, - stack: process.env.NODE_ENV === 'production' ? null : err.stack, - }); - }); + app.use(notFound); + app.use(errorHandler); const PORT = process.env.PORT || 5001; diff --git a/src/utils/logger.js b/src/utils/logger.js index 475c850..cdf4549 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -1,5 +1,5 @@ -const winston = require('winston'); -const path = require('path'); +import winston from 'winston'; +import path from 'path'; // Define the custom format with color const logFormat = winston.format.combine( @@ -49,4 +49,4 @@ const logger = winston.createLogger({ // Ensure logs directory exists (this can be done with fs if needed) -module.exports = logger; +export default logger; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3643c7c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "esModuleInterop": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + "dist" + ] +} \ No newline at end of file