{"version":3,"file":"routes.mjs","names":[],"sources":["../../../../../src/services/scaffold/upgrade-audit/checks/routes.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport { Finding, KNOWN_MOUNTS, MOUNT_ALIASES } from \"../domain\";\nimport { listFiles, readText } from \"../fs\";\n\nconst ROUTE_FILE_RE = /createFileRoute(?:<[^>]*>)?\\(\\s*[\"'`]([^\"'`]+)[\"'`]/;\n\nconst AUTHED_MOUNTS = new Set([\"authenticated\", \"admin\", \"organization\"]);\n\nconst SESSION_CALLEES = [\"authClient.getSession(\", \"sessionQueryOptions(\"];\n\nconst firstSegment = (routePath: string): string | null => {\n  const segments = routePath.split(\"/\").filter(Boolean);\n  return segments.length > 0 ? segments[0] : null;\n};\n\nconst isLayoutPath = (routePath: string): boolean =>\n  routePath.split(\"/\").filter(Boolean).length === 1;\n\nconst parseRoutePath = (source: string): string | null => {\n  const match = source.match(ROUTE_FILE_RE);\n  return match?.[1] ?? null;\n};\n\nconst checkRoot = (relativePath: string, source: string): Finding[] => {\n  const findings: Finding[] = [];\n  if (!/<html[\\s>]/.test(source)) {\n    findings.push(\n      new Finding({\n        check: \"routes\",\n        path: relativePath,\n        severity: \"warn\",\n        message: \"root route has no document shell — already thin, graft-ready\",\n      }),\n    );\n    return findings;\n  }\n  findings.push(\n    new Finding({\n      check: \"routes\",\n      path: relativePath,\n      severity: \"warn\",\n      message:\n        \"root route owns the document shell (<html>) — acceptable today, must become thin (<Outlet />) before the app.ts/grafting model\",\n    }),\n  );\n  return findings;\n};\n\nconst checkRouteFile = (relativePath: string, source: string): Finding[] => {\n  if (relativePath.endsWith(\"__root.tsx\")) {\n    return checkRoot(relativePath, source);\n  }\n\n  const routePath = parseRoutePath(source);\n  if (!routePath) {\n    return [\n      new Finding({\n        check: \"routes\",\n        path: relativePath,\n        severity: \"blocker\",\n        message: \"no createFileRoute() call with a path literal found\",\n      }),\n    ];\n  }\n\n  const segment = firstSegment(routePath);\n  if (!segment?.startsWith(\"_\")) {\n    return [\n      new Finding({\n        check: \"routes\",\n        path: relativePath,\n        severity: \"blocker\",\n        message: `route \"/${routePath.replace(/^\\//, \"\")}\" is not mounted under a pathless layout — re-home under _<mount>`,\n      }),\n    ];\n  }\n\n  const mountId = segment.slice(1);\n  const canonical = MOUNT_ALIASES[mountId] ?? mountId;\n  const findings: Finding[] = [];\n\n  if (!KNOWN_MOUNTS.includes(canonical as (typeof KNOWN_MOUNTS)[number])) {\n    findings.push(\n      new Finding({\n        check: \"routes\",\n        path: relativePath,\n        severity: \"blocker\",\n        message: `unknown mount \"_${mountId}\" — not in the host mount registry`,\n      }),\n    );\n    return findings;\n  }\n\n  if (\n    isLayoutPath(routePath) &&\n    AUTHED_MOUNTS.has(canonical) &&\n    !/beforeLoad\\s*[:(]/.test(source)\n  ) {\n    findings.push(\n      new Finding({\n        check: \"routes\",\n        path: relativePath,\n        severity: \"blocker\",\n        message: `authed layout \"_${mountId}\" has no beforeLoad gate — session checks must live on the mount`,\n      }),\n    );\n  }\n\n  if (!isLayoutPath(routePath) && SESSION_CALLEES.some((callee) => source.includes(callee))) {\n    findings.push(\n      new Finding({\n        check: \"routes\",\n        path: relativePath,\n        severity: \"warn\",\n        message: \"leaf route resolves the session directly — inherit the mount gate instead\",\n      }),\n    );\n  }\n\n  return findings;\n};\n\nexport const checkRoutes = (projectDir: string): Effect.Effect<Finding[], Error> =>\n  Effect.gen(function* () {\n    const files = (yield* listFiles(projectDir, \"ui/src/routes/**/*.{ts,tsx}\")).filter(\n      (file) => !file.endsWith(\".test.ts\"),\n    );\n\n    if (files.length === 0) {\n      return [\n        new Finding({\n          check: \"routes\",\n          path: \"ui/src/routes\",\n          severity: \"blocker\",\n          message: \"no route files found — is this a child app with UI overrides?\",\n        }),\n      ];\n    }\n\n    const findings: Finding[] = [];\n    for (const file of files) {\n      const source = yield* readText(projectDir, file);\n      findings.push(...checkRouteFile(file, source));\n    }\n    return findings;\n  });\n"],"mappings":";;;;;AAIA,MAAM,gBAAgB;AAEtB,MAAM,gCAAgB,IAAI,IAAI;CAAC;CAAiB;CAAS;AAAc,CAAC;AAExE,MAAM,kBAAkB,CAAC,0BAA0B,sBAAsB;AAEzE,MAAM,gBAAgB,cAAqC;CACzD,MAAM,WAAW,UAAU,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO;CACpD,OAAO,SAAS,SAAS,IAAI,SAAS,KAAK;AAC7C;AAEA,MAAM,gBAAgB,cACpB,UAAU,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,WAAW;AAElD,MAAM,kBAAkB,WAAkC;CAExD,OADc,OAAO,MAAM,aAChB,CAAC,GAAG,MAAM;AACvB;AAEA,MAAM,aAAa,cAAsB,WAA8B;CACrE,MAAM,WAAsB,CAAC;CAC7B,IAAI,CAAC,aAAa,KAAK,MAAM,GAAG;EAC9B,SAAS,KACP,IAAI,QAAQ;GACV,OAAO;GACP,MAAM;GACN,UAAU;GACV,SAAS;EACX,CAAC,CACH;EACA,OAAO;CACT;CACA,SAAS,KACP,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SACE;CACJ,CAAC,CACH;CACA,OAAO;AACT;AAEA,MAAM,kBAAkB,cAAsB,WAA8B;CAC1E,IAAI,aAAa,SAAS,YAAY,GACpC,OAAO,UAAU,cAAc,MAAM;CAGvC,MAAM,YAAY,eAAe,MAAM;CACvC,IAAI,CAAC,WACH,OAAO,CACL,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS;CACX,CAAC,CACH;CAGF,MAAM,UAAU,aAAa,SAAS;CACtC,IAAI,CAAC,SAAS,WAAW,GAAG,GAC1B,OAAO,CACL,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS,WAAW,UAAU,QAAQ,OAAO,EAAE,EAAE;CACnD,CAAC,CACH;CAGF,MAAM,UAAU,QAAQ,MAAM,CAAC;CAC/B,MAAM,YAAY,cAAc,YAAY;CAC5C,MAAM,WAAsB,CAAC;CAE7B,IAAI,CAAC,aAAa,SAAS,SAA0C,GAAG;EACtE,SAAS,KACP,IAAI,QAAQ;GACV,OAAO;GACP,MAAM;GACN,UAAU;GACV,SAAS,mBAAmB,QAAQ;EACtC,CAAC,CACH;EACA,OAAO;CACT;CAEA,IACE,aAAa,SAAS,KACtB,cAAc,IAAI,SAAS,KAC3B,CAAC,oBAAoB,KAAK,MAAM,GAEhC,SAAS,KACP,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS,mBAAmB,QAAQ;CACtC,CAAC,CACH;CAGF,IAAI,CAAC,aAAa,SAAS,KAAK,gBAAgB,MAAM,WAAW,OAAO,SAAS,MAAM,CAAC,GACtF,SAAS,KACP,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS;CACX,CAAC,CACH;CAGF,OAAO;AACT;AAEA,MAAa,eAAe,eAC1B,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,UAAU,YAAY,6BAA6B,EAAC,CAAE,QACzE,SAAS,CAAC,KAAK,SAAS,UAAU,CACrC;CAEA,IAAI,MAAM,WAAW,GACnB,OAAO,CACL,IAAI,QAAQ;EACV,OAAO;EACP,MAAM;EACN,UAAU;EACV,SAAS;CACX,CAAC,CACH;CAGF,MAAM,WAAsB,CAAC;CAC7B,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,SAAS,OAAO,SAAS,YAAY,IAAI;EAC/C,SAAS,KAAK,GAAG,eAAe,MAAM,MAAM,CAAC;CAC/C;CACA,OAAO;AACT,CAAC"}