Public record
Software health reportschema 0.31.0 · metrics 2.5.0 · 2026-08-06 21:39 UTC

FriendsOfFlarum / seo

Perfect SEO for your Flarum forum

PHP · TypeScriptMIT★ 50 stars⑂ 25 forkssince Nov 2018View on GitHub ↗

FriendsOfFlarum/seo holds a health index of 83 out of 100, placing it in the Excellent band. It scores highest on Engineering Quality (90/100) and lowest on Community & Adoption (56/100). It was last updated today. A single contributor accounts for most of its recent work.

83
overall / 100
Excellent

Software health index

Metrics are grouped into weighted categories on one standardized 1–100 scale. Overall starts as their weighted mean, calibrated against the distribution of the public record so bands carry percentile meaning; when public evidence triggers the High-Risk Jurisdiction Policy, the rating is adjusted and receives an At Risk ceiling of 34.

83
Exceptional93-100The record's top tier (≈ top 5%); essentially all checked criteria met
Excellent80-92Strong across the board; minor gaps
Good65-79Healthy; gaps are limited and manageable
Moderate50-64Acceptable with notable gaps; review recommended
Weak35-49Material weaknesses across several areas
At Risk20-34Significant weaknesses; adoption warrants caution
Critical1-19Severe problems (abandoned, single-maintainer, no hygiene)
VitalityCommunity &AdoptionSustainability &GovernanceEngineeringQualitySecurityAI Readiness

Score profile

Each axis is a category. The shape matters more than the average — a healthy subject fills the whole shape, while a spike-and-crater profile means strength in one dimension is masking risk in another.

The weighted overall 70 is calibrated to 83 on the published index scale (record calibration 2026-08-02).

Ownership

Friends of FlarumOrganization
103 followers106 public repossince Sep 2018

This repository is backed by an organization — shared, accountable stewardship that can outlive any single maintainer.

Package ecosystems

RegistryPackageVersionDownloads / moVersionsLast publish
Packagistfof/seo4.0.0-beta.72,2014750 days ago

Metrics by category

Vitality

Is the project alive — is code being written and are releases shipping?

78Good · 21% of overall
How it's scored
36/36Push recency — last push 0 days ago
3.5/36Commit cadence — 5/52 weeks with commits
15/18Commit volume — 46 commits in the last year
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 16 issue activity found in the last 90 days -- score normalized to 10
Inputs used
commits_last_year46
human_commit_share0.98
days_since_last_push0
active_weeks_last_year5

Release discipline

100Exceptional
How it's scored
27/27Ships releases — 39 releases published
36/36Release recency — latest release 50 days ago
27/27Release cadence — a release every ~0.9 days
0/10OpenSSF Scorecard: Signed-Releases — no data
Inputs used
releases_count39
latest_release_tag4.0.0-beta.7
releases_from_tagsno
days_since_latest_release50
mean_days_between_releases0.9
Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.

Community & Adoption

Does the project have users, downloads, attention, and a welcoming setup for contributors?

56Moderate · 17% of overall
How it's scored
27.4/60Stars — 50 stars
11.5/25Forks — 25 forks
1.7/15Watchers — 3 watchers
Inputs used
forks25
stars50
watchers3
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
How it's scored
22.5/22.5README
22.5/22.5License — recognized license (MIT)
18/18CONTRIBUTING guide
0/13.5Code of conduct
0/7.2Issue template
6.3/6.3PR template
Inputs used
has_readmeyes
has_licenseyes
readme_badges3
has_contributingyes
has_issue_templateno
has_code_of_conductno
readme_badge_servicesshields.io
has_pull_request_templateyes
How it's scored
44.6/80Monthly downloads — 2,201 downloads/month across packagist
4/20Registry dependents — 3 packages depend on it
Inputs used
packagesfof/seo
dependents3
ecosystemspackagist
total_downloads3,785
monthly_downloads2,201

Sustainability & Governance

Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?

66Good · 23% of overall
How it's scored
9/54Bus factor — 1 contributor(s) cover half of all commits
6.6/22.5Commit distribution — top contributor authored 71% of commits
13.5/13.5Contributor breadth — 14 contributors
10/10OpenSSF Scorecard: Contributors — project has 13 contributing companies or organizations
Inputs used
bus_factor1
contributors_sampled14
top_contributor_share0.707
How it's scored
36.2/42Issue resolution — 86% of issues closed
22.4/30PR acceptance — 76/102 decided PRs merged
0/13Newcomer PR acceptance — no first-time contributor's PR decided in 30d
0/15OpenSSF Scorecard: Code-Review — Found 0/28 approved changesets -- score normalized to 0
Inputs used
merged_prs76
open_issues11
closed_issues68
prs_merged_7d0
prs_decided_7d0
prs_merged_30d0
prs_decided_30d0
issue_closed_ratio0.861
closed_unmerged_prs26
first_time_authors_30d0
first_time_prs_merged_30d0
first_time_prs_decided_30d0
Excluded from scoring (no data or not applicable): newcomer_pr_acceptance. Remaining weights renormalized.
How it's scored
30/30Ownership backing — organization-owned
0/20Verified domain
14.5/25Owner reach — 103 followers of FriendsOfFlarum
25/25Track record — 106 public repos, account ~7 yr old
Inputs used
followers103
owner_typeOrganization
is_verified
owner_loginFriendsOfFlarum
public_repos106
account_age_days2,880

Package maintenance

100Exceptional
How it's scored
25/25Published & resolvable — 1 package(s) on packagist
35/35Publish recency — latest publish 50 days ago
20/20Version history — 47 published versions
20/20Not deprecated — active, not deprecated or yanked
Inputs used
packagesfof/seo
ecosystemspackagist
any_deprecatedno
min_days_since_publish50

Engineering Quality

Are baseline engineering and documentation practices in place?

90Excellent · 19% of overall
How it's scored
24/24CI workflows — 3 workflow(s)
24/24Tests present
16/16Linter config — phpstan.neon
0/9.6Pre-commit hooks
0/6.4.editorconfig
20/20OpenSSF Scorecard: CI-Tests — 27 out of 27 merged PRs checked by a CI test -- score normalized to 10
Inputs used
has_ciyes
has_testsyes
has_editorconfigno
has_linter_configyes
has_precommit_configno

Documentation

100Exceptional
How it's scored
30/30README
25/25Documentation directory
15/15Documentation / homepage site — https://discuss.flarum.org/d/18316-flarum-seo
10/10Repository description
10/10Topics — 5 topics
10/10Wiki
Inputs used
topicsflarum, flarum-seo, flarum-forum, seo-tags, flagrow
has_wikiyes
homepagehttps://discuss.flarum.org/d/18316-flarum-seo
has_readmeyes
has_docs_diryes
has_descriptionyes

Security

Are visible security and supply-chain practices strong, without unresolved high-risk jurisdiction exposure?

62Moderate · 16% of overall
How it's scored
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
2.5/2.5CI-Tests — 27 out of 27 merged PRs checked by a CI test -- score normalized to 10
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/28 approved changesets -- score normalized to 0
2.5/2.5Contributors — project has 13 contributing companies or organizations
10/10Dangerous-Workflow — no dangerous workflow patterns detected
7.5/7.5Dependency-Update-Tool — update tool detected
0/5Fuzzing — project is not fuzzed
2.5/2.5License — license file detected
7.5/7.5Maintained — 30 commit(s) and 16 issue activity found in the last 90 days -- score normalized to 10
0/5Packaging — no data
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — SAST tool is not run on all commits -- score normalized to 0
5/5Security-Policy — security policy file detected
0/7.5Signed-Releases — no data
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
3/7.5Vulnerabilities — 6 existing vulnerabilities detected
Inputs used
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate5.2
Excluded from scoring (no data or not applicable): packaging, signed_releases. Remaining weights renormalized.

Dependency advisories

100Exceptional
How it's scored
35/35Direct dependencies free of known advisories — no direct dependency carries a known advisory
0/25Indirect dependencies free of known advisories — transitive set not separable from development and test dependencies in this scope
0/40No advisories left outstanding — no advisory carries a publication date
Inputs used
sourceosv
advisories9
affected_packages3
assessed_packages387
unassessed_packages13
affected_by_severityhigh 3
direct_affected_packages0
Excluded from scoring (no data or not applicable): Indirect dependencies free of known advisories, No advisories left outstanding. Remaining weights renormalized. Matched 387 resolved dependencies against OSV. 13 could not be assessed — no resolved version, an unsupported ecosystem, or beyond the reported package list. This repository publishes no package the index resolves, so the repository dependency graph was assessed instead. That graph mixes development and test pins with shipped dependencies, so only the declared runtime dependencies are scored; transitive findings are reported as context and excluded from the score. Reachability is not analyzed.

AI Readiness

How well is the repo equipped to be developed and maintained with AI coding agents? Carries a deliberately small weight (4%): agent tooling is a real maintenance signal, but a repository with none can still reach 100/100.

58Moderate · 4% of overall
How it's scored
0/45Agent instructions — no CLAUDE.md / AGENTS.md / editor rules
0/15Machine-readable docs (llms.txt)
40/40Legible commit history — 74 of 98 human commits state their intent (structured subject or explanatory body)
Inputs used
has_llms_txtno
legible_history_share0.755
agent_instruction_files
agent_instruction_max_bytes
How it's scored
0/18One-command bootstrap
22/22Automated tests
11/11Lint / format config — phpstan.neon
11/11Static type checking — js/tsconfig.json
10/10Reproducible environment — lockfile
0/10Demonstrated agent practice — no agent-authored commits among the last 100
8/8Automated maintenance — 2 of the last 100 commits are automated dependency updates
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Inputs used
has_nixno
has_testsyes
lockfilesyarn.lock
has_dockerfileno
typed_languageno
bootstrap_files
has_devcontainerno
has_linter_configyes
typecheck_configsjs/tsconfig.json
agent_commit_share0
toolchain_manifests
dependency_bot_commit_share0.02
How it's scored
27/45Type-checkable code — PHP with type-check config (js/tsconfig.json)
55/55Manageable file sizes — 0/110 source files over 60KB
Inputs used
primary_languagePHP
largest_source_bytes35,992
source_files_sampled110
oversized_source_files0

Key facts

50GitHub stars
14contributors
46commits, last 12 months
0days since last push
39releases
1bus factor
11open issues
npm, Packagistpackage ecosystems

Data collection warnings

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token

More detail

Star and fork history 0 ★ / 25 ⇿
0Stars
25Forks
25Releases

When each star and fork was added, collected from GitHub and bucketed by day. Cumulative growth sits directly above the daily additions it is made of, so the two read against each other: steady organic accretion looks nothing like an abrupt, short-lived burst. Where that difference is measurable, it is reported as growth authenticity.

05101520252512019-012022-092026-05
Major 0Minor 4Patch 17

Each point covers 7 days.

OpenSSF Scorecard 5.2 / 10
5.2aggregate

Independent, tool-agnostic security assessment from the open-source OpenSSF Scorecard. Each check rewards a security practice, not a specific vendor's tool. Checks Scorecard could not determine are marked n/a and excluded from the security score (never counted as zero).Scorecard v5.5.0 · 2026-08-06 21:38 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
10CI-Tests27 out of 27 merged PRs checked by a CI test -- score normalized to 10
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/28 approved changesets -- score normalized to 0
10Contributorsproject has 13 contributing companies or organizations
10Dangerous-Workflowno dangerous workflow patterns detected
10Dependency-Update-Toolupdate tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained30 commit(s) and 16 issue activity found in the last 90 days -- score normalized to 10
n/aPackagingpackaging workflow not detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTSAST tool is not run on all commits -- score normalized to 0
10Security-Policysecurity policy file detected
n/aSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
4Vulnerabilities6 existing vulnerabilities detected
Direct dependencies 2
RegistryPackageVersion constraintManifest
Packagistflarum/core^2.0.0composer.json
Packagistflarum/tags*composer.json
All dependencies 400

Full resolved dependency set from the GitHub dependency graph: 2 direct and 398 indirect (transitive) packages. The transitive closure is complete when the repository commits a lockfile.

RegistryPackageVersionRelation
Packagistflarum/coredirect
Packagistflarum/tagsdirect
npm@babel/code-frame7.29.7indirect
npm@babel/compat-data7.29.7indirect
npm@babel/core7.29.7indirect
npm@babel/generator7.29.7indirect
npm@babel/helper-annotate-as-pure7.29.7indirect
npm@babel/helper-compilation-targets7.29.7indirect
npm@babel/helper-create-class-features-plugin7.29.7indirect
npm@babel/helper-create-regexp-features-plugin7.29.7indirect
npm@babel/helper-define-polyfill-provider0.6.8indirect
npm@babel/helper-globals7.29.7indirect
npm@babel/helper-member-expression-to-functions7.29.7indirect
npm@babel/helper-module-imports7.29.7indirect
npm@babel/helper-module-transforms7.29.7indirect
npm@babel/helper-optimise-call-expression7.29.7indirect
npm@babel/helper-plugin-utils7.29.7indirect
npm@babel/helper-remap-async-to-generator7.29.7indirect
npm@babel/helper-replace-supers7.29.7indirect
npm@babel/helper-skip-transparent-expression-wrappers7.29.7indirect
npm@babel/helper-string-parser7.29.7indirect
npm@babel/helper-validator-identifier7.29.7indirect
npm@babel/helper-validator-option7.29.7indirect
npm@babel/helper-wrap-function7.29.7indirect
npm@babel/helpers7.29.7indirect
npm@babel/parser7.29.7indirect
npm@babel/plugin-bugfix-firefox-class-in-computed-class-key7.29.7indirect
npm@babel/plugin-bugfix-safari-class-field-initializer-scope7.29.7indirect
npm@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression7.29.7indirect
npm@babel/plugin-bugfix-safari-rest-destructuring-rhs-array7.29.7indirect
npm@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining7.29.7indirect
npm@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly7.29.7indirect
npm@babel/plugin-proposal-private-property-in-object7.21.0-placeholder-for-preset-env.2indirect
npm@babel/plugin-syntax-import-assertions7.29.7indirect
npm@babel/plugin-syntax-import-attributes7.29.7indirect
npm@babel/plugin-syntax-jsx7.29.7indirect
npm@babel/plugin-syntax-typescript7.29.7indirect
npm@babel/plugin-syntax-unicode-sets-regex7.18.6indirect
npm@babel/plugin-transform-arrow-functions7.29.7indirect
npm@babel/plugin-transform-async-generator-functions7.29.7indirect
npm@babel/plugin-transform-async-to-generator7.29.7indirect
npm@babel/plugin-transform-block-scoped-functions7.29.7indirect
npm@babel/plugin-transform-block-scoping7.29.7indirect
npm@babel/plugin-transform-class-properties7.29.7indirect
npm@babel/plugin-transform-class-static-block7.29.7indirect
npm@babel/plugin-transform-classes7.29.7indirect
npm@babel/plugin-transform-computed-properties7.29.7indirect
npm@babel/plugin-transform-destructuring7.29.7indirect
npm@babel/plugin-transform-dotall-regex7.29.7indirect
npm@babel/plugin-transform-duplicate-keys7.29.7indirect
npm@babel/plugin-transform-duplicate-named-capturing-groups-regex7.29.7indirect
npm@babel/plugin-transform-dynamic-import7.29.7indirect
npm@babel/plugin-transform-explicit-resource-management7.29.7indirect
npm@babel/plugin-transform-exponentiation-operator7.29.7indirect
npm@babel/plugin-transform-export-namespace-from7.29.7indirect
npm@babel/plugin-transform-for-of7.29.7indirect
npm@babel/plugin-transform-function-name7.29.7indirect
npm@babel/plugin-transform-json-strings7.29.7indirect
npm@babel/plugin-transform-literals7.29.7indirect
npm@babel/plugin-transform-logical-assignment-operators7.29.7indirect
npm@babel/plugin-transform-member-expression-literals7.29.7indirect
npm@babel/plugin-transform-modules-amd7.29.7indirect
npm@babel/plugin-transform-modules-commonjs7.29.7indirect
npm@babel/plugin-transform-modules-systemjs7.29.7indirect
npm@babel/plugin-transform-modules-umd7.29.7indirect
npm@babel/plugin-transform-named-capturing-groups-regex7.29.7indirect
npm@babel/plugin-transform-new-target7.29.7indirect
npm@babel/plugin-transform-nullish-coalescing-operator7.29.7indirect
npm@babel/plugin-transform-numeric-separator7.29.7indirect
npm@babel/plugin-transform-object-assign7.29.7indirect
npm@babel/plugin-transform-object-rest-spread7.29.7indirect
npm@babel/plugin-transform-object-super7.29.7indirect
npm@babel/plugin-transform-optional-catch-binding7.29.7indirect
npm@babel/plugin-transform-optional-chaining7.29.7indirect
npm@babel/plugin-transform-parameters7.29.7indirect
npm@babel/plugin-transform-private-methods7.29.7indirect
npm@babel/plugin-transform-private-property-in-object7.29.7indirect
npm@babel/plugin-transform-property-literals7.29.7indirect
npm@babel/plugin-transform-react-display-name7.29.7indirect
npm@babel/plugin-transform-react-jsx7.29.7indirect
npm@babel/plugin-transform-react-jsx-development7.29.7indirect
npm@babel/plugin-transform-react-pure-annotations7.29.7indirect
npm@babel/plugin-transform-regenerator7.29.7indirect
npm@babel/plugin-transform-regexp-modifiers7.29.7indirect
npm@babel/plugin-transform-reserved-words7.29.7indirect
npm@babel/plugin-transform-runtime7.29.7indirect
npm@babel/plugin-transform-shorthand-properties7.29.7indirect
npm@babel/plugin-transform-spread7.29.7indirect
npm@babel/plugin-transform-sticky-regex7.29.7indirect
npm@babel/plugin-transform-template-literals7.29.7indirect
npm@babel/plugin-transform-typeof-symbol7.29.7indirect
npm@babel/plugin-transform-typescript7.29.7indirect
npm@babel/plugin-transform-unicode-escapes7.29.7indirect
npm@babel/plugin-transform-unicode-property-regex7.29.7indirect
npm@babel/plugin-transform-unicode-regex7.29.7indirect
npm@babel/plugin-transform-unicode-sets-regex7.29.7indirect
npm@babel/preset-env7.29.7indirect
npm@babel/preset-modules0.1.6-no-external-pluginsindirect
npm@babel/preset-react7.29.7indirect
npm@babel/preset-typescript7.29.7indirect
npm@babel/runtime7.29.7indirect
npm@babel/template7.29.7indirect
npm@babel/traverse7.29.7indirect
npm@babel/types7.29.7indirect
npm@colors/colors1.5.0indirect
npm@discoveryjs/json-ext0.5.7indirect
npm@flarum/prettier-config1.0.0indirect
npm@hypnosphi/create-react-context0.3.1indirect
npm@jridgewell/gen-mapping0.3.13indirect
npm@jridgewell/remapping2.3.5indirect
npm@jridgewell/resolve-uri3.1.2indirect
npm@jridgewell/source-map0.3.11indirect
npm@jridgewell/sourcemap-codec1.5.5indirect
npm@jridgewell/trace-mapping0.3.31indirect
npm@nodelib/fs.scandir2.1.5indirect
npm@nodelib/fs.stat2.0.5indirect
npm@nodelib/fs.walk1.2.8indirect
npm@polka/url1.0.0-next.29indirect
npm@semantic-ui-react/event-stack3.1.3indirect
npm@stardust-ui/react-component-event-listener0.38.0indirect
npm@stardust-ui/react-component-ref0.38.0indirect
npm@types/estree1.0.9indirect
npm@types/jquery3.5.34indirect
npm@types/json-schema7.0.15indirect
npm@types/mithril2.2.8indirect
npm@types/node25.9.2indirect
npm@types/sizzle2.3.10indirect
npm@types/throttle-debounce2.1.0indirect
npm@webassemblyjs/ast1.14.1indirect
npm@webassemblyjs/floating-point-hex-parser1.13.2indirect
npm@webassemblyjs/helper-api-error1.13.2indirect
npm@webassemblyjs/helper-buffer1.14.1indirect
npm@webassemblyjs/helper-numbers1.13.2indirect
npm@webassemblyjs/helper-wasm-bytecode1.13.2indirect
npm@webassemblyjs/helper-wasm-section1.14.1indirect
npm@webassemblyjs/ieee7541.13.2indirect
npm@webassemblyjs/leb1281.13.2indirect
npm@webassemblyjs/utf81.13.2indirect
npm@webassemblyjs/wasm-edit1.14.1indirect
npm@webassemblyjs/wasm-gen1.14.1indirect
npm@webassemblyjs/wasm-opt1.14.1indirect
npm@webassemblyjs/wasm-parser1.14.1indirect
npm@webassemblyjs/wast-printer1.14.1indirect
npm@webpack-cli/configtest1.2.0indirect
npm@webpack-cli/info1.5.0indirect
npm@webpack-cli/serve1.7.0indirect
npm@xtuc/ieee7541.2.0indirect
npm@xtuc/long4.2.2indirect
npmacorn8.16.0indirect
npmacorn-import-phases1.0.4indirect
npmacorn-walk8.3.5indirect
npmajv6.15.0indirect
npmajv8.20.0indirect
npmajv-formats2.1.1indirect
npmajv-keywords3.5.2indirect
npmajv-keywords5.1.0indirect
npmansi-regex5.0.1indirect
npmansi-styles4.3.0indirect
npmbabel-loader9.2.1indirect
npmbabel-plugin-polyfill-corejs20.4.17indirect
npmbabel-plugin-polyfill-corejs30.13.0indirect
npmbabel-plugin-polyfill-corejs30.14.2indirect
npmbabel-plugin-polyfill-regenerator0.6.8indirect
npmbalanced-match1.0.2indirect
npmbalanced-match4.0.4indirect
npmbaseline-browser-mapping2.10.34indirect
npmbig.js5.2.2indirect
npmbrace-expansion1.1.15indirect
npmbrace-expansion5.0.6indirect
npmbraces3.0.3indirect
npmbrowserslist4.28.2indirect
npmbuffer-from1.1.2indirect
npmcall-bind1.0.9indirect
npmcall-bind-apply-helpers1.0.2indirect
npmcall-bound1.0.4indirect
npmcaniuse-lite1.0.30001797indirect
npmchalk4.1.2indirect
npmchrome-trace-event1.0.4indirect
npmclassnames2.5.1indirect
npmcli-table30.6.5indirect
npmclone-deep4.0.1indirect
npmcolor-convert2.0.1indirect
npmcolor-name1.1.4indirect
npmcolorette2.0.20indirect
npmcommander2.20.3indirect
npmcommander5.1.0indirect
npmcommander7.2.0indirect
npmcommon-path-prefix3.0.0indirect
npmconcat-map0.0.1indirect
npmconvert-source-map2.0.0indirect
npmcore-js-compat3.49.0indirect
npmcross-spawn7.0.6indirect
npmdayjs1.11.21indirect
npmdebounce1.2.1indirect
npmdebug4.4.3indirect
npmdeep-equal1.1.2indirect
npmdefine-data-property1.1.4indirect
npmdefine-properties1.2.1indirect
npmdunder-proto1.0.1indirect
npmduplexer0.1.2indirect
npmelectron-to-chromium1.5.368indirect
npmemoji-regex8.0.0indirect
npmemojis-list3.0.0indirect
npmenhanced-resolve5.23.0indirect
npmenvinfo7.21.0indirect
npmes-define-property1.0.1indirect
npmes-errors1.3.0indirect
npmes-module-lexer2.1.0indirect
npmes-object-atoms1.1.2indirect
npmescalade3.2.0indirect
npmescape-string-regexp4.0.0indirect
npmeslint-scope5.1.1indirect
npmesrecurse4.3.0indirect
npmestraverse4.3.0indirect
npmestraverse5.3.0indirect
npmesutils2.0.3indirect
npmevents3.3.0indirect
npmexenv1.2.2indirect
npmfast-deep-equal3.1.3indirect
npmfast-glob3.3.3indirect
npmfast-json-stable-stringify2.1.0indirect
npmfast-uri3.1.2indirect
npmfastest-levenshtein1.0.16indirect
npmfastq1.20.1indirect
npmfill-range7.1.1indirect
npmfind-cache-dir4.0.0indirect
npmfind-up4.1.0indirect
npmfind-up6.3.0indirect
npmflarum-tsconfig2.0.0indirect
npmflarum-webpack-config3.0.4indirect
npmflat5.0.2indirect
npmfs.realpath1.0.0indirect
npmfunction-bind1.1.2indirect
npmfunctions-have-names1.2.3indirect
npmgensync1.0.0-beta.2indirect
npmget-intrinsic1.3.0indirect
npmget-proto1.0.1indirect
npmglob7.2.3indirect
npmglob-parent5.1.2indirect
npmglob-to-regexp0.4.1indirect
npmgopd1.2.0indirect
npmgraceful-fs4.2.11indirect
npmgud1.0.0indirect
npmgzip-size6.0.0indirect
npmhas-flag4.0.0indirect
npmhas-property-descriptors1.0.2indirect
npmhas-symbols1.1.0indirect
npmhas-tostringtag1.0.2indirect
npmhasown2.0.4indirect
npmhtml-escaper2.0.2indirect
npmimport-local3.2.0indirect
npminflight1.0.6indirect
npminherits2.0.4indirect
npminterpret2.2.0indirect
npmis-arguments1.2.0indirect
npmis-core-module2.16.2indirect
npmis-date-object1.1.0indirect
npmis-extglob2.1.1indirect
npmis-fullwidth-code-point3.0.0indirect
npmis-glob4.0.3indirect
npmis-number7.0.0indirect
npmis-plain-object2.0.4indirect
npmis-regex1.2.1indirect
npmisexe2.0.0indirect
npmisobject3.0.1indirect
npmjest-worker27.5.1indirect
npmjs-tokens4.0.0indirect
npmjsesc3.1.0indirect
npmjson-schema-traverse0.4.1indirect
npmjson-schema-traverse1.0.0indirect
npmjson51.0.2indirect
npmjson52.2.3indirect
npmkeyboard-key1.1.0indirect
npmkind-of6.0.3indirect
npmloader-runner4.3.2indirect
npmloader-utils1.4.2indirect
npmlocate-path5.0.0indirect
npmlocate-path7.2.0indirect
npmlodash4.18.1indirect
npmlodash.debounce4.0.8indirect
npmloose-envify1.4.0indirect
npmlru-cache5.1.1indirect
npmmath-intrinsics1.1.0indirect
npmmerge-stream2.0.0indirect
npmmerge21.4.1indirect
npmmicromatch4.0.8indirect
npmmime-db1.54.0indirect
npmminimatch10.2.5indirect
npmminimatch3.1.5indirect
npmminimist1.2.8indirect
npmmrmime2.0.1indirect
npmms2.1.3indirect
npmncp2.0.0indirect
npmneo-async2.6.2indirect
npmnode-releases2.0.47indirect
npmnormalize-path3.0.0indirect
npmobject-assign4.1.1indirect
npmobject-is1.1.6indirect
npmobject-keys1.1.1indirect
npmonce1.4.0indirect
npmopener1.5.2indirect
npmp-limit2.3.0indirect
npmp-limit4.0.0indirect
npmp-locate4.1.0indirect
npmp-locate6.0.0indirect
npmp-try2.2.0indirect
npmpath-exists4.0.0indirect
npmpath-exists5.0.0indirect
npmpath-is-absolute1.0.1indirect
npmpath-key3.1.1indirect
npmpath-parse1.0.7indirect
npmpicocolors1.1.1indirect
npmpicomatch2.3.2indirect
npmpkg-dir4.2.0indirect
npmpkg-dir7.0.0indirect
npmpopper.js1.16.1indirect
npmprettier3.8.3indirect
npmprop-types15.8.1indirect
npmpunycode2.3.1indirect
npmqueue-microtask1.2.3indirect
npmreact16.14.0indirect
npmreact-dom16.14.0indirect
npmreact-is16.13.1indirect
npmreact-popper1.3.11indirect
npmrechoir0.7.1indirect
npmregenerate1.4.2indirect
npmregenerate-unicode-properties10.2.2indirect
npmregexp.prototype.flags1.5.4indirect
npmregexpu-core6.4.0indirect
npmregjsgen0.8.0indirect
npmregjsparser0.13.1indirect
npmrequire-from-string2.0.2indirect
npmresolve1.22.12indirect
npmresolve-cwd3.0.0indirect
npmresolve-from5.0.0indirect
npmreusify1.1.0indirect
npmrimraf3.0.2indirect
npmrun-parallel1.2.0indirect
npmscheduler0.19.1indirect
npmschema-utils3.3.0indirect
npmschema-utils4.3.3indirect
npmsemantic-ui-react0.88.2indirect
npmsemver6.3.1indirect
npmset-function-length1.2.2indirect
npmset-function-name2.0.2indirect
npmshallow-clone3.0.1indirect
npmshallowequal1.1.0indirect
npmshebang-command2.0.0indirect
npmshebang-regex3.0.0indirect
npmsirv2.0.4indirect
npmsource-map0.6.1indirect
npmsource-map-support0.5.21indirect
npmstring-width4.2.3indirect
npmstrip-ansi6.0.1indirect
npmsupports-color7.2.0indirect
npmsupports-color8.1.1indirect
npmsupports-preserve-symlinks-flag1.0.0indirect
npmtapable2.3.3indirect
npmterser5.48.0indirect
npmterser-webpack-plugin5.6.1indirect
npmto-regex-range5.0.1indirect
npmtotalist3.0.1indirect
npmtslib1.14.1indirect
npmtslib2.8.1indirect
npmtsutils3.21.0indirect
npmtype-coverage-core2.29.7indirect
npmtyped-styles0.0.7indirect
npmtypescript4.9.5indirect
npmtypescript-coverage-report0.6.4indirect
npmundici-types7.24.6indirect
npmunicode-canonical-property-names-ecmascript2.0.1indirect
npmunicode-match-property-ecmascript2.0.0indirect
npmunicode-match-property-value-ecmascript2.2.1indirect
npmunicode-property-aliases-ecmascript2.2.0indirect
npmupdate-browserslist-db1.2.3indirect
npmuri-js4.4.1indirect
npmwarning4.0.3indirect
npmwatchpack2.5.1indirect
npmwebpack5.107.2indirect
npmwebpack-bundle-analyzer4.10.2indirect
npmwebpack-cli4.10.0indirect
npmwebpack-merge5.10.0indirect
npmwebpack-sources3.5.0indirect
npmwhich2.0.2indirect
npmwildcard2.0.1indirect
npmwrappy1.0.2indirect
npmws7.5.11indirect
npmyallist3.1.1indirect
npmyocto-queue1.2.2indirect
Packagistext-jsonindirect
Packagistflarum/likesindirect
Packagistflarum/markdownindirect
Packagistflarum/phpstanindirect
Packagistflarum/testingindirect
Packagistfof/best-answerindirect
Packagistfof/discussion-viewsindirect
Packagistfof/gamificationindirect
Packagistfof/pagesindirect
Packagistfof/uploadindirect
Packagistfof/user-directoryindirect
Dependency advisories 3

This repository publishes no package the index resolves, so its own dependency graph was assessed — 387 packages, which also include development and test pins that never ship: 3 carry known advisories, of which 0 are direct. 13 could not be assessed — no resolved version, an unsupported ecosystem, or beyond the reported package list.

PackageVersionRelationSeverityAdvisoriesFixed in
brace-expansion1.1.15indirecthigh35.0.9
brace-expansion5.0.6indirecthigh35.0.9
fast-uri3.1.2indirecthigh34.1.2

An advisory means the version recorded in the dependency graph falls inside an advisory’s affected range. Reachability is not analysed, and the graph includes development and test pins — a finding may concern tooling rather than shipped software.

Raw JSON report machine-readable
{
  "data": {
    "icon": {
      "bytes": 747,
      "width": 64,
      "height": 64,
      "rejected": [],
      "collected": true,
      "media_type": "image/png",
      "source_url": "https://discuss.flarum.org/assets/favicon-9tnghur0.png",
      "source_type": "homepage",
      "content_hash": "61d6dae14d2b76223eb55c812752dc200d2cf391b8759d38709beeed2c90e4eb",
      "candidates_considered": 1
    },
    "repo": {
      "topics": [
        "flarum",
        "flarum-seo",
        "flarum-forum",
        "seo-tags",
        "flagrow"
      ],
      "is_fork": false,
      "size_kb": 1426,
      "has_wiki": true,
      "homepage": "https://discuss.flarum.org/d/18316-flarum-seo",
      "languages": {
        "PHP": 359378,
        "Less": 7084,
        "JavaScript": 79,
        "TypeScript": 79562
      },
      "pushed_at": "2026-08-06T21:23:54Z",
      "created_at": "2018-11-22T19:01:25Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-16T12:45:23Z",
      "description": "Perfect SEO for your Flarum forum",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "2.x",
      "license_spdx_raw": "MIT",
      "primary_language": "PHP",
      "significant_languages": [
        "PHP",
        "TypeScript"
      ]
    },
    "owner": {
      "blog": "https://friendsofflarum.org",
      "name": "Friends of Flarum",
      "type": "Organization",
      "login": "FriendsOfFlarum",
      "company": null,
      "location": null,
      "followers": 103,
      "avatar_url": "https://avatars.githubusercontent.com/u/43339973?v=4",
      "created_at": "2018-09-17T11:10:19Z",
      "is_verified": null,
      "public_repos": 106,
      "account_age_days": 2880
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "4.0.0-beta.7",
          "kind": "prerelease",
          "published_at": "2026-06-16T22:21:32Z"
        },
        {
          "tag": "4.0.0-beta.6",
          "kind": "prerelease",
          "published_at": "2026-06-15T15:25:36Z"
        },
        {
          "tag": "3.0.6",
          "kind": "patch",
          "published_at": "2026-06-15T15:24:30Z"
        },
        {
          "tag": "4.0.0-beta.5",
          "kind": "prerelease",
          "published_at": "2026-06-15T10:21:18Z"
        },
        {
          "tag": "4.0.0-beta.4",
          "kind": "prerelease",
          "published_at": "2026-06-15T08:00:58Z"
        },
        {
          "tag": "4.0.0-beta.3",
          "kind": "prerelease",
          "published_at": "2026-06-13T12:43:21Z"
        },
        {
          "tag": "3.0.5",
          "kind": "patch",
          "published_at": "2026-06-11T19:38:21Z"
        },
        {
          "tag": "3.0.4",
          "kind": "patch",
          "published_at": "2026-06-08T18:02:23Z"
        },
        {
          "tag": "4.0.0-beta.2",
          "kind": "prerelease",
          "published_at": "2026-06-08T18:02:45Z"
        },
        {
          "tag": "3.0.3",
          "kind": "patch",
          "published_at": "2026-06-08T09:38:56Z"
        },
        {
          "tag": "3.0.2",
          "kind": "patch",
          "published_at": "2026-06-08T07:51:01Z"
        },
        {
          "tag": "4.0.0-beta.1",
          "kind": "prerelease",
          "published_at": "2026-06-07T22:43:08Z"
        },
        {
          "tag": "3.0.1",
          "kind": "patch",
          "published_at": "2026-06-06T10:19:49Z"
        },
        {
          "tag": "3.0.0",
          "kind": "major",
          "published_at": "2026-06-04T22:35:05Z"
        },
        {
          "tag": "v2.0.9",
          "kind": "patch",
          "published_at": "2025-10-14T14:13:17Z"
        },
        {
          "tag": "v2.0.8",
          "kind": "patch",
          "published_at": "2025-09-17T12:48:23Z"
        },
        {
          "tag": "v2.0.7",
          "kind": "patch",
          "published_at": "2025-04-17T09:27:32Z"
        },
        {
          "tag": "v2.0.6",
          "kind": "patch",
          "published_at": "2025-02-25T09:38:28Z"
        },
        {
          "tag": "v2.0.5",
          "kind": "patch",
          "published_at": "2024-12-20T14:02:40Z"
        },
        {
          "tag": "v2.0.4",
          "kind": "patch",
          "published_at": "2024-12-17T12:40:28Z"
        },
        {
          "tag": "v2.0.3",
          "kind": "patch",
          "published_at": "2024-10-18T11:50:18Z"
        },
        {
          "tag": "v2.0.2",
          "kind": "patch",
          "published_at": "2024-10-14T20:07:48Z"
        },
        {
          "tag": "v2.0.1",
          "kind": "patch",
          "published_at": "2024-10-14T19:53:32Z"
        },
        {
          "tag": "v1.8.1",
          "kind": "patch",
          "published_at": "2024-03-01T12:20:21Z"
        },
        {
          "tag": "v1.8.0",
          "kind": "minor",
          "published_at": "2021-05-29T13:18:04Z"
        },
        {
          "tag": "v1.7.2",
          "kind": "patch",
          "published_at": "2021-03-21T12:45:26Z"
        },
        {
          "tag": "v1.7.1",
          "kind": "patch",
          "published_at": "2021-03-20T21:37:29Z"
        },
        {
          "tag": "v1.7.0",
          "kind": "minor",
          "published_at": "2021-03-20T00:04:18Z"
        },
        {
          "tag": "v1.6.2",
          "kind": "patch",
          "published_at": "2021-02-17T16:58:27Z"
        },
        {
          "tag": "v1.6.1",
          "kind": "patch",
          "published_at": "2021-02-17T13:10:36Z"
        },
        {
          "tag": "v1.6.0",
          "kind": "minor",
          "published_at": "2021-02-17T12:24:15Z"
        },
        {
          "tag": "1.5.1",
          "kind": "patch",
          "published_at": "2020-12-21T00:36:47Z"
        },
        {
          "tag": "1.5.0",
          "kind": "minor",
          "published_at": "2020-12-21T00:16:18Z"
        },
        {
          "tag": "1.4",
          "kind": "other",
          "published_at": "2020-10-21T15:39:02Z"
        },
        {
          "tag": "1.3.1",
          "kind": "patch",
          "published_at": "2020-09-29T22:21:08Z"
        },
        {
          "tag": "1.3",
          "kind": "other",
          "published_at": "2020-09-28T22:56:54Z"
        },
        {
          "tag": "1.2.1",
          "kind": "patch",
          "published_at": "2020-05-26T11:29:17Z"
        },
        {
          "tag": "1.2",
          "kind": "other",
          "published_at": "2020-05-10T13:49:12Z"
        },
        {
          "tag": "1.1",
          "kind": "other",
          "published_at": "2019-12-22T15:25:01Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "fc5b9a44a31499e130e700a9625ae522fd1468e6",
          "body": "The is_primary column can drift out of sync (it is only re-set when tag\norder is saved through the admin UI), leaving top-level tags with a\nposition but is_primary=0. TagBreadcrumb relied on is_primary, so on\naffected forums every discussion lost its category breadcrumb. Key off\nposition (matching core's own logic) instead. Also drop the 'Tags' root\ncrumb from discussion trails (kept on tag pages).",
          "is_bot": false,
          "headline": "fix: detect primary tags by position, not the is_primary column (#180)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-16T22:12:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cf860586e255462e05d30b54d72d8492e3500519",
          "body": "The JSON-LD block was a bare top-level array of schema.org nodes. A bare array\nhas no `@context` key, so Safari's structured-data parser throws when it calls\n`root[\"@context\"].toLowerCase()` (GH #177).\n\nEmit a single root object with one `@context` and an `@graph` list of nodes —\nthe spec-preferred \n[…]\nstency and\ncurrent best practice.\n\nThe test helper unwraps `@graph` (still accepting a bare array for resilience),\nand a regression test locks the @graph object root and the https context.\n\nFixes #177",
          "is_bot": false,
          "headline": "fix: emit JSON-LD as an @graph root instead of a bare array (#179)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-15T15:21:13Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1868d893d031193a18cb47b7c7af8317b8bc6bb3",
          "body": "* perf: resolve tag breadcrumb ancestry from an in-memory map (no per-level N+1)\n\nThe breadcrumb lineage walk read `$tag->parent` per level. Core only\neager-loads one level of `tags.parent`, so a deep tag chain lazy-loaded a tag\nper extra level — one query per breadcrumb level.\n\nWalk `parent_id` aga\n[…]\nAnchor each answer/comment URL on the always-unique post id via a\n`#post-{id}` fragment. The fragment is ignored by the router, so navigation is\nunchanged, but every URL is now unique by construction.",
          "is_bot": false,
          "headline": "[2.x] Make answer/comment URLs unique with a #post-{id} fragment (#175)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-15T10:11:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6b34b5a0ae866e236dfbc63b607f4a9c977ac88f",
          "body": "…evel N+1) (#176)\n\nThe breadcrumb lineage walk read `$tag->parent` per level. Core only\neager-loads one level of `tags.parent`, so a deep tag chain lazy-loaded a tag\nper extra level — one query per breadcrumb level.\n\nWalk `parent_id` against a single `Tag::all()` map instead (tags are a small,\nbound\n[…]\ndeep vs shallow tag chain (per-level) and a\nmulti-primary vs single-primary discussion (per-lineage). The previous guard\nused a 2-level chain, within the one level core eager-loads, so it missed this.",
          "is_bot": false,
          "headline": "perf: resolve tag breadcrumb ancestry from an in-memory map (no per-l…",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-15T10:10:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "adff916354415a106959808d9ea114a822664f41",
          "body": "…wer fields (#174)\n\n`author` (and `author.name`) are required on DiscussionForumPosting and\nComment, and an omitted author is a critical error that blocks rich results.\nA previous change dropped the whole `author` for deleted users when it should\nonly have dropped the recommended `url`. A deleted us\n[…]\n without the required `text`.\n- PostMedia only emits absolute http(s) image/video URLs, so relative,\n  protocol-relative, data: and blob: sources no longer produce an\n  \"Invalid URL in field 'image'\".",
          "is_bot": false,
          "headline": "fix: always emit author on forum/Q&A nodes; only emit valid image/ans…",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-15T07:48:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "29e10582840857371e726d57b71bbebe4a1122fe",
          "body": "* feat: rich, hierarchical, extensible breadcrumbs\n\nReplaces the flat tag-list breadcrumb with proper schema.org BreadcrumbList\ntrails:\n\n- Core pages: Home › {title} for discussions, profiles and fof/pages pages.\n- flarum/tags: Home › Tags › {primary tag lineage} › {title}, walking the\n  primary tag\n[…]\ne id, id-with-name and utf8 slug drivers (previously a non-default user slug\ndriver emitted no profile SEO at all).\n\n* Apply fixes from StyleCI\n\n---------\n\nCo-authored-by: StyleCI Bot <bot@styleci.io>",
          "is_bot": false,
          "headline": "[2.x] feat: Rich, hierarchical, extensible breadcrumbs (#173)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-13T12:33:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "64eb545745dcc873717f250d2f2e45d1f908b0c4",
          "body": "A reply or answer that is only an image (or video) rendered to a schema node\nwith an empty `text` and no other content, tripping Search Console's \"Either\n'text', 'image' or 'video' should be specified\" on both DiscussionForumPosting\n`comment` nodes and QAPage Question/Answer nodes.\n\nExtract images (\n[…]\ned invalid.\n\nThe shared extraction lives in FoF\\Seo\\Support\\PostMedia so both page drivers\nuse it. flarum/markdown is added as a dev/optional dependency so the markdown\nimage path is covered by tests.",
          "is_bot": false,
          "headline": "fix: emit image/video on comment and Q&A nodes that have no text (#172)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-13T10:59:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ed5ca878bcb26b46fa9d2ece71bce9d472527b0e",
          "body": "Includes transpiled JS/TS, and Typescript declaration files (typings).\n\n[skip ci]",
          "is_bot": false,
          "headline": "Bundled output for commit 4c52fa67df39536eee30a1996101ac64a1f19126",
          "author_name": "flarum-bot",
          "author_login": "flarum-bot",
          "committed_at": "2026-06-13T10:09:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4c52fa67df39536eee30a1996101ac64a1f19126",
          "body": "* perf: lazy-load MetaSeoModal so it stays out of the critical forum bundle\n\nThe modal is only reachable via the staff-only \"Configure SEO\" button, but\nits ~22KB was eagerly imported into the forum entry and bundled into the\ncritical forum.js every visitor downloads.\n\nPass an async importer to app.m\n[…]\nplits the modal into a\nchunk loaded on demand, and register the common JS directory so Flarum serves\nthat chunk. forum.js drops from ~22KB to ~6KB.\n\nFixes #169\n\n* chore: lazy load admin modals as well",
          "is_bot": false,
          "headline": "[2.x] fix: lazy load modals for reduced js bundle size (#171)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-13T10:09:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "084b84f40f976d4998b506f15bd34c255fab301f",
          "body": "…umPosting (#170)\n\nA deleted user has no profile page, so emitting an author Person without a\nurl tripped Search Console's \"Missing field 'url' (in 'mainEntity.author')\"\n(QAPage) and \"Missing field 'url' (in 'author')\" (DiscussionForumPosting).\n\nauthor is a recommended field, not required, so omit it entirely for a\ndeleted user rather than emit an incomplete Person. Live authors keep their\nname and profile url.",
          "is_bot": false,
          "headline": "fix: omit author when the user is deleted in QAPage and DiscussionFor…",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-13T09:47:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "75c389a0355f359aadfb41bdd98491286d659f7a",
          "body": "The dashboard SEO widget's \"Do the health-check\" CTA rendered with no white\npill and low-contrast text on the widget's blue background (#159).\n\nThe widget's button styling targeted `button`, but the CTA is a `LinkButton`,\nwhich in Flarum 2.x renders an `<a class=\"LinkButton\">` (no `Button` class) —\nso the rule never matched. Target `.LinkButton` instead, pin the text colour\nacross the anchor's link/hover/focus states and `.Button-label`, and remove the\nanchor's hover underline.\n\nRefs #159",
          "is_bot": false,
          "headline": "fix: restore SeoWidget CTA contrast on the dashboard (#163)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-08T17:03:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8359465fd62b5e192604f2a0015cbfbaac82c17c",
          "body": "The search-engine information page's \"I have read this\" CTA floated right with\nno spacing below the page container, so it blended into the next admin section\n(the permissions header) — GH #154.\n\nGive .FlarumSeoPage-container a bottom margin (kept in the ≥992px breakpoint\nthat previously reset all margins) and clear its float so the margin applies\nbelow the floated CTA.\n\nRefs #154",
          "is_bot": false,
          "headline": "fix: add bottom margin to SEO admin page container (#161)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-08T16:07:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5106d8f5311017d1dab149976d45d96a0290729b",
          "body": "…result (#158)\n\nThe health-check table's first column showed each check's label as an\naffirmative statement (e.g. \"Your forum has a description\"). When the check\nfailed, the warning beneath it (\"You did not set up a forum description yet!\")\ncontradicted the label, making it look like the result was \n[…]\nl \"Checks whether…\" phrasing so the label reads as the\ntechnique being checked, not its outcome. The already-imperative labels (crawl,\nsocial media, search engines, review) are unchanged.\n\nCloses #151",
          "is_bot": false,
          "headline": "i18n: reword health-check labels to describe the check, not assert a …",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-08T15:55:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0af98db7b1f108d710ed88a6be6cae3be8e9ebfb",
          "body": "Note in the per-item SEO guide that auto image detection skips SVG (raster\nonly) and document the new `fof:seo:fix-svg-images` console command, including\nits --batch and --dry-run options. Cross-reference from the developer SeoMeta\nimages section.\n\nRefs #149",
          "is_bot": false,
          "headline": "docs: document SVG image exclusion and the fix-svg-images command (#157)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-08T15:35:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e15f555910bd8614321f39dd3d81a7127c4e6369",
          "body": "…el (#153)\n\nTwo health-check string fixes (#152):\n\n- Refer to the \"FriendsOfFlarum Sitemap extension\" with a capital S, matching\n  the robots health-check item and the extension's own name.\n- The robots.txt item's button read \"Read more about adding a sitemap\", which\n  looked mislabelled stacked beneath the sitemap item's identical button. It\n  now reads \"Read more about adding a robots.txt\".\n\nRefs #152",
          "is_bot": false,
          "headline": "i18n: capitalize Sitemap extension name and fix robots.txt button lab…",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-08T15:14:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d611b445ed38ec257ca99c355e684ffec8c31012",
          "body": "…(#150)\n\nSlack and X reject SVG for og:image (only raster formats render a preview),\nso a discussion whose first post leads with an SVG badge (e.g. a shields.io\nlicense badge) unfurled as a plain card with no image.\n\ngetImageFromContent now skips SVG and returns the first raster image\n(png/jpg/webp/\n[…]\n is an SVG, in id-ordered chunks (configurable --batch, default\n100) to keep memory and DB load flat, with a --dry-run preview. Manual and\nother-extension-managed images are left untouched.\n\nRefs #149",
          "is_bot": false,
          "headline": "fix: don't auto-select SVG images for og:image, add backfill command …",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-08T13:39:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8f082258f1deb509e52a0b4bbd041c68085cf5c1",
          "body": "Update the admin settings and meta-editor English strings to refer to the\nplatform as \"X (formerly Twitter)\" instead of \"Twitter\". Only the displayed\ntext changes — the translation key paths and the twitter:* meta tag names\n(the literal X Cards / Open Graph protocol properties) are left untouched.\n\nAlso corrects og.description.placeholder, which read \"Custom Twitter\ndescription\" under the Open Graph block — a copy/paste leftover — to\n\"Custom Open Graph description\".\n\nRefs #146",
          "is_bot": false,
          "headline": "i18n: refer to X (formerly Twitter) in user-facing strings (#147)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-08T09:16:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2ca3af43af3af747871b4069f1f447dd23291910",
          "body": "The QAPage Question and Answer `text` fields used strip_tags($post->content),\nwhich operates on the unparsed source — so mentions appeared as raw\n@\"Name\"#id syntax and labelled markdown links leaked their [label](url) form.\n\nRender the stored content to HTML first (resolving mentions and links), then\ndecode entities and strip tags, via a shared plainText() helper. This mirrors\nthe DiscussionForumPosting comment handling from #141 so both schema types\nexpose clean plain text.",
          "is_bot": false,
          "headline": "fix: render QAPage question/answer text to plain text (#145)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-08T09:04:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c0568fca06068a227557450286dfccb899bb5ebe",
          "body": "…off (#144)\n\nPreviously a QAPage was only emitted when the post crawler was enabled, so a\nQ&A discussion with an accepted best answer fell back to a plain\nDiscussionForumPosting unless full post crawling was turned on.\n\nWhen the crawler is off and a Q&A discussion has an accepted answer, emit a\nQAPa\n[…]\nill falls back to DiscussionForumPosting.\n\nThe deferral in DiscussionPage and the emit condition in\nDiscussionBestAnswerPage are kept in lock-step so exactly one schema type is\nemitted per discussion.",
          "is_bot": false,
          "headline": "feat: emit QAPage for answered Q&A discussions even with the crawler …",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-08T08:09:58Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0256b62897a16bd2cf13a68250a5552f75b54e89",
          "body": "Google requires `author` as a nested field on schema.org Comment and\nAnswer/Question nodes. Comments/answers whose author has been deleted were\neither omitting `author` entirely (DiscussionForumPosting) or emitting it\nwith `name: null` (QAPage), which Search Console reports as a missing field.\n\nIntr\n[…]\n profile url) or the\nlocalized \"[deleted]\" display name (no url) when the user no longer exists.\nApplies to the top-level posting author, each comment, the QAPage question,\nand each answer.\n\nRefs #140",
          "is_bot": false,
          "headline": "fix: always emit author on comment/answer nodes for deleted users (#142)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-08T07:40:08Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "851f30e36fd24cbbe4544d891e3e20fd6a7b8483",
          "body": "…lies (#141)\n\n- Add the Google-required `datePublished` to each schema.org Comment node\n  (previously only `dateCreated` was set, which Search Console flagged as\n  \"Missing field 'comment'\" once post crawling is enabled).\n- Set `datePublished` (required) and `dateModified` (recommended) on the\n  top\n[…]\n.\n- Cap the number of replies emitted as Comment nodes via a new\n  `seo_post_crawler_limit` setting (default 100); rendering each reply is the\n  dominant page-load cost. A value <= 0 disables the cap.",
          "is_bot": false,
          "headline": "fix: emit datePublished on comment nodes and posting, cap crawled rep…",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-08T07:20:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ee7b8c74225417f6225df67bd736a1cf83ede3e4",
          "body": "Follow-up to #138, which renamed/gated the OG article date tags.\n\n- README \"Methods used\": article:updated_time -> article:modified_time (noted\n  it's emitted on article pages only), and list og:image / og:locale.\n- seoproperties.md: setPublishedOn/setUpdatedOn record the schema.org\n  datePublished/dateModified; the matching og article:* property tags are now\n  emitted automatically only when og:type is \"article\".",
          "is_bot": false,
          "headline": "docs: correct Open Graph article tag references (#139)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-07T22:42:14Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "06487fa2d0a020b6f37cba1ec18caf20d38a50bd",
          "body": "…ale and og:image:alt (#138)\n\n* fix: emit Open Graph article dates as property tags, add og:locale and og:image:alt\n\nA crawler-perspective audit of the rendered <head> surfaced a few Open Graph gaps:\n\n- article:published_time / article:updated_time were emitted as `name` meta tags,\n  and article:upd\n[…]\natedOn now record the dates on the schema.org entity only; the matching\nog tags are emitted in finish() solely when og:type is \"article\". The listing\nsemantics remain on the schema.org CollectionPage.",
          "is_bot": false,
          "headline": "[2.x] fix: emit Open Graph article dates as property tags, add og:loc…",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-07T21:56:13Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "61b593264db373fe2e1e44291c6c6b342f734405",
          "body": "Includes transpiled JS/TS, and Typescript declaration files (typings).\n\n[skip ci]",
          "is_bot": false,
          "headline": "Bundled output for commit 2e568128bb4cf84fc513bd2115f83b6aedd9141d",
          "author_name": "flarum-bot",
          "author_login": "flarum-bot",
          "committed_at": "2026-06-07T21:02:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2e568128bb4cf84fc513bd2115f83b6aedd9141d",
          "body": "* chore(2.0): code formatting with prettier\n\nStart by formatting your code with Prettier.\n\n* chore(2.0): update dependencies\n\nUpdate dependencies to Flarum 2.0 compatible versions.\n\n* chore(2.0): update infrastructure\n\nUpdate the extension infrastructure\n\n* chore(2.0): use new import from extension \n[…]\n most are now used as CSS variables instead.\n\n* chore: Flarum 2.0 upgrade\n\n* Apply fixes from StyleCI\n\n* misc fixes\n\n* more fixes\n\n* more fixes\n\n---------\n\nCo-authored-by: StyleCI Bot <bot@styleci.io>",
          "is_bot": false,
          "headline": "Flarum 2.0 upgrade (#137)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-07T21:02:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2a2449b04ff476c649068179b91ae2e435cc1537",
          "body": "Bumps [@babel/plugin-transform-modules-systemjs](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-systemjs) from 7.29.0 to 7.29.7.\n- [Release notes](https://github.com/babel/babel/releases)\n- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)\n- [Commit\n[…]\ns\"\n  dependency-version: 7.29.7\n  dependency-type: indirect\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "chore(deps): bump @babel/plugin-transform-modules-systemjs in /js (#135)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-06-06T06:49:17Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ce2f38cfbe8396f75ded6a6223a1e8c50c5d6b33",
          "body": "Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.0 to 3.1.2.\n- [Release notes](https://github.com/fastify/fast-uri/releases)\n- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.0...v3.1.2)\n\n---\nupdated-dependencies:\n- dependency-name: fast-uri\n  dependency-version: 3.1.2\n  dependency-type: indirect\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "chore(deps): bump fast-uri from 3.1.0 to 3.1.2 in /js (#134)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-06-06T06:48:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a41070400f430790c9e731c251e9a35c6fe76e1c",
          "body": "…136)\n\n* feat: expose per-reply upvotes in DiscussionForumPosting comment[]\n\nWhen post crawling is enabled, replies are now emitted as schema.org\nComment nodes on the DiscussionForumPosting, each with a LikeAction\nupvote count. The count combines flarum/likes likes and fof/gamification\nupvotes (down\n[…]\n fof/gamification as a dev dependency and registers its extend.php\nwith PHPStan so its vote relations are known.\n\nCloses #130\n\n* docs: note per-reply like/upvote exposure in crawl-all help text (#130)",
          "is_bot": false,
          "headline": "feat: expose per-reply upvotes in DiscussionForumPosting comment[] (#…",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-06T06:42:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "89c04a3eca6d758fd4916ee1cf429ff010238b18",
          "body": "Includes transpiled JS/TS, and Typescript declaration files (typings).\n\n[skip ci]",
          "is_bot": false,
          "headline": "Bundled output for commit 2646923a00ed368f112922613190b587214d3e4a",
          "author_name": "flarum-bot",
          "author_login": "flarum-bot",
          "committed_at": "2026-06-05T22:15:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2646923a00ed368f112922613190b587214d3e4a",
          "body": "* feat: exclude discussions in chosen tags from indexing\n\nAdds a `seo_noindex_tags` setting (JSON array of tag IDs). Discussions in any\nlisted tag — and the tags' own listing pages — emit `noindex, follow`, with\nchild tags of a listed parent covered too. Selection reuses flarum/tags'\nTagSelectionMod\n[…]\n, so the button did\nnothing. Import it directly from flarum/tags (webpack maps this to\nflarum.core.compat) and promote flarum/tags from require-dev to\nrequire, since the integration now depends on it.",
          "is_bot": false,
          "headline": "feat: exclude discussions in chosen tags from indexing (#133)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-05T22:15:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "acdf509b7757412d0074cc29293ff40133ebdf4b",
          "body": "Includes transpiled JS/TS, and Typescript declaration files (typings).\n\n[skip ci]",
          "is_bot": false,
          "headline": "Bundled output for commit 439286eee5c5216b76e9ea5c79d3e60e192d7c67",
          "author_name": "flarum-bot",
          "author_login": "flarum-bot",
          "committed_at": "2026-06-05T21:41:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "439286eee5c5216b76e9ea5c79d3e60e192d7c67",
          "body": "* feat: option to de-index profile pages\n\nAdds a `seo_noindex_profiles` setting (default off). When enabled, profile\npages emit `noindex, follow` so thin profile pages are kept out of search\nresults while crawlers still follow the links on them.\n\nCloses #62\n\n* docs: document profile de-indexing setting (#62)",
          "is_bot": false,
          "headline": "feat: option to de-index profile pages (#132)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-05T21:41:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b958f5f1562a73c045ffc3561a0334129787a721",
          "body": "getImageFromContent only matched URLs starting with http(s), so\nprotocol-relative sources (//host/img.png) were never used as og:image.\nIt now matches them and normalises to https so og:image stays absolute.",
          "is_bot": false,
          "headline": "fix: pick up protocol-relative image URLs for the social image (#131)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-05T06:42:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e66ee20f9e6060ef15eeb6b9b5b36a50ffe1c3ae",
          "body": "The Q&A detection only checked each of a discussion's own tags for\nis_qna, so a discussion tagged with a child (secondary) tag whose parent\nis the Q&A tag fell through to DiscussionForumPosting. It now also\nhonours the parent tag (flarum-tags nests one level).",
          "is_bot": false,
          "headline": "fix: emit QAPage for discussions in a child of a Q&A tag (#129)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-04T23:39:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4cd382e6aefc16c10e943dd477d503783509c618",
          "body": "The WebSite JSON-LD block had no name, so search engines fell back to\nshowing the bare domain instead of the forum name. It now carries the\nforum title.",
          "is_bot": false,
          "headline": "fix: include the forum name in the WebSite schema (#128)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-04T23:29:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "db6f66cae1dc8a13d39a2ffc1de06646f4a00361",
          "body": "Do-follow domains (the forum itself and the configured allow-list) now\nrender with rel=\"noopener\" instead of \"ugc noopener\", so they pass\nranking signals to search engines. Untrusted external links keep\n\"ugc noopener nofollow\".",
          "is_bot": false,
          "headline": "fix: do-follow links should not carry rel=\"ugc\" (#127)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-04T23:21:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "09dd0b730a602034072973c02b8bd4c404960016",
          "body": "- Per-discussion social image drives og:image / twitter:image (#30)\n- Discussion meta description is generated from its first post (#114)\n- QAPage carries a top-level url (#92)",
          "is_bot": false,
          "headline": "test: lock regression coverage for closed issues #92, #30, #114 (#126)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-04T23:05:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c54b1f5eff7049c6c1f3cf538bc284059c44265a",
          "body": null,
          "is_bot": false,
          "headline": "chore: update links",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-04T22:17:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "10f1a9576da39e3ac5494586e5a62dc3738622c4",
          "body": null,
          "is_bot": false,
          "headline": "chore: update README, detail versioning to preserve v17 tags",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-04T22:10:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f78d21f5b4fd68cfc32f319db0b377bd0724daad",
          "body": "Includes transpiled JS/TS, and Typescript declaration files (typings).\n\n[skip ci]",
          "is_bot": false,
          "headline": "Bundled output for commit 3b2f711a89b99b25295c685cc871db51ec546ef6",
          "author_name": "flarum-bot",
          "author_login": "flarum-bot",
          "committed_at": "2026-06-04T21:52:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3b2f711a89b99b25295c685cc871db51ec546ef6",
          "body": "* chore: begin FoF adoption\n\n* Apply fixes from StyleCI\n\n* fix: various typings\n\n* chore: fof only bundles english\n\n* chore: begin moving to translations\n\n* chore: modernize php, min 8.2\n\n* Apply fixes from StyleCI\n\n* chore: add tests, fix discovered bug\n\n* Apply fixes from StyleCI\n\n* chore: enable \n[…]\ne: continue refactoring, add docs\n\n* chore: remove clsx\n\n* feat: structured data improvements, better extensibility\n\n* Apply fixes from StyleCI\n\n---------\n\nCo-authored-by: StyleCI Bot <bot@styleci.io>",
          "is_bot": false,
          "headline": "feat: Migrate to FoF, refactoring/modernizing (#124)",
          "author_name": "IanM",
          "author_login": "imorland",
          "committed_at": "2026-06-04T21:52:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d76aa7dd02f72dfdeba08e790550317fe16a597f",
          "body": "feat: set url of author in QAPage",
          "is_bot": false,
          "headline": "Merge pull request #112 from glowingblue/di/author-url",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2025-10-13T16:24:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c85d02413b30b4913391e6976890852b4f2d9144",
          "body": "Depending on which slug driver a community uses, the username would route to a not existing route. Using the Slug Manager here ensures that dynamically the correct route is constructed",
          "is_bot": false,
          "headline": "fix: use slug manager to get route for author url",
          "author_name": "Davide Iadeluca",
          "author_login": "DavideIadeluca",
          "committed_at": "2025-10-07T11:38:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0a9481f32cccdfb108a0d240bdad741d6494f5a7",
          "body": null,
          "is_bot": false,
          "headline": "feat: set url of author in QAPage",
          "author_name": "Davide Iadeluca",
          "author_login": "DavideIadeluca",
          "committed_at": "2025-10-07T11:37:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fa84a0ca1803f86507914e5816eb20acbc89c998",
          "body": "fix: change condition of early return in best answer page",
          "is_bot": false,
          "headline": "Merge pull request #111 from glowingblue/di/QA-structured-data",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2025-09-17T12:47:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b2d933ca403f41076a7c873292400951e0e75233",
          "body": "The if statement currently wrongly early returns when best answer is enabled and a tag of a discussion is a QNA tag, this must be the other way around.",
          "is_bot": false,
          "headline": "fix: change condition of early return in best answer page",
          "author_name": "Davide Iadeluca",
          "author_login": "DavideIadeluca",
          "committed_at": "2025-09-17T11:56:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "27fafcb456d95057c96bc4ed7c00c98ac281a78f",
          "body": "fix: `seoMeta` relationship not included in `ShowDiscussionController`",
          "is_bot": false,
          "headline": "Merge pull request #109 from glowingblue/di/missing-relationship",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2025-03-29T13:48:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7ff6259ad7372586c1d3bdf67fa032a20c668770",
          "body": null,
          "is_bot": false,
          "headline": "feat: add optional include on ListDiscussionsController",
          "author_name": "Davide Iadeluca",
          "author_login": "DavideIadeluca",
          "committed_at": "2025-03-26T08:56:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "87dfc0c883f8119bad02c23ef2d4b024926cc5a3",
          "body": null,
          "is_bot": false,
          "headline": "fix: `seoMeta` relationship not included in show discussion controller",
          "author_name": "Davide Iadeluca",
          "author_login": "DavideIadeluca",
          "committed_at": "2025-03-25T16:57:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d63d3d4be53abbbad31f6e4d27c114f46eabd62e",
          "body": "Includes transpiled JS/TS, and Typescript declaration files (typings).\n\n[skip ci]",
          "is_bot": false,
          "headline": "Bundled output for commit 37a9785e217791ff60885a35325adcbacac09725",
          "author_name": "flarum-bot",
          "author_login": "flarum-bot",
          "committed_at": "2025-02-25T09:37:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "37a9785e217791ff60885a35325adcbacac09725",
          "body": "refactor: increase admin page extensibility",
          "is_bot": false,
          "headline": "Merge pull request #107 from glowingblue/di/admin-page-extensibility",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2025-02-25T09:36:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4488d4c388f8f81d096e6e240c33fe1b82880ef8",
          "body": null,
          "is_bot": false,
          "headline": "fix: missing import",
          "author_name": "Davide Iadeluca",
          "author_login": "DavideIadeluca",
          "committed_at": "2025-02-13T13:35:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d15380a81e0d9b512f618b09e373f6a03c66006a",
          "body": null,
          "is_bot": false,
          "headline": "refactor: improve extensibility of admin frontend",
          "author_name": "Davide Iadeluca",
          "author_login": "DavideIadeluca",
          "committed_at": "2025-02-13T13:27:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "37274e913f1513e029cc2698c5205d8c10d50995",
          "body": "Includes transpiled JS/TS, and Typescript declaration files (typings).\n\n[skip ci]",
          "is_bot": false,
          "headline": "Bundled output for commit 97594b793494cf62cf0e3bb98823ee3dc4d57444",
          "author_name": "flarum-bot",
          "author_login": "flarum-bot",
          "committed_at": "2025-01-08T09:57:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "97594b793494cf62cf0e3bb98823ee3dc4d57444",
          "body": "chore: add workflows",
          "is_bot": false,
          "headline": "Merge pull request #106 from glowingblue/di/workflows",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2025-01-08T09:56:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e254c6df3fc5021a8940db1e2e04832e09092e66",
          "body": null,
          "is_bot": false,
          "headline": "chore: add frontend workflow",
          "author_name": "Davide Iadeluca",
          "author_login": "DavideIadeluca",
          "committed_at": "2025-01-06T06:23:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b1fd4af19971a37015ad82175f785e13f69699b1",
          "body": "fix: add missing `flarum/tags` as optional dependency",
          "is_bot": false,
          "headline": "Merge pull request #104 from glowingblue/di/missing-dep",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-12-20T14:01:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0f447ce2b584f80c6e5f9143256c014f19d526a8",
          "body": null,
          "is_bot": false,
          "headline": "fix: add `flarum/tags` as optional dependency",
          "author_name": "Davide Iadeluca",
          "author_login": "DavideIadeluca",
          "committed_at": "2024-12-20T08:46:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "02fd51872feef2d2b5475d60d50acfe89d22af41",
          "body": null,
          "is_bot": false,
          "headline": "Fix: Wrong Tag import",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-12-16T23:11:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e495989f306f1b073903490c7121df898cb7f288",
          "body": "Fix some issues with v2",
          "is_bot": false,
          "headline": "Merge pull request #102 from rafaucau/fix-v2",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-12-16T22:36:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8d2c934c10abc0c1407e0a396a04076d25ad7b29",
          "body": null,
          "is_bot": false,
          "headline": "docs: add SeoMeta properties PHPDoc",
          "author_name": "Rafał Całka",
          "author_login": "rafaucau",
          "committed_at": "2024-11-17T15:13:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7f2247bab096116518d3ce3e85527e61b5a10584",
          "body": null,
          "is_bot": false,
          "headline": "chore: set a minimum supported PHP version to 8.0",
          "author_name": "Rafał Całka",
          "author_login": "rafaucau",
          "committed_at": "2024-11-17T15:02:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9b37af11a3b6c8d97cf983fe4d1a898cc912c0e9",
          "body": "…from the database in more places\n\nrefactor: simplify date formatting using Carbon",
          "is_bot": false,
          "headline": "perf: prevent unnecessary loading of an already loaded user and tags …",
          "author_name": "Rafał Całka",
          "author_login": "rafaucau",
          "committed_at": "2024-11-17T14:03:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7be034187553ed5d3e9cbf7a8b0c050c3392774e",
          "body": null,
          "is_bot": false,
          "headline": "chore: add PHPStan",
          "author_name": "Rafał Całka",
          "author_login": "rafaucau",
          "committed_at": "2024-11-17T12:56:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "982eeea69b5de2995a3618ca56542efe496d662d",
          "body": "…autocompletion",
          "is_bot": false,
          "headline": "chore: install flarum/tags and fof/pages as dev dependencies for IDE …",
          "author_name": "Rafał Całka",
          "author_login": "rafaucau",
          "committed_at": "2024-11-17T12:51:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0f800ca92dea42dd42a39003f8a9a8dbf6552bd2",
          "body": null,
          "is_bot": false,
          "headline": "perf: use already loaded tags in DiscussionPage.php",
          "author_name": "Rafał Całka",
          "author_login": "rafaucau",
          "committed_at": "2024-11-17T12:37:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cfb1be12429c44140d78db914b3a7f477482ce77",
          "body": null,
          "is_bot": false,
          "headline": "perf: use already loaded tags",
          "author_name": "Rafał Całka",
          "author_login": "rafaucau",
          "committed_at": "2024-11-17T12:22:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "da55de225d93889ede37418578328ced5a6733af",
          "body": "Co-authored-by: alexisrazok <alexisrazok@users.noreply.github.com>",
          "is_bot": false,
          "headline": "fix: error when user()->first() is null in DiscussionBestAnswerPage.php",
          "author_name": "Rafał Całka",
          "author_login": "rafaucau",
          "committed_at": "2024-11-17T12:11:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "da3cb93fc740c05fa19d454282718ded2b161992",
          "body": "fix: unique key cannot be created with TEXT column",
          "is_bot": false,
          "headline": "Merge pull request #97 from luceos/patch-1",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-10-18T11:43:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "31d5c4dcf6fa267b69451e651e3c294fd0b75f83",
          "body": "See:\r\n\r\nissue: https://discuss.flarum.org/d/18316-flarum-seo/587\r\n\r\n65535 supported by mysql and pgsql (for 2.x)",
          "is_bot": false,
          "headline": "fix: unique key cannot be created with TEXT column",
          "author_name": "Daniël Klabbers",
          "author_login": "luceos",
          "committed_at": "2024-10-18T08:07:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "28dae84ea3a2bf4a59497a2997a61608134b3812",
          "body": "Need to add a auto-bundler...",
          "is_bot": false,
          "headline": "Updated js bundle",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-10-14T20:07:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ecd90e9eb20072bf4d3c03c671c9a3525276c047",
          "body": null,
          "is_bot": false,
          "headline": "Fix bug",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-10-14T19:52:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7930e18214801ea13c0c1f81a97130fe27256b34",
          "body": null,
          "is_bot": false,
          "headline": "Update package-lock.json",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-10-14T19:11:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6d079f75c244e0f6b98fb4f552d32830a5f82c9b",
          "body": "Flarum SEO V2",
          "is_bot": false,
          "headline": "Merge pull request #78 from v17development/v2/dev",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-10-14T19:08:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d619dd129ff2b4afeb402949370c6201f2ef2e22",
          "body": null,
          "is_bot": false,
          "headline": "Update PageListener.php",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-10-14T19:01:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "67a92f83839ea289c8b1c1941a11ac85b691f572",
          "body": null,
          "is_bot": false,
          "headline": "Added mainEntity #90",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-10-14T18:13:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e14db886e7f4323e20648c299fbf22e6d29a7efa",
          "body": "Small performance fixes",
          "is_bot": false,
          "headline": "Merge pull request #94 from mbakirov/v2/dev",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-10-14T18:07:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "455dbd9105e84e07b50198797ec232a35140a40c",
          "body": null,
          "is_bot": false,
          "headline": "Added findOrCreateBySlug function",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-10-14T17:25:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8b7f1db0a28094d76039c63d13a44f2e546fa194",
          "body": null,
          "is_bot": false,
          "headline": "Added prependApplicationUrl to canonical URL",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-10-14T17:24:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e51e4203a4a566f585a658906aafd766f2410591",
          "body": null,
          "is_bot": false,
          "headline": "Tweaks",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-10-14T17:21:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "48505cac49ed7d74b5bffc6961e939229311d093",
          "body": null,
          "is_bot": false,
          "headline": "Small performance fixes",
          "author_name": "Marat Bakirov",
          "author_login": "mbakirov",
          "committed_at": "2024-10-02T20:40:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8abb97144fba2c329b6c37e85dcd6d942522f477",
          "body": null,
          "is_bot": false,
          "headline": "Added SEO-meta editor modal",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-09-29T22:13:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "42c6f435f33bace0ad1e2d55547f905ae3c267cf",
          "body": null,
          "is_bot": false,
          "headline": "Show Meta controller & better model event subscribing",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-09-29T12:21:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "df13dca09fb575b34d059c0928f8587ab5705e9e",
          "body": null,
          "is_bot": false,
          "headline": "Started on new seo-modal",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-12T12:02:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2a48c588b7794e191a517c3e66e705ead97d6bf8",
          "body": null,
          "is_bot": false,
          "headline": "Added new SEO permissions to the admin",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-12T12:01:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "31096dc12ef0d477be6f1364dcfffcb48749d34b",
          "body": null,
          "is_bot": false,
          "headline": "Updated npm packages",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-12T12:01:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f931080ac1ce7bb9c5f19a7c1129541283d44c93",
          "body": "…ifferent extension",
          "is_bot": false,
          "headline": "Only update image if source was set to auto and is not managed by a d…",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-05T22:32:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a1fc9e91f732b16895077118c02675f3f0ef5aa2",
          "body": "Images weren't detected if they contained a query",
          "is_bot": false,
          "headline": "Better image recognizer",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-05T22:26:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d731e5824add07839f8e0109f5459ea5e02f399e",
          "body": null,
          "is_bot": false,
          "headline": "Updated tag page and page-extension page",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-05T22:21:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8c7b37c82ceb0e669d27912bc647e6ba6c41bd9e",
          "body": null,
          "is_bot": false,
          "headline": "Added auto updating meta-tags for models",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-05T22:20:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c76efb018b676f582d3b53745abbcbe581cff790",
          "body": null,
          "is_bot": false,
          "headline": "Formatting and renamed Discussion page handler",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-05T22:19:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "072e2f0b9c09b25d1a70d29ac42699547abe3450",
          "body": null,
          "is_bot": false,
          "headline": "Auto generate page meta tags",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-05T16:04:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cf069f1ea38100f6437679a267a3a45345218046",
          "body": null,
          "is_bot": false,
          "headline": "Fix crash when fof-best-answer is not installed",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-04T22:32:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1846abebce1c13f59bbf5f3793caaeea4ee5aad6",
          "body": null,
          "is_bot": false,
          "headline": "Fixed file upload controller path",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-04T22:31:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "993b3b3b0c16bb472ac0bfb78e30359272888de0",
          "body": null,
          "is_bot": false,
          "headline": "Merge branch 'master' into v2/dev",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-04-04T22:30:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c80eb6144bd341c4e05ba4a9feb2259650c4efae",
          "body": "Fix: adds support for cloud filesystem",
          "is_bot": false,
          "headline": "Merge pull request #86 from luceos/dk/support-filesystem",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2024-03-01T12:17:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "80dbf6573831d6b8ad0ca63c02d62d21e7fe67cb",
          "body": null,
          "is_bot": false,
          "headline": "fix: preview not working, update deps to fix ssl on js build",
          "author_name": "Daniël Klabbers",
          "author_login": "luceos",
          "committed_at": "2024-02-28T15:17:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2091f651cde88fccf4ddc49d97ed33cbad44200e",
          "body": null,
          "is_bot": false,
          "headline": "fix: incorrect binding used",
          "author_name": "Daniël Klabbers",
          "author_login": "luceos",
          "committed_at": "2024-02-28T15:07:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3849a045cffaefc04973f64febb1f132a373b5ab",
          "body": null,
          "is_bot": false,
          "headline": "fix: files are expected locally, flarum supports filesystems",
          "author_name": "Daniël Klabbers",
          "author_login": "luceos",
          "committed_at": "2024-02-28T14:55:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bf52d762db27686684c196a4647eadee6d30aaa6",
          "body": null,
          "is_bot": false,
          "headline": "Query SEO Meta by Model object or object type",
          "author_name": "Jasper Vriends",
          "author_login": "jaspervriends",
          "committed_at": "2023-02-03T16:32:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        }
      ],
      "releases_count": 39,
      "commits_last_year": 46,
      "latest_release_at": "2026-06-16T22:21:32Z",
      "latest_release_tag": "4.0.0-beta.7",
      "releases_from_tags": false,
      "days_since_last_push": 0,
      "active_weeks_last_year": 5,
      "days_since_latest_release": 50,
      "mean_days_between_releases": 0.9
    },
    "artifacts": {
      "collected": true,
      "structure": [
        "tree.migrations"
      ],
      "declarations": [
        {
          "name": "fof/seo",
          "path": "composer.json",
          "tokens": [
            "composer.type:flarum-extension"
          ],
          "ecosystem": "packagist"
        },
        {
          "name": null,
          "path": "js/package.json",
          "tokens": [
            "npm.private"
          ],
          "ecosystem": "npm"
        }
      ]
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "readme_badges": {
        "hosts": [
          "shields.io"
        ],
        "total": 3,
        "header": 3,
        "collected": true,
        "has_inspect_badge": false
      },
      "has_description": true,
      "has_contributing": true,
      "health_percentage": 75,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": true
    },
    "ecosystem": {
      "packages": [
        {
          "name": "fof/seo",
          "exists": true,
          "license": "MIT",
          "keywords": [],
          "ecosystem": "packagist",
          "categories": [],
          "matches_repo": true,
          "registry_url": "https://packagist.org/packages/fof/seo",
          "declared_type": "flarum-extension",
          "is_deprecated": false,
          "latest_version": "4.0.0-beta.7",
          "repository_url": "https://github.com/FriendsOfFlarum/seo",
          "versions_count": 47,
          "total_downloads": 3785,
          "dependents_count": 3,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": 2201,
          "first_published_at": null,
          "latest_published_at": "2026-06-16T22:12:07Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 50
        }
      ]
    },
    "popularity": {
      "forks": 25,
      "stars": 50,
      "watchers": 3,
      "fork_history": {
        "days": [
          {
            "date": "2019-01-04",
            "count": 1
          },
          {
            "date": "2019-02-21",
            "count": 1
          },
          {
            "date": "2019-06-24",
            "count": 1
          },
          {
            "date": "2019-07-17",
            "count": 1
          },
          {
            "date": "2019-10-04",
            "count": 1
          },
          {
            "date": "2020-02-15",
            "count": 1
          },
          {
            "date": "2020-05-13",
            "count": 1
          },
          {
            "date": "2020-06-18",
            "count": 1
          },
          {
            "date": "2020-07-18",
            "count": 1
          },
          {
            "date": "2020-07-27",
            "count": 1
          },
          {
            "date": "2020-08-25",
            "count": 1
          },
          {
            "date": "2021-05-28",
            "count": 1
          },
          {
            "date": "2021-08-30",
            "count": 1
          },
          {
            "date": "2021-09-09",
            "count": 1
          },
          {
            "date": "2022-08-17",
            "count": 1
          },
          {
            "date": "2024-01-18",
            "count": 1
          },
          {
            "date": "2024-02-02",
            "count": 1
          },
          {
            "date": "2024-02-28",
            "count": 1
          },
          {
            "date": "2024-06-15",
            "count": 1
          },
          {
            "date": "2024-09-27",
            "count": 1
          },
          {
            "date": "2024-10-29",
            "count": 1
          },
          {
            "date": "2024-11-17",
            "count": 1
          },
          {
            "date": "2024-12-16",
            "count": 1
          },
          {
            "date": "2026-03-22",
            "count": 1
          },
          {
            "date": "2026-05-10",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 25,
        "total_forks": 25
      },
      "star_history": null,
      "open_issues_and_prs": 13
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [
        "js/tsconfig.json"
      ],
      "toolchain_manifests": [],
      "largest_source_bytes": 35992,
      "source_files_sampled": 110,
      "oversized_source_files": 0,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "composer.json",
        "js/package.json"
      ],
      "advisories": {
        "error": null,
        "scope": "repository_graph",
        "source": "osv",
        "findings": [
          {
            "name": "brace-expansion",
            "direct": false,
            "version": "1.1.15",
            "severity": "high",
            "ecosystem": "npm",
            "cvss_score": 7.5,
            "advisory_ids": [
              "GHSA-3jxr-9vmj-r5cp",
              "GHSA-mh99-v99m-4gvg",
              "GHSA-rgw5-rvv9-x895"
            ],
            "fixed_version": "5.0.9",
            "advisory_count": 3,
            "oldest_advisory_days": 17
          },
          {
            "name": "brace-expansion",
            "direct": false,
            "version": "5.0.6",
            "severity": "high",
            "ecosystem": "npm",
            "cvss_score": 7.5,
            "advisory_ids": [
              "GHSA-3jxr-9vmj-r5cp",
              "GHSA-mh99-v99m-4gvg",
              "GHSA-rgw5-rvv9-x895"
            ],
            "fixed_version": "5.0.9",
            "advisory_count": 3,
            "oldest_advisory_days": 17
          },
          {
            "name": "fast-uri",
            "direct": false,
            "version": "3.1.2",
            "severity": "high",
            "ecosystem": "npm",
            "cvss_score": 7.5,
            "advisory_ids": [
              "GHSA-4c8g-83qw-93j6",
              "GHSA-7p8r-x3mc-p8w7",
              "GHSA-v2hh-gcrm-f6hx"
            ],
            "fixed_version": "4.1.2",
            "advisory_count": 3,
            "oldest_advisory_days": 16
          }
        ],
        "collected": true,
        "malicious": [],
        "truncated": false,
        "by_severity": {
          "high": 3
        },
        "advisory_count": 9,
        "affected_count": 3,
        "assessed_count": 387,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 13,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "npm",
        "packagist"
      ],
      "dependencies": [
        {
          "name": "flarum/core",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "^2.0.0"
        },
        {
          "name": "flarum/tags",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "*"
        }
      ],
      "all_dependencies": {
        "error": null,
        "source": "github-sbom",
        "packages": [
          {
            "name": "flarum/core",
            "direct": true,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "flarum/tags",
            "direct": true,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "@babel/code-frame",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/compat-data",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/core",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/generator",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-annotate-as-pure",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-compilation-targets",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-create-class-features-plugin",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-create-regexp-features-plugin",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-define-polyfill-provider",
            "direct": false,
            "version": "0.6.8",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-globals",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-member-expression-to-functions",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-module-imports",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-module-transforms",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-optimise-call-expression",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-plugin-utils",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-remap-async-to-generator",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-replace-supers",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-skip-transparent-expression-wrappers",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-string-parser",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-validator-identifier",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-validator-option",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helper-wrap-function",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/helpers",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/parser",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-bugfix-firefox-class-in-computed-class-key",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-bugfix-safari-class-field-initializer-scope",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-proposal-private-property-in-object",
            "direct": false,
            "version": "7.21.0-placeholder-for-preset-env.2",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-syntax-import-assertions",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-syntax-import-attributes",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-syntax-jsx",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-syntax-typescript",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-syntax-unicode-sets-regex",
            "direct": false,
            "version": "7.18.6",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-arrow-functions",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-async-generator-functions",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-async-to-generator",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-block-scoped-functions",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-block-scoping",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-class-properties",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-class-static-block",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-classes",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-computed-properties",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-destructuring",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-dotall-regex",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-duplicate-keys",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-duplicate-named-capturing-groups-regex",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-dynamic-import",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-explicit-resource-management",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-exponentiation-operator",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-export-namespace-from",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-for-of",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-function-name",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-json-strings",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-literals",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-logical-assignment-operators",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-member-expression-literals",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-modules-amd",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-modules-commonjs",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-modules-systemjs",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-modules-umd",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-named-capturing-groups-regex",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-new-target",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-nullish-coalescing-operator",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-numeric-separator",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-object-assign",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-object-rest-spread",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-object-super",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-optional-catch-binding",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-optional-chaining",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-parameters",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-private-methods",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-private-property-in-object",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-property-literals",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-react-display-name",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-react-jsx",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-react-jsx-development",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-react-pure-annotations",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-regenerator",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-regexp-modifiers",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-reserved-words",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-runtime",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-shorthand-properties",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-spread",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-sticky-regex",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-template-literals",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-typeof-symbol",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-typescript",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-unicode-escapes",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-unicode-property-regex",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-unicode-regex",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/plugin-transform-unicode-sets-regex",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/preset-env",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/preset-modules",
            "direct": false,
            "version": "0.1.6-no-external-plugins",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/preset-react",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/preset-typescript",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/runtime",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/template",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/traverse",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@babel/types",
            "direct": false,
            "version": "7.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "@colors/colors",
            "direct": false,
            "version": "1.5.0",
            "ecosystem": "npm"
          },
          {
            "name": "@discoveryjs/json-ext",
            "direct": false,
            "version": "0.5.7",
            "ecosystem": "npm"
          },
          {
            "name": "@flarum/prettier-config",
            "direct": false,
            "version": "1.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "@hypnosphi/create-react-context",
            "direct": false,
            "version": "0.3.1",
            "ecosystem": "npm"
          },
          {
            "name": "@jridgewell/gen-mapping",
            "direct": false,
            "version": "0.3.13",
            "ecosystem": "npm"
          },
          {
            "name": "@jridgewell/remapping",
            "direct": false,
            "version": "2.3.5",
            "ecosystem": "npm"
          },
          {
            "name": "@jridgewell/resolve-uri",
            "direct": false,
            "version": "3.1.2",
            "ecosystem": "npm"
          },
          {
            "name": "@jridgewell/source-map",
            "direct": false,
            "version": "0.3.11",
            "ecosystem": "npm"
          },
          {
            "name": "@jridgewell/sourcemap-codec",
            "direct": false,
            "version": "1.5.5",
            "ecosystem": "npm"
          },
          {
            "name": "@jridgewell/trace-mapping",
            "direct": false,
            "version": "0.3.31",
            "ecosystem": "npm"
          },
          {
            "name": "@nodelib/fs.scandir",
            "direct": false,
            "version": "2.1.5",
            "ecosystem": "npm"
          },
          {
            "name": "@nodelib/fs.stat",
            "direct": false,
            "version": "2.0.5",
            "ecosystem": "npm"
          },
          {
            "name": "@nodelib/fs.walk",
            "direct": false,
            "version": "1.2.8",
            "ecosystem": "npm"
          },
          {
            "name": "@polka/url",
            "direct": false,
            "version": "1.0.0-next.29",
            "ecosystem": "npm"
          },
          {
            "name": "@semantic-ui-react/event-stack",
            "direct": false,
            "version": "3.1.3",
            "ecosystem": "npm"
          },
          {
            "name": "@stardust-ui/react-component-event-listener",
            "direct": false,
            "version": "0.38.0",
            "ecosystem": "npm"
          },
          {
            "name": "@stardust-ui/react-component-ref",
            "direct": false,
            "version": "0.38.0",
            "ecosystem": "npm"
          },
          {
            "name": "@types/estree",
            "direct": false,
            "version": "1.0.9",
            "ecosystem": "npm"
          },
          {
            "name": "@types/jquery",
            "direct": false,
            "version": "3.5.34",
            "ecosystem": "npm"
          },
          {
            "name": "@types/json-schema",
            "direct": false,
            "version": "7.0.15",
            "ecosystem": "npm"
          },
          {
            "name": "@types/mithril",
            "direct": false,
            "version": "2.2.8",
            "ecosystem": "npm"
          },
          {
            "name": "@types/node",
            "direct": false,
            "version": "25.9.2",
            "ecosystem": "npm"
          },
          {
            "name": "@types/sizzle",
            "direct": false,
            "version": "2.3.10",
            "ecosystem": "npm"
          },
          {
            "name": "@types/throttle-debounce",
            "direct": false,
            "version": "2.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/ast",
            "direct": false,
            "version": "1.14.1",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/floating-point-hex-parser",
            "direct": false,
            "version": "1.13.2",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/helper-api-error",
            "direct": false,
            "version": "1.13.2",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/helper-buffer",
            "direct": false,
            "version": "1.14.1",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/helper-numbers",
            "direct": false,
            "version": "1.13.2",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/helper-wasm-bytecode",
            "direct": false,
            "version": "1.13.2",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/helper-wasm-section",
            "direct": false,
            "version": "1.14.1",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/ieee754",
            "direct": false,
            "version": "1.13.2",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/leb128",
            "direct": false,
            "version": "1.13.2",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/utf8",
            "direct": false,
            "version": "1.13.2",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/wasm-edit",
            "direct": false,
            "version": "1.14.1",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/wasm-gen",
            "direct": false,
            "version": "1.14.1",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/wasm-opt",
            "direct": false,
            "version": "1.14.1",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/wasm-parser",
            "direct": false,
            "version": "1.14.1",
            "ecosystem": "npm"
          },
          {
            "name": "@webassemblyjs/wast-printer",
            "direct": false,
            "version": "1.14.1",
            "ecosystem": "npm"
          },
          {
            "name": "@webpack-cli/configtest",
            "direct": false,
            "version": "1.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "@webpack-cli/info",
            "direct": false,
            "version": "1.5.0",
            "ecosystem": "npm"
          },
          {
            "name": "@webpack-cli/serve",
            "direct": false,
            "version": "1.7.0",
            "ecosystem": "npm"
          },
          {
            "name": "@xtuc/ieee754",
            "direct": false,
            "version": "1.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "@xtuc/long",
            "direct": false,
            "version": "4.2.2",
            "ecosystem": "npm"
          },
          {
            "name": "acorn",
            "direct": false,
            "version": "8.16.0",
            "ecosystem": "npm"
          },
          {
            "name": "acorn-import-phases",
            "direct": false,
            "version": "1.0.4",
            "ecosystem": "npm"
          },
          {
            "name": "acorn-walk",
            "direct": false,
            "version": "8.3.5",
            "ecosystem": "npm"
          },
          {
            "name": "ajv",
            "direct": false,
            "version": "6.15.0",
            "ecosystem": "npm"
          },
          {
            "name": "ajv",
            "direct": false,
            "version": "8.20.0",
            "ecosystem": "npm"
          },
          {
            "name": "ajv-formats",
            "direct": false,
            "version": "2.1.1",
            "ecosystem": "npm"
          },
          {
            "name": "ajv-keywords",
            "direct": false,
            "version": "3.5.2",
            "ecosystem": "npm"
          },
          {
            "name": "ajv-keywords",
            "direct": false,
            "version": "5.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "ansi-regex",
            "direct": false,
            "version": "5.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "ansi-styles",
            "direct": false,
            "version": "4.3.0",
            "ecosystem": "npm"
          },
          {
            "name": "babel-loader",
            "direct": false,
            "version": "9.2.1",
            "ecosystem": "npm"
          },
          {
            "name": "babel-plugin-polyfill-corejs2",
            "direct": false,
            "version": "0.4.17",
            "ecosystem": "npm"
          },
          {
            "name": "babel-plugin-polyfill-corejs3",
            "direct": false,
            "version": "0.13.0",
            "ecosystem": "npm"
          },
          {
            "name": "babel-plugin-polyfill-corejs3",
            "direct": false,
            "version": "0.14.2",
            "ecosystem": "npm"
          },
          {
            "name": "babel-plugin-polyfill-regenerator",
            "direct": false,
            "version": "0.6.8",
            "ecosystem": "npm"
          },
          {
            "name": "balanced-match",
            "direct": false,
            "version": "1.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "balanced-match",
            "direct": false,
            "version": "4.0.4",
            "ecosystem": "npm"
          },
          {
            "name": "baseline-browser-mapping",
            "direct": false,
            "version": "2.10.34",
            "ecosystem": "npm"
          },
          {
            "name": "big.js",
            "direct": false,
            "version": "5.2.2",
            "ecosystem": "npm"
          },
          {
            "name": "brace-expansion",
            "direct": false,
            "version": "1.1.15",
            "ecosystem": "npm"
          },
          {
            "name": "brace-expansion",
            "direct": false,
            "version": "5.0.6",
            "ecosystem": "npm"
          },
          {
            "name": "braces",
            "direct": false,
            "version": "3.0.3",
            "ecosystem": "npm"
          },
          {
            "name": "browserslist",
            "direct": false,
            "version": "4.28.2",
            "ecosystem": "npm"
          },
          {
            "name": "buffer-from",
            "direct": false,
            "version": "1.1.2",
            "ecosystem": "npm"
          },
          {
            "name": "call-bind",
            "direct": false,
            "version": "1.0.9",
            "ecosystem": "npm"
          },
          {
            "name": "call-bind-apply-helpers",
            "direct": false,
            "version": "1.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "call-bound",
            "direct": false,
            "version": "1.0.4",
            "ecosystem": "npm"
          },
          {
            "name": "caniuse-lite",
            "direct": false,
            "version": "1.0.30001797",
            "ecosystem": "npm"
          },
          {
            "name": "chalk",
            "direct": false,
            "version": "4.1.2",
            "ecosystem": "npm"
          },
          {
            "name": "chrome-trace-event",
            "direct": false,
            "version": "1.0.4",
            "ecosystem": "npm"
          },
          {
            "name": "classnames",
            "direct": false,
            "version": "2.5.1",
            "ecosystem": "npm"
          },
          {
            "name": "cli-table3",
            "direct": false,
            "version": "0.6.5",
            "ecosystem": "npm"
          },
          {
            "name": "clone-deep",
            "direct": false,
            "version": "4.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "color-convert",
            "direct": false,
            "version": "2.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "color-name",
            "direct": false,
            "version": "1.1.4",
            "ecosystem": "npm"
          },
          {
            "name": "colorette",
            "direct": false,
            "version": "2.0.20",
            "ecosystem": "npm"
          },
          {
            "name": "commander",
            "direct": false,
            "version": "2.20.3",
            "ecosystem": "npm"
          },
          {
            "name": "commander",
            "direct": false,
            "version": "5.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "commander",
            "direct": false,
            "version": "7.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "common-path-prefix",
            "direct": false,
            "version": "3.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "concat-map",
            "direct": false,
            "version": "0.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "convert-source-map",
            "direct": false,
            "version": "2.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "core-js-compat",
            "direct": false,
            "version": "3.49.0",
            "ecosystem": "npm"
          },
          {
            "name": "cross-spawn",
            "direct": false,
            "version": "7.0.6",
            "ecosystem": "npm"
          },
          {
            "name": "dayjs",
            "direct": false,
            "version": "1.11.21",
            "ecosystem": "npm"
          },
          {
            "name": "debounce",
            "direct": false,
            "version": "1.2.1",
            "ecosystem": "npm"
          },
          {
            "name": "debug",
            "direct": false,
            "version": "4.4.3",
            "ecosystem": "npm"
          },
          {
            "name": "deep-equal",
            "direct": false,
            "version": "1.1.2",
            "ecosystem": "npm"
          },
          {
            "name": "define-data-property",
            "direct": false,
            "version": "1.1.4",
            "ecosystem": "npm"
          },
          {
            "name": "define-properties",
            "direct": false,
            "version": "1.2.1",
            "ecosystem": "npm"
          },
          {
            "name": "dunder-proto",
            "direct": false,
            "version": "1.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "duplexer",
            "direct": false,
            "version": "0.1.2",
            "ecosystem": "npm"
          },
          {
            "name": "electron-to-chromium",
            "direct": false,
            "version": "1.5.368",
            "ecosystem": "npm"
          },
          {
            "name": "emoji-regex",
            "direct": false,
            "version": "8.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "emojis-list",
            "direct": false,
            "version": "3.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "enhanced-resolve",
            "direct": false,
            "version": "5.23.0",
            "ecosystem": "npm"
          },
          {
            "name": "envinfo",
            "direct": false,
            "version": "7.21.0",
            "ecosystem": "npm"
          },
          {
            "name": "es-define-property",
            "direct": false,
            "version": "1.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "es-errors",
            "direct": false,
            "version": "1.3.0",
            "ecosystem": "npm"
          },
          {
            "name": "es-module-lexer",
            "direct": false,
            "version": "2.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "es-object-atoms",
            "direct": false,
            "version": "1.1.2",
            "ecosystem": "npm"
          },
          {
            "name": "escalade",
            "direct": false,
            "version": "3.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "escape-string-regexp",
            "direct": false,
            "version": "4.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "eslint-scope",
            "direct": false,
            "version": "5.1.1",
            "ecosystem": "npm"
          },
          {
            "name": "esrecurse",
            "direct": false,
            "version": "4.3.0",
            "ecosystem": "npm"
          },
          {
            "name": "estraverse",
            "direct": false,
            "version": "4.3.0",
            "ecosystem": "npm"
          },
          {
            "name": "estraverse",
            "direct": false,
            "version": "5.3.0",
            "ecosystem": "npm"
          },
          {
            "name": "esutils",
            "direct": false,
            "version": "2.0.3",
            "ecosystem": "npm"
          },
          {
            "name": "events",
            "direct": false,
            "version": "3.3.0",
            "ecosystem": "npm"
          },
          {
            "name": "exenv",
            "direct": false,
            "version": "1.2.2",
            "ecosystem": "npm"
          },
          {
            "name": "fast-deep-equal",
            "direct": false,
            "version": "3.1.3",
            "ecosystem": "npm"
          },
          {
            "name": "fast-glob",
            "direct": false,
            "version": "3.3.3",
            "ecosystem": "npm"
          },
          {
            "name": "fast-json-stable-stringify",
            "direct": false,
            "version": "2.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "fast-uri",
            "direct": false,
            "version": "3.1.2",
            "ecosystem": "npm"
          },
          {
            "name": "fastest-levenshtein",
            "direct": false,
            "version": "1.0.16",
            "ecosystem": "npm"
          },
          {
            "name": "fastq",
            "direct": false,
            "version": "1.20.1",
            "ecosystem": "npm"
          },
          {
            "name": "fill-range",
            "direct": false,
            "version": "7.1.1",
            "ecosystem": "npm"
          },
          {
            "name": "find-cache-dir",
            "direct": false,
            "version": "4.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "find-up",
            "direct": false,
            "version": "4.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "find-up",
            "direct": false,
            "version": "6.3.0",
            "ecosystem": "npm"
          },
          {
            "name": "flarum-tsconfig",
            "direct": false,
            "version": "2.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "flarum-webpack-config",
            "direct": false,
            "version": "3.0.4",
            "ecosystem": "npm"
          },
          {
            "name": "flat",
            "direct": false,
            "version": "5.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "fs.realpath",
            "direct": false,
            "version": "1.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "function-bind",
            "direct": false,
            "version": "1.1.2",
            "ecosystem": "npm"
          },
          {
            "name": "functions-have-names",
            "direct": false,
            "version": "1.2.3",
            "ecosystem": "npm"
          },
          {
            "name": "gensync",
            "direct": false,
            "version": "1.0.0-beta.2",
            "ecosystem": "npm"
          },
          {
            "name": "get-intrinsic",
            "direct": false,
            "version": "1.3.0",
            "ecosystem": "npm"
          },
          {
            "name": "get-proto",
            "direct": false,
            "version": "1.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "glob",
            "direct": false,
            "version": "7.2.3",
            "ecosystem": "npm"
          },
          {
            "name": "glob-parent",
            "direct": false,
            "version": "5.1.2",
            "ecosystem": "npm"
          },
          {
            "name": "glob-to-regexp",
            "direct": false,
            "version": "0.4.1",
            "ecosystem": "npm"
          },
          {
            "name": "gopd",
            "direct": false,
            "version": "1.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "graceful-fs",
            "direct": false,
            "version": "4.2.11",
            "ecosystem": "npm"
          },
          {
            "name": "gud",
            "direct": false,
            "version": "1.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "gzip-size",
            "direct": false,
            "version": "6.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "has-flag",
            "direct": false,
            "version": "4.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "has-property-descriptors",
            "direct": false,
            "version": "1.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "has-symbols",
            "direct": false,
            "version": "1.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "has-tostringtag",
            "direct": false,
            "version": "1.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "hasown",
            "direct": false,
            "version": "2.0.4",
            "ecosystem": "npm"
          },
          {
            "name": "html-escaper",
            "direct": false,
            "version": "2.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "import-local",
            "direct": false,
            "version": "3.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "inflight",
            "direct": false,
            "version": "1.0.6",
            "ecosystem": "npm"
          },
          {
            "name": "inherits",
            "direct": false,
            "version": "2.0.4",
            "ecosystem": "npm"
          },
          {
            "name": "interpret",
            "direct": false,
            "version": "2.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "is-arguments",
            "direct": false,
            "version": "1.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "is-core-module",
            "direct": false,
            "version": "2.16.2",
            "ecosystem": "npm"
          },
          {
            "name": "is-date-object",
            "direct": false,
            "version": "1.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "is-extglob",
            "direct": false,
            "version": "2.1.1",
            "ecosystem": "npm"
          },
          {
            "name": "is-fullwidth-code-point",
            "direct": false,
            "version": "3.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "is-glob",
            "direct": false,
            "version": "4.0.3",
            "ecosystem": "npm"
          },
          {
            "name": "is-number",
            "direct": false,
            "version": "7.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "is-plain-object",
            "direct": false,
            "version": "2.0.4",
            "ecosystem": "npm"
          },
          {
            "name": "is-regex",
            "direct": false,
            "version": "1.2.1",
            "ecosystem": "npm"
          },
          {
            "name": "isexe",
            "direct": false,
            "version": "2.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "isobject",
            "direct": false,
            "version": "3.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "jest-worker",
            "direct": false,
            "version": "27.5.1",
            "ecosystem": "npm"
          },
          {
            "name": "js-tokens",
            "direct": false,
            "version": "4.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "jsesc",
            "direct": false,
            "version": "3.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "json-schema-traverse",
            "direct": false,
            "version": "0.4.1",
            "ecosystem": "npm"
          },
          {
            "name": "json-schema-traverse",
            "direct": false,
            "version": "1.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "json5",
            "direct": false,
            "version": "1.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "json5",
            "direct": false,
            "version": "2.2.3",
            "ecosystem": "npm"
          },
          {
            "name": "keyboard-key",
            "direct": false,
            "version": "1.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "kind-of",
            "direct": false,
            "version": "6.0.3",
            "ecosystem": "npm"
          },
          {
            "name": "loader-runner",
            "direct": false,
            "version": "4.3.2",
            "ecosystem": "npm"
          },
          {
            "name": "loader-utils",
            "direct": false,
            "version": "1.4.2",
            "ecosystem": "npm"
          },
          {
            "name": "locate-path",
            "direct": false,
            "version": "5.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "locate-path",
            "direct": false,
            "version": "7.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "lodash",
            "direct": false,
            "version": "4.18.1",
            "ecosystem": "npm"
          },
          {
            "name": "lodash.debounce",
            "direct": false,
            "version": "4.0.8",
            "ecosystem": "npm"
          },
          {
            "name": "loose-envify",
            "direct": false,
            "version": "1.4.0",
            "ecosystem": "npm"
          },
          {
            "name": "lru-cache",
            "direct": false,
            "version": "5.1.1",
            "ecosystem": "npm"
          },
          {
            "name": "math-intrinsics",
            "direct": false,
            "version": "1.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "merge-stream",
            "direct": false,
            "version": "2.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "merge2",
            "direct": false,
            "version": "1.4.1",
            "ecosystem": "npm"
          },
          {
            "name": "micromatch",
            "direct": false,
            "version": "4.0.8",
            "ecosystem": "npm"
          },
          {
            "name": "mime-db",
            "direct": false,
            "version": "1.54.0",
            "ecosystem": "npm"
          },
          {
            "name": "minimatch",
            "direct": false,
            "version": "10.2.5",
            "ecosystem": "npm"
          },
          {
            "name": "minimatch",
            "direct": false,
            "version": "3.1.5",
            "ecosystem": "npm"
          },
          {
            "name": "minimist",
            "direct": false,
            "version": "1.2.8",
            "ecosystem": "npm"
          },
          {
            "name": "mrmime",
            "direct": false,
            "version": "2.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "ms",
            "direct": false,
            "version": "2.1.3",
            "ecosystem": "npm"
          },
          {
            "name": "ncp",
            "direct": false,
            "version": "2.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "neo-async",
            "direct": false,
            "version": "2.6.2",
            "ecosystem": "npm"
          },
          {
            "name": "node-releases",
            "direct": false,
            "version": "2.0.47",
            "ecosystem": "npm"
          },
          {
            "name": "normalize-path",
            "direct": false,
            "version": "3.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "object-assign",
            "direct": false,
            "version": "4.1.1",
            "ecosystem": "npm"
          },
          {
            "name": "object-is",
            "direct": false,
            "version": "1.1.6",
            "ecosystem": "npm"
          },
          {
            "name": "object-keys",
            "direct": false,
            "version": "1.1.1",
            "ecosystem": "npm"
          },
          {
            "name": "once",
            "direct": false,
            "version": "1.4.0",
            "ecosystem": "npm"
          },
          {
            "name": "opener",
            "direct": false,
            "version": "1.5.2",
            "ecosystem": "npm"
          },
          {
            "name": "p-limit",
            "direct": false,
            "version": "2.3.0",
            "ecosystem": "npm"
          },
          {
            "name": "p-limit",
            "direct": false,
            "version": "4.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "p-locate",
            "direct": false,
            "version": "4.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "p-locate",
            "direct": false,
            "version": "6.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "p-try",
            "direct": false,
            "version": "2.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "path-exists",
            "direct": false,
            "version": "4.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "path-exists",
            "direct": false,
            "version": "5.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "path-is-absolute",
            "direct": false,
            "version": "1.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "path-key",
            "direct": false,
            "version": "3.1.1",
            "ecosystem": "npm"
          },
          {
            "name": "path-parse",
            "direct": false,
            "version": "1.0.7",
            "ecosystem": "npm"
          },
          {
            "name": "picocolors",
            "direct": false,
            "version": "1.1.1",
            "ecosystem": "npm"
          },
          {
            "name": "picomatch",
            "direct": false,
            "version": "2.3.2",
            "ecosystem": "npm"
          },
          {
            "name": "pkg-dir",
            "direct": false,
            "version": "4.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "pkg-dir",
            "direct": false,
            "version": "7.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "popper.js",
            "direct": false,
            "version": "1.16.1",
            "ecosystem": "npm"
          },
          {
            "name": "prettier",
            "direct": false,
            "version": "3.8.3",
            "ecosystem": "npm"
          },
          {
            "name": "prop-types",
            "direct": false,
            "version": "15.8.1",
            "ecosystem": "npm"
          },
          {
            "name": "punycode",
            "direct": false,
            "version": "2.3.1",
            "ecosystem": "npm"
          },
          {
            "name": "queue-microtask",
            "direct": false,
            "version": "1.2.3",
            "ecosystem": "npm"
          },
          {
            "name": "react",
            "direct": false,
            "version": "16.14.0",
            "ecosystem": "npm"
          },
          {
            "name": "react-dom",
            "direct": false,
            "version": "16.14.0",
            "ecosystem": "npm"
          },
          {
            "name": "react-is",
            "direct": false,
            "version": "16.13.1",
            "ecosystem": "npm"
          },
          {
            "name": "react-popper",
            "direct": false,
            "version": "1.3.11",
            "ecosystem": "npm"
          },
          {
            "name": "rechoir",
            "direct": false,
            "version": "0.7.1",
            "ecosystem": "npm"
          },
          {
            "name": "regenerate",
            "direct": false,
            "version": "1.4.2",
            "ecosystem": "npm"
          },
          {
            "name": "regenerate-unicode-properties",
            "direct": false,
            "version": "10.2.2",
            "ecosystem": "npm"
          },
          {
            "name": "regexp.prototype.flags",
            "direct": false,
            "version": "1.5.4",
            "ecosystem": "npm"
          },
          {
            "name": "regexpu-core",
            "direct": false,
            "version": "6.4.0",
            "ecosystem": "npm"
          },
          {
            "name": "regjsgen",
            "direct": false,
            "version": "0.8.0",
            "ecosystem": "npm"
          },
          {
            "name": "regjsparser",
            "direct": false,
            "version": "0.13.1",
            "ecosystem": "npm"
          },
          {
            "name": "require-from-string",
            "direct": false,
            "version": "2.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "resolve",
            "direct": false,
            "version": "1.22.12",
            "ecosystem": "npm"
          },
          {
            "name": "resolve-cwd",
            "direct": false,
            "version": "3.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "resolve-from",
            "direct": false,
            "version": "5.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "reusify",
            "direct": false,
            "version": "1.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "rimraf",
            "direct": false,
            "version": "3.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "run-parallel",
            "direct": false,
            "version": "1.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "scheduler",
            "direct": false,
            "version": "0.19.1",
            "ecosystem": "npm"
          },
          {
            "name": "schema-utils",
            "direct": false,
            "version": "3.3.0",
            "ecosystem": "npm"
          },
          {
            "name": "schema-utils",
            "direct": false,
            "version": "4.3.3",
            "ecosystem": "npm"
          },
          {
            "name": "semantic-ui-react",
            "direct": false,
            "version": "0.88.2",
            "ecosystem": "npm"
          },
          {
            "name": "semver",
            "direct": false,
            "version": "6.3.1",
            "ecosystem": "npm"
          },
          {
            "name": "set-function-length",
            "direct": false,
            "version": "1.2.2",
            "ecosystem": "npm"
          },
          {
            "name": "set-function-name",
            "direct": false,
            "version": "2.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "shallow-clone",
            "direct": false,
            "version": "3.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "shallowequal",
            "direct": false,
            "version": "1.1.0",
            "ecosystem": "npm"
          },
          {
            "name": "shebang-command",
            "direct": false,
            "version": "2.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "shebang-regex",
            "direct": false,
            "version": "3.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "sirv",
            "direct": false,
            "version": "2.0.4",
            "ecosystem": "npm"
          },
          {
            "name": "source-map",
            "direct": false,
            "version": "0.6.1",
            "ecosystem": "npm"
          },
          {
            "name": "source-map-support",
            "direct": false,
            "version": "0.5.21",
            "ecosystem": "npm"
          },
          {
            "name": "string-width",
            "direct": false,
            "version": "4.2.3",
            "ecosystem": "npm"
          },
          {
            "name": "strip-ansi",
            "direct": false,
            "version": "6.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "supports-color",
            "direct": false,
            "version": "7.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "supports-color",
            "direct": false,
            "version": "8.1.1",
            "ecosystem": "npm"
          },
          {
            "name": "supports-preserve-symlinks-flag",
            "direct": false,
            "version": "1.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "tapable",
            "direct": false,
            "version": "2.3.3",
            "ecosystem": "npm"
          },
          {
            "name": "terser",
            "direct": false,
            "version": "5.48.0",
            "ecosystem": "npm"
          },
          {
            "name": "terser-webpack-plugin",
            "direct": false,
            "version": "5.6.1",
            "ecosystem": "npm"
          },
          {
            "name": "to-regex-range",
            "direct": false,
            "version": "5.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "totalist",
            "direct": false,
            "version": "3.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "tslib",
            "direct": false,
            "version": "1.14.1",
            "ecosystem": "npm"
          },
          {
            "name": "tslib",
            "direct": false,
            "version": "2.8.1",
            "ecosystem": "npm"
          },
          {
            "name": "tsutils",
            "direct": false,
            "version": "3.21.0",
            "ecosystem": "npm"
          },
          {
            "name": "type-coverage-core",
            "direct": false,
            "version": "2.29.7",
            "ecosystem": "npm"
          },
          {
            "name": "typed-styles",
            "direct": false,
            "version": "0.0.7",
            "ecosystem": "npm"
          },
          {
            "name": "typescript",
            "direct": false,
            "version": "4.9.5",
            "ecosystem": "npm"
          },
          {
            "name": "typescript-coverage-report",
            "direct": false,
            "version": "0.6.4",
            "ecosystem": "npm"
          },
          {
            "name": "undici-types",
            "direct": false,
            "version": "7.24.6",
            "ecosystem": "npm"
          },
          {
            "name": "unicode-canonical-property-names-ecmascript",
            "direct": false,
            "version": "2.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "unicode-match-property-ecmascript",
            "direct": false,
            "version": "2.0.0",
            "ecosystem": "npm"
          },
          {
            "name": "unicode-match-property-value-ecmascript",
            "direct": false,
            "version": "2.2.1",
            "ecosystem": "npm"
          },
          {
            "name": "unicode-property-aliases-ecmascript",
            "direct": false,
            "version": "2.2.0",
            "ecosystem": "npm"
          },
          {
            "name": "update-browserslist-db",
            "direct": false,
            "version": "1.2.3",
            "ecosystem": "npm"
          },
          {
            "name": "uri-js",
            "direct": false,
            "version": "4.4.1",
            "ecosystem": "npm"
          },
          {
            "name": "warning",
            "direct": false,
            "version": "4.0.3",
            "ecosystem": "npm"
          },
          {
            "name": "watchpack",
            "direct": false,
            "version": "2.5.1",
            "ecosystem": "npm"
          },
          {
            "name": "webpack",
            "direct": false,
            "version": "5.107.2",
            "ecosystem": "npm"
          },
          {
            "name": "webpack-bundle-analyzer",
            "direct": false,
            "version": "4.10.2",
            "ecosystem": "npm"
          },
          {
            "name": "webpack-cli",
            "direct": false,
            "version": "4.10.0",
            "ecosystem": "npm"
          },
          {
            "name": "webpack-merge",
            "direct": false,
            "version": "5.10.0",
            "ecosystem": "npm"
          },
          {
            "name": "webpack-sources",
            "direct": false,
            "version": "3.5.0",
            "ecosystem": "npm"
          },
          {
            "name": "which",
            "direct": false,
            "version": "2.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "wildcard",
            "direct": false,
            "version": "2.0.1",
            "ecosystem": "npm"
          },
          {
            "name": "wrappy",
            "direct": false,
            "version": "1.0.2",
            "ecosystem": "npm"
          },
          {
            "name": "ws",
            "direct": false,
            "version": "7.5.11",
            "ecosystem": "npm"
          },
          {
            "name": "yallist",
            "direct": false,
            "version": "3.1.1",
            "ecosystem": "npm"
          },
          {
            "name": "yocto-queue",
            "direct": false,
            "version": "1.2.2",
            "ecosystem": "npm"
          },
          {
            "name": "ext-json",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "flarum/likes",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "flarum/markdown",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "flarum/phpstan",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "flarum/testing",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "fof/best-answer",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "fof/discussion-views",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "fof/gamification",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "fof/pages",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "fof/upload",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "fof/user-directory",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          }
        ],
        "collected": true,
        "truncated": false,
        "total_count": 400,
        "direct_count": 2,
        "indirect_count": 398
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 2,
        "merged_prs": 76,
        "open_issues": 11,
        "closed_ratio": 0.861,
        "closed_issues": 68,
        "closed_unmerged_prs": 26
      },
      "bus_factor": 1,
      "bot_contributors": 1,
      "top_contributors": [
        {
          "type": "User",
          "login": "jaspervriends",
          "commits": 186,
          "avatar_url": "https://avatars.githubusercontent.com/u/4417659?v=4"
        },
        {
          "type": "User",
          "login": "imorland",
          "commits": 35,
          "avatar_url": "https://avatars.githubusercontent.com/u/16573496?v=4"
        },
        {
          "type": "User",
          "login": "DavideIadeluca",
          "commits": 9,
          "avatar_url": "https://avatars.githubusercontent.com/u/146922689?v=4"
        },
        {
          "type": "User",
          "login": "rafaucau",
          "commits": 8,
          "avatar_url": "https://avatars.githubusercontent.com/u/25438601?v=4"
        },
        {
          "type": "User",
          "login": "flarum-bot",
          "commits": 7,
          "avatar_url": "https://avatars.githubusercontent.com/u/39334649?v=4"
        },
        {
          "type": "User",
          "login": "pboschma",
          "commits": 5,
          "avatar_url": "https://avatars.githubusercontent.com/u/25609730?v=4"
        },
        {
          "type": "User",
          "login": "luceos",
          "commits": 4,
          "avatar_url": "https://avatars.githubusercontent.com/u/504687?v=4"
        },
        {
          "type": "User",
          "login": "os555",
          "commits": 2,
          "avatar_url": "https://avatars.githubusercontent.com/u/4261168?v=4"
        },
        {
          "type": "User",
          "login": "ziymed",
          "commits": 2,
          "avatar_url": "https://avatars.githubusercontent.com/u/2720164?v=4"
        },
        {
          "type": "User",
          "login": "davwheat",
          "commits": 1,
          "avatar_url": "https://avatars.githubusercontent.com/u/7406822?v=4"
        }
      ],
      "contributors_sampled": 14,
      "top_contributor_share": 0.707
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "backend.yml",
        "flarum-release-notification.yml",
        "frontend.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [
        "phpstan.neon"
      ],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [
        "yarn.lock"
      ],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 0,
            "reason": "branch protection not enabled on development/release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": 10,
            "reason": "27 out of 27 merged PRs checked by a CI test -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 0/28 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 10,
            "reason": "project has 13 contributing companies or organizations",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": 10,
            "reason": "no dangerous workflow patterns detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 10,
            "reason": "update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 10,
            "reason": "license file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 10,
            "reason": "30 commit(s) and 16 issue activity found in the last 90 days -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 0,
            "reason": "dependency not pinned by hash detected -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "SAST tool is not run on all commits -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 10,
            "reason": "security policy file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 4,
            "reason": "6 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "fc5b9a44a31499e130e700a9625ae522fd1468e6",
        "ran_at": "2026-08-06T21:38:38Z",
        "aggregate_score": 5.2,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "recent_prs": {
        "merged_7d": 0,
        "decided_7d": 0,
        "merged_30d": 0,
        "authors_30d": 0,
        "decided_30d": 0,
        "sample_size": 60,
        "window_days": 30,
        "sample_exhausted": false,
        "authors_probed_30d": 0,
        "newcomer_merged_30d": 0,
        "bot_prs_excluded_30d": 1,
        "newcomer_authors_30d": 0,
        "newcomer_decided_30d": 0
      },
      "ci_last_run_at": "2026-08-05T18:14:17Z",
      "oldest_open_prs": [
        {
          "number": 183,
          "created_at": "2026-08-05T18:14:15Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 184,
          "created_at": "2026-08-06T21:24:11Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ],
      "last_merged_pr_at": "2026-06-16T22:12:07Z",
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": [
        {
          "number": 60,
          "created_at": "2021-06-04T09:21:40Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 65,
          "created_at": "2021-06-19T13:03:13Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 71,
          "created_at": "2021-08-18T09:57:12Z",
          "last_comment_at": "2021-09-13T09:42:52Z",
          "last_comment_author": "Maximysss93"
        },
        {
          "number": 75,
          "created_at": "2022-07-15T23:12:16Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 80,
          "created_at": "2022-11-26T06:32:18Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 82,
          "created_at": "2022-12-23T12:39:01Z",
          "last_comment_at": "2024-10-01T01:55:14Z",
          "last_comment_author": "iamdarkle"
        },
        {
          "number": 85,
          "created_at": "2023-05-07T07:47:55Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 95,
          "created_at": "2024-10-11T11:32:09Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 165,
          "created_at": "2026-06-09T08:10:38Z",
          "last_comment_at": "2026-06-09T08:11:55Z",
          "last_comment_author": "claudiushenrichs"
        },
        {
          "number": 166,
          "created_at": "2026-06-09T08:29:15Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 181,
          "created_at": "2026-07-15T16:26:04Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ]
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/FriendsOfFlarum/seo",
    "host": "github.com",
    "name": "seo",
    "owner": "FriendsOfFlarum"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "excellent",
      "name": "Overall health",
      "note": "The weighted overall 70 is calibrated to 83 on the published index scale (record calibration 2026-08-02).",
      "notes": [
        {
          "code": "overall_calibration",
          "params": {
            "raw": 70,
            "calibrated": 83,
            "calibration": "2026-08-02"
          }
        }
      ],
      "value": 83,
      "inputs": {
        "security": 62,
        "vitality": 78,
        "community": 56,
        "governance": 66,
        "calibration": "2026-08-02",
        "engineering": 90,
        "ai_readiness": 58,
        "weighted_overall_raw": 70
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 78,
        "weight": 0.21,
        "metrics": [
          {
            "key": "development_activity",
            "band": "moderate",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 64,
            "inputs": {
              "commits_last_year": 46,
              "human_commit_share": 0.98,
              "days_since_last_push": 0,
              "active_weeks_last_year": 5
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 0 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "5/52 weeks with commits",
                "points": 3.5,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 5
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "46 commits in the last year",
                "points": 15,
                "status": "partial",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 46
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 16 issue activity found in the last 90 days -- score normalized to 10",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "exceptional",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "releases_count": 39,
              "latest_release_tag": "4.0.0-beta.7",
              "releases_from_tags": false,
              "days_since_latest_release": 50,
              "mean_days_between_releases": 0.9
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "39 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 39
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 50 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 50
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~0.9 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 0.9
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "exceptional",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "maintained",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": null,
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": 50,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "last human commit 50 days ago",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_maintained",
                    "params": {
                      "days": 50
                    }
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "moderate",
        "name": "Community & Adoption",
        "value": 56,
        "weight": 0.17,
        "metrics": [
          {
            "key": "popularity",
            "band": "weak",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 41,
            "inputs": {
              "forks": 25,
              "stars": 50,
              "watchers": 3,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "50 stars",
                "points": 27.4,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 50
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "25 forks",
                "points": 11.5,
                "status": "partial",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 25
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "3 watchers",
                "points": 1.7,
                "status": "partial",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "good",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 77,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "readme_badges": 3,
              "has_contributing": true,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "readme_badge_services": [
                "shields.io"
              ],
              "has_pull_request_template": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (MIT)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "MIT"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 18,
                "status": "met",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 6.3,
                "status": "met",
                "details": [],
                "max_points": 6.3
              }
            ]
          },
          {
            "key": "ecosystem_adoption",
            "band": "weak",
            "name": "Ecosystem adoption (downloads)",
            "note": null,
            "notes": [],
            "value": 49,
            "inputs": {
              "packages": [
                "fof/seo"
              ],
              "dependents": 3,
              "ecosystems": "packagist",
              "total_downloads": 3785,
              "monthly_downloads": 2201
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "2,201 downloads/month across packagist",
                "points": 44.6,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 2201,
                      "ecosystems": "packagist"
                    }
                  }
                ],
                "max_points": 80
              },
              {
                "key": "registry_dependents",
                "name": "Registry dependents",
                "detail": "3 packages depend on it",
                "points": 4,
                "status": "partial",
                "details": [
                  {
                    "code": "registry_dependents",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "good",
        "name": "Sustainability & Governance",
        "value": 66,
        "weight": 0.23,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "weak",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 39,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 14,
              "top_contributor_share": 0.707
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 71% of commits",
                "points": 6.6,
                "status": "partial",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 71
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "14 contributors",
                "points": 13.5,
                "status": "met",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 14
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 13 contributing companies or organizations",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "good",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Newcomer PR acceptance. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "newcomer_pr_acceptance"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 67,
            "inputs": {
              "merged_prs": 76,
              "open_issues": 11,
              "closed_issues": 68,
              "prs_merged_7d": 0,
              "prs_decided_7d": 0,
              "prs_merged_30d": 0,
              "prs_decided_30d": 0,
              "issue_closed_ratio": 0.861,
              "closed_unmerged_prs": 26,
              "first_time_authors_30d": 0,
              "first_time_prs_merged_30d": 0,
              "first_time_prs_decided_30d": 0
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "86% of issues closed",
                "points": 36.2,
                "status": "partial",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 86
                    }
                  }
                ],
                "max_points": 42
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "76/102 decided PRs merged",
                "points": 22.4,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 76,
                      "decided": 102
                    }
                  }
                ],
                "max_points": 30
              },
              {
                "key": "newcomer_pr_acceptance",
                "name": "Newcomer PR acceptance",
                "detail": "no first-time contributor's PR decided in 30d",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_newcomer_prs",
                    "params": {
                      "days": 30
                    }
                  }
                ],
                "max_points": 13
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/28 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "good",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 70,
            "inputs": {
              "followers": 103,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "FriendsOfFlarum",
              "public_repos": 106,
              "account_age_days": 2880
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "organization-owned",
                "points": 30,
                "status": "met",
                "details": [
                  {
                    "code": "owner_organization",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "103 followers of FriendsOfFlarum",
                "points": 14.5,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 103,
                      "login": "FriendsOfFlarum"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "106 public repos, account ~7 yr old",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 106
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 7
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "exceptional",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "fof/seo"
              ],
              "ecosystems": "packagist",
              "any_deprecated": false,
              "min_days_since_publish": 50
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on packagist",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "packagist"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 50 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 50
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "47 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 47
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "excellent",
        "name": "Engineering Quality",
        "value": 90,
        "weight": 0.19,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "excellent",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 84,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "3 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": "phpstan.neon",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "phpstan.neon"
                    }
                  }
                ],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "27 out of 27 merged PRs checked by a CI test -- score normalized to 10",
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "exceptional",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "topics": [
                "flarum",
                "flarum-seo",
                "flarum-forum",
                "seo-tags",
                "flagrow"
              ],
              "has_wiki": true,
              "homepage": "https://discuss.flarum.org/d/18316-flarum-seo",
              "has_readme": true,
              "has_docs_dir": true,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 25,
                "status": "met",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": "https://discuss.flarum.org/d/18316-flarum-seo",
                "points": 15,
                "status": "met",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": "5 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 5
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "moderate",
        "name": "Security",
        "value": 62,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "moderate",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Packaging, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "packaging",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 52,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 5.2
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "no binaries found in the repo",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "branch protection not enabled on development/release branches",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "27 out of 27 merged PRs checked by a CI test -- score normalized to 10",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 0/28 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 13 contributing companies or organizations",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "update tool detected",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file detected",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "30 commit(s) and 16 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is not run on all commits -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "6 existing vulnerabilities detected",
                "points": 3,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "dependency_advisories",
            "band": "exceptional",
            "name": "Dependency advisories",
            "note": "Excluded from scoring (no data or not applicable): Indirect dependencies free of known advisories, No advisories left outstanding. Remaining weights renormalized. Matched 387 resolved dependencies against OSV; 13 could not be assessed (no resolved version, an unsupported ecosystem, or beyond the reported package list). This repository publishes no package the index resolves, so the repository dependency graph was assessed instead. That graph mixes development and test pins with shipped dependencies, so only the declared runtime dependencies are scored; transitive findings are reported as context and excluded from the score. Reachability is not analyzed.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "indirect_dependencies_free_of_known_advisories",
                    "no_advisories_left_outstanding"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              },
              {
                "code": "advisories_scope_repository",
                "params": {
                  "assessed": 387
                }
              },
              {
                "code": "advisories_unassessed",
                "params": {
                  "count": 13
                }
              },
              {
                "code": "advisories_repo_graph_caveat",
                "params": {}
              },
              {
                "code": "advisories_reachability",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "source": "osv",
              "advisories": 9,
              "affected_packages": 3,
              "assessed_packages": 387,
              "unassessed_packages": 13,
              "affected_by_severity": "high 3",
              "direct_affected_packages": 0
            },
            "components": [
              {
                "key": "direct_dependencies_free_of_known_advisories",
                "name": "Direct dependencies free of known advisories",
                "detail": "no direct dependency carries a known advisory",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "no_direct_advisories",
                    "params": {}
                  }
                ],
                "max_points": 35
              },
              {
                "key": "indirect_dependencies_free_of_known_advisories",
                "name": "Indirect dependencies free of known advisories",
                "detail": "transitive set not separable from development and test dependencies in this scope",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "advisories_scope_not_separable",
                    "params": {}
                  }
                ],
                "max_points": 25
              },
              {
                "key": "no_advisories_left_outstanding",
                "name": "No advisories left outstanding",
                "detail": "no advisory carries a publication date",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "advisories_no_publication_date",
                    "params": {}
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "malicious_dependencies",
            "band": "exceptional",
            "name": "Malicious dependencies",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "source": "osv",
              "meaning": "reported as a malicious package by the OpenSSF corpus; the remedy is removal or moving off the compromised name, never an upgrade of the same artifact. Versions the registry has since pulled are listed but not scored",
              "packages": [],
              "red_flag": false,
              "assessed_packages": 387,
              "malicious_packages": 0,
              "direct_malicious_packages": 0,
              "withdrawn_malicious_packages": 0,
              "installable_malicious_packages": 0
            },
            "components": [
              {
                "key": "no_dependency_reported_as_a_malicious_package",
                "name": "No dependency reported as a malicious package",
                "detail": "no dependency is reported as a malicious package",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "no_malicious_dependencies",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          },
          {
            "key": "high_risk_jurisdiction_exposure",
            "band": "exceptional",
            "name": "High-Risk Jurisdiction Exposure",
            "note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
            "notes": [
              {
                "code": "jurisdiction_evidence_limits",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "meaning": "self-published location evidence; not nationality or citizenship",
              "red_flag": false,
              "exposures": [],
              "policy_countries": [
                "Russia",
                "Iran",
                "North Korea"
              ],
              "commit_weight_rule": {
                "min_commits": 50,
                "min_commit_share": 0.1
              },
              "review_only_matches": 0,
              "below_threshold_exposures": [],
              "assessed_self_published_locations": 22
            },
            "components": [
              {
                "key": "policy_exposure_multiplier",
                "name": "Policy exposure multiplier",
                "detail": "no confirmed policy-scope location match",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "jurisdiction_no_match",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "moderate",
        "name": "AI Readiness",
        "value": 58,
        "weight": 0.04,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "weak",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.755,
              "agent_instruction_files": [],
              "agent_instruction_max_bytes": null
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "no CLAUDE.md / AGENTS.md / editor rules",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_instructions",
                    "params": {}
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "74 of 98 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 74,
                      "sampled": 98
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "moderate",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 62,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "yarn.lock"
              ],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [
                "js/tsconfig.json"
              ],
              "agent_commit_share": 0,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0.02
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": "phpstan.neon",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "phpstan.neon"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "js/tsconfig.json",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "js/tsconfig.json"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": "lockfile",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "lockfile"
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "no agent-authored commits among the last 100",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_authored_commits",
                    "params": {
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "2 of the last 100 commits are automated dependency updates",
                "points": 8,
                "status": "met",
                "details": [
                  {
                    "code": "dependency_bot_commits",
                    "params": {
                      "count": 2,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "excellent",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 82,
            "inputs": {
              "primary_language": "PHP",
              "largest_source_bytes": 35992,
              "source_files_sampled": 110,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "PHP with type-check config (js/tsconfig.json)",
                "points": 27,
                "status": "partial",
                "details": [
                  {
                    "code": "typecheck_config_language",
                    "params": {
                      "files": "js/tsconfig.json",
                      "language": "PHP"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/110 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 110,
                      "oversized": 0
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? Carries a deliberately small weight: agent tooling is a real maintenance signal, but its absence must never gate the top of the scale (calibration saturates at raw 91, so 100/100 remains reachable with AI Readiness at zero)."
      }
    ],
    "classification": {
      "top": [],
      "labels": [],
      "scores": {
        "library": 2.4
      },
      "primary": null,
      "evidence": [
        {
          "tier": "distribution",
          "label": "library",
          "source": "registry:packagist",
          "weight": 6
        },
        {
          "tier": "declared",
          "label": "library",
          "source": "npm.private",
          "weight": -3.6
        }
      ],
      "artifacts": [
        {
          "path": "composer.json",
          "labels": [],
          "ecosystem": "packagist"
        },
        {
          "path": "js/package.json",
          "labels": [],
          "ecosystem": "npm"
        }
      ],
      "confidence": "none",
      "host_extension": false,
      "runs_as_process": false,
      "consumed_by_code": false
    },
    "metrics_version": "2.5.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token"
  ],
  "report_type": "repository",
  "generated_at": "2026-08-06T21:39:07.617280Z",
  "schema_version": "0.31.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/f/FriendsOfFlarum/seo.svg",
  "full_name": "FriendsOfFlarum/seo",
  "license_state": "standard",
  "license_spdx": "MIT"
}

Scores are signals, not warranties. They reflect publicly visible practices on GitHub — not a code audit, and not a security guarantee.

Missing data is excluded and weights renormalized, never scored as zero. Methodology is versioned and open: metrics v2.5.0, schema v0.31.0 — full methodology · metrics wiki.

How one result sits in the wider record: aggregate statisticsPackagist.