# retoor <retoor@molodetz.nl>
from __future__ import annotations
import re
from devplacepy.services.jobs.isslop.analysis.signals import (
AXIS_ORIGIN,
SEVERITY_MEDIUM,
SEVERITY_STRONG,
SEVERITY_WEAK,
FileContext,
Signal,
)
PATH_FRAMEWORK_PATTERNS: tuple[tuple[re.Pattern[str], str, str], ...] = (
(re.compile(r"(^|/)_next/", re.IGNORECASE), "Next.js build output (_next/)", SEVERITY_WEAK),
(re.compile(r"(^|/)_nuxt/", re.IGNORECASE), "Nuxt build output (_nuxt/)", SEVERITY_WEAK),
(re.compile(r"(^|/)_astro/", re.IGNORECASE), "Astro build output (_astro/)", SEVERITY_WEAK),
(re.compile(r"(^|/)\.svelte-kit/", re.IGNORECASE), "SvelteKit build output", SEVERITY_WEAK),
(re.compile(r"(^|/)webpack-[0-9a-f]{8,}\.js$", re.IGNORECASE), "Webpack runtime chunk", SEVERITY_WEAK),
(re.compile(r"(^|/)polyfills-[0-9a-f]{6,}\.js$", re.IGNORECASE), "Bundler polyfills chunk", SEVERITY_WEAK),
(re.compile(r"(^|/)main-app-[0-9a-f]{6,}\.js$", re.IGNORECASE), "Next.js app-router bundle", SEVERITY_MEDIUM),
(re.compile(r"(^|/)chunks/app/(page|layout|loading|error|not-found)-[0-9a-f]{6,}\.js$", re.IGNORECASE), "Next.js app-router page bundle", SEVERITY_MEDIUM),
(re.compile(r"(^|/)framework-[0-9a-f]{8,}\.js$", re.IGNORECASE), "Next.js framework chunk", SEVERITY_WEAK),
(re.compile(r"(^|/)assets/index-[0-9A-Za-z_-]{8,}\.(js|css)$"), "Vite hashed asset bundle", SEVERITY_WEAK),
(re.compile(r"(^|/)gptengineer\.js$|(^|/)cdn\.gpteng\.co", re.IGNORECASE), "GPT Engineer / Lovable runtime", SEVERITY_STRONG),
(re.compile(r"(^|/)lovable-uploads/", re.IGNORECASE), "Lovable uploads directory", SEVERITY_STRONG),
(re.compile(r"(^|/)gatsby-", re.IGNORECASE), "Gatsby build artifact", SEVERITY_WEAK),
)
VITE_HASH_ASSET = re.compile(r"index-[0-9A-Za-z_-]{8,}\.(js|css)$")
CONTENT_FRAMEWORK_PATTERNS: tuple[tuple[re.Pattern[str], str], ...] = (
(re.compile(r"self\.__next_f|__NEXT_DATA__|next/dist/", re.IGNORECASE), "next.js runtime"),
(re.compile(r"__nuxt|nuxt\.config", re.IGNORECASE), "nuxt runtime"),
(re.compile(r"astro-island|astro:", re.IGNORECASE), "astro runtime"),
(re.compile(r"data-radix|@radix-ui|radix-ui", re.IGNORECASE), "radix primitives"),
(re.compile(r"lucide", re.IGNORECASE), "lucide icons"),
(re.compile(r"shadcn|class-variance-authority|\bcn\(", re.IGNORECASE), "shadcn/ui"),
(re.compile(r"tailwind|--tw-", re.IGNORECASE), "tailwind"),
(re.compile(r"framer-motion", re.IGNORECASE), "framer motion"),
(re.compile(r"createClient\([^)]*supabase|supabase\.co", re.IGNORECASE), "supabase backend"),
(re.compile(r"clerk\.|@clerk/", re.IGNORECASE), "clerk auth"),
)
CONTENT_STRONG_MARKERS: tuple[tuple[re.Pattern[str], str], ...] = (
(re.compile(r"gpteng|lovable|data-lov-id", re.IGNORECASE), "Lovable/GPT-Engineer marker"),
(re.compile(r"generated by (v0|bolt|base44|lovable)", re.IGNORECASE), "AI builder attribution"),
)
VIBE_COMBO_THRESHOLD: int = 3
def detect_infrastructure(context: FileContext) -> list[Signal]:
findings: list[Signal] = []
relative = context.relative
for pattern, title, severity in PATH_FRAMEWORK_PATTERNS:
if pattern.search(relative):
weight = 3.0 if severity == SEVERITY_STRONG else (1.0 if severity == SEVERITY_MEDIUM else 0.5)
findings.append(
Signal("BUILD_ARTIFACT_PATH", f"Framework build artifact path: {title}", severity, AXIS_ORIGIN, weight, 1, relative)
)
break
text = context.text
for pattern, marker in CONTENT_STRONG_MARKERS:
match = pattern.search(text)
if match:
findings.append(
Signal(
"AI_BUILDER_FINGERPRINT",
f"AI builder marker in bundle: {marker}",
SEVERITY_STRONG,
AXIS_ORIGIN,
4.0,
text.count("\n", 0, match.start()) + 1,
match.group(0)[:100],
)
)
break
stack_hits = [label for pattern, label in CONTENT_FRAMEWORK_PATTERNS if pattern.search(text)]
if len(stack_hits) >= VIBE_COMBO_THRESHOLD:
findings.append(
Signal(
"VIBE_STACK",
f"Default AI frontend stack in bundle ({len(stack_hits)} libraries)",
SEVERITY_STRONG if len(stack_hits) >= 5 else SEVERITY_MEDIUM,
AXIS_ORIGIN,
3.0,
1,
", ".join(stack_hits[:8]),
)
)
return findings