@launchsecure/launch-kit 0.0.17 → 0.0.19

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.
Files changed (78) hide show
  1. package/dist/chart-client/assets/index--120d9P9.css +1 -0
  2. package/dist/chart-client/assets/index-C8ANseEa.js +441 -0
  3. package/dist/chart-client/index.html +2 -2
  4. package/dist/client/assets/index-Bf8zdL3x.css +32 -0
  5. package/dist/client/assets/index-Ds9UP_cj.js +291 -0
  6. package/dist/client/index.html +2 -2
  7. package/dist/council-client/assets/index-CofZh7pS.css +1 -0
  8. package/dist/council-client/assets/index-Dc41S-R2.js +198 -0
  9. package/dist/council-client/index.html +21 -0
  10. package/dist/deck-client/assets/{_baseUniq-BbqvoK-V.js → _baseUniq-DsfOm3t_.js} +1 -1
  11. package/dist/deck-client/assets/{arc-CMtYsIZt.js → arc-NJuvkBv1.js} +1 -1
  12. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-BEN5hESa.js → architectureDiagram-Q4EWVU46-BgrcgZs0.js} +1 -1
  13. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-BV4CZ6k8.js → blockDiagram-DXYQGD6D-C3XoLi15.js} +1 -1
  14. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-fLcBXqdD.js → c4Diagram-AHTNJAMY-FX2PjLfb.js} +1 -1
  15. package/dist/deck-client/assets/channel-ChQjD1T1.js +1 -0
  16. package/dist/deck-client/assets/{chunk-4BX2VUAB-BO_19zwB.js → chunk-4BX2VUAB-D0aqsJV0.js} +1 -1
  17. package/dist/deck-client/assets/{chunk-4TB4RGXK-iYegd5fu.js → chunk-4TB4RGXK-7qRCCAgK.js} +1 -1
  18. package/dist/deck-client/assets/{chunk-55IACEB6-DM3QwYFL.js → chunk-55IACEB6-DfHG-iqb.js} +1 -1
  19. package/dist/deck-client/assets/{chunk-EDXVE4YY-DGznOul1.js → chunk-EDXVE4YY-DrR52j3B.js} +1 -1
  20. package/dist/deck-client/assets/{chunk-FMBD7UC4-DsANJqYW.js → chunk-FMBD7UC4-D5KSGATB.js} +1 -1
  21. package/dist/deck-client/assets/{chunk-OYMX7WX6-6PGH1F7d.js → chunk-OYMX7WX6-M7hsLRNU.js} +1 -1
  22. package/dist/deck-client/assets/{chunk-QZHKN3VN-Dihf0Uq7.js → chunk-QZHKN3VN-1ynAWO2m.js} +1 -1
  23. package/dist/deck-client/assets/{chunk-YZCP3GAM-Cali2wW5.js → chunk-YZCP3GAM-S2-nGw3D.js} +1 -1
  24. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-B_9iqK1S.js +1 -0
  25. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-B_9iqK1S.js +1 -0
  26. package/dist/deck-client/assets/clone-BYt1AMfz.js +1 -0
  27. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-DsRY4vbI.js → cose-bilkent-S5V4N54A-BcMwozS2.js} +1 -1
  28. package/dist/deck-client/assets/cytoscape.esm-BQk4lpUV.js +331 -0
  29. package/dist/deck-client/assets/{dagre-KV5264BT-DJIKE_pI.js → dagre-KV5264BT-DtKMhl_1.js} +1 -1
  30. package/dist/deck-client/assets/{diagram-5BDNPKRD-Ckgli1SP.js → diagram-5BDNPKRD-1plH69us.js} +1 -1
  31. package/dist/deck-client/assets/{diagram-G4DWMVQ6-CozcDzae.js → diagram-G4DWMVQ6-D_o-BHO3.js} +1 -1
  32. package/dist/deck-client/assets/{diagram-MMDJMWI5-xVSwW3f_.js → diagram-MMDJMWI5-ClZ1LIx6.js} +1 -1
  33. package/dist/deck-client/assets/{diagram-TYMM5635-CJeZVY-P.js → diagram-TYMM5635-B8dKHfRh.js} +1 -1
  34. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-j4wjAERH.js → erDiagram-SMLLAGMA-CY2aCH7-.js} +1 -1
  35. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-CVLZ1efi.js → flowDiagram-DWJPFMVM-DZZWHti8.js} +1 -1
  36. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-CcIJ7pkP.js → ganttDiagram-T4ZO3ILL-OwGGa6Lu.js} +1 -1
  37. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-BZRhQX-a.js → gitGraphDiagram-UUTBAWPF-GKyWD4Qt.js} +1 -1
  38. package/dist/deck-client/assets/{graph-D0l25xfo.js → graph-CORzYQdB.js} +1 -1
  39. package/dist/deck-client/assets/index-765AIQ9z.css +1 -0
  40. package/dist/deck-client/assets/{index-BXcoHWVM.js → index-hiIpM7EP.js} +93 -93
  41. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-BLwgxnYT.js → infoDiagram-42DDH7IO-DmgqJCcF.js} +1 -1
  42. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-BfOLoWv3.js → ishikawaDiagram-UXIWVN3A-D-1v7knu.js} +1 -1
  43. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-CPuL6C9h.js → journeyDiagram-VCZTEJTY-CYrGQE7b.js} +1 -1
  44. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-D3uf7_tx.js → kanban-definition-6JOO6SKY-BJFDWiH-.js} +1 -1
  45. package/dist/deck-client/assets/{layout-CzToiXdK.js → layout-BTFFcaxF.js} +1 -1
  46. package/dist/deck-client/assets/{linear-BU36t460.js → linear-DAbl6COS.js} +1 -1
  47. package/dist/deck-client/assets/{min-DX_q-lqP.js → min-oWHBrFBm.js} +1 -1
  48. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-Ccty4O16.js → mindmap-definition-QFDTVHPH-BTCB0VLO.js} +1 -1
  49. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-DVjsvH19.js → pieDiagram-DEJITSTG-CUZChWNA.js} +1 -1
  50. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-DtOXFVW9.js → quadrantDiagram-34T5L4WZ-4M1Um_e4.js} +1 -1
  51. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-BbO_kKg6.js → requirementDiagram-MS252O5E-DLzQZ0B3.js} +1 -1
  52. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-qbbj-CmC.js → sankeyDiagram-XADWPNL6-DcNgzV3E.js} +1 -1
  53. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-JNKZAgfQ.js → sequenceDiagram-FGHM5R23-CAcI2vC9.js} +1 -1
  54. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-dtFalcNx.js → stateDiagram-FHFEXIEX-CntjTTm5.js} +1 -1
  55. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-YiaphOU_.js +1 -0
  56. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-Dpp5nqSJ.js → timeline-definition-GMOUNBTQ-D8zrit4U.js} +1 -1
  57. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-D8qEutX7.js → vennDiagram-DHZGUBPP-C4SuFPgo.js} +1 -1
  58. package/dist/deck-client/assets/{wardley-RL74JXVD-BwMqiNcL.js → wardley-RL74JXVD-B3F-Olcq.js} +58 -58
  59. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-Bxl9X3CK.js → wardleyDiagram-NUSXRM2D-kj73r6f-.js} +1 -1
  60. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-DYcvxLhi.js → xychartDiagram-5P7HB3ND-CC_d_Ey3.js} +1 -1
  61. package/dist/deck-client/index.html +2 -2
  62. package/dist/server/chart-serve.js +452 -244
  63. package/dist/server/cli.js +558 -750
  64. package/dist/server/council-entry.js +1418 -0
  65. package/dist/server/council-serve.js +1039 -0
  66. package/dist/server/graph-mcp-entry.js +486 -695
  67. package/package.json +10 -9
  68. package/dist/chart-client/assets/index-BUhuLBaw.js +0 -441
  69. package/dist/chart-client/assets/index-CWRZxjqR.css +0 -1
  70. package/dist/client/assets/index-CAAipH3V.js +0 -291
  71. package/dist/client/assets/index-DtbN793z.css +0 -32
  72. package/dist/deck-client/assets/channel-Nf-B3Qor.js +0 -1
  73. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-3i3-miMR.js +0 -1
  74. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-3i3-miMR.js +0 -1
  75. package/dist/deck-client/assets/clone-DXBuQlG8.js +0 -1
  76. package/dist/deck-client/assets/cytoscape.esm-BiciSPf8.js +0 -331
  77. package/dist/deck-client/assets/index-Cdh-f3-c.css +0 -1
  78. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CviYYulW.js +0 -1
@@ -90,15 +90,15 @@ function getQuery(name) {
90
90
  ensureInit();
91
91
  const cached = queryCache.get(name);
92
92
  if (cached) return cached;
93
- const scmPath = (0, import_node_path3.join)(queriesDir, `${name}.scm`);
94
- const scm = (0, import_node_fs3.readFileSync)(scmPath, "utf-8");
93
+ const scmPath = (0, import_node_path4.join)(queriesDir, `${name}.scm`);
94
+ const scm = (0, import_node_fs4.readFileSync)(scmPath, "utf-8");
95
95
  const query = tsxLanguage.query(scm);
96
96
  queryCache.set(name, query);
97
97
  return query;
98
98
  }
99
99
  function parseSource(absPath) {
100
100
  ensureInit();
101
- const content = (0, import_node_fs3.readFileSync)(absPath, "utf-8");
101
+ const content = (0, import_node_fs4.readFileSync)(absPath, "utf-8");
102
102
  return parserInstance.parse(content);
103
103
  }
104
104
  function parseCodeTS(code) {
@@ -563,17 +563,17 @@ function extractDeep(absPath) {
563
563
  }
564
564
  return { elements, stateVars, conditions, variables, responses, params };
565
565
  }
566
- var import_node_fs3, import_node_path3, tsxLanguage, parserInstance, initPromise, initialized, queriesDir, queryCache, PRISMA_MUTATION_METHODS_BUILTIN, DB_IDENTIFIERS_FALLBACK, extraDbIdentifiers, extraMutationMethods;
566
+ var import_node_fs4, import_node_path4, tsxLanguage, parserInstance, initPromise, initialized, queriesDir, queryCache, PRISMA_MUTATION_METHODS_BUILTIN, DB_IDENTIFIERS_FALLBACK, extraDbIdentifiers, extraMutationMethods;
567
567
  var init_ts_extractor = __esm({
568
568
  "src/server/graph/core/ts-extractor.ts"() {
569
569
  "use strict";
570
- import_node_fs3 = require("node:fs");
571
- import_node_path3 = require("node:path");
570
+ import_node_fs4 = require("node:fs");
571
+ import_node_path4 = require("node:path");
572
572
  initialized = false;
573
573
  queriesDir = (() => {
574
- const srcPath = (0, import_node_path3.join)((0, import_node_path3.dirname)(__filename), "..", "queries");
574
+ const srcPath = (0, import_node_path4.join)((0, import_node_path4.dirname)(__filename), "..", "queries");
575
575
  if (require("fs").existsSync(srcPath)) return srcPath;
576
- return (0, import_node_path3.join)((0, import_node_path3.dirname)(__filename), "graph", "queries");
576
+ return (0, import_node_path4.join)((0, import_node_path4.dirname)(__filename), "graph", "queries");
577
577
  })();
578
578
  queryCache = /* @__PURE__ */ new Map();
579
579
  PRISMA_MUTATION_METHODS_BUILTIN = [
@@ -601,15 +601,15 @@ __export(chart_serve_exports, {
601
601
  });
602
602
  module.exports = __toCommonJS(chart_serve_exports);
603
603
  var import_node_http = __toESM(require("node:http"));
604
- var import_node_fs17 = __toESM(require("node:fs"));
604
+ var import_node_fs18 = __toESM(require("node:fs"));
605
605
  var import_node_path19 = __toESM(require("node:path"));
606
606
 
607
607
  // src/server/graph/index.ts
608
- var import_node_fs14 = require("node:fs");
608
+ var import_node_fs15 = require("node:fs");
609
609
  var import_node_path16 = require("node:path");
610
610
 
611
611
  // src/server/graph/core/graph-builder.ts
612
- var import_node_fs11 = require("node:fs");
612
+ var import_node_fs12 = require("node:fs");
613
613
  var import_node_path12 = require("node:path");
614
614
  init_config();
615
615
 
@@ -617,36 +617,243 @@ init_config();
617
617
  var import_node_path11 = require("node:path");
618
618
 
619
619
  // src/server/graph/parsers/ts/typescript-project.ts
620
- var import_node_fs4 = require("node:fs");
621
- var import_node_path4 = require("node:path");
620
+ var import_node_fs5 = require("node:fs");
621
+ var import_node_path5 = require("node:path");
622
622
  init_config();
623
623
 
624
624
  // src/server/graph/core/resolve-paths.ts
625
+ var import_node_fs3 = require("node:fs");
626
+ var import_node_path3 = require("node:path");
627
+
628
+ // src/server/graph/core/walk.ts
625
629
  var import_node_fs2 = require("node:fs");
626
630
  var import_node_path2 = require("node:path");
627
- function detectDbDir(rootDir, config) {
628
- if (config.paths?.dbDir) return (0, import_node_path2.join)(rootDir, config.paths.dbDir);
629
- const prismaDir = (0, import_node_path2.join)(rootDir, "prisma");
630
- if ((0, import_node_fs2.existsSync)(prismaDir)) return prismaDir;
631
- return null;
631
+ var DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
632
+ "node_modules",
633
+ ".git",
634
+ ".next",
635
+ ".launchsecure",
636
+ ".claude",
637
+ "dist",
638
+ "build",
639
+ "out",
640
+ ".turbo",
641
+ ".vercel",
642
+ "coverage"
643
+ ]);
644
+ function walk(dir, exts) {
645
+ const results = [];
646
+ if (!(0, import_node_fs2.existsSync)(dir)) return results;
647
+ for (const entry of (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true })) {
648
+ const full = (0, import_node_path2.join)(dir, entry.name);
649
+ if (entry.isDirectory()) {
650
+ results.push(...walk(full, exts));
651
+ } else if (exts.includes((0, import_node_path2.extname)(entry.name))) {
652
+ results.push(full);
653
+ }
654
+ }
655
+ return results;
632
656
  }
633
- function resolveProjectPaths(rootDir, config) {
634
- const dbDir = detectDbDir(rootDir, config);
635
- if (config.paths?.appDir) {
636
- const appDir = (0, import_node_path2.join)(rootDir, config.paths.appDir);
637
- const srcDir = config.paths.srcDir ? (0, import_node_path2.join)(rootDir, config.paths.srcDir) : (0, import_node_path2.dirname)(appDir);
638
- return { srcDir, appDir, apiDir: (0, import_node_path2.join)(appDir, "api"), dbDir };
657
+ function walkWithIgnore(dir, exts, opts = {}) {
658
+ const results = [];
659
+ if (!(0, import_node_fs2.existsSync)(dir)) return results;
660
+ const skip = opts.extraIgnore ? /* @__PURE__ */ new Set([...DEFAULT_IGNORE_DIRS, ...opts.extraIgnore]) : DEFAULT_IGNORE_DIRS;
661
+ for (const entry of (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true })) {
662
+ if (entry.isDirectory()) {
663
+ if (skip.has(entry.name)) continue;
664
+ results.push(...walkWithIgnore((0, import_node_path2.join)(dir, entry.name), exts, opts));
665
+ } else if (exts.includes((0, import_node_path2.extname)(entry.name))) {
666
+ results.push((0, import_node_path2.join)(dir, entry.name));
667
+ }
639
668
  }
640
- const srcApp = (0, import_node_path2.join)(rootDir, "src", "app");
641
- if ((0, import_node_fs2.existsSync)(srcApp)) {
642
- return { srcDir: (0, import_node_path2.join)(rootDir, "src"), appDir: srcApp, apiDir: (0, import_node_path2.join)(srcApp, "api"), dbDir };
669
+ return results;
670
+ }
671
+
672
+ // src/server/graph/core/resolve-paths.ts
673
+ function hasSqlFiles(dir) {
674
+ if (!(0, import_node_fs3.existsSync)(dir)) return false;
675
+ try {
676
+ return (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true }).some(
677
+ (e) => e.isFile() && e.name.endsWith(".sql")
678
+ );
679
+ } catch {
680
+ return false;
681
+ }
682
+ }
683
+ function hasNestedMigrationSql(dir) {
684
+ if (!(0, import_node_fs3.existsSync)(dir)) return false;
685
+ try {
686
+ return (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true }).some(
687
+ (e) => e.isDirectory() && (0, import_node_fs3.existsSync)((0, import_node_path3.join)(dir, e.name, "migration.sql"))
688
+ );
689
+ } catch {
690
+ return false;
691
+ }
692
+ }
693
+ function resolveDbFromDir(dir) {
694
+ if (!(0, import_node_fs3.existsSync)(dir)) return { kind: "none", schemaPath: null, migrationsDir: null };
695
+ const schemaPath = (0, import_node_path3.join)(dir, "schema.prisma");
696
+ if ((0, import_node_fs3.existsSync)(schemaPath)) {
697
+ const migrationsDir2 = (0, import_node_path3.join)(dir, "migrations");
698
+ return {
699
+ kind: "prisma",
700
+ schemaPath,
701
+ migrationsDir: (0, import_node_fs3.existsSync)(migrationsDir2) ? migrationsDir2 : null
702
+ };
703
+ }
704
+ const migrationsDir = (0, import_node_path3.join)(dir, "migrations");
705
+ if (hasSqlFiles(migrationsDir) || hasNestedMigrationSql(migrationsDir)) {
706
+ return { kind: "sql-migrations", migrationsDir, schemaPath: null };
707
+ }
708
+ if (hasSqlFiles(dir) || hasNestedMigrationSql(dir)) {
709
+ return { kind: "sql-migrations", migrationsDir: dir, schemaPath: null };
710
+ }
711
+ return { kind: "none", schemaPath: null, migrationsDir: null };
712
+ }
713
+ function detectDbConfig(rootDir, config) {
714
+ if (config.paths?.dbDir) {
715
+ return resolveDbFromDir((0, import_node_path3.join)(rootDir, config.paths.dbDir));
643
716
  }
644
- const rootApp = (0, import_node_path2.join)(rootDir, "app");
645
- if ((0, import_node_fs2.existsSync)(rootApp)) {
646
- return { srcDir: rootDir, appDir: rootApp, apiDir: (0, import_node_path2.join)(rootApp, "api"), dbDir };
717
+ const candidates = ["prisma", "supabase", "drizzle", (0, import_node_path3.join)("db", "migrations"), "migrations"];
718
+ for (const c of candidates) {
719
+ const dir = (0, import_node_path3.join)(rootDir, c);
720
+ const resolved = resolveDbFromDir(dir);
721
+ if (resolved.kind !== "none") return resolved;
647
722
  }
723
+ return { kind: "none", schemaPath: null, migrationsDir: null };
724
+ }
725
+ function detectDbDir(rootDir, config, dbConfig) {
726
+ if (config.paths?.dbDir) return (0, import_node_path3.join)(rootDir, config.paths.dbDir);
727
+ if (dbConfig.kind === "prisma") return (0, import_node_path3.dirname)(dbConfig.schemaPath);
728
+ if (dbConfig.kind === "sql-migrations") return dbConfig.migrationsDir;
648
729
  return null;
649
730
  }
731
+ var NON_SOURCE_DIRS = /* @__PURE__ */ new Set([
732
+ ...DEFAULT_IGNORE_DIRS,
733
+ // DB conventions (handled by db parsers)
734
+ "prisma",
735
+ "supabase",
736
+ "drizzle",
737
+ "migrations",
738
+ // Web assets
739
+ "public",
740
+ "static",
741
+ "assets",
742
+ // Docs
743
+ "docs",
744
+ "documentation",
745
+ // Test dirs (project tests aren't part of the structural graph)
746
+ "tests",
747
+ "__tests__",
748
+ "e2e",
749
+ "playwright",
750
+ "cypress",
751
+ // Monorepo workspace roots — separate graph projects per .launchchart.json
752
+ "packages",
753
+ "apps",
754
+ "services",
755
+ "libs"
756
+ ]);
757
+ function dirHasTSFiles(dir) {
758
+ if (!(0, import_node_fs3.existsSync)(dir)) return false;
759
+ try {
760
+ const stack = [dir];
761
+ while (stack.length > 0) {
762
+ const cur = stack.pop();
763
+ const entries = (0, import_node_fs3.readdirSync)(cur, { withFileTypes: true });
764
+ for (const e of entries) {
765
+ if (e.isFile() && (e.name.endsWith(".ts") || e.name.endsWith(".tsx"))) return true;
766
+ if (e.isDirectory() && !e.name.startsWith(".") && !DEFAULT_IGNORE_DIRS.has(e.name)) {
767
+ stack.push((0, import_node_path3.join)(cur, e.name));
768
+ }
769
+ }
770
+ }
771
+ } catch {
772
+ }
773
+ return false;
774
+ }
775
+ function collectCodeBearingChildren(dir, extraSkip) {
776
+ if (!(0, import_node_fs3.existsSync)(dir)) return [];
777
+ const out = [];
778
+ try {
779
+ for (const entry of (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true })) {
780
+ if (!entry.isDirectory()) continue;
781
+ if (entry.name.startsWith(".")) continue;
782
+ if (NON_SOURCE_DIRS.has(entry.name)) continue;
783
+ if (extraSkip?.has(entry.name)) continue;
784
+ const full = (0, import_node_path3.join)(dir, entry.name);
785
+ if (dirHasTSFiles(full)) out.push(full);
786
+ }
787
+ } catch {
788
+ }
789
+ return out;
790
+ }
791
+ function detectSrcRoots(rootDir, srcDir, appDir, config) {
792
+ if (config.paths?.srcRoots && config.paths.srcRoots.length > 0) {
793
+ const roots2 = /* @__PURE__ */ new Set();
794
+ roots2.add(appDir);
795
+ for (const r of config.paths.srcRoots) {
796
+ const abs = (0, import_node_path3.isAbsolute)(r) ? r : (0, import_node_path3.resolve)(rootDir, r);
797
+ roots2.add(abs);
798
+ }
799
+ return [...roots2];
800
+ }
801
+ const roots = /* @__PURE__ */ new Set();
802
+ roots.add(appDir);
803
+ for (const c of collectCodeBearingChildren(srcDir)) roots.add(c);
804
+ if (srcDir !== rootDir) {
805
+ const skipSrcWrapper = /* @__PURE__ */ new Set([(0, import_node_path3.basename)(srcDir)]);
806
+ for (const c of collectCodeBearingChildren(rootDir, skipSrcWrapper)) roots.add(c);
807
+ }
808
+ return [...roots];
809
+ }
810
+ var CONVENTION_NAMES = ["middleware.ts", "middleware.tsx", "instrumentation.ts", "instrumentation.tsx"];
811
+ function detectConventionFiles(rootDir, srcDir) {
812
+ const out = [];
813
+ const seen = /* @__PURE__ */ new Set();
814
+ const dirs = srcDir === rootDir ? [rootDir] : [srcDir, rootDir];
815
+ for (const dir of dirs) {
816
+ for (const name of CONVENTION_NAMES) {
817
+ const full = (0, import_node_path3.join)(dir, name);
818
+ if (!seen.has(full) && (0, import_node_fs3.existsSync)(full)) {
819
+ try {
820
+ if ((0, import_node_fs3.statSync)(full).isFile()) {
821
+ seen.add(full);
822
+ out.push(full);
823
+ }
824
+ } catch {
825
+ }
826
+ }
827
+ }
828
+ }
829
+ return out;
830
+ }
831
+ function resolveProjectPaths(rootDir, config) {
832
+ let srcDir;
833
+ let appDir;
834
+ if (config.paths?.appDir) {
835
+ appDir = (0, import_node_path3.join)(rootDir, config.paths.appDir);
836
+ srcDir = config.paths.srcDir ? (0, import_node_path3.join)(rootDir, config.paths.srcDir) : (0, import_node_path3.dirname)(appDir);
837
+ } else {
838
+ const srcApp = (0, import_node_path3.join)(rootDir, "src", "app");
839
+ const rootApp = (0, import_node_path3.join)(rootDir, "app");
840
+ if ((0, import_node_fs3.existsSync)(srcApp)) {
841
+ srcDir = (0, import_node_path3.join)(rootDir, "src");
842
+ appDir = srcApp;
843
+ } else if ((0, import_node_fs3.existsSync)(rootApp)) {
844
+ srcDir = rootDir;
845
+ appDir = rootApp;
846
+ } else {
847
+ return null;
848
+ }
849
+ }
850
+ const apiDir = (0, import_node_path3.join)(appDir, "api");
851
+ const dbConfig = detectDbConfig(rootDir, config);
852
+ const dbDir = detectDbDir(rootDir, config, dbConfig);
853
+ const srcRoots = detectSrcRoots(rootDir, srcDir, appDir, config);
854
+ const conventionFiles = detectConventionFiles(rootDir, srcDir);
855
+ return { srcDir, appDir, apiDir, dbDir, srcRoots, conventionFiles, dbConfig };
856
+ }
650
857
 
651
858
  // src/server/graph/parsers/ts/typescript-project.ts
652
859
  init_ts_extractor();
@@ -664,48 +871,26 @@ var CLASSIFICATION_TO_LAYER = {
664
871
  "mcp-tool": "ui",
665
872
  external: "ui"
666
873
  };
667
- function walk(dir, exts) {
668
- const results = [];
669
- if (!(0, import_node_fs4.existsSync)(dir)) return results;
670
- for (const entry of (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true })) {
671
- const full = (0, import_node_path4.join)(dir, entry.name);
672
- if (entry.isDirectory()) {
673
- results.push(...walk(full, exts));
674
- } else if (exts.includes((0, import_node_path4.extname)(entry.name))) {
675
- results.push(full);
676
- }
874
+ function toNodeId(srcDir, rootDir, absPath) {
875
+ const relFromSrc = (0, import_node_path5.relative)(srcDir, absPath).replace(/\\/g, "/");
876
+ if (relFromSrc.startsWith("..")) {
877
+ return (0, import_node_path5.relative)(rootDir, absPath).replace(/\\/g, "/");
677
878
  }
678
- return results;
679
- }
680
- function walkWithIgnore(dir, exts, ignoreDirs) {
681
- const results = [];
682
- if (!(0, import_node_fs4.existsSync)(dir)) return results;
683
- for (const entry of (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true })) {
684
- if (entry.isDirectory()) {
685
- if (ignoreDirs.has(entry.name)) continue;
686
- results.push(...walkWithIgnore((0, import_node_path4.join)(dir, entry.name), exts, ignoreDirs));
687
- } else if (exts.includes((0, import_node_path4.extname)(entry.name))) {
688
- results.push((0, import_node_path4.join)(dir, entry.name));
689
- }
690
- }
691
- return results;
692
- }
693
- function toNodeId(srcDir, absPath) {
694
- return (0, import_node_path4.relative)(srcDir, absPath).replace(/\\/g, "/");
879
+ return relFromSrc;
695
880
  }
696
881
  function resolveImport(srcDir, specifier) {
697
882
  if (!specifier.startsWith("@/")) return null;
698
883
  const rel = specifier.slice(2);
699
- const base = (0, import_node_path4.join)(srcDir, rel);
700
- for (const c of [base, base + ".ts", base + ".tsx", (0, import_node_path4.join)(base, "index.ts"), (0, import_node_path4.join)(base, "index.tsx")]) {
701
- if ((0, import_node_fs4.existsSync)(c) && (0, import_node_fs4.statSync)(c).isFile()) return c;
884
+ const base = (0, import_node_path5.join)(srcDir, rel);
885
+ for (const c of [base, base + ".ts", base + ".tsx", (0, import_node_path5.join)(base, "index.ts"), (0, import_node_path5.join)(base, "index.tsx")]) {
886
+ if ((0, import_node_fs5.existsSync)(c) && (0, import_node_fs5.statSync)(c).isFile()) return c;
702
887
  }
703
888
  return null;
704
889
  }
705
890
  function resolveRelativeImport(fromFile, specifier) {
706
- const base = (0, import_node_path4.join)((0, import_node_path4.dirname)(fromFile), specifier);
707
- for (const c of [base, base + ".ts", base + ".tsx", (0, import_node_path4.join)(base, "index.ts"), (0, import_node_path4.join)(base, "index.tsx")]) {
708
- if ((0, import_node_fs4.existsSync)(c) && (0, import_node_fs4.statSync)(c).isFile()) return c;
891
+ const base = (0, import_node_path5.join)((0, import_node_path5.dirname)(fromFile), specifier);
892
+ for (const c of [base, base + ".ts", base + ".tsx", (0, import_node_path5.join)(base, "index.ts"), (0, import_node_path5.join)(base, "index.tsx")]) {
893
+ if ((0, import_node_fs5.existsSync)(c) && (0, import_node_fs5.statSync)(c).isFile()) return c;
709
894
  }
710
895
  return null;
711
896
  }
@@ -726,7 +911,7 @@ function resolveBarrelMap(barrelAbsPath, parsedByPath, memo, visiting) {
726
911
  const resolved = resolveRelativeImport(barrelAbsPath, re.from);
727
912
  if (!resolved) continue;
728
913
  if (re.isWildcard) {
729
- const targetBn = (0, import_node_path4.basename)(resolved);
914
+ const targetBn = (0, import_node_path5.basename)(resolved);
730
915
  const targetIsBarrel = targetBn === "index.ts" || targetBn === "index.tsx";
731
916
  if (targetIsBarrel) {
732
917
  const nested = resolveBarrelMap(resolved, parsedByPath, memo, visiting);
@@ -753,12 +938,12 @@ function buildAllBarrelMaps(srcDir, parsedByPath) {
753
938
  const barrels = /* @__PURE__ */ new Map();
754
939
  const memo = /* @__PURE__ */ new Map();
755
940
  for (const [absPath, parsed] of parsedByPath) {
756
- const bn = (0, import_node_path4.basename)(absPath);
941
+ const bn = (0, import_node_path5.basename)(absPath);
757
942
  if (bn !== "index.ts" && bn !== "index.tsx") continue;
758
943
  if (parsed.reExports.length === 0) continue;
759
944
  const map = resolveBarrelMap(absPath, parsedByPath, memo, /* @__PURE__ */ new Set());
760
945
  if (map.size > 0) {
761
- const barrelId = (0, import_node_path4.relative)(srcDir, (0, import_node_path4.dirname)(absPath)).replace(/\\/g, "/");
946
+ const barrelId = (0, import_node_path5.relative)(srcDir, (0, import_node_path5.dirname)(absPath)).replace(/\\/g, "/");
762
947
  barrels.set(barrelId, map);
763
948
  }
764
949
  }
@@ -779,14 +964,15 @@ function extractRoute(id) {
779
964
  return route || "/";
780
965
  }
781
966
  function nameFromFilename(absPath) {
782
- return (0, import_node_path4.basename)(absPath, (0, import_node_path4.extname)(absPath)).replace(/[-_](\w)/g, (_, c) => c.toUpperCase()).replace(/^(\w)/, (_, c) => c.toUpperCase());
967
+ return (0, import_node_path5.basename)(absPath, (0, import_node_path5.extname)(absPath)).replace(/[-_](\w)/g, (_, c) => c.toUpperCase()).replace(/^(\w)/, (_, c) => c.toUpperCase());
783
968
  }
784
- function filePathToApiRoute(apiDir, absPath) {
785
- let route = "/" + (0, import_node_path4.relative)(apiDir, absPath).replace(/\\/g, "/").replace(/\/route\.tsx?$/, "");
969
+ function filePathToAppRoute(appDir, absPath) {
970
+ let route = ("/" + (0, import_node_path5.relative)(appDir, absPath).replace(/\\/g, "/")).replace(/\/route\.tsx?$/, "");
971
+ route = route.replace(/\/\([^)]+\)/g, "");
972
+ route = route.replace(/\[\.\.\.([^\]]+)\]/g, "*$1");
786
973
  route = route.replace(/\[([^\]]+)\]/g, ":$1");
787
974
  route = route.replace(/\/+/g, "/");
788
- if (route === "/") return "/api";
789
- return "/api" + route;
975
+ return route === "" ? "/" : route;
790
976
  }
791
977
  function camelToPascal(s) {
792
978
  if (!s) return s;
@@ -871,7 +1057,7 @@ function matchRouteToPage(route, routeToNodeId) {
871
1057
  if (routeToNodeId.has(normalized)) return routeToNodeId.get(normalized);
872
1058
  return null;
873
1059
  }
874
- function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps, routeToNodeId) {
1060
+ function extractEdges(srcDir, rootDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps, routeToNodeId) {
875
1061
  const edges = [];
876
1062
  const flagged = [];
877
1063
  const seen = /* @__PURE__ */ new Set();
@@ -899,7 +1085,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
899
1085
  for (const name of names) {
900
1086
  const targetAbs = barrelMap.get(name);
901
1087
  if (targetAbs) {
902
- const targetId = toNodeId(srcDir, targetAbs);
1088
+ const targetId = toNodeId(srcDir, rootDir, targetAbs);
903
1089
  if (nodeIdSet.has(targetId)) {
904
1090
  if (!byTarget.has(targetId)) byTarget.set(targetId, []);
905
1091
  byTarget.get(targetId).push(name);
@@ -913,7 +1099,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
913
1099
  } else {
914
1100
  const resolved = resolveImport(srcDir, specifier);
915
1101
  if (resolved) {
916
- const targetId = toNodeId(srcDir, resolved);
1102
+ const targetId = toNodeId(srcDir, rootDir, resolved);
917
1103
  if (nodeIdSet.has(targetId) && !targetId.endsWith("/index.ts") && !targetId.endsWith("/index.tsx")) {
918
1104
  addEdge(targetId, edgeTypeFor(isTypeOnly, names));
919
1105
  }
@@ -922,7 +1108,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
922
1108
  } else if (specifier.startsWith(".")) {
923
1109
  const resolved = resolveRelativeImport(absPath, specifier);
924
1110
  if (resolved) {
925
- const targetId = toNodeId(srcDir, resolved);
1111
+ const targetId = toNodeId(srcDir, rootDir, resolved);
926
1112
  if (nodeIdSet.has(targetId) && !targetId.endsWith("/index.ts") && !targetId.endsWith("/index.tsx")) {
927
1113
  addEdge(targetId, edgeTypeFor(isTypeOnly, names));
928
1114
  }
@@ -966,24 +1152,30 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
966
1152
  }
967
1153
  return { edges, flagged };
968
1154
  }
969
- function hasNextConfig(rootDir) {
970
- return (0, import_node_fs4.existsSync)((0, import_node_path4.join)(rootDir, "next.config.ts")) || (0, import_node_fs4.existsSync)((0, import_node_path4.join)(rootDir, "next.config.js")) || (0, import_node_fs4.existsSync)((0, import_node_path4.join)(rootDir, "next.config.mjs"));
971
- }
972
1155
  function detect(rootDir) {
973
1156
  const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
974
- return paths !== null && hasNextConfig(rootDir);
1157
+ return paths !== null;
975
1158
  }
976
1159
  function generate(rootDir) {
977
1160
  const config = loadConfig(rootDir);
978
1161
  const paths = resolveProjectPaths(rootDir, config);
979
1162
  const srcDir = paths.srcDir;
980
- const apiDir = paths.apiDir;
981
- const appFiles = walk(paths.appDir, [".tsx", ".ts"]);
982
- const clientFiles = walk((0, import_node_path4.join)(srcDir, "client"), [".tsx", ".ts"]);
983
- const serverFiles = walk((0, import_node_path4.join)(srcDir, "server"), [".ts", ".tsx"]);
984
- const libFiles = walk((0, import_node_path4.join)(srcDir, "lib"), [".ts", ".tsx"]);
985
- const configFiles = walk((0, import_node_path4.join)(srcDir, "config"), [".ts", ".tsx"]);
986
- const allDiscovered = [...appFiles, ...clientFiles, ...serverFiles, ...libFiles, ...configFiles];
1163
+ const allDiscovered = [];
1164
+ const discoveredSet = /* @__PURE__ */ new Set();
1165
+ for (const root of paths.srcRoots) {
1166
+ for (const f of walk(root, [".tsx", ".ts"])) {
1167
+ if (!discoveredSet.has(f)) {
1168
+ discoveredSet.add(f);
1169
+ allDiscovered.push(f);
1170
+ }
1171
+ }
1172
+ }
1173
+ for (const conv of paths.conventionFiles) {
1174
+ if (!discoveredSet.has(conv)) {
1175
+ discoveredSet.add(conv);
1176
+ allDiscovered.push(conv);
1177
+ }
1178
+ }
987
1179
  const parsedByPath = /* @__PURE__ */ new Map();
988
1180
  for (const absPath of allDiscovered) {
989
1181
  parsedByPath.set(absPath, parseFileTS(absPath));
@@ -993,9 +1185,9 @@ function generate(rootDir) {
993
1185
  const apiNodes = [];
994
1186
  const nodeIdSet = /* @__PURE__ */ new Set();
995
1187
  const routeToNodeId = /* @__PURE__ */ new Map();
996
- const fileSet = allDiscovered.filter((f) => !(0, import_node_path4.basename)(f).startsWith("index."));
1188
+ const fileSet = allDiscovered.filter((f) => !(0, import_node_path5.basename)(f).startsWith("index."));
997
1189
  for (const absPath of fileSet) {
998
- const id = toNodeId(srcDir, absPath);
1190
+ const id = toNodeId(srcDir, rootDir, absPath);
999
1191
  const type = classifyType(absPath, id);
1000
1192
  if (type === "test" || type === "story") continue;
1001
1193
  const parsed = parsedByPath.get(absPath);
@@ -1010,7 +1202,7 @@ function generate(rootDir) {
1010
1202
  const dbCalls = extractDbCallsTS(absPath);
1011
1203
  const authWrappers = extractAuthWrappersTS(absPath);
1012
1204
  const deep = extractDeep(absPath);
1013
- const routePath = (0, import_node_fs4.existsSync)(apiDir) ? filePathToApiRoute(apiDir, absPath) : `/api/${id.replace(/\/route\.tsx?$/, "")}`;
1205
+ const routePath = filePathToAppRoute(paths.appDir, absPath);
1014
1206
  const mutations = dbCalls.filter((c) => c.isMutation);
1015
1207
  const mutates = mutations.length > 0;
1016
1208
  const authStrategy = [...authWrappers];
@@ -1054,11 +1246,12 @@ function generate(rootDir) {
1054
1246
  const uiEdges = [];
1055
1247
  const uiFlagged = [];
1056
1248
  for (const absPath of fileSet) {
1057
- const id = toNodeId(srcDir, absPath);
1249
+ const id = toNodeId(srcDir, rootDir, absPath);
1058
1250
  if (!nodeIdSet.has(id)) continue;
1059
1251
  const parsed = parsedByPath.get(absPath);
1060
1252
  const { edges, flagged } = extractEdges(
1061
1253
  srcDir,
1254
+ rootDir,
1062
1255
  absPath,
1063
1256
  id,
1064
1257
  parsed,
@@ -1071,7 +1264,7 @@ function generate(rootDir) {
1071
1264
  }
1072
1265
  const fetchCallEntries = [];
1073
1266
  for (const absPath of fileSet) {
1074
- const sourceId = toNodeId(srcDir, absPath);
1267
+ const sourceId = toNodeId(srcDir, rootDir, absPath);
1075
1268
  if (!nodeIdSet.has(sourceId)) continue;
1076
1269
  const parsed = parsedByPath.get(absPath);
1077
1270
  if (parsed.fetchCalls.length === 0) continue;
@@ -1087,20 +1280,7 @@ function generate(rootDir) {
1087
1280
  });
1088
1281
  }
1089
1282
  const externalScanned = new Set(allDiscovered.map((f) => f.replace(/\\/g, "/")));
1090
- const IGNORE_DIRS = /* @__PURE__ */ new Set([
1091
- "node_modules",
1092
- ".next",
1093
- "dist",
1094
- ".launchsecure",
1095
- ".git",
1096
- "src",
1097
- "coverage",
1098
- ".turbo",
1099
- "build",
1100
- "out",
1101
- ".vercel"
1102
- ]);
1103
- const externalCandidates = walkWithIgnore(rootDir, [".ts", ".tsx"], IGNORE_DIRS);
1283
+ const externalCandidates = walkWithIgnore(rootDir, [".ts", ".tsx"], { extraIgnore: /* @__PURE__ */ new Set(["src"]) });
1104
1284
  for (const absPath of externalCandidates) {
1105
1285
  const normalized = absPath.replace(/\\/g, "/");
1106
1286
  if (externalScanned.has(normalized)) continue;
@@ -1110,7 +1290,7 @@ function generate(rootDir) {
1110
1290
  } catch {
1111
1291
  continue;
1112
1292
  }
1113
- const externalId = (0, import_node_path4.relative)(rootDir, absPath).replace(/\\/g, "/");
1293
+ const externalId = (0, import_node_path5.relative)(rootDir, absPath).replace(/\\/g, "/");
1114
1294
  const edgesFromThis = [];
1115
1295
  const seen = /* @__PURE__ */ new Set();
1116
1296
  for (const imp of parsed.imports) {
@@ -1123,7 +1303,7 @@ function generate(rootDir) {
1123
1303
  for (const name of names) {
1124
1304
  const targetAbs = barrelMap.get(name);
1125
1305
  if (!targetAbs) continue;
1126
- const targetId2 = toNodeId(srcDir, targetAbs);
1306
+ const targetId2 = toNodeId(srcDir, rootDir, targetAbs);
1127
1307
  if (!nodeIdSet.has(targetId2)) continue;
1128
1308
  const key2 = `${externalId}\u2192${targetId2}`;
1129
1309
  if (seen.has(key2)) continue;
@@ -1137,7 +1317,7 @@ function generate(rootDir) {
1137
1317
  resolved = resolveRelativeImport(absPath, specifier);
1138
1318
  }
1139
1319
  if (!resolved) continue;
1140
- const targetId = toNodeId(srcDir, resolved);
1320
+ const targetId = toNodeId(srcDir, rootDir, resolved);
1141
1321
  if (!nodeIdSet.has(targetId)) continue;
1142
1322
  if (targetId.endsWith("/index.ts") || targetId.endsWith("/index.tsx")) continue;
1143
1323
  const key = `${externalId}\u2192${targetId}`;
@@ -1314,8 +1494,8 @@ var typescriptProjectParser = {
1314
1494
  };
1315
1495
 
1316
1496
  // src/server/graph/parsers/db/prisma-schema.ts
1317
- var import_node_fs5 = require("node:fs");
1318
- var import_node_path5 = require("node:path");
1497
+ var import_node_fs6 = require("node:fs");
1498
+ init_config();
1319
1499
  function parseModels(content) {
1320
1500
  const nodes = [];
1321
1501
  const relations = [];
@@ -1406,11 +1586,25 @@ function parseEnums(content) {
1406
1586
  return nodes;
1407
1587
  }
1408
1588
  function detect2(rootDir) {
1409
- return (0, import_node_fs5.existsSync)((0, import_node_path5.join)(rootDir, "prisma", "schema.prisma"));
1589
+ const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
1590
+ return paths?.dbConfig.kind === "prisma" && (0, import_node_fs6.existsSync)(paths.dbConfig.schemaPath);
1410
1591
  }
1411
1592
  function generate2(rootDir) {
1412
- const schemaPath = (0, import_node_path5.join)(rootDir, "prisma", "schema.prisma");
1413
- const content = (0, import_node_fs5.readFileSync)(schemaPath, "utf-8");
1593
+ const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
1594
+ if (paths.dbConfig.kind !== "prisma") {
1595
+ return {
1596
+ metadata: { generated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), layer: "db", source: "none" },
1597
+ nodes: [],
1598
+ edges: [],
1599
+ cross_refs: [],
1600
+ contradictions: [],
1601
+ warnings: [],
1602
+ flagged_edges: [],
1603
+ patterns: { total_tables: 0, total_enums: 0, total_relations: 0 }
1604
+ };
1605
+ }
1606
+ const schemaPath = paths.dbConfig.schemaPath;
1607
+ const content = (0, import_node_fs6.readFileSync)(schemaPath, "utf-8");
1414
1608
  const { nodes: modelNodes, relations } = parseModels(content);
1415
1609
  const enumNodes = parseEnums(content);
1416
1610
  const allNodes = [...modelNodes, ...enumNodes];
@@ -1430,7 +1624,7 @@ function generate2(rootDir) {
1430
1624
  metadata: {
1431
1625
  generated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
1432
1626
  scope: "prisma-schema",
1433
- source: "prisma/schema.prisma",
1627
+ source: schemaPath,
1434
1628
  provider: "postgresql",
1435
1629
  layer: "db",
1436
1630
  total_models: modelNodes.length,
@@ -1467,8 +1661,9 @@ var prismaSchemaParser = {
1467
1661
  };
1468
1662
 
1469
1663
  // src/server/graph/parsers/db/sql-migrations.ts
1470
- var import_node_fs6 = require("node:fs");
1664
+ var import_node_fs7 = require("node:fs");
1471
1665
  var import_node_path6 = require("node:path");
1666
+ init_config();
1472
1667
  var PG_TO_PRISMA = {
1473
1668
  "TEXT": "String",
1474
1669
  "VARCHAR": "String",
@@ -1617,20 +1812,30 @@ function parseUniqueIndex(sql, state) {
1617
1812
  state.uniqueIndexes.get(m[1]).add(m[2]);
1618
1813
  }
1619
1814
  }
1620
- function parseMigrations(rootDir) {
1621
- const migrationsDir = (0, import_node_path6.join)(rootDir, "prisma", "migrations");
1815
+ function discoverMigrationFiles(migrationsDir) {
1816
+ if (!(0, import_node_fs7.existsSync)(migrationsDir)) return [];
1817
+ const out = [];
1818
+ const entries = (0, import_node_fs7.readdirSync)(migrationsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
1819
+ for (const entry of entries) {
1820
+ if (entry.isDirectory()) {
1821
+ const sqlPath = (0, import_node_path6.join)(migrationsDir, entry.name, "migration.sql");
1822
+ if ((0, import_node_fs7.existsSync)(sqlPath)) out.push(sqlPath);
1823
+ } else if (entry.isFile() && entry.name.endsWith(".sql")) {
1824
+ out.push((0, import_node_path6.join)(migrationsDir, entry.name));
1825
+ }
1826
+ }
1827
+ return out;
1828
+ }
1829
+ function parseMigrations(migrationsDir) {
1622
1830
  const state = {
1623
1831
  tables: /* @__PURE__ */ new Map(),
1624
1832
  enums: /* @__PURE__ */ new Map(),
1625
1833
  fks: [],
1626
1834
  uniqueIndexes: /* @__PURE__ */ new Map()
1627
1835
  };
1628
- if (!(0, import_node_fs6.existsSync)(migrationsDir)) return state;
1629
- const dirs = (0, import_node_fs6.readdirSync)(migrationsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
1630
- for (const dir of dirs) {
1631
- const sqlPath = (0, import_node_path6.join)(migrationsDir, dir, "migration.sql");
1632
- if (!(0, import_node_fs6.existsSync)(sqlPath)) continue;
1633
- const sql = (0, import_node_fs6.readFileSync)(sqlPath, "utf-8");
1836
+ if (!migrationsDir) return state;
1837
+ for (const sqlPath of discoverMigrationFiles(migrationsDir)) {
1838
+ const sql = (0, import_node_fs7.readFileSync)(sqlPath, "utf-8");
1634
1839
  parseCreateEnum(sql, state);
1635
1840
  parseCreateTable(sql, state);
1636
1841
  parseAlterTable(sql, state);
@@ -1640,10 +1845,9 @@ function parseMigrations(rootDir) {
1640
1845
  }
1641
1846
  return state;
1642
1847
  }
1643
- function loadPrismaState(rootDir) {
1644
- const schemaPath = (0, import_node_path6.join)(rootDir, "prisma", "schema.prisma");
1645
- if (!(0, import_node_fs6.existsSync)(schemaPath)) return null;
1646
- const content = (0, import_node_fs6.readFileSync)(schemaPath, "utf-8");
1848
+ function loadPrismaState(schemaPath) {
1849
+ if (!schemaPath || !(0, import_node_fs7.existsSync)(schemaPath)) return null;
1850
+ const content = (0, import_node_fs7.readFileSync)(schemaPath, "utf-8");
1647
1851
  const tables = /* @__PURE__ */ new Map();
1648
1852
  const enums = /* @__PURE__ */ new Map();
1649
1853
  const relations = [];
@@ -1807,14 +2011,28 @@ function verify(sqlState, prisma) {
1807
2011
  }
1808
2012
  return { contradictions, flaggedEdges };
1809
2013
  }
2014
+ function migrationsDirFor(rootDir) {
2015
+ const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
2016
+ if (!paths) return null;
2017
+ if (paths.dbConfig.kind === "prisma" || paths.dbConfig.kind === "sql-migrations") {
2018
+ return paths.dbConfig.migrationsDir;
2019
+ }
2020
+ return null;
2021
+ }
2022
+ function schemaPathFor(rootDir) {
2023
+ const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
2024
+ if (!paths) return null;
2025
+ return paths.dbConfig.kind === "prisma" ? paths.dbConfig.schemaPath : null;
2026
+ }
1810
2027
  function detect3(rootDir) {
1811
- const migrationsDir = (0, import_node_path6.join)(rootDir, "prisma", "migrations");
1812
- if (!(0, import_node_fs6.existsSync)(migrationsDir)) return false;
1813
- return (0, import_node_fs6.readdirSync)(migrationsDir, { withFileTypes: true }).some((d) => d.isDirectory() && (0, import_node_fs6.existsSync)((0, import_node_path6.join)(migrationsDir, d.name, "migration.sql")));
2028
+ const dir = migrationsDirFor(rootDir);
2029
+ if (!dir) return false;
2030
+ return discoverMigrationFiles(dir).length > 0;
1814
2031
  }
1815
2032
  function generate3(rootDir) {
1816
- const sqlState = parseMigrations(rootDir);
1817
- const prisma = loadPrismaState(rootDir);
2033
+ const migrationsDir = migrationsDirFor(rootDir);
2034
+ const sqlState = parseMigrations(migrationsDir);
2035
+ const prisma = loadPrismaState(schemaPathFor(rootDir));
1818
2036
  const prismaTableIds = prisma ? new Set(prisma.tables.keys()) : /* @__PURE__ */ new Set();
1819
2037
  const prismaEnumIds = prisma ? new Set(prisma.enums.keys()) : /* @__PURE__ */ new Set();
1820
2038
  const nodes = [];
@@ -1860,7 +2078,7 @@ function generate3(rootDir) {
1860
2078
  metadata: {
1861
2079
  generated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
1862
2080
  scope: "sql-migrations",
1863
- source: "prisma/migrations/",
2081
+ source: migrationsDir ?? "none",
1864
2082
  layer: "db",
1865
2083
  sql_tables: sqlState.tables.size,
1866
2084
  sql_enums: sqlState.enums.size,
@@ -2098,13 +2316,13 @@ var fetchResolverParser = {
2098
2316
  };
2099
2317
 
2100
2318
  // src/server/graph/parsers/crosslayer/api-annotations.ts
2101
- var import_node_fs7 = require("node:fs");
2319
+ var import_node_fs8 = require("node:fs");
2102
2320
  var import_node_path7 = require("node:path");
2103
2321
  var API_ANNOTATION_RE = /@api\s+(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+(\/\S+)/g;
2104
2322
  function walk2(dir, exts) {
2105
- if (!(0, import_node_fs7.existsSync)(dir)) return [];
2323
+ if (!(0, import_node_fs8.existsSync)(dir)) return [];
2106
2324
  const results = [];
2107
- for (const entry of (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true })) {
2325
+ for (const entry of (0, import_node_fs8.readdirSync)(dir, { withFileTypes: true })) {
2108
2326
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
2109
2327
  const full = (0, import_node_path7.join)(dir, entry.name);
2110
2328
  if (entry.isDirectory()) {
@@ -2123,7 +2341,7 @@ var apiAnnotationsParser = {
2123
2341
  layer: "crosslayer",
2124
2342
  concern: "api-binding",
2125
2343
  detect(rootDir) {
2126
- return (0, import_node_fs7.existsSync)((0, import_node_path7.join)(rootDir, "src"));
2344
+ return (0, import_node_fs8.existsSync)((0, import_node_path7.join)(rootDir, "src"));
2127
2345
  },
2128
2346
  generate(rootDir, layerOutputs) {
2129
2347
  const apiOutput = layerOutputs.get("api");
@@ -2140,7 +2358,7 @@ var apiAnnotationsParser = {
2140
2358
  const flaggedEdges = [];
2141
2359
  const seen = /* @__PURE__ */ new Set();
2142
2360
  for (const absPath of files) {
2143
- const content = (0, import_node_fs7.readFileSync)(absPath, "utf-8");
2361
+ const content = (0, import_node_fs8.readFileSync)(absPath, "utf-8");
2144
2362
  const sourceId = toNodeId2(srcDir, absPath);
2145
2363
  if (!uiNodeIds.has(sourceId)) continue;
2146
2364
  let match;
@@ -2184,14 +2402,14 @@ var apiAnnotationsParser = {
2184
2402
  };
2185
2403
 
2186
2404
  // src/server/graph/parsers/crosslayer/url-literal-scanner.ts
2187
- var import_node_fs8 = require("node:fs");
2405
+ var import_node_fs9 = require("node:fs");
2188
2406
  var import_node_path8 = require("node:path");
2189
2407
  init_config();
2190
2408
  var URL_LITERAL_RE = /['"`](\/api\/[^'"`\s]+?)['"`]/g;
2191
2409
  function walk3(dir, exts) {
2192
- if (!(0, import_node_fs8.existsSync)(dir)) return [];
2410
+ if (!(0, import_node_fs9.existsSync)(dir)) return [];
2193
2411
  const results = [];
2194
- for (const entry of (0, import_node_fs8.readdirSync)(dir, { withFileTypes: true })) {
2412
+ for (const entry of (0, import_node_fs9.readdirSync)(dir, { withFileTypes: true })) {
2195
2413
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
2196
2414
  const full = (0, import_node_path8.join)(dir, entry.name);
2197
2415
  if (entry.isDirectory()) {
@@ -2234,7 +2452,7 @@ var urlLiteralScannerParser = {
2234
2452
  for (const absPath of files) {
2235
2453
  const sourceId = toNodeId3(srcDir, absPath);
2236
2454
  if (!uiNodeIds.has(sourceId)) continue;
2237
- const content = (0, import_node_fs8.readFileSync)(absPath, "utf-8");
2455
+ const content = (0, import_node_fs9.readFileSync)(absPath, "utf-8");
2238
2456
  let match;
2239
2457
  URL_LITERAL_RE.lastIndex = 0;
2240
2458
  while ((match = URL_LITERAL_RE.exec(content)) !== null) {
@@ -2265,7 +2483,7 @@ var urlLiteralScannerParser = {
2265
2483
  };
2266
2484
 
2267
2485
  // src/server/graph/parsers/static/static-values.ts
2268
- var import_node_fs9 = require("node:fs");
2486
+ var import_node_fs10 = require("node:fs");
2269
2487
  var import_node_path9 = require("node:path");
2270
2488
  var parseCode = null;
2271
2489
  function tryLoadTreeSitter() {
@@ -2304,14 +2522,14 @@ function extractEnumValues(rootDir) {
2304
2522
  ];
2305
2523
  let content = "";
2306
2524
  for (const p of schemaPaths) {
2307
- if ((0, import_node_fs9.existsSync)(p)) {
2525
+ if ((0, import_node_fs10.existsSync)(p)) {
2308
2526
  try {
2309
- const stat = (0, import_node_fs9.statSync)(p);
2527
+ const stat = (0, import_node_fs10.statSync)(p);
2310
2528
  if (stat.isFile()) {
2311
- content = (0, import_node_fs9.readFileSync)(p, "utf-8");
2529
+ content = (0, import_node_fs10.readFileSync)(p, "utf-8");
2312
2530
  } else if (stat.isDirectory()) {
2313
- const files = (0, import_node_fs9.readdirSync)(p).filter((f) => f.endsWith(".prisma"));
2314
- content = files.map((f) => (0, import_node_fs9.readFileSync)((0, import_node_path9.join)(p, f), "utf-8")).join("\n");
2531
+ const files = (0, import_node_fs10.readdirSync)(p).filter((f) => f.endsWith(".prisma"));
2532
+ content = files.map((f) => (0, import_node_fs10.readFileSync)((0, import_node_path9.join)(p, f), "utf-8")).join("\n");
2315
2533
  }
2316
2534
  } catch {
2317
2535
  continue;
@@ -2385,7 +2603,7 @@ function extractStringArrayFromNode(node) {
2385
2603
  return values;
2386
2604
  }
2387
2605
  function findArrayDecl(root, varName) {
2388
- function walk5(node) {
2606
+ function walk4(node) {
2389
2607
  if (node.type === "variable_declarator") {
2390
2608
  const nameNode = node.childForFieldName("name");
2391
2609
  const valueNode = node.childForFieldName("value");
@@ -2398,12 +2616,12 @@ function findArrayDecl(root, varName) {
2398
2616
  }
2399
2617
  }
2400
2618
  for (const child of node.namedChildren) {
2401
- const found = walk5(child);
2619
+ const found = walk4(child);
2402
2620
  if (found) return found;
2403
2621
  }
2404
2622
  return null;
2405
2623
  }
2406
- return walk5(root);
2624
+ return walk4(root);
2407
2625
  }
2408
2626
  function extractObjectPropsRegex(objStr) {
2409
2627
  const props = {};
@@ -2470,10 +2688,10 @@ function extractSeedData(rootDir) {
2470
2688
  (0, import_node_path9.join)(rootDir, "prisma", "seed.ts"),
2471
2689
  (0, import_node_path9.join)(rootDir, "prisma", "seed.js"),
2472
2690
  (0, import_node_path9.join)(rootDir, "src", "server", "lib", "system-tags.ts")
2473
- ].filter(import_node_fs9.existsSync);
2691
+ ].filter(import_node_fs10.existsSync);
2474
2692
  const useTreeSitter = tryLoadTreeSitter();
2475
2693
  for (const filePath of seedFiles) {
2476
- const content = (0, import_node_fs9.readFileSync)(filePath, "utf-8");
2694
+ const content = (0, import_node_fs10.readFileSync)(filePath, "utf-8");
2477
2695
  const relPath = (0, import_node_path9.relative)(rootDir, filePath);
2478
2696
  const seeded = detectSeededArrays(content, relPath);
2479
2697
  let astRoot = null;
@@ -2568,9 +2786,9 @@ function extractSeedData(rootDir) {
2568
2786
  return { nodes, edges };
2569
2787
  }
2570
2788
  function walkDir(dir, exts) {
2571
- if (!(0, import_node_fs9.existsSync)(dir)) return [];
2789
+ if (!(0, import_node_fs10.existsSync)(dir)) return [];
2572
2790
  const results = [];
2573
- for (const entry of (0, import_node_fs9.readdirSync)(dir, { withFileTypes: true })) {
2791
+ for (const entry of (0, import_node_fs10.readdirSync)(dir, { withFileTypes: true })) {
2574
2792
  if (entry.name === "node_modules" || entry.name === ".next" || entry.name === "dist") continue;
2575
2793
  const full = (0, import_node_path9.join)(dir, entry.name);
2576
2794
  if (entry.isDirectory()) results.push(...walkDir(full, exts));
@@ -2581,9 +2799,9 @@ function walkDir(dir, exts) {
2581
2799
  function extractConstants(rootDir) {
2582
2800
  const nodes = [];
2583
2801
  const srcDir = (0, import_node_path9.join)(rootDir, "src");
2584
- if (!(0, import_node_fs9.existsSync)(srcDir)) return { nodes };
2802
+ if (!(0, import_node_fs10.existsSync)(srcDir)) return { nodes };
2585
2803
  for (const filePath of walkDir(srcDir, [".ts", ".tsx"])) {
2586
- const content = (0, import_node_fs9.readFileSync)(filePath, "utf-8");
2804
+ const content = (0, import_node_fs10.readFileSync)(filePath, "utf-8");
2587
2805
  const relPath = (0, import_node_path9.relative)(rootDir, filePath);
2588
2806
  const constArrayRe = /export\s+const\s+([A-Z][A-Z_0-9]+)\s*(?::[^=]+)?\s*=\s*\[/g;
2589
2807
  let cm;
@@ -2617,7 +2835,7 @@ function extractConstants(rootDir) {
2617
2835
  return { nodes };
2618
2836
  }
2619
2837
  function detect4(rootDir) {
2620
- return (0, import_node_fs9.existsSync)((0, import_node_path9.join)(rootDir, "prisma", "schema.prisma")) || (0, import_node_fs9.existsSync)((0, import_node_path9.join)(rootDir, "prisma", "seed.ts"));
2838
+ return (0, import_node_fs10.existsSync)((0, import_node_path9.join)(rootDir, "prisma", "schema.prisma")) || (0, import_node_fs10.existsSync)((0, import_node_path9.join)(rootDir, "prisma", "seed.ts"));
2621
2839
  }
2622
2840
  function generate4(rootDir) {
2623
2841
  const enumResult = extractEnumValues(rootDir);
@@ -2692,26 +2910,9 @@ var staticValuesParser = {
2692
2910
  };
2693
2911
 
2694
2912
  // src/server/graph/parsers/crosslayer/static-ref-scanner.ts
2695
- var import_node_fs10 = require("node:fs");
2913
+ var import_node_fs11 = require("node:fs");
2696
2914
  var import_node_path10 = require("node:path");
2697
2915
  init_config();
2698
- function walk4(dir, exts) {
2699
- if (!(0, import_node_fs10.existsSync)(dir)) return [];
2700
- const results = [];
2701
- function recurse(d) {
2702
- for (const entry of (0, import_node_fs10.readdirSync)(d, { withFileTypes: true })) {
2703
- const full = (0, import_node_path10.join)(d, entry.name);
2704
- if (entry.isDirectory()) {
2705
- if (entry.name === "node_modules" || entry.name === ".next" || entry.name === "dist") continue;
2706
- recurse(full);
2707
- } else if (exts.some((ext) => entry.name.endsWith(ext))) {
2708
- results.push(full);
2709
- }
2710
- }
2711
- }
2712
- recurse(dir);
2713
- return results;
2714
- }
2715
2916
  var MIN_VALUE_LENGTH = 4;
2716
2917
  var SKIP_VALUES = /* @__PURE__ */ new Set([
2717
2918
  "true",
@@ -2854,11 +3055,11 @@ var staticRefScannerParser = {
2854
3055
  if (!paths) return { cross_refs: [], flagged_edges: [], warnings: [] };
2855
3056
  const srcDir = paths.srcDir;
2856
3057
  const files = [
2857
- ...walk4((0, import_node_path10.join)(srcDir, "client"), [".ts", ".tsx"]),
2858
- ...walk4(paths.appDir, [".ts", ".tsx"]),
2859
- ...walk4((0, import_node_path10.join)(srcDir, "server"), [".ts", ".tsx"]),
2860
- ...walk4((0, import_node_path10.join)(srcDir, "lib"), [".ts", ".tsx"]),
2861
- ...walk4((0, import_node_path10.join)(srcDir, "config"), [".ts", ".tsx"])
3058
+ ...walkWithIgnore((0, import_node_path10.join)(srcDir, "client"), [".ts", ".tsx"]),
3059
+ ...walkWithIgnore(paths.appDir, [".ts", ".tsx"]),
3060
+ ...walkWithIgnore((0, import_node_path10.join)(srcDir, "server"), [".ts", ".tsx"]),
3061
+ ...walkWithIgnore((0, import_node_path10.join)(srcDir, "lib"), [".ts", ".tsx"]),
3062
+ ...walkWithIgnore((0, import_node_path10.join)(srcDir, "config"), [".ts", ".tsx"])
2862
3063
  ];
2863
3064
  const uiOutput = layerOutputs.get("ui");
2864
3065
  const apiOutput = layerOutputs.get("api");
@@ -2879,7 +3080,7 @@ var staticRefScannerParser = {
2879
3080
  const sourceId = (0, import_node_path10.relative)(srcDir, absPath).replace(/\\/g, "/");
2880
3081
  const sourceLayer = uiNodeIds.has(sourceId) ? "ui" : apiNodeIds.has(sourceId) ? "api" : null;
2881
3082
  if (!sourceLayer) continue;
2882
- const content = (0, import_node_fs10.readFileSync)(absPath, "utf-8");
3083
+ const content = (0, import_node_fs11.readFileSync)(absPath, "utf-8");
2883
3084
  filesScanned++;
2884
3085
  let fileRefs;
2885
3086
  if (parseCode2) {
@@ -3126,9 +3327,9 @@ function applyCrossLayerResults(uiOutput, results) {
3126
3327
  // src/server/graph/core/graph-builder.ts
3127
3328
  function readGraphFromDisk(rootDir, layer) {
3128
3329
  const filePath = (0, import_node_path12.join)(rootDir, ".launchsecure", "graphs", `${layer}.json`);
3129
- if (!(0, import_node_fs11.existsSync)(filePath)) return null;
3330
+ if (!(0, import_node_fs12.existsSync)(filePath)) return null;
3130
3331
  try {
3131
- return JSON.parse((0, import_node_fs11.readFileSync)(filePath, "utf-8"));
3332
+ return JSON.parse((0, import_node_fs12.readFileSync)(filePath, "utf-8"));
3132
3333
  } catch {
3133
3334
  return null;
3134
3335
  }
@@ -3232,7 +3433,7 @@ init_config();
3232
3433
  var import_node_path14 = require("node:path");
3233
3434
 
3234
3435
  // src/server/graph/taggers/module-tagger.ts
3235
- var import_node_fs12 = require("node:fs");
3436
+ var import_node_fs13 = require("node:fs");
3236
3437
  var import_node_path13 = require("node:path");
3237
3438
  function matchGlob(pattern, id) {
3238
3439
  const patParts = pattern.split("/");
@@ -3273,11 +3474,11 @@ function detectConventionDirs(rootDir, extraConventionDirs = []) {
3273
3474
  for (const base of searchDirs) {
3274
3475
  for (const convention of conventionDirs) {
3275
3476
  const dir = (0, import_node_path13.join)(base, convention);
3276
- if (!(0, import_node_fs12.existsSync)(dir)) continue;
3477
+ if (!(0, import_node_fs13.existsSync)(dir)) continue;
3277
3478
  try {
3278
- const stat = (0, import_node_fs12.statSync)(dir);
3479
+ const stat = (0, import_node_fs13.statSync)(dir);
3279
3480
  if (!stat.isDirectory()) continue;
3280
- const entries = (0, import_node_fs12.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
3481
+ const entries = (0, import_node_fs13.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
3281
3482
  if (entries.length > 0) {
3282
3483
  const relPath = dir.replace(rootDir + "/", "").replace(rootDir + "\\", "");
3283
3484
  result.set(relPath, entries);
@@ -3594,7 +3795,7 @@ function createTaggerRegistry(config, rootDir) {
3594
3795
  }
3595
3796
 
3596
3797
  // src/server/graph/core/tag-store.ts
3597
- var import_node_fs13 = require("node:fs");
3798
+ var import_node_fs14 = require("node:fs");
3598
3799
  var import_node_path15 = require("node:path");
3599
3800
  var TAGS_FILENAME = "tags.json";
3600
3801
  var GRAPHS_DIR = ".launchsecure/graphs";
@@ -3604,14 +3805,14 @@ function tagsFilePath(rootDir) {
3604
3805
  }
3605
3806
  function readTagStore(rootDir) {
3606
3807
  const filePath = tagsFilePath(rootDir);
3607
- if (!(0, import_node_fs13.existsSync)(filePath)) return {};
3608
- const stat = (0, import_node_fs13.statSync)(filePath);
3808
+ if (!(0, import_node_fs14.existsSync)(filePath)) return {};
3809
+ const stat = (0, import_node_fs14.statSync)(filePath);
3609
3810
  const cached = tagCache.get(filePath);
3610
3811
  if (cached && cached.mtimeMs === stat.mtimeMs) {
3611
3812
  return cached.store;
3612
3813
  }
3613
3814
  try {
3614
- const content = (0, import_node_fs13.readFileSync)(filePath, "utf-8");
3815
+ const content = (0, import_node_fs14.readFileSync)(filePath, "utf-8");
3615
3816
  const store = JSON.parse(content);
3616
3817
  tagCache.set(filePath, { mtimeMs: stat.mtimeMs, store });
3617
3818
  return store;
@@ -3622,14 +3823,14 @@ function readTagStore(rootDir) {
3622
3823
  function writeTagStore(rootDir, store) {
3623
3824
  const filePath = tagsFilePath(rootDir);
3624
3825
  const dir = (0, import_node_path15.dirname)(filePath);
3625
- (0, import_node_fs13.mkdirSync)(dir, { recursive: true });
3826
+ (0, import_node_fs14.mkdirSync)(dir, { recursive: true });
3626
3827
  const cleaned = {};
3627
3828
  for (const [nodeId, tags] of Object.entries(store)) {
3628
3829
  if (Object.keys(tags).length > 0) {
3629
3830
  cleaned[nodeId] = tags;
3630
3831
  }
3631
3832
  }
3632
- (0, import_node_fs13.writeFileSync)(filePath, JSON.stringify(cleaned, null, 2) + "\n", "utf-8");
3833
+ (0, import_node_fs14.writeFileSync)(filePath, JSON.stringify(cleaned, null, 2) + "\n", "utf-8");
3633
3834
  tagCache.delete(filePath);
3634
3835
  }
3635
3836
  function setTag(rootDir, nodeId, key, value) {
@@ -3653,8 +3854,8 @@ init_ts_extractor();
3653
3854
  var GRAPHS_DIR2 = ".launchsecure/graphs";
3654
3855
  function getAvailableLayers(rootDir) {
3655
3856
  const dir = (0, import_node_path16.join)(rootDir, GRAPHS_DIR2);
3656
- if (!(0, import_node_fs14.existsSync)(dir)) return [];
3657
- return (0, import_node_fs14.readdirSync)(dir).filter((f) => f.endsWith(".json") && f !== "tags.json").map((f) => f.replace(".json", ""));
3857
+ if (!(0, import_node_fs15.existsSync)(dir)) return [];
3858
+ return (0, import_node_fs15.readdirSync)(dir).filter((f) => f.endsWith(".json") && f !== "tags.json").map((f) => f.replace(".json", ""));
3658
3859
  }
3659
3860
  var graphCache = /* @__PURE__ */ new Map();
3660
3861
  var taggedCache = /* @__PURE__ */ new Map();
@@ -3668,8 +3869,8 @@ function tagsFilePath2(rootDir) {
3668
3869
  return (0, import_node_path16.join)(graphsDir(rootDir), "tags.json");
3669
3870
  }
3670
3871
  function getMtimeMs(filePath) {
3671
- if (!(0, import_node_fs14.existsSync)(filePath)) return 0;
3672
- return (0, import_node_fs14.statSync)(filePath).mtimeMs;
3872
+ if (!(0, import_node_fs15.existsSync)(filePath)) return 0;
3873
+ return (0, import_node_fs15.statSync)(filePath).mtimeMs;
3673
3874
  }
3674
3875
  function invalidateCache(filePath) {
3675
3876
  graphCache.delete(filePath);
@@ -3708,20 +3909,20 @@ function applyTags(graph, layer, rootDir) {
3708
3909
  }
3709
3910
  function readGraphRaw(rootDir, layer) {
3710
3911
  const filePath = graphFilePath(rootDir, layer);
3711
- if (!(0, import_node_fs14.existsSync)(filePath)) return null;
3712
- const stat = (0, import_node_fs14.statSync)(filePath);
3912
+ if (!(0, import_node_fs15.existsSync)(filePath)) return null;
3913
+ const stat = (0, import_node_fs15.statSync)(filePath);
3713
3914
  const cached = graphCache.get(filePath);
3714
3915
  if (cached && cached.mtimeMs === stat.mtimeMs) {
3715
3916
  return cached.graph;
3716
3917
  }
3717
- const content = (0, import_node_fs14.readFileSync)(filePath, "utf-8");
3918
+ const content = (0, import_node_fs15.readFileSync)(filePath, "utf-8");
3718
3919
  const graph = JSON.parse(content);
3719
3920
  graphCache.set(filePath, { mtimeMs: stat.mtimeMs, graph });
3720
3921
  return graph;
3721
3922
  }
3722
3923
  function readGraph(rootDir, layer) {
3723
3924
  const rawFilePath = graphFilePath(rootDir, layer);
3724
- if (!(0, import_node_fs14.existsSync)(rawFilePath)) return null;
3925
+ if (!(0, import_node_fs15.existsSync)(rawFilePath)) return null;
3725
3926
  const rawMtime = getMtimeMs(rawFilePath);
3726
3927
  const tagsMtime = getMtimeMs(tagsFilePath2(rootDir));
3727
3928
  const cacheKey = `${rootDir}:${layer}`;
@@ -3751,11 +3952,11 @@ async function generateGraph(rootDir, layer) {
3751
3952
  mutationMethods: config.parsers?.patterns?.mutationMethods
3752
3953
  });
3753
3954
  const dir = graphsDir(rootDir);
3754
- (0, import_node_fs14.mkdirSync)(dir, { recursive: true });
3955
+ (0, import_node_fs15.mkdirSync)(dir, { recursive: true });
3755
3956
  const results = layer ? [generateLayer(rootDir, layer)].filter((r) => r !== null) : generateAll(rootDir);
3756
3957
  for (const result of results) {
3757
3958
  const filePath = graphFilePath(rootDir, result.layer);
3758
- (0, import_node_fs14.writeFileSync)(filePath, JSON.stringify(result.output, null, 2) + "\n", "utf-8");
3959
+ (0, import_node_fs15.writeFileSync)(filePath, JSON.stringify(result.output, null, 2) + "\n", "utf-8");
3759
3960
  invalidateCache(filePath);
3760
3961
  invalidateTaggedCache(rootDir, result.layer);
3761
3962
  }
@@ -3764,7 +3965,7 @@ async function generateGraph(rootDir, layer) {
3764
3965
 
3765
3966
  // src/server/lockfile.ts
3766
3967
  var import_node_child_process = require("node:child_process");
3767
- var import_node_fs15 = require("node:fs");
3968
+ var import_node_fs16 = require("node:fs");
3768
3969
  var import_node_os = require("node:os");
3769
3970
  var import_node_path17 = require("node:path");
3770
3971
  function lockDir(projectRoot) {
@@ -3780,12 +3981,12 @@ var _activeProjectRoot;
3780
3981
  function readLock(projectRoot) {
3781
3982
  const root = projectRoot ?? _activeProjectRoot;
3782
3983
  const p = lockPath(root);
3783
- if (!(0, import_node_fs15.existsSync)(p)) {
3984
+ if (!(0, import_node_fs16.existsSync)(p)) {
3784
3985
  if (root) {
3785
3986
  const globalP = lockPath();
3786
- if ((0, import_node_fs15.existsSync)(globalP)) {
3987
+ if ((0, import_node_fs16.existsSync)(globalP)) {
3787
3988
  try {
3788
- const data = JSON.parse((0, import_node_fs15.readFileSync)(globalP, "utf-8"));
3989
+ const data = JSON.parse((0, import_node_fs16.readFileSync)(globalP, "utf-8"));
3789
3990
  if (typeof data.pid === "number" && typeof data.port === "number" && data.cwd === root) {
3790
3991
  return data;
3791
3992
  }
@@ -3796,7 +3997,7 @@ function readLock(projectRoot) {
3796
3997
  return null;
3797
3998
  }
3798
3999
  try {
3799
- const data = JSON.parse((0, import_node_fs15.readFileSync)(p, "utf-8"));
4000
+ const data = JSON.parse((0, import_node_fs16.readFileSync)(p, "utf-8"));
3800
4001
  if (typeof data.pid !== "number" || typeof data.port !== "number") return null;
3801
4002
  return data;
3802
4003
  } catch {
@@ -3833,7 +4034,7 @@ function getLiveLock(projectRoot) {
3833
4034
  const live = listenerPid !== null ? listenerPid === lock.pid : isPidAlive(lock.pid);
3834
4035
  if (!live) {
3835
4036
  try {
3836
- (0, import_node_fs15.unlinkSync)(lockPath(root));
4037
+ (0, import_node_fs16.unlinkSync)(lockPath(root));
3837
4038
  } catch {
3838
4039
  }
3839
4040
  return null;
@@ -3842,14 +4043,14 @@ function getLiveLock(projectRoot) {
3842
4043
  }
3843
4044
  function writeLock(data, projectRoot) {
3844
4045
  const root = projectRoot ?? _activeProjectRoot;
3845
- (0, import_node_fs15.mkdirSync)(lockDir(root), { recursive: true });
3846
- (0, import_node_fs15.writeFileSync)(lockPath(root), JSON.stringify(data, null, 2) + "\n", "utf-8");
4046
+ (0, import_node_fs16.mkdirSync)(lockDir(root), { recursive: true });
4047
+ (0, import_node_fs16.writeFileSync)(lockPath(root), JSON.stringify(data, null, 2) + "\n", "utf-8");
3847
4048
  if (root) _activeProjectRoot = root;
3848
4049
  }
3849
4050
  function clearLock(projectRoot) {
3850
4051
  const root = projectRoot ?? _activeProjectRoot;
3851
4052
  try {
3852
- (0, import_node_fs15.unlinkSync)(lockPath(root));
4053
+ (0, import_node_fs16.unlinkSync)(lockPath(root));
3853
4054
  } catch {
3854
4055
  }
3855
4056
  }
@@ -3858,13 +4059,13 @@ function clearLock(projectRoot) {
3858
4059
  init_config();
3859
4060
 
3860
4061
  // src/server/graph/core/audit-core.ts
3861
- var import_node_fs16 = require("node:fs");
4062
+ var import_node_fs17 = require("node:fs");
3862
4063
  var import_node_path18 = require("node:path");
3863
4064
  function readGraphFile(rootDir, layer) {
3864
4065
  const filePath = (0, import_node_path18.join)(rootDir, ".launchsecure", "graphs", `${layer}.json`);
3865
- if (!(0, import_node_fs16.existsSync)(filePath)) return null;
4066
+ if (!(0, import_node_fs17.existsSync)(filePath)) return null;
3866
4067
  try {
3867
- return JSON.parse((0, import_node_fs16.readFileSync)(filePath, "utf-8"));
4068
+ return JSON.parse((0, import_node_fs17.readFileSync)(filePath, "utf-8"));
3868
4069
  } catch {
3869
4070
  return null;
3870
4071
  }
@@ -3910,8 +4111,8 @@ function checkUnprotectedRoutes(rootDir) {
3910
4111
  if (!api) return buildReport("api", "unprotected_routes", findings);
3911
4112
  const routePermsPath = (0, import_node_path18.join)(rootDir, "src", "config", "route-permissions.ts");
3912
4113
  let routePermsContent = "";
3913
- if ((0, import_node_fs16.existsSync)(routePermsPath)) {
3914
- routePermsContent = (0, import_node_fs16.readFileSync)(routePermsPath, "utf-8");
4114
+ if ((0, import_node_fs17.existsSync)(routePermsPath)) {
4115
+ routePermsContent = (0, import_node_fs17.readFileSync)(routePermsPath, "utf-8");
3915
4116
  }
3916
4117
  const registeredRoutes = /* @__PURE__ */ new Set();
3917
4118
  const routeEntryRe = /path:\s*'([^']+)'/g;
@@ -3990,8 +4191,8 @@ function checkUnenforcedPermissions(rootDir) {
3990
4191
  const permissions = staticGraph.nodes.filter((n) => n.type === "seed_permission").map((n) => ({ id: n.id, key: n.value, name: n.name }));
3991
4192
  const routePermsPath = (0, import_node_path18.join)(rootDir, "src", "config", "route-permissions.ts");
3992
4193
  let routePermsContent = "";
3993
- if ((0, import_node_fs16.existsSync)(routePermsPath)) {
3994
- routePermsContent = (0, import_node_fs16.readFileSync)(routePermsPath, "utf-8");
4194
+ if ((0, import_node_fs17.existsSync)(routePermsPath)) {
4195
+ routePermsContent = (0, import_node_fs17.readFileSync)(routePermsPath, "utf-8");
3995
4196
  }
3996
4197
  for (const perm of permissions) {
3997
4198
  const regex = new RegExp(`permission:\\s*['"]${perm.key}['"]`);
@@ -4022,8 +4223,8 @@ function checkHardcodedValues(rootDir) {
4022
4223
  for (const node of api.nodes) {
4023
4224
  if (node.type !== "endpoint") continue;
4024
4225
  const filePath = (0, import_node_path18.join)(rootDir, "src", node.id);
4025
- if (!(0, import_node_fs16.existsSync)(filePath)) continue;
4026
- const content = (0, import_node_fs16.readFileSync)(filePath, "utf-8");
4226
+ if (!(0, import_node_fs17.existsSync)(filePath)) continue;
4227
+ const content = (0, import_node_fs17.readFileSync)(filePath, "utf-8");
4027
4228
  let m;
4028
4229
  allCapsRe.lastIndex = 0;
4029
4230
  while ((m = allCapsRe.exec(content)) !== null) {
@@ -4154,14 +4355,14 @@ function findProjectRoot(startDir) {
4154
4355
  let dir = startDir;
4155
4356
  for (let i = 0; i < 8; i++) {
4156
4357
  const graphsDir2 = import_node_path19.default.join(dir, ".launchsecure", "graphs");
4157
- if (import_node_fs17.default.existsSync(import_node_path19.default.join(graphsDir2, "ui.json")) || import_node_fs17.default.existsSync(import_node_path19.default.join(graphsDir2, "api.json")) || import_node_fs17.default.existsSync(import_node_path19.default.join(graphsDir2, "db.json"))) return dir;
4358
+ if (import_node_fs18.default.existsSync(import_node_path19.default.join(graphsDir2, "ui.json")) || import_node_fs18.default.existsSync(import_node_path19.default.join(graphsDir2, "api.json")) || import_node_fs18.default.existsSync(import_node_path19.default.join(graphsDir2, "db.json"))) return dir;
4158
4359
  const parent = import_node_path19.default.dirname(dir);
4159
4360
  if (parent === dir) break;
4160
4361
  dir = parent;
4161
4362
  }
4162
4363
  dir = startDir;
4163
4364
  for (let i = 0; i < 8; i++) {
4164
- if (import_node_fs17.default.existsSync(import_node_path19.default.join(dir, ".git"))) return dir;
4365
+ if (import_node_fs18.default.existsSync(import_node_path19.default.join(dir, ".git"))) return dir;
4165
4366
  const parent = import_node_path19.default.dirname(dir);
4166
4367
  if (parent === dir) break;
4167
4368
  dir = parent;
@@ -4222,16 +4423,16 @@ async function buildMergedGraph(root) {
4222
4423
  };
4223
4424
  }
4224
4425
  function serveStatic(res, filePath) {
4225
- if (!import_node_fs17.default.existsSync(filePath) || !import_node_fs17.default.statSync(filePath).isFile()) return false;
4426
+ if (!import_node_fs18.default.existsSync(filePath) || !import_node_fs18.default.statSync(filePath).isFile()) return false;
4226
4427
  const ext = import_node_path19.default.extname(filePath).toLowerCase();
4227
4428
  const mime = MIME_TYPES[ext] ?? "application/octet-stream";
4228
4429
  res.writeHead(200, { "Content-Type": mime, "Cache-Control": "no-cache" });
4229
- import_node_fs17.default.createReadStream(filePath).pipe(res);
4430
+ import_node_fs18.default.createReadStream(filePath).pipe(res);
4230
4431
  return true;
4231
4432
  }
4232
4433
  function serveIndex(res, clientDir) {
4233
4434
  const indexPath = import_node_path19.default.join(clientDir, "index.html");
4234
- if (!import_node_fs17.default.existsSync(indexPath)) {
4435
+ if (!import_node_fs18.default.existsSync(indexPath)) {
4235
4436
  res.writeHead(500, { "Content-Type": "text/plain" });
4236
4437
  res.end(`LaunchChart client bundle not found at ${clientDir}. Run 'npm run build:chart-client'.`);
4237
4438
  return;
@@ -4239,14 +4440,14 @@ function serveIndex(res, clientDir) {
4239
4440
  serveStatic(res, indexPath);
4240
4441
  }
4241
4442
  function tryListen(server, port) {
4242
- return new Promise((resolve3, reject) => {
4443
+ return new Promise((resolve4, reject) => {
4243
4444
  const onError = (err) => {
4244
4445
  server.off("listening", onListening);
4245
4446
  reject(err);
4246
4447
  };
4247
4448
  const onListening = () => {
4248
4449
  server.off("error", onError);
4249
- resolve3(port);
4450
+ resolve4(port);
4250
4451
  };
4251
4452
  server.once("error", onError);
4252
4453
  server.once("listening", onListening);
@@ -4300,10 +4501,10 @@ async function startChartServer(opts = {}) {
4300
4501
  if (req.method === "GET" && url2.pathname === "/api/projects") {
4301
4502
  const projectList = projects.length > 0 ? projects.map((p) => {
4302
4503
  const absRoot = import_node_path19.default.resolve(projectRoot, p.root);
4303
- const hasGraphs = import_node_fs17.default.existsSync(import_node_path19.default.join(absRoot, ".launchsecure", "graphs"));
4304
- const hasNextConfig2 = import_node_fs17.default.existsSync(import_node_path19.default.join(absRoot, "next.config.ts")) || import_node_fs17.default.existsSync(import_node_path19.default.join(absRoot, "next.config.js")) || import_node_fs17.default.existsSync(import_node_path19.default.join(absRoot, "next.config.mjs"));
4305
- return { name: p.name, root: p.root, hasGraphs, hasNextConfig: hasNextConfig2 };
4306
- }) : [{ name: import_node_path19.default.basename(projectRoot), root: ".", hasGraphs: import_node_fs17.default.existsSync(import_node_path19.default.join(projectRoot, ".launchsecure", "graphs")), hasNextConfig: true }];
4504
+ const hasGraphs = import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, ".launchsecure", "graphs"));
4505
+ const hasNextConfig = import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, "next.config.ts")) || import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, "next.config.js")) || import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, "next.config.mjs"));
4506
+ return { name: p.name, root: p.root, hasGraphs, hasNextConfig };
4507
+ }) : [{ name: import_node_path19.default.basename(projectRoot), root: ".", hasGraphs: import_node_fs18.default.existsSync(import_node_path19.default.join(projectRoot, ".launchsecure", "graphs")), hasNextConfig: true }];
4307
4508
  res.writeHead(200, { "Content-Type": "application/json" });
4308
4509
  res.end(JSON.stringify({ projects: projectList, monorepoRoot: projectRoot }));
4309
4510
  return;
@@ -4355,14 +4556,14 @@ async function startChartServer(opts = {}) {
4355
4556
  return;
4356
4557
  }
4357
4558
  const filePath = import_node_path19.default.join(reqRoot, relPath);
4358
- if (!filePath.startsWith(reqRoot) || !import_node_fs17.default.existsSync(filePath) || !import_node_fs17.default.statSync(filePath).isFile()) {
4559
+ if (!filePath.startsWith(reqRoot) || !import_node_fs18.default.existsSync(filePath) || !import_node_fs18.default.statSync(filePath).isFile()) {
4359
4560
  res.writeHead(404, { "Content-Type": "application/json" });
4360
4561
  res.end(JSON.stringify({ error: "File not found" }));
4361
4562
  return;
4362
4563
  }
4363
4564
  const ext = import_node_path19.default.extname(filePath).toLowerCase();
4364
4565
  const langMap = { ".ts": "typescript", ".tsx": "tsx", ".js": "javascript", ".jsx": "jsx", ".prisma": "prisma", ".json": "json", ".css": "css" };
4365
- const content = import_node_fs17.default.readFileSync(filePath, "utf-8");
4566
+ const content = import_node_fs18.default.readFileSync(filePath, "utf-8");
4366
4567
  res.writeHead(200, { "Content-Type": "application/json" });
4367
4568
  res.end(JSON.stringify({ content, language: langMap[ext] ?? "text", path: relPath }));
4368
4569
  return;
@@ -4405,7 +4606,7 @@ async function startChartServer(opts = {}) {
4405
4606
  try {
4406
4607
  const newConfig = JSON.parse(body);
4407
4608
  const configPath = import_node_path19.default.join(reqRoot, ".launchchart.json");
4408
- import_node_fs17.default.writeFileSync(configPath, JSON.stringify(newConfig, null, 2) + "\n", "utf-8");
4609
+ import_node_fs18.default.writeFileSync(configPath, JSON.stringify(newConfig, null, 2) + "\n", "utf-8");
4409
4610
  res.writeHead(200, { "Content-Type": "application/json" });
4410
4611
  res.end(JSON.stringify({ ok: true }));
4411
4612
  } catch (err) {
@@ -4439,7 +4640,7 @@ async function startChartServer(opts = {}) {
4439
4640
  const config2 = loadConfig(reqRoot);
4440
4641
  config2.taggers = taggerConfig;
4441
4642
  const configPath = import_node_path19.default.join(reqRoot, ".launchchart.json");
4442
- import_node_fs17.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
4643
+ import_node_fs18.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
4443
4644
  res.writeHead(200, { "Content-Type": "application/json" });
4444
4645
  res.end(JSON.stringify({ ok: true }));
4445
4646
  } catch (err) {
@@ -4506,16 +4707,23 @@ async function startChartServer(opts = {}) {
4506
4707
  const paths = resolveProjectPaths(reqRoot, config2);
4507
4708
  const overrides = {
4508
4709
  appDir: !!config2.paths?.appDir,
4509
- dbDir: !!config2.paths?.dbDir
4710
+ dbDir: !!config2.paths?.dbDir,
4711
+ srcRoots: !!(config2.paths?.srcRoots && config2.paths.srcRoots.length > 0)
4510
4712
  };
4713
+ const relFromRoot = (abs) => import_node_path19.default.relative(reqRoot, abs) || ".";
4511
4714
  res.writeHead(200, { "Content-Type": "application/json" });
4512
4715
  res.end(JSON.stringify({
4513
4716
  projectRoot: reqRoot,
4514
4717
  detected: paths ? {
4515
- srcDir: import_node_path19.default.relative(reqRoot, paths.srcDir) || ".",
4516
- appDir: import_node_path19.default.relative(reqRoot, paths.appDir),
4517
- apiDir: import_node_path19.default.relative(reqRoot, paths.apiDir),
4518
- dbDir: paths.dbDir ? import_node_path19.default.relative(reqRoot, paths.dbDir) : null
4718
+ srcDir: relFromRoot(paths.srcDir),
4719
+ appDir: relFromRoot(paths.appDir),
4720
+ apiDir: relFromRoot(paths.apiDir),
4721
+ dbDir: paths.dbDir ? relFromRoot(paths.dbDir) : null,
4722
+ dbKind: paths.dbConfig.kind,
4723
+ dbSchemaPath: paths.dbConfig.schemaPath ? relFromRoot(paths.dbConfig.schemaPath) : null,
4724
+ dbMigrationsDir: paths.dbConfig.migrationsDir ? relFromRoot(paths.dbConfig.migrationsDir) : null,
4725
+ srcRoots: paths.srcRoots.map(relFromRoot),
4726
+ conventionFiles: paths.conventionFiles.map(relFromRoot)
4519
4727
  } : null,
4520
4728
  overrides,
4521
4729
  isOverride: overrides.appDir
@@ -4532,7 +4740,7 @@ async function startChartServer(opts = {}) {
4532
4740
  return;
4533
4741
  }
4534
4742
  try {
4535
- const entries = import_node_fs17.default.readdirSync(abs, { withFileTypes: true });
4743
+ const entries = import_node_fs18.default.readdirSync(abs, { withFileTypes: true });
4536
4744
  const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules" && e.name !== "dist" && e.name !== ".next").map((e) => e.name).sort();
4537
4745
  const parent = abs !== twoUp ? import_node_path19.default.dirname(abs) : null;
4538
4746
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -4563,7 +4771,7 @@ async function startChartServer(opts = {}) {
4563
4771
  const config2 = loadConfig(projectRoot);
4564
4772
  config2.projects = newProjects.length > 0 ? newProjects : void 0;
4565
4773
  const configPath = import_node_path19.default.join(projectRoot, ".launchchart.json");
4566
- import_node_fs17.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
4774
+ import_node_fs18.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
4567
4775
  projects.length = 0;
4568
4776
  if (config2.projects) projects.push(...config2.projects);
4569
4777
  res.writeHead(200, { "Content-Type": "application/json" });