{"schema_version":"2","scorer_version":"0.1.0","scoring_version":"prov-5+qual-2+sec-2","generated_at":"2026-11-14T14:52:01.657774Z","repo":{"ref":{"host":"github.com","owner":"RaihanKaskandar","name":"bubbleseerr"},"default_branch":"main","clone_url":"https://github.com/RaihanKaskandar/bubbleseerr.git","html_url":"https://github.com/RaihanKaskandar/bubbleseerr","description":"A Seerr-style comic request app for Bubblarr","stars":0,"forks":0,"is_fork":false,"is_archived":false,"is_private":false,"size_kb":4337,"primary_language":"TypeScript","license_spdx":"NOASSERTION","created_at":"2026-10-23T10:25:42Z","pushed_at":"2026-11-10T11:51:16Z","security_policy_url":null,"distribution":{"owner_created_at":"2020-12-25T16:12:57Z","owner_type":"User","release_assets":[]}},"commit_sha":"97233694072ca88f8b7462e67d3330331a967535","profile":{"lines_by_language":{"TypeScript":12397,"JavaScript":98},"files_by_category":{"source":139,"test":16,"binary":14,"config":7,"other":4,"docs":2,"oversized":2,"generated":1},"source_lines":10871,"test_lines":1624},"pillars":{"ai_provenance":{"score":0.0,"confidence":0.42,"coverage":1.0,"label":"none-detected","breakdown":{"attributed_share":0.0,"suspected_share":0.0,"estimated_share":0.0,"ai_artifacts":0.0,"flagged_work_units":0.0,"flagged_releases":null,"style_index":0.0},"summary":"No AI attribution or AI-consistent velocity signals detected."},"quality":{"score":75.1,"confidence":0.9,"coverage":1.0,"label":"adequate","breakdown":{"complexity":55.6,"duplication":87.8,"lint":91.2,"testing":72.5,"docs":65.0,"type_safety":100.0,"process":null},"summary":"Code quality is adequate: test-to-source ratio 0.15, CI runs tests; 20% of function code in complex or long functions; 2% duplicated lines. Weakest area: complexity."},"security":{"score":69.1,"confidence":0.85,"coverage":1.0,"label":"weak","breakdown":{"secrets":96.7,"sast":99.0,"dependencies":55.8,"ci_actions":46.8,"posture":0.0,"scorecard":null},"summary":"Security posture is weak: 11 known vulnerabilities in 6 dependencies; 3 high-severity workflow issues. Weakest area: posture."}},"analyzer_results":[{"analyzer":"sec-scorecard","pillar":"security","status":"skipped","tool_version":null,"metrics":{},"findings":[],"evidence":[],"data":{},"error":"no published OpenSSF Scorecard result for this repo"},{"analyzer":"sec-registry","pillar":"security","status":"ok","tool_version":null,"metrics":{"declared":27.0,"checked":27.0,"missing":0.0,"recent":0.0,"lookup_errors":0.0},"findings":[],"evidence":[],"data":{"phantom":false},"error":null},{"analyzer":"ai-attribution","pillar":"ai_provenance","status":"ok","tool_version":"signatures-v1","metrics":{"commits":6.0,"attributed_commits":0.0,"attributed_commit_share":0.0,"effective_loc":12640.0,"attributed_loc":0.0,"attributed_loc_share":0.0,"tools_detected":0.0},"findings":[],"evidence":[],"data":{"attributed_loc_by_commit":{},"tools":{},"monthly":{"2026-10":{"loc":11101,"attributed_loc":0},"2026-11":{"loc":1539,"attributed_loc":0}}},"error":null},{"analyzer":"ai-artifacts","pillar":"ai_provenance","status":"ok","tool_version":"signatures-v1","metrics":{"artifacts":0.0,"workflow_agents":0.0,"tools_configured":0.0},"findings":[],"evidence":[],"data":{"artifacts":[],"workflows":[],"tools":[]},"error":null},{"analyzer":"ai-velocity","pillar":"ai_provenance","status":"ok","tool_version":"prov-4","metrics":{"work_units":3.0,"flagged_units":0.0,"pre_ai_era_bursts":0.0,"pre_ai_era_measured_loc":0.0,"pre_ai_era_burst_loc":0.0,"measured_loc":2367.0,"flagged_loc":0.0,"flagged_loc_share":0.0,"max_loc_per_hour":283.25,"median_loc_per_hour":189.29319870451516,"pr_timed_units":0.0,"excluded_import_loc":10273.0},"findings":[],"evidence":[],"data":{"flagged_loc_by_commit":{},"excluded_import_commits":["e85072398d6319d9620357368342910fb93b7f34"]},"error":null},{"analyzer":"ai-release-velocity","pillar":"ai_provenance","status":"skipped","tool_version":null,"metrics":{},"findings":[],"evidence":[],"data":{},"error":null},{"analyzer":"ai-stylometry","pillar":"ai_provenance","status":"ok","tool_version":null,"metrics":{"lines_scanned":10871.0,"index":0.0,"count:narrated_steps":0.0,"per_kloc:narrated_steps":0.0,"files:narrated_steps":0.0,"count:placeholder":0.0,"per_kloc:placeholder":0.0,"files:placeholder":0.0,"count:emoji_in_code":0.0,"per_kloc:emoji_in_code":0.0,"files:emoji_in_code":0.0,"count:assistant_voice":0.0,"per_kloc:assistant_voice":0.0,"files:assistant_voice":0.0},"findings":[],"evidence":[],"data":{},"error":null},{"analyzer":"sec-secrets","pillar":"security","status":"ok","tool_version":"gitleaks 8.30.1","metrics":{"secrets_current":0.0,"secrets_history":0.0,"secrets_nonprod":1.0,"secrets_low_confidence":0.0},"findings":[{"rule_id":"gitleaks/generic-api-key","severity":"low","message":"Possible generic-api-key secret in the current tree (test/docs/example file)","path":"tests/unit/fixtures/cbr-absolute-batman-2024.html","line":129,"fingerprint":"tests/unit/fixtures/cbr-absolute-batman-2024.html:generic-api-key:129"}],"evidence":[],"data":{},"error":null},{"analyzer":"sec-sast","pillar":"security","status":"ok","tool_version":"opengrep 1.33.1; rules f1d2b562b414","metrics":{"findings":2.0,"findings_high":0.0,"findings_medium":1.0,"findings_low":1.0,"weighted_findings":0.312,"scanned_lines":10871.0,"parse_errors":0.0},"findings":[{"rule_id":"opengrep/javascript.lang.security.audit.detect-non-literal-regexp","severity":"medium","message":"RegExp() called with a `label` function argument, this might allow an attacker to cause a Regular Expression Denial-of-Service (ReDoS) within your application as RegExP blocks the main thread. For this reason, it is recommended to use hardcoded regexes instead. If your regex is run on user-controlle [confidence low]","path":"src/server/ratings.ts","line":97,"fingerprint":"javascript.lang.security.audit.detect-non-literal-regexp:src/server/ratings.ts:97"},{"rule_id":"opengrep/javascript.lang.security.audit.unsafe-formatstring","severity":"low","message":"Detected string concatenation with a non-literal variable in a util.format / console.log function. If an attacker injects a format specifier in the string, it will forge the log message. Try to use constant values for the format string. [confidence low]","path":"src/server/stored-cache.ts","line":78,"fingerprint":"javascript.lang.security.audit.unsafe-formatstring:src/server/stored-cache.ts:78"}],"evidence":[],"data":{"rulesets":["typescript","javascript","dockerfile"]},"error":null},{"analyzer":"sec-deps","pillar":"security","status":"ok","tool_version":"osv-scanner version: 2.7.1","metrics":{"vulnerable_packages":6.0,"vulnerabilities":11.0,"vulns_critical":2.0,"vulns_high":2.0,"vulns_medium":7.0,"vulns_low":0.0,"vulns_unknown":0.0,"penalty":35.0},"findings":[{"rule_id":"osv/GHSA-5gmw-xhrv-c9v3","severity":"critical","message":"tinypool@1.1.1 (npm) is affected by GHSA-5gmw-xhrv-c9v3","path":"package-lock.json","line":null,"fingerprint":"tinypool@1.1.1 (npm):GHSA-5gmw-xhrv-c9v3"},{"rule_id":"osv/GHSA-85c8-ppgw-ccpr","severity":"critical","message":"tinypool@1.1.1 (npm) is affected by GHSA-85c8-ppgw-ccpr","path":"package-lock.json","line":null,"fingerprint":"tinypool@1.1.1 (npm):GHSA-85c8-ppgw-ccpr"},{"rule_id":"osv/GHSA-6g55-p6wh-862q","severity":"high","message":"postcss@8.4.31 (npm) is affected by GHSA-6g55-p6wh-862q","path":"package-lock.json","line":null,"fingerprint":"postcss@8.4.31 (npm):GHSA-6g55-p6wh-862q"},{"rule_id":"osv/GHSA-r28c-9q8g-f849","severity":"high","message":"postcss@8.4.31 (npm) is affected by GHSA-r28c-9q8g-f849","path":"package-lock.json","line":null,"fingerprint":"postcss@8.4.31 (npm):GHSA-r28c-9q8g-f849"},{"rule_id":"osv/GHSA-82fw-gwwq-j7x9","severity":"medium","message":"@vitest/mocker@3.2.7 (npm) is affected by GHSA-82fw-gwwq-j7x9","path":"package-lock.json","line":null,"fingerprint":"@vitest/mocker@3.2.7 (npm):GHSA-82fw-gwwq-j7x9"},{"rule_id":"osv/GHSA-qxg6-89mg-rqv8","severity":"medium","message":"@vitest/mocker@3.2.7 (npm) is affected by GHSA-qxg6-89mg-rqv8","path":"package-lock.json","line":null,"fingerprint":"@vitest/mocker@3.2.7 (npm):GHSA-qxg6-89mg-rqv8"},{"rule_id":"osv/GHSA-fxqj-rqcc-2cmp","severity":"medium","message":"postcss@8.4.31 (npm) is affected by GHSA-fxqj-rqcc-2cmp","path":"package-lock.json","line":null,"fingerprint":"postcss@8.4.31 (npm):GHSA-fxqj-rqcc-2cmp"},{"rule_id":"osv/GHSA-qx2v-qp2m-jg93","severity":"medium","message":"postcss@8.4.31 (npm) is affected by GHSA-qx2v-qp2m-jg93","path":"package-lock.json","line":null,"fingerprint":"postcss@8.4.31 (npm):GHSA-qx2v-qp2m-jg93"},{"rule_id":"osv/GHSA-rj75-hqrm-r3gf","severity":"medium","message":"postcss-selector-parser@6.0.10 (npm) is affected by GHSA-rj75-hqrm-r3gf","path":"package-lock.json","line":null,"fingerprint":"postcss-selector-parser@6.0.10 (npm):GHSA-rj75-hqrm-r3gf"},{"rule_id":"osv/GHSA-rj75-hqrm-r3gf","severity":"medium","message":"postcss-selector-parser@6.1.4 (npm) is affected by GHSA-rj75-hqrm-r3gf","path":"package-lock.json","line":null,"fingerprint":"postcss-selector-parser@6.1.4 (npm):GHSA-rj75-hqrm-r3gf"},{"rule_id":"osv/GHSA-82fw-gwwq-j7x9","severity":"medium","message":"vitest@3.2.7 (npm) is affected by GHSA-82fw-gwwq-j7x9","path":"package-lock.json","line":null,"fingerprint":"vitest@3.2.7 (npm):GHSA-82fw-gwwq-j7x9"}],"evidence":[],"data":{"ecosystems":["npm"]},"error":null},{"analyzer":"sec-actions","pillar":"security","status":"ok","tool_version":"zizmor 1.32.0","metrics":{"workflows":1.0,"findings":8.0,"findings_high":3.0,"findings_medium":5.0,"findings_low":0.0,"penalty":45.6},"findings":[{"rule_id":"zizmor/unpinned-uses","severity":"high","message":"unpinned action reference [confidence high]","path":".github/workflows/ci.yml","line":12,"fingerprint":"unpinned-uses:.github/workflows/ci.yml:12"},{"rule_id":"zizmor/unpinned-uses","severity":"high","message":"unpinned action reference [confidence high]","path":".github/workflows/ci.yml","line":13,"fingerprint":"unpinned-uses:.github/workflows/ci.yml:13"},{"rule_id":"zizmor/unpinned-uses","severity":"high","message":"unpinned action reference [confidence high]","path":".github/workflows/ci.yml","line":28,"fingerprint":"unpinned-uses:.github/workflows/ci.yml:28"},{"rule_id":"zizmor/artipacked","severity":"medium","message":"credential persistence through GitHub Actions artifacts [confidence low]","path":".github/workflows/ci.yml","line":12,"fingerprint":"artipacked:.github/workflows/ci.yml:12"},{"rule_id":"zizmor/artipacked","severity":"medium","message":"credential persistence through GitHub Actions artifacts [confidence low]","path":".github/workflows/ci.yml","line":28,"fingerprint":"artipacked:.github/workflows/ci.yml:28"},{"rule_id":"zizmor/excessive-permissions","severity":"medium","message":"overly broad permissions [confidence medium]","path":".github/workflows/ci.yml","line":1,"fingerprint":"excessive-permissions:.github/workflows/ci.yml:1"},{"rule_id":"zizmor/excessive-permissions","severity":"medium","message":"overly broad permissions [confidence medium]","path":".github/workflows/ci.yml","line":9,"fingerprint":"excessive-permissions:.github/workflows/ci.yml:9"},{"rule_id":"zizmor/excessive-permissions","severity":"medium","message":"overly broad permissions [confidence medium]","path":".github/workflows/ci.yml","line":25,"fingerprint":"excessive-permissions:.github/workflows/ci.yml:25"}],"evidence":[],"data":{},"error":null},{"analyzer":"sec-posture","pillar":"security","status":"ok","tool_version":null,"metrics":{"check:security_policy":0.0,"check:dependency_updates":0.0,"check:signed_commits":0.0,"check:lockfiles":1.0,"signed_commit_share":0.0,"unpinned_manifests":0.0},"findings":[],"evidence":[],"data":{},"error":null},{"analyzer":"sec-distribution","pillar":"security","status":"ok","tool_version":null,"metrics":{"signals":0.0,"binary_assets":0.0,"suspicious":0.0},"findings":[],"evidence":[],"data":{"signals":[],"suspicious":false,"unusual_traction":false,"metadata":true},"error":null},{"analyzer":"qual-complexity","pillar":"quality","status":"ok","tool_version":"lizard 1.25.1","metrics":{"functions":731.0,"function_nloc":7284.0,"mean_ccn":2.75,"p90_ccn":5.0,"complex_functions":13.0,"long_functions":5.0,"many_param_functions":0.0,"risky_nloc_share":0.1954},"findings":[],"evidence":[{"kind":"complex-function","description":"Profile in src/components/Profile.tsx: complexity 38, 153 lines","ref":"src/components/Profile.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/Profile.tsx#L25","weight":0.6333333333333333},{"kind":"complex-function","description":"ComicDetails in src/components/ComicDetails.tsx: complexity 35, 196 lines","ref":"src/components/ComicDetails.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/ComicDetails.tsx#L27","weight":0.5833333333333334},{"kind":"complex-function","description":"updateUser in src/server/users.ts: complexity 30, 68 lines","ref":"src/server/users.ts","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/server/users.ts#L144","weight":0.5},{"kind":"complex-function","description":"UserList in src/components/UserList.tsx: complexity 26, 107 lines","ref":"src/components/UserList.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/UserList.tsx#L51","weight":0.43333333333333335},{"kind":"complex-function","description":"ReadingSync in src/components/ReadingSync.tsx: complexity 22, 76 lines","ref":"src/components/ReadingSync.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/ReadingSync.tsx#L14","weight":0.36666666666666664},{"kind":"complex-function","description":"SearchResults in src/components/SearchResults.tsx: complexity 21, 134 lines","ref":"src/components/SearchResults.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/SearchResults.tsx#L47","weight":0.35},{"kind":"complex-function","description":"call in src/server/bubblarr.ts: complexity 21, 58 lines","ref":"src/server/bubblarr.ts","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/server/bubblarr.ts#L168","weight":0.35},{"kind":"complex-function","description":"authenticate in src/server/users.ts: complexity 19, 59 lines","ref":"src/server/users.ts","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/server/users.ts#L270","weight":0.31666666666666665},{"kind":"complex-function","description":"EntryRow in src/components/Calendar.tsx: complexity 18, 98 lines","ref":"src/components/Calendar.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/Calendar.tsx#L58","weight":0.3},{"kind":"complex-function","description":"TitleCard in src/components/TitleCard.tsx: complexity 18, 76 lines","ref":"src/components/TitleCard.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/TitleCard.tsx#L39","weight":0.3}],"data":{},"error":null},{"analyzer":"qual-duplication","pillar":"quality","status":"ok","tool_version":"repotrust-dup-w6","metrics":{"compared_lines":7754.0,"duplicated_lines":168.0,"duplicated_share":0.0217,"files":139.0},"findings":[],"evidence":[{"kind":"duplicate-code","description":"6+ duplicated lines: src/app/api/v1/users/[id]/route.ts:29 and src/app/api/v1/users/route.ts:11","ref":"src/app/api/v1/users/[id]/route.ts","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/app/api/v1/users/[id]/route.ts#L29","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: src/components/Library.tsx:101 and src/components/PublisherPage.tsx:31","ref":"src/components/Library.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/Library.tsx#L101","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: src/components/Profile.tsx:108 and src/components/Profile.tsx:122","ref":"src/components/Profile.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/Profile.tsx#L108","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: src/components/Profile.tsx:231 and src/components/ReadingSync.tsx:53","ref":"src/components/Profile.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/Profile.tsx#L231","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: src/components/Settings/BubblarrForm.tsx:67 and src/components/Settings/KavitaForm.tsx:49","ref":"src/components/Settings/BubblarrForm.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/Settings/BubblarrForm.tsx#L67","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: src/components/Settings/KavitaForm.tsx:38 and src/components/Settings/Pages.tsx:222","ref":"src/components/Settings/KavitaForm.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/Settings/KavitaForm.tsx#L38","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: src/components/Settings/Pages.tsx:55 and src/components/Settings/Pages.tsx:134","ref":"src/components/Settings/Pages.tsx","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/Settings/Pages.tsx#L55","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: src/components/Settings/useSettings.ts:10 and src/components/SetupWizard.tsx:53","ref":"src/components/Settings/useSettings.ts","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/components/Settings/useSettings.ts#L10","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: src/server/cards.ts:63 and src/server/discover.ts:35","ref":"src/server/cards.ts","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/server/cards.ts#L63","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: src/server/discover.ts:34 and src/server/discover.ts:146","ref":"src/server/discover.ts","url":"https://github.com/RaihanKaskandar/bubbleseerr/blob/97233694072ca88f8b7462e67d3330331a967535/src/server/discover.ts#L34","weight":0.3}],"data":{},"error":null},{"analyzer":"qual-lint","pillar":"quality","status":"ok","tool_version":"opengrep 1.33.1; rules f1d2b562b414","metrics":{"findings":4.0,"weighted_findings":4.0,"scanned_lines":10871.0,"category:best-practice":3.0,"category:correctness":1.0},"findings":[{"rule_id":"opengrep/typescript.react.best-practice.react-props-spreading","severity":"medium","message":"It's best practice to explicitly pass props to an HTML component rather than use the spread operator. The spread operator risks passing invalid HTML props to an HTML element, which can cause console warnings or worse, give malicious actors a way to inject unexpected attributes.","path":"src/components/Common/Button.tsx","line":73,"fingerprint":"typescript.react.best-practice.react-props-spreading:src/components/Common/Button.tsx:73"},{"rule_id":"opengrep/typescript.react.best-practice.react-props-spreading","severity":"medium","message":"It's best practice to explicitly pass props to an HTML component rather than use the spread operator. The spread operator risks passing invalid HTML props to an HTML element, which can cause console warnings or worse, give malicious actors a way to inject unexpected attributes.","path":"src/components/Common/Table.tsx","line":8,"fingerprint":"typescript.react.best-practice.react-props-spreading:src/components/Common/Table.tsx:8"},{"rule_id":"opengrep/typescript.react.best-practice.react-props-spreading","severity":"medium","message":"It's best practice to explicitly pass props to an HTML component rather than use the spread operator. The spread operator risks passing invalid HTML props to an HTML element, which can cause console warnings or worse, give malicious actors a way to inject unexpected attributes.","path":"src/components/Common/Table.tsx","line":14,"fingerprint":"typescript.react.best-practice.react-props-spreading:src/components/Common/Table.tsx:14"},{"rule_id":"opengrep/javascript.lang.correctness.no-stringify-keys","severity":"medium","message":"JSON stringify does not produce a stable key ordering, and should not be relied on for producing object keys. Consider using json-stable-stringify instead.","path":"src/server/bubblarr.ts","line":220,"fingerprint":"javascript.lang.correctness.no-stringify-keys:src/server/bubblarr.ts:220"}],"evidence":[],"data":{},"error":null},{"analyzer":"qual-testing","pillar":"quality","status":"ok","tool_version":null,"metrics":{"check:has_tests":1.0,"check:test_ratio":0.2988,"check:ci_present":1.0,"check:ci_runs_tests":1.0,"check:ci_runs_lint":0.0,"check:tests_cochange":1.0,"test_lines":1624.0,"source_lines":10871.0,"test_ratio":0.1494,"ci_files":1.0,"cochange_share":0.8333,"cochange_commits":6.0},"findings":[],"evidence":[{"kind":"ci","description":"CI config: .github/workflows/ci.yml","ref":".github/workflows/ci.yml","url":null,"weight":0.0},{"kind":"ci","description":"CI runs tests (npm test)","ref":null,"url":null,"weight":0.0}],"data":{"test_commands":["npm test"],"lint_commands":[]},"error":null},{"analyzer":"qual-docs","pillar":"quality","status":"ok","tool_version":null,"metrics":{"check:readme":1.0,"check:license":1.0,"check:contributing":0.0,"check:changelog":0.0,"readme_lines":70.0},"findings":[],"evidence":[{"kind":"docs","description":"README.md","ref":"README.md","url":null,"weight":0.0},{"kind":"docs","description":"LICENSE","ref":"LICENSE","url":null,"weight":0.0}],"data":{},"error":null},{"analyzer":"qual-type-safety","pillar":"quality","status":"ok","tool_version":null,"metrics":{"check:strict_config":1.0,"check:low_escape_density":1.0,"escapes":0.0,"escapes_per_kloc":0.0},"findings":[],"evidence":[{"kind":"typing","description":"TypeScript strict type checking: tsconfig.json","ref":"tsconfig.json","url":null,"weight":0.0}],"data":{},"error":null}],"flags":[]}