@rankcli/agent-runtime 0.0.30 → 0.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3622,6 +3622,7 @@ var RECIPES = {
3622
3622
  jekyll: null,
3623
3623
  eleventy: null,
3624
3624
  pelican: null,
3625
+ sphinx: null,
3625
3626
  // Unknown
3626
3627
  unknown: null
3627
3628
  };
@@ -7741,234 +7742,156 @@ var ISSUE_DEFINITIONS = {
7741
7742
  // src/audit/engine.ts
7742
7743
  init_http();
7743
7744
 
7744
- // src/frameworks/detector.ts
7745
- var HTML_SIGNATURES = [
7746
- // ============ JavaScript/TypeScript Frameworks ============
7747
- // Next.js
7748
- {
7749
- framework: "nextjs",
7750
- language: "typescript",
7751
- patterns: [/__next/, /_next\//, /data-nscript/, /__NEXT_DATA__/, /next\/script/],
7752
- confidence: "high"
7753
- },
7754
- // Nuxt
7755
- {
7756
- framework: "nuxt",
7757
- language: "typescript",
7758
- patterns: [/__nuxt/, /_nuxt\//, /data-n-head/, /nuxt-link/, /__NUXT__/],
7759
- confidence: "high"
7760
- },
7761
- // Gatsby
7762
- {
7763
- framework: "gatsby",
7764
- language: "javascript",
7765
- patterns: [/___gatsby/, /gatsby-/, /gatsby-image/, /data-gatsby/],
7766
- confidence: "high"
7767
- },
7768
- // Astro
7769
- {
7770
- framework: "astro",
7771
- language: "typescript",
7772
- patterns: [/astro-/, /data-astro/, /_astro\//],
7773
- confidence: "high"
7774
- },
7775
- // SvelteKit
7776
- {
7777
- framework: "sveltekit",
7778
- language: "typescript",
7779
- patterns: [/__sveltekit/, /_app\/immutable/, /svelte-kit/],
7780
- confidence: "high"
7781
- },
7782
- // Svelte (standalone)
7783
- {
7784
- framework: "svelte",
7785
- language: "javascript",
7786
- patterns: [/svelte-\w+/, /__svelte/],
7787
- confidence: "medium"
7788
- },
7789
- // Remix
7790
- {
7791
- framework: "remix",
7792
- language: "typescript",
7793
- patterns: [/__remix/, /data-remix/],
7794
- confidence: "high"
7795
- },
7796
- // Solid.js
7797
- {
7798
- framework: "solidjs",
7799
- language: "typescript",
7800
- patterns: [/data-solid/, /_solid/],
7801
- confidence: "high"
7802
- },
7803
- // Qwik
7804
- {
7805
- framework: "qwik",
7806
- language: "typescript",
7807
- patterns: [/q:container/, /q:base/, /q:version/, /q:id=/, /qwik/],
7808
- confidence: "high"
7809
- },
7810
- // Fresh (Deno)
7811
- {
7812
- framework: "fresh",
7813
- language: "typescript",
7814
- patterns: [/_frsh\//, /data-fresh/],
7815
- confidence: "high"
7816
- },
7817
- // React (generic - lower priority)
7745
+ // src/frameworks/signals.ts
7746
+ var WEIGHT = { DECISIVE: 100, STRONG: 45, WEAK: 15 };
7747
+ var SCORE_FLOOR = 45;
7748
+ var SIGNALS = [
7749
+ // ---- Meta-frameworks: JavaScript ------------------------------------------------
7750
+ { framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-build-dir", html: /\/_next\/(static|image|data)\// },
7751
+ { framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-data", html: /__NEXT_DATA__|self\.__next_f/ },
7752
+ { framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-mount", html: /<div id=["']__next["']/ },
7753
+ { framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-powered-by", header: /^x-powered-by: next\.js/ },
7754
+ { framework: "nuxt", language: "typescript", weight: WEIGHT.DECISIVE, name: "nuxt-build-dir", html: /\/_nuxt\// },
7755
+ { framework: "nuxt", language: "typescript", weight: WEIGHT.DECISIVE, name: "nuxt-payload", html: /__NUXT__|__NUXT_DATA__|<div id="__nuxt"/ },
7756
+ { framework: "nuxt", language: "typescript", weight: WEIGHT.DECISIVE, name: "nuxt-powered-by", header: /^x-powered-by: nuxt/ },
7757
+ { framework: "astro", language: "typescript", weight: WEIGHT.DECISIVE, name: "astro-build-dir", html: /\/_astro\// },
7758
+ { framework: "astro", language: "typescript", weight: WEIGHT.DECISIVE, name: "astro-island", html: /<astro-island|astro-slot|data-astro-(cid|source-file|transition)/ },
7759
+ { framework: "astro", language: "typescript", weight: WEIGHT.STRONG, name: "astro-generator", html: /name=["']?generator["']?[^>]*content=["']?Astro/i },
7760
+ { framework: "sveltekit", language: "typescript", weight: WEIGHT.DECISIVE, name: "sveltekit-immutable", html: /\/_app\/immutable\// },
7761
+ { framework: "sveltekit", language: "typescript", weight: WEIGHT.DECISIVE, name: "sveltekit-runtime", html: /__sveltekit|kit\.start\(/ },
7762
+ { framework: "remix", language: "typescript", weight: WEIGHT.DECISIVE, name: "remix-context", html: /__remixContext|__remixManifest|__remixRouteModules/ },
7763
+ { framework: "remix", language: "typescript", weight: WEIGHT.STRONG, name: "remix-build-dir", html: /\/build\/_shared\/|\/build\/entry\.client-/ },
7764
+ { framework: "gatsby", language: "javascript", weight: WEIGHT.DECISIVE, name: "gatsby-runtime", html: /___gatsby|window\.___chunkMapping/ },
7765
+ { framework: "gatsby", language: "javascript", weight: WEIGHT.STRONG, name: "gatsby-page-data", html: /\/page-data\/(app-data\.json|sq\/)/ },
7766
+ { framework: "qwik", language: "typescript", weight: WEIGHT.DECISIVE, name: "qwik-container", html: /q:container|q:base|q:version|qwikloader/ },
7767
+ { framework: "fresh", language: "typescript", weight: WEIGHT.DECISIVE, name: "fresh-build-dir", html: /\/_frsh\// },
7768
+ { framework: "solidjs", language: "typescript", weight: WEIGHT.STRONG, name: "solid-hydration", html: /_\$HY\b|data-hk=["']/ },
7769
+ // ---- Base UI libraries: weaker by nature, and that is the point -----------------
7770
+ { framework: "angular", language: "typescript", weight: WEIGHT.DECISIVE, name: "angular-version", html: /ng-version=["']/ },
7771
+ { framework: "angular", language: "typescript", weight: WEIGHT.DECISIVE, name: "angular-content-attr", html: /_ngcontent-|_nghost-/ },
7772
+ { framework: "react", language: "javascript", weight: WEIGHT.STRONG, name: "react-root-marker", html: /data-reactroot|__reactContainer\$|__REACT_DEVTOOLS/ },
7773
+ // A Vite-built SPA: an empty mount point plus Vite's hashed entry bundle. Two weak facts
7774
+ // that together mean something; either one alone does not.
7775
+ { framework: "react", language: "javascript", weight: WEIGHT.WEAK, name: "react-empty-root", html: /<div id="root"><\/div>/ },
7776
+ { framework: "react", language: "javascript", weight: WEIGHT.WEAK, name: "react-refresh", html: /RefreshRuntime|react-refresh/ },
7818
7777
  {
7819
7778
  framework: "react",
7820
7779
  language: "javascript",
7821
- // data-reactroot/data-react-helmet are React <=15 hydration markers -
7822
- // React 18's createRoot() never adds them, so every modern CRA/Vite
7823
- // React app (an empty <div id="root">) went undetected from HTML alone.
7824
- patterns: [/data-reactroot/, /data-react-helmet/, /_react/, /react-app/, /<div id="root"><\/div>/],
7825
- confidence: "medium"
7826
- },
7827
- // Vue (generic - lower priority)
7828
- {
7829
- framework: "vue",
7830
- language: "javascript",
7831
- patterns: [/data-v-[a-f0-9]+/, /v-cloak/, /__vue/],
7832
- confidence: "medium"
7833
- },
7834
- // Angular
7835
- {
7836
- framework: "angular",
7837
- language: "typescript",
7838
- patterns: [/ng-version/, /_ngcontent/, /ng-\w+=/, /\[ng/],
7839
- confidence: "high"
7840
- },
7841
- // ============ Hypermedia Frameworks ============
7842
- // HTMX
7843
- {
7844
- framework: "htmx",
7845
- language: "unknown",
7846
- patterns: [/hx-get/, /hx-post/, /hx-trigger/, /hx-swap/, /htmx\.org/],
7847
- confidence: "high"
7848
- },
7849
- // Hotwire/Turbo
7850
- {
7851
- framework: "hotwire",
7852
- language: "ruby",
7853
- patterns: [/turbo-frame/, /turbo-stream/, /data-turbo/],
7854
- confidence: "high"
7855
- },
7856
- // ============ PHP Frameworks ============
7857
- // WordPress
7858
- {
7859
- framework: "wordpress",
7860
- language: "php",
7861
- // Technical signatures only. These patterns run over the whole page, so the old
7862
- // /wordpress/i matched the word anywhere in the copy: rankcli.dev's own "detects
7863
- // Rails, Django, Laravel, and WordPress" line was enough to detect its React SPA as
7864
- // WordPress and then tell it to install an SEO plugin. A generator meta tag is the
7865
- // one place the page says it about itself.
7866
- patterns: [/wp-content/, /wp-includes/, /wp-json/, /name=["']generator["'][^>]*content=["']WordPress/i],
7867
- confidence: "high"
7868
- },
7869
- // Laravel
7870
- {
7871
- framework: "laravel",
7872
- language: "php",
7873
- // /laravel/i had the same problem as WordPress above - it matched the word in prose.
7874
- patterns: [/laravel_session/, /XSRF-TOKEN/, /@csrf/],
7875
- confidence: "medium"
7876
- },
7877
- // Symfony
7878
- {
7879
- framework: "symfony",
7880
- language: "php",
7881
- // Likewise /symfony/, which matched any lowercase mention.
7882
- patterns: [/sf2/, /_sf_/, /profiler\.css/],
7883
- confidence: "medium"
7884
- },
7885
- // ============ Ruby Frameworks ============
7886
- // Rails
7887
- {
7888
- framework: "rails",
7889
- language: "ruby",
7890
- patterns: [/csrf-token/, /turbolinks/, /data-turbo/, /rails-ujs/, /action-cable/],
7891
- confidence: "medium"
7892
- },
7893
- // ============ Python Frameworks ============
7894
- // Django
7895
- {
7896
- framework: "django",
7897
- language: "python",
7898
- patterns: [/csrfmiddlewaretoken/, /django/, /__debug__\//],
7899
- confidence: "medium"
7900
- },
7901
- // Flask
7902
- {
7903
- framework: "flask",
7904
- language: "python",
7905
- patterns: [/flask/, /werkzeug/i],
7906
- confidence: "low"
7907
- },
7908
- // FastAPI
7909
- {
7910
- framework: "fastapi",
7911
- language: "python",
7912
- patterns: [/fastapi/, /openapi\.json/],
7913
- confidence: "low"
7914
- },
7915
- // ============ Java Frameworks ============
7916
- // Spring Boot
7917
- {
7918
- framework: "spring",
7919
- language: "java",
7920
- // /th:/ matched "width:" and "height:" in inline CSS, which made almost every styled
7921
- // page on the web a Spring Boot app - rankcli.dev included, once WordPress stopped
7922
- // matching first. Thymeleaf attributes are what th: actually means.
7923
- patterns: [/\sth:[a-z-]+=/, /thymeleaf/, /_csrf/],
7924
- confidence: "medium"
7925
- },
7926
- // ============ C# Frameworks ============
7927
- // ASP.NET Core
7928
- {
7929
- framework: "aspnet",
7930
- language: "csharp",
7931
- patterns: [/aspnetcore/, /__RequestVerificationToken/, /_framework\/blazor/],
7932
- confidence: "medium"
7933
- },
7934
- // ============ Elixir Frameworks ============
7935
- // Phoenix
7936
- {
7937
- framework: "phoenix",
7938
- language: "elixir",
7939
- patterns: [/phx-/, /live-socket/, /data-phx/],
7940
- confidence: "high"
7941
- },
7942
- // ============ Static Site Generators ============
7943
- // Hugo
7944
- {
7945
- framework: "hugo",
7946
- language: "go",
7947
- patterns: [/name=["']generator["'][^>]*content=["']Hugo/i],
7948
- confidence: "medium"
7780
+ weight: WEIGHT.STRONG,
7781
+ name: "react-vite-spa",
7782
+ // Vite's entry chunk plus React's conventional mount point. Medium confidence by
7783
+ // construction: #app instead of #root stays unknown, because Vue, Solid and others use it.
7784
+ // Not `<div id="root"></div>`: a prerendered SPA has content inside its mount point, which
7785
+ // is what RankCLI's own site does and what the first version of this signal missed.
7786
+ allHtml: [/<script[^>]+src=["'][^"']+\.js["']/, /<div id="root"[\s>]/]
7949
7787
  },
7950
- // Jekyll
7951
- {
7952
- framework: "jekyll",
7953
- language: "ruby",
7954
- patterns: [/name=["']generator["'][^>]*content=["']Jekyll/i],
7955
- confidence: "medium"
7956
- },
7957
- // Eleventy
7958
- {
7959
- framework: "eleventy",
7960
- language: "javascript",
7961
- patterns: [/11ty/, /eleventy/, /generator.*eleventy/i],
7962
- confidence: "medium"
7963
- },
7964
- // Pelican
7965
- {
7966
- framework: "pelican",
7967
- language: "python",
7968
- patterns: [/pelican/, /generator.*pelican/i],
7969
- confidence: "medium"
7970
- }
7788
+ { framework: "vue", language: "javascript", weight: WEIGHT.DECISIVE, name: "vitepress-runtime", html: /__VP_HASH_MAP__|__VP_SITE_DATA__/ },
7789
+ { framework: "vue", language: "javascript", weight: WEIGHT.DECISIVE, name: "vue-scoped-style", html: /data-v-[0-9a-f]{6,10}\b/ },
7790
+ { framework: "vue", language: "javascript", weight: WEIGHT.WEAK, name: "vue-cloak", html: /v-cloak|<div id="app"><\/div>/ },
7791
+ { framework: "svelte", language: "javascript", weight: WEIGHT.WEAK, name: "svelte-class", html: /class=["'][^"']*svelte-[0-9a-z]{6}/ },
7792
+ // ---- Ruby -----------------------------------------------------------------------
7793
+ // csrf-token alone is emitted by Rails, Laravel, Phoenix and Django. csrf-param is Rails.
7794
+ { framework: "rails", language: "ruby", weight: WEIGHT.DECISIVE, name: "rails-csrf-param", html: /name=["']csrf-param["']/ },
7795
+ { framework: "rails", language: "ruby", weight: WEIGHT.STRONG, name: "rails-ujs", html: /rails-ujs|turbolinks|data-remote=["']true["']|action_cable|actioncable/ },
7796
+ { framework: "rails", language: "ruby", weight: WEIGHT.STRONG, name: "rails-session-cookie", header: /^set-cookie: [^;]*_session=/ },
7797
+ { framework: "rails", language: "ruby", weight: WEIGHT.STRONG, name: "rails-app-server", header: /^(server|x-powered-by): (puma|passenger|unicorn|phusion)/ },
7798
+ { framework: "hotwire", language: "ruby", weight: WEIGHT.STRONG, name: "turbo-elements", html: /<turbo-frame|<turbo-stream|data-turbo-track/ },
7799
+ // ---- Python ---------------------------------------------------------------------
7800
+ { framework: "django", language: "python", weight: WEIGHT.DECISIVE, name: "django-csrf-input", html: /name=["']csrfmiddlewaretoken["']/ },
7801
+ { framework: "django", language: "python", weight: WEIGHT.DECISIVE, name: "django-csrf-cookie", header: /^set-cookie: csrftoken=/ },
7802
+ { framework: "django", language: "python", weight: WEIGHT.STRONG, name: "django-admin-static", html: /\/static\/admin\/(css|js)\// },
7803
+ { framework: "django", language: "python", weight: WEIGHT.WEAK, name: "django-wsgi-server", header: /^(server|x-powered-by): (gunicorn|uvicorn|daphne)/ },
7804
+ { framework: "flask", language: "python", weight: WEIGHT.STRONG, name: "flask-server", header: /^server: werkzeug/ },
7805
+ { framework: "fastapi", language: "python", weight: WEIGHT.STRONG, name: "fastapi-docs", html: /swagger-ui-bundle\.js[\s\S]{0,200}openapi\.json/ },
7806
+ // ---- PHP ------------------------------------------------------------------------
7807
+ { framework: "wordpress", language: "php", weight: WEIGHT.DECISIVE, name: "wp-paths", html: /\/wp-content\/|\/wp-includes\/|\/wp-json\// },
7808
+ { framework: "wordpress", language: "php", weight: WEIGHT.DECISIVE, name: "wp-generator", html: /name=["']?generator["']?[^>]*content=["']?WordPress/i },
7809
+ { framework: "wordpress", language: "php", weight: WEIGHT.STRONG, name: "wp-powered-by", header: /^x-powered-by: wordpress/ },
7810
+ { framework: "laravel", language: "php", weight: WEIGHT.DECISIVE, name: "laravel-session-cookie", header: /^set-cookie: laravel_session=/ },
7811
+ { framework: "laravel", language: "php", weight: WEIGHT.STRONG, name: "laravel-xsrf-cookie", header: /^set-cookie: XSRF-TOKEN=/i },
7812
+ { framework: "laravel", language: "php", weight: WEIGHT.STRONG, name: "laravel-livewire", html: /livewire|wire:(click|model|submit)/i },
7813
+ { framework: "laravel", language: "php", weight: WEIGHT.WEAK, name: "laravel-vite-build", html: /\/build\/assets\/app-[A-Za-z0-9_-]{8}\.js/ },
7814
+ { framework: "symfony", language: "php", weight: WEIGHT.DECISIVE, name: "symfony-profiler", html: /\/_profiler\/|sf-toolbar|sf-dump/ },
7815
+ { framework: "symfony", language: "php", weight: WEIGHT.STRONG, name: "symfony-cookie", header: /^set-cookie: (sfsession|symfony)/i },
7816
+ // ---- Elixir ---------------------------------------------------------------------
7817
+ { framework: "phoenix", language: "elixir", weight: WEIGHT.DECISIVE, name: "phoenix-liveview", html: /phx-(click|submit|change|value|hook|socket|main|track-static)/ },
7818
+ { framework: "phoenix", language: "elixir", weight: WEIGHT.STRONG, name: "phoenix-cowboy", header: /^server: cowboy/ },
7819
+ // ---- Java / C# / Go -------------------------------------------------------------
7820
+ { framework: "spring", language: "java", weight: WEIGHT.STRONG, name: "thymeleaf-attr", html: /\sth:[a-z-]+=/ },
7821
+ { framework: "spring", language: "java", weight: WEIGHT.STRONG, name: "spring-server", header: /^server: (apache-coyote|tomcat|jetty)/ },
7822
+ { framework: "aspnet", language: "csharp", weight: WEIGHT.DECISIVE, name: "aspnet-verification-token", html: /__RequestVerificationToken|__VIEWSTATE/ },
7823
+ { framework: "aspnet", language: "csharp", weight: WEIGHT.STRONG, name: "aspnet-server", header: /^(server: kestrel|x-powered-by: asp\.net|x-aspnet-version:)/ },
7824
+ // "gin" used to be tested as a substring of the Server header, which matches nginx - the
7825
+ // single most common web server there is. Anchored to the whole value now.
7826
+ { framework: "go", language: "go", weight: WEIGHT.STRONG, name: "go-server", header: /^server: (gin|echo|fiber|gorilla)(\/|$| )/ },
7827
+ // ---- Hypermedia -----------------------------------------------------------------
7828
+ { framework: "htmx", language: "unknown", weight: WEIGHT.STRONG, name: "htmx-attrs", html: /\shx-(get|post|put|delete|trigger|swap|target)=/ },
7829
+ { framework: "htmx", language: "unknown", weight: WEIGHT.DECISIVE, name: "htmx-script", html: /htmx(\.min)?\.js|unpkg\.com\/htmx/ },
7830
+ // ---- Static site generators: they announce themselves -----------------------------
7831
+ { framework: "hugo", language: "go", weight: WEIGHT.DECISIVE, name: "hugo-generator", html: /name=["']?generator["']?[^>]*content=["']?Hugo/i },
7832
+ { framework: "hugo", language: "go", weight: WEIGHT.STRONG, name: "hugo-generator-header", header: /^x-generator: hugo/ },
7833
+ { framework: "jekyll", language: "ruby", weight: WEIGHT.DECISIVE, name: "jekyll-generator", html: /name=["']?generator["']?[^>]*content=["']?Jekyll/i },
7834
+ { framework: "eleventy", language: "javascript", weight: WEIGHT.DECISIVE, name: "eleventy-generator", html: /name=["']?generator["']?[^>]*content=["']?Eleventy/i },
7835
+ { framework: "pelican", language: "python", weight: WEIGHT.DECISIVE, name: "pelican-generator", html: /name=["']?generator["']?[^>]*content=["']?Pelican/i },
7836
+ // Sphinx: what nearly every Python project's documentation is built with, and the shape
7837
+ // flask.palletsprojects.com turned out to be. Structural markers only - not the word
7838
+ // "sphinx", which appears in plenty of prose.
7839
+ { framework: "sphinx", language: "python", weight: WEIGHT.DECISIVE, name: "sphinx-doc-options", html: /_static\/documentation_options\.js/ },
7840
+ { framework: "sphinx", language: "python", weight: WEIGHT.STRONG, name: "sphinx-theme-markup", html: /class=["'][^"']*sphinxsidebar|sphinx-highlight|_static\/searchtools\.js/ },
7841
+ { framework: "sphinx", language: "python", weight: WEIGHT.STRONG, name: "docutils-generator", html: /name=["']?generator["']?[^>]*content=["']?Docutils/i }
7971
7842
  ];
7843
+ function headerLines(headers) {
7844
+ if (!headers) return "";
7845
+ return Object.entries(headers).map(([k, v]) => `${k.toLowerCase()}: ${String(v).toLowerCase()}`).join("\n");
7846
+ }
7847
+ function detectFromEvidence(input) {
7848
+ const html = input.html ?? "";
7849
+ const headers = headerLines(input.headers);
7850
+ const byFramework = /* @__PURE__ */ new Map();
7851
+ for (const signal of SIGNALS) {
7852
+ const headerHit = signal.header && headers && new RegExp(
7853
+ signal.header.source,
7854
+ signal.header.flags.includes("m") ? signal.header.flags : `${signal.header.flags}m`
7855
+ ).test(headers);
7856
+ const allHit = signal.allHtml && html && signal.allHtml.every((re) => re.test(html));
7857
+ const hit = signal.html && html && signal.html.test(html) || headerHit || allHit;
7858
+ if (!hit) continue;
7859
+ const existing = byFramework.get(signal.framework);
7860
+ if (existing) {
7861
+ existing.score += signal.weight;
7862
+ existing.matched.push(signal.name);
7863
+ } else {
7864
+ byFramework.set(signal.framework, {
7865
+ framework: signal.framework,
7866
+ language: signal.language,
7867
+ score: signal.weight,
7868
+ matched: [signal.name]
7869
+ });
7870
+ }
7871
+ }
7872
+ const ranked = [...byFramework.values()].sort((a, b) => b.score - a.score || a.framework.localeCompare(b.framework));
7873
+ const best = ranked[0];
7874
+ if (!best || best.score < SCORE_FLOOR) {
7875
+ return {
7876
+ framework: "unknown",
7877
+ language: "unknown",
7878
+ confidence: "low",
7879
+ score: best?.score ?? 0,
7880
+ matched: best?.matched ?? [],
7881
+ alternatives: ranked
7882
+ };
7883
+ }
7884
+ return {
7885
+ framework: best.framework,
7886
+ language: best.language,
7887
+ confidence: best.score >= WEIGHT.DECISIVE ? "high" : "medium",
7888
+ score: best.score,
7889
+ matched: best.matched,
7890
+ alternatives: ranked.slice(1)
7891
+ };
7892
+ }
7893
+
7894
+ // src/frameworks/detector.ts
7972
7895
  var PACKAGE_DEPENDENCIES = [
7973
7896
  // Meta-frameworks (check first - more specific)
7974
7897
  { framework: "nextjs", language: "typescript", packages: ["next"], meta_framework: "react" },
@@ -8018,19 +7941,14 @@ var ELIXIR_DEPENDENCIES = [
8018
7941
  { framework: "phoenix", language: "elixir", packages: [":phoenix", "phoenix"] }
8019
7942
  ];
8020
7943
  function detectFromHtml(html) {
8021
- for (const signature of HTML_SIGNATURES) {
8022
- for (const pattern of signature.patterns) {
8023
- if (pattern.test(html)) {
8024
- return {
8025
- framework: signature.framework,
8026
- language: signature.language,
8027
- confidence: signature.confidence,
8028
- detected_from: "html"
8029
- };
8030
- }
8031
- }
8032
- }
8033
- return null;
7944
+ const result = detectFromEvidence({ html });
7945
+ if (result.framework === "unknown") return null;
7946
+ return {
7947
+ framework: result.framework,
7948
+ language: result.language,
7949
+ confidence: result.confidence,
7950
+ detected_from: "html"
7951
+ };
8034
7952
  }
8035
7953
  function detectFromPackageJson(packageJson) {
8036
7954
  const allDeps = {
@@ -8172,52 +8090,14 @@ function detectFromMixExs(mixContent) {
8172
8090
  return null;
8173
8091
  }
8174
8092
  function detectFromHeaders(headers) {
8175
- const poweredBy = headers["x-powered-by"]?.toLowerCase() || "";
8176
- const server = headers["server"]?.toLowerCase() || "";
8177
- const generator = headers["x-generator"]?.toLowerCase() || "";
8178
- if (poweredBy.includes("next.js") || poweredBy.includes("next")) {
8179
- return { framework: "nextjs", language: "typescript", confidence: "high", detected_from: "headers" };
8180
- }
8181
- if (poweredBy.includes("nuxt")) {
8182
- return { framework: "nuxt", language: "typescript", confidence: "high", detected_from: "headers" };
8183
- }
8184
- if (poweredBy.includes("remix")) {
8185
- return { framework: "remix", language: "typescript", confidence: "high", detected_from: "headers" };
8186
- }
8187
- if (poweredBy.includes("phusion") || poweredBy.includes("passenger") || poweredBy.includes("puma") || poweredBy.includes("unicorn")) {
8188
- return { framework: "rails", language: "ruby", confidence: "medium", detected_from: "headers" };
8189
- }
8190
- if (poweredBy.includes("gunicorn") || poweredBy.includes("uvicorn") || poweredBy.includes("daphne")) {
8191
- return null;
8192
- }
8193
- if (poweredBy.includes("php")) {
8194
- return null;
8195
- }
8196
- if (server.includes("tomcat") || server.includes("jetty") || poweredBy.includes("spring")) {
8197
- return { framework: "spring", language: "java", confidence: "medium", detected_from: "headers" };
8198
- }
8199
- if (poweredBy.includes("asp.net") || server.includes("kestrel")) {
8200
- return { framework: "aspnet", language: "csharp", confidence: "high", detected_from: "headers" };
8201
- }
8202
- if (poweredBy.includes("phoenix") || poweredBy.includes("cowboy")) {
8203
- return { framework: "phoenix", language: "elixir", confidence: "medium", detected_from: "headers" };
8204
- }
8205
- if (server.includes("gin") || server.includes("echo") || server.includes("fiber")) {
8206
- return { framework: "go", language: "go", confidence: "medium", detected_from: "headers" };
8207
- }
8208
- if (generator.includes("hugo")) {
8209
- return { framework: "hugo", language: "go", confidence: "high", detected_from: "headers" };
8210
- }
8211
- if (generator.includes("jekyll")) {
8212
- return { framework: "jekyll", language: "ruby", confidence: "high", detected_from: "headers" };
8213
- }
8214
- if (generator.includes("eleventy") || generator.includes("11ty")) {
8215
- return { framework: "eleventy", language: "javascript", confidence: "high", detected_from: "headers" };
8216
- }
8217
- if (generator.includes("pelican")) {
8218
- return { framework: "pelican", language: "python", confidence: "high", detected_from: "headers" };
8219
- }
8220
- return null;
8093
+ const result = detectFromEvidence({ headers });
8094
+ if (result.framework === "unknown") return null;
8095
+ return {
8096
+ framework: result.framework,
8097
+ language: result.language,
8098
+ confidence: result.confidence,
8099
+ detected_from: "headers"
8100
+ };
8221
8101
  }
8222
8102
  function detectFramework(options) {
8223
8103
  if (options.packageJson) {
@@ -8252,13 +8132,16 @@ function detectFramework(options) {
8252
8132
  const result = detectFromMixExs(options.mixExsContent);
8253
8133
  if (result) return result;
8254
8134
  }
8255
- if (options.headers) {
8256
- const result = detectFromHeaders(options.headers);
8257
- if (result) return result;
8258
- }
8259
- if (options.html) {
8260
- const result = detectFromHtml(options.html);
8261
- if (result) return result;
8135
+ if (options.html || options.headers) {
8136
+ const result = detectFromEvidence({ html: options.html, headers: options.headers });
8137
+ if (result.framework !== "unknown") {
8138
+ return {
8139
+ framework: result.framework,
8140
+ language: result.language,
8141
+ confidence: result.confidence,
8142
+ detected_from: options.html ? "html" : "headers"
8143
+ };
8144
+ }
8262
8145
  }
8263
8146
  if (options.files) {
8264
8147
  if (options.files.some((f) => f.includes("next.config"))) {
@@ -8365,6 +8248,7 @@ function getFrameworkDisplayName(framework) {
8365
8248
  jekyll: "Jekyll",
8366
8249
  eleventy: "Eleventy (11ty)",
8367
8250
  pelican: "Pelican",
8251
+ sphinx: "Sphinx",
8368
8252
  unknown: "Unknown"
8369
8253
  };
8370
8254
  return names[framework];
@@ -35740,8 +35624,12 @@ async function analyzeComprehensive(html, url, options = {}) {
35740
35624
  // src/frameworks/index.ts
35741
35625
  var frameworks_exports = {};
35742
35626
  __export(frameworks_exports, {
35627
+ SCORE_FLOOR: () => SCORE_FLOOR,
35628
+ SIGNALS: () => SIGNALS,
35743
35629
  SUPPORTED_FRAMEWORKS: () => SUPPORTED_FRAMEWORKS,
35630
+ WEIGHT: () => WEIGHT,
35744
35631
  detectFramework: () => detectFramework,
35632
+ detectFromEvidence: () => detectFromEvidence,
35745
35633
  detectFromHeaders: () => detectFromHeaders,
35746
35634
  detectFromHtml: () => detectFromHtml,
35747
35635
  detectFromPackageJson: () => detectFromPackageJson,