@launchsecure/launch-kit 0.0.18 → 0.0.20

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 (69) hide show
  1. package/dist/chart-client/assets/index-C8ANseEa.js +441 -0
  2. package/dist/chart-client/index.html +1 -1
  3. package/dist/deck-client/assets/{_baseUniq-2gclQXo7.js → _baseUniq-DsfOm3t_.js} +1 -1
  4. package/dist/deck-client/assets/{arc-DcMY5Wm0.js → arc-NJuvkBv1.js} +1 -1
  5. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-B8iirmmJ.js → architectureDiagram-Q4EWVU46-BgrcgZs0.js} +1 -1
  6. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-B4JBLjmJ.js → blockDiagram-DXYQGD6D-C3XoLi15.js} +1 -1
  7. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-CojrJAk8.js → c4Diagram-AHTNJAMY-FX2PjLfb.js} +1 -1
  8. package/dist/deck-client/assets/channel-ChQjD1T1.js +1 -0
  9. package/dist/deck-client/assets/{chunk-4BX2VUAB-Bmb_BMDo.js → chunk-4BX2VUAB-D0aqsJV0.js} +1 -1
  10. package/dist/deck-client/assets/{chunk-4TB4RGXK-CumBy8qe.js → chunk-4TB4RGXK-7qRCCAgK.js} +1 -1
  11. package/dist/deck-client/assets/{chunk-55IACEB6-Ka8Hb1wD.js → chunk-55IACEB6-DfHG-iqb.js} +1 -1
  12. package/dist/deck-client/assets/{chunk-EDXVE4YY-B3sIPiQo.js → chunk-EDXVE4YY-DrR52j3B.js} +1 -1
  13. package/dist/deck-client/assets/{chunk-FMBD7UC4-C1tYkaqu.js → chunk-FMBD7UC4-D5KSGATB.js} +1 -1
  14. package/dist/deck-client/assets/{chunk-OYMX7WX6-D7Wacbky.js → chunk-OYMX7WX6-M7hsLRNU.js} +1 -1
  15. package/dist/deck-client/assets/{chunk-QZHKN3VN-ChXI0vO3.js → chunk-QZHKN3VN-1ynAWO2m.js} +1 -1
  16. package/dist/deck-client/assets/{chunk-YZCP3GAM-BXhiqf8u.js → chunk-YZCP3GAM-S2-nGw3D.js} +1 -1
  17. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-B_9iqK1S.js +1 -0
  18. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-B_9iqK1S.js +1 -0
  19. package/dist/deck-client/assets/clone-BYt1AMfz.js +1 -0
  20. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-Bqp3p68D.js → cose-bilkent-S5V4N54A-BcMwozS2.js} +1 -1
  21. package/dist/deck-client/assets/{dagre-KV5264BT-BS-rtyhZ.js → dagre-KV5264BT-DtKMhl_1.js} +1 -1
  22. package/dist/deck-client/assets/{diagram-5BDNPKRD-BIrj9YGI.js → diagram-5BDNPKRD-1plH69us.js} +1 -1
  23. package/dist/deck-client/assets/{diagram-G4DWMVQ6-noHWPIg4.js → diagram-G4DWMVQ6-D_o-BHO3.js} +1 -1
  24. package/dist/deck-client/assets/{diagram-MMDJMWI5-C2qHxvqV.js → diagram-MMDJMWI5-ClZ1LIx6.js} +1 -1
  25. package/dist/deck-client/assets/{diagram-TYMM5635-BytnGQr-.js → diagram-TYMM5635-B8dKHfRh.js} +1 -1
  26. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-BfK5m2YQ.js → erDiagram-SMLLAGMA-CY2aCH7-.js} +1 -1
  27. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-Cq925G1Z.js → flowDiagram-DWJPFMVM-DZZWHti8.js} +1 -1
  28. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-DhhHPAmj.js → ganttDiagram-T4ZO3ILL-OwGGa6Lu.js} +1 -1
  29. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-B3Lc0h9q.js → gitGraphDiagram-UUTBAWPF-GKyWD4Qt.js} +1 -1
  30. package/dist/deck-client/assets/{graph-RTawgVWm.js → graph-CORzYQdB.js} +1 -1
  31. package/dist/deck-client/assets/{index-BfIfJXmS.js → index-hiIpM7EP.js} +3 -3
  32. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-BlR584kX.js → infoDiagram-42DDH7IO-DmgqJCcF.js} +1 -1
  33. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DygKoNGY.js → ishikawaDiagram-UXIWVN3A-D-1v7knu.js} +1 -1
  34. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-BnaiYp9N.js → journeyDiagram-VCZTEJTY-CYrGQE7b.js} +1 -1
  35. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-BQBUBzJC.js → kanban-definition-6JOO6SKY-BJFDWiH-.js} +1 -1
  36. package/dist/deck-client/assets/{layout-DeZ8HI1T.js → layout-BTFFcaxF.js} +1 -1
  37. package/dist/deck-client/assets/{linear-C6roLi_9.js → linear-DAbl6COS.js} +1 -1
  38. package/dist/deck-client/assets/{min-CbUksbuI.js → min-oWHBrFBm.js} +1 -1
  39. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-iNxV62yN.js → mindmap-definition-QFDTVHPH-BTCB0VLO.js} +1 -1
  40. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-DHVA0jaG.js → pieDiagram-DEJITSTG-CUZChWNA.js} +1 -1
  41. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-DBeKKLUQ.js → quadrantDiagram-34T5L4WZ-4M1Um_e4.js} +1 -1
  42. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-CBwITx7p.js → requirementDiagram-MS252O5E-DLzQZ0B3.js} +1 -1
  43. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BtE-1YTU.js → sankeyDiagram-XADWPNL6-DcNgzV3E.js} +1 -1
  44. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-DN96yPP2.js → sequenceDiagram-FGHM5R23-CAcI2vC9.js} +1 -1
  45. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-VUkKC2uJ.js → stateDiagram-FHFEXIEX-CntjTTm5.js} +1 -1
  46. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-YiaphOU_.js +1 -0
  47. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-oUeZhRns.js → timeline-definition-GMOUNBTQ-D8zrit4U.js} +1 -1
  48. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-D87fK90n.js → vennDiagram-DHZGUBPP-C4SuFPgo.js} +1 -1
  49. package/dist/deck-client/assets/wardley-RL74JXVD-B3F-Olcq.js +162 -0
  50. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-Ca_i0QRA.js → wardleyDiagram-NUSXRM2D-kj73r6f-.js} +1 -1
  51. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-CUOJVIvq.js → xychartDiagram-5P7HB3ND-CC_d_Ey3.js} +1 -1
  52. package/dist/deck-client/index.html +1 -1
  53. package/dist/server/chart-serve.js +549 -207
  54. package/dist/server/cli.js +541 -206
  55. package/dist/server/council-entry.js +0 -0
  56. package/dist/server/deck-server/deck-mcp-entry.js +0 -0
  57. package/dist/server/fb-wizard.js +0 -0
  58. package/dist/server/graph-mcp-entry.js +587 -244
  59. package/dist/server/server/cli.js +0 -0
  60. package/dist/server/server/fb-wizard.js +0 -0
  61. package/dist/server/server/graph-mcp-entry.js +0 -0
  62. package/package.json +18 -17
  63. package/dist/chart-client/assets/index-D7x8nz-H.js +0 -441
  64. package/dist/deck-client/assets/channel-ERh5jKXV.js +0 -1
  65. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-CMi1Gaev.js +0 -1
  66. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-CMi1Gaev.js +0 -1
  67. package/dist/deck-client/assets/clone-DfWhlD4X.js +0 -1
  68. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CA0IjulK.js +0 -1
  69. package/dist/deck-client/assets/wardley-RL74JXVD-DYbYcpDp.js +0 -162
@@ -602,19 +602,19 @@ __export(chart_serve_exports, {
602
602
  module.exports = __toCommonJS(chart_serve_exports);
603
603
  var import_node_http = __toESM(require("node:http"));
604
604
  var import_node_fs18 = __toESM(require("node:fs"));
605
- var import_node_path20 = __toESM(require("node:path"));
605
+ var import_node_path19 = __toESM(require("node:path"));
606
606
 
607
607
  // src/server/graph/index.ts
608
608
  var import_node_fs15 = require("node:fs");
609
- var import_node_path17 = require("node:path");
609
+ var import_node_path16 = require("node:path");
610
610
 
611
611
  // src/server/graph/core/graph-builder.ts
612
612
  var import_node_fs12 = require("node:fs");
613
- var import_node_path13 = require("node:path");
613
+ var import_node_path12 = require("node:path");
614
614
  init_config();
615
615
 
616
616
  // src/server/graph/core/parser-registry.ts
617
- var import_node_path12 = require("node:path");
617
+ var import_node_path11 = require("node:path");
618
618
 
619
619
  // src/server/graph/parsers/ts/typescript-project.ts
620
620
  var import_node_fs5 = require("node:fs");
@@ -622,35 +622,12 @@ 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_fs2 = require("node:fs");
626
- 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;
632
- }
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 };
639
- }
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 };
643
- }
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 };
647
- }
648
- return null;
649
- }
650
-
651
- // src/server/graph/core/walk.ts
652
625
  var import_node_fs3 = require("node:fs");
653
626
  var import_node_path3 = require("node:path");
627
+
628
+ // src/server/graph/core/walk.ts
629
+ var import_node_fs2 = require("node:fs");
630
+ var import_node_path2 = require("node:path");
654
631
  var DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
655
632
  "node_modules",
656
633
  ".git",
@@ -666,12 +643,12 @@ var DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
666
643
  ]);
667
644
  function walk(dir, exts) {
668
645
  const results = [];
669
- if (!(0, import_node_fs3.existsSync)(dir)) return results;
670
- for (const entry of (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true })) {
671
- const full = (0, import_node_path3.join)(dir, entry.name);
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);
672
649
  if (entry.isDirectory()) {
673
650
  results.push(...walk(full, exts));
674
- } else if (exts.includes((0, import_node_path3.extname)(entry.name))) {
651
+ } else if (exts.includes((0, import_node_path2.extname)(entry.name))) {
675
652
  results.push(full);
676
653
  }
677
654
  }
@@ -679,19 +656,205 @@ function walk(dir, exts) {
679
656
  }
680
657
  function walkWithIgnore(dir, exts, opts = {}) {
681
658
  const results = [];
682
- if (!(0, import_node_fs3.existsSync)(dir)) return results;
659
+ if (!(0, import_node_fs2.existsSync)(dir)) return results;
683
660
  const skip = opts.extraIgnore ? /* @__PURE__ */ new Set([...DEFAULT_IGNORE_DIRS, ...opts.extraIgnore]) : DEFAULT_IGNORE_DIRS;
684
- for (const entry of (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true })) {
661
+ for (const entry of (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true })) {
685
662
  if (entry.isDirectory()) {
686
663
  if (skip.has(entry.name)) continue;
687
- results.push(...walkWithIgnore((0, import_node_path3.join)(dir, entry.name), exts, opts));
688
- } else if (exts.includes((0, import_node_path3.extname)(entry.name))) {
689
- results.push((0, import_node_path3.join)(dir, entry.name));
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));
690
667
  }
691
668
  }
692
669
  return results;
693
670
  }
694
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));
716
+ }
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;
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;
729
+ return null;
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
+ }
857
+
695
858
  // src/server/graph/parsers/ts/typescript-project.ts
696
859
  init_ts_extractor();
697
860
  var HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]);
@@ -708,8 +871,12 @@ var CLASSIFICATION_TO_LAYER = {
708
871
  "mcp-tool": "ui",
709
872
  external: "ui"
710
873
  };
711
- function toNodeId(srcDir, absPath) {
712
- return (0, import_node_path5.relative)(srcDir, absPath).replace(/\\/g, "/");
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, "/");
878
+ }
879
+ return relFromSrc;
713
880
  }
714
881
  function resolveImport(srcDir, specifier) {
715
882
  if (!specifier.startsWith("@/")) return null;
@@ -799,12 +966,13 @@ function extractRoute(id) {
799
966
  function nameFromFilename(absPath) {
800
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());
801
968
  }
802
- function filePathToApiRoute(apiDir, absPath) {
803
- let route = "/" + (0, import_node_path5.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");
804
973
  route = route.replace(/\[([^\]]+)\]/g, ":$1");
805
974
  route = route.replace(/\/+/g, "/");
806
- if (route === "/") return "/api";
807
- return "/api" + route;
975
+ return route === "" ? "/" : route;
808
976
  }
809
977
  function camelToPascal(s) {
810
978
  if (!s) return s;
@@ -889,7 +1057,7 @@ function matchRouteToPage(route, routeToNodeId) {
889
1057
  if (routeToNodeId.has(normalized)) return routeToNodeId.get(normalized);
890
1058
  return null;
891
1059
  }
892
- function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps, routeToNodeId) {
1060
+ function extractEdges(srcDir, rootDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps, routeToNodeId) {
893
1061
  const edges = [];
894
1062
  const flagged = [];
895
1063
  const seen = /* @__PURE__ */ new Set();
@@ -917,7 +1085,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
917
1085
  for (const name of names) {
918
1086
  const targetAbs = barrelMap.get(name);
919
1087
  if (targetAbs) {
920
- const targetId = toNodeId(srcDir, targetAbs);
1088
+ const targetId = toNodeId(srcDir, rootDir, targetAbs);
921
1089
  if (nodeIdSet.has(targetId)) {
922
1090
  if (!byTarget.has(targetId)) byTarget.set(targetId, []);
923
1091
  byTarget.get(targetId).push(name);
@@ -931,7 +1099,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
931
1099
  } else {
932
1100
  const resolved = resolveImport(srcDir, specifier);
933
1101
  if (resolved) {
934
- const targetId = toNodeId(srcDir, resolved);
1102
+ const targetId = toNodeId(srcDir, rootDir, resolved);
935
1103
  if (nodeIdSet.has(targetId) && !targetId.endsWith("/index.ts") && !targetId.endsWith("/index.tsx")) {
936
1104
  addEdge(targetId, edgeTypeFor(isTypeOnly, names));
937
1105
  }
@@ -940,7 +1108,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
940
1108
  } else if (specifier.startsWith(".")) {
941
1109
  const resolved = resolveRelativeImport(absPath, specifier);
942
1110
  if (resolved) {
943
- const targetId = toNodeId(srcDir, resolved);
1111
+ const targetId = toNodeId(srcDir, rootDir, resolved);
944
1112
  if (nodeIdSet.has(targetId) && !targetId.endsWith("/index.ts") && !targetId.endsWith("/index.tsx")) {
945
1113
  addEdge(targetId, edgeTypeFor(isTypeOnly, names));
946
1114
  }
@@ -992,13 +1160,22 @@ function generate(rootDir) {
992
1160
  const config = loadConfig(rootDir);
993
1161
  const paths = resolveProjectPaths(rootDir, config);
994
1162
  const srcDir = paths.srcDir;
995
- const apiDir = paths.apiDir;
996
- const appFiles = walk(paths.appDir, [".tsx", ".ts"]);
997
- const clientFiles = walk((0, import_node_path5.join)(srcDir, "client"), [".tsx", ".ts"]);
998
- const serverFiles = walk((0, import_node_path5.join)(srcDir, "server"), [".ts", ".tsx"]);
999
- const libFiles = walk((0, import_node_path5.join)(srcDir, "lib"), [".ts", ".tsx"]);
1000
- const configFiles = walk((0, import_node_path5.join)(srcDir, "config"), [".ts", ".tsx"]);
1001
- 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
+ }
1002
1179
  const parsedByPath = /* @__PURE__ */ new Map();
1003
1180
  for (const absPath of allDiscovered) {
1004
1181
  parsedByPath.set(absPath, parseFileTS(absPath));
@@ -1010,7 +1187,7 @@ function generate(rootDir) {
1010
1187
  const routeToNodeId = /* @__PURE__ */ new Map();
1011
1188
  const fileSet = allDiscovered.filter((f) => !(0, import_node_path5.basename)(f).startsWith("index."));
1012
1189
  for (const absPath of fileSet) {
1013
- const id = toNodeId(srcDir, absPath);
1190
+ const id = toNodeId(srcDir, rootDir, absPath);
1014
1191
  const type = classifyType(absPath, id);
1015
1192
  if (type === "test" || type === "story") continue;
1016
1193
  const parsed = parsedByPath.get(absPath);
@@ -1025,7 +1202,7 @@ function generate(rootDir) {
1025
1202
  const dbCalls = extractDbCallsTS(absPath);
1026
1203
  const authWrappers = extractAuthWrappersTS(absPath);
1027
1204
  const deep = extractDeep(absPath);
1028
- const routePath = (0, import_node_fs5.existsSync)(apiDir) ? filePathToApiRoute(apiDir, absPath) : `/api/${id.replace(/\/route\.tsx?$/, "")}`;
1205
+ const routePath = filePathToAppRoute(paths.appDir, absPath);
1029
1206
  const mutations = dbCalls.filter((c) => c.isMutation);
1030
1207
  const mutates = mutations.length > 0;
1031
1208
  const authStrategy = [...authWrappers];
@@ -1069,11 +1246,12 @@ function generate(rootDir) {
1069
1246
  const uiEdges = [];
1070
1247
  const uiFlagged = [];
1071
1248
  for (const absPath of fileSet) {
1072
- const id = toNodeId(srcDir, absPath);
1249
+ const id = toNodeId(srcDir, rootDir, absPath);
1073
1250
  if (!nodeIdSet.has(id)) continue;
1074
1251
  const parsed = parsedByPath.get(absPath);
1075
1252
  const { edges, flagged } = extractEdges(
1076
1253
  srcDir,
1254
+ rootDir,
1077
1255
  absPath,
1078
1256
  id,
1079
1257
  parsed,
@@ -1086,7 +1264,7 @@ function generate(rootDir) {
1086
1264
  }
1087
1265
  const fetchCallEntries = [];
1088
1266
  for (const absPath of fileSet) {
1089
- const sourceId = toNodeId(srcDir, absPath);
1267
+ const sourceId = toNodeId(srcDir, rootDir, absPath);
1090
1268
  if (!nodeIdSet.has(sourceId)) continue;
1091
1269
  const parsed = parsedByPath.get(absPath);
1092
1270
  if (parsed.fetchCalls.length === 0) continue;
@@ -1125,7 +1303,7 @@ function generate(rootDir) {
1125
1303
  for (const name of names) {
1126
1304
  const targetAbs = barrelMap.get(name);
1127
1305
  if (!targetAbs) continue;
1128
- const targetId2 = toNodeId(srcDir, targetAbs);
1306
+ const targetId2 = toNodeId(srcDir, rootDir, targetAbs);
1129
1307
  if (!nodeIdSet.has(targetId2)) continue;
1130
1308
  const key2 = `${externalId}\u2192${targetId2}`;
1131
1309
  if (seen.has(key2)) continue;
@@ -1139,7 +1317,7 @@ function generate(rootDir) {
1139
1317
  resolved = resolveRelativeImport(absPath, specifier);
1140
1318
  }
1141
1319
  if (!resolved) continue;
1142
- const targetId = toNodeId(srcDir, resolved);
1320
+ const targetId = toNodeId(srcDir, rootDir, resolved);
1143
1321
  if (!nodeIdSet.has(targetId)) continue;
1144
1322
  if (targetId.endsWith("/index.ts") || targetId.endsWith("/index.tsx")) continue;
1145
1323
  const key = `${externalId}\u2192${targetId}`;
@@ -1317,7 +1495,7 @@ var typescriptProjectParser = {
1317
1495
 
1318
1496
  // src/server/graph/parsers/db/prisma-schema.ts
1319
1497
  var import_node_fs6 = require("node:fs");
1320
- var import_node_path6 = require("node:path");
1498
+ init_config();
1321
1499
  function parseModels(content) {
1322
1500
  const nodes = [];
1323
1501
  const relations = [];
@@ -1408,10 +1586,24 @@ function parseEnums(content) {
1408
1586
  return nodes;
1409
1587
  }
1410
1588
  function detect2(rootDir) {
1411
- return (0, import_node_fs6.existsSync)((0, import_node_path6.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);
1412
1591
  }
1413
1592
  function generate2(rootDir) {
1414
- const schemaPath = (0, import_node_path6.join)(rootDir, "prisma", "schema.prisma");
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;
1415
1607
  const content = (0, import_node_fs6.readFileSync)(schemaPath, "utf-8");
1416
1608
  const { nodes: modelNodes, relations } = parseModels(content);
1417
1609
  const enumNodes = parseEnums(content);
@@ -1432,7 +1624,7 @@ function generate2(rootDir) {
1432
1624
  metadata: {
1433
1625
  generated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
1434
1626
  scope: "prisma-schema",
1435
- source: "prisma/schema.prisma",
1627
+ source: schemaPath,
1436
1628
  provider: "postgresql",
1437
1629
  layer: "db",
1438
1630
  total_models: modelNodes.length,
@@ -1470,7 +1662,8 @@ var prismaSchemaParser = {
1470
1662
 
1471
1663
  // src/server/graph/parsers/db/sql-migrations.ts
1472
1664
  var import_node_fs7 = require("node:fs");
1473
- var import_node_path7 = require("node:path");
1665
+ var import_node_path6 = require("node:path");
1666
+ init_config();
1474
1667
  var PG_TO_PRISMA = {
1475
1668
  "TEXT": "String",
1476
1669
  "VARCHAR": "String",
@@ -1501,51 +1694,149 @@ function pgTypeToPrisma(pgType) {
1501
1694
  const upper = pgType.toUpperCase().trim();
1502
1695
  return PG_TO_PRISMA[upper] ?? upper;
1503
1696
  }
1697
+ var ID = `(?:"[\\w$]+"|[\\w$]+)`;
1698
+ var QID = `(?:${ID}\\.)?${ID}`;
1699
+ function bareName(captured) {
1700
+ const parts = captured.split(".");
1701
+ const last = parts[parts.length - 1];
1702
+ return last.replace(/^"(.*)"$/, "$1").trim();
1703
+ }
1504
1704
  function parseCreateTable(sql, state) {
1505
- const re = /CREATE\s+TABLE\s+"(\w+)"\s*\(([\s\S]*?)\);/gi;
1705
+ const re = new RegExp(
1706
+ `CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(${QID})\\s*\\(([\\s\\S]*?)\\);`,
1707
+ "gi"
1708
+ );
1506
1709
  let m;
1507
1710
  while ((m = re.exec(sql)) !== null) {
1508
- const tableName = m[1];
1711
+ const tableName = bareName(m[1]);
1509
1712
  const body = m[2];
1510
1713
  const columns = /* @__PURE__ */ new Map();
1511
1714
  let primaryCol = null;
1512
- for (const line of body.split("\n")) {
1513
- const trimmed = line.trim().replace(/,\s*$/, "");
1715
+ const inlineFks = [];
1716
+ const lines = splitTopLevelCommas(body);
1717
+ for (const raw of lines) {
1718
+ const trimmed = raw.trim().replace(/,\s*$/, "");
1514
1719
  if (!trimmed || trimmed.startsWith("--")) continue;
1515
- const pkMatch = trimmed.match(/CONSTRAINT\s+"[^"]+"\s+PRIMARY\s+KEY\s*\("(\w+)"\)/i);
1516
- if (pkMatch) {
1517
- primaryCol = pkMatch[1];
1720
+ const namedPk = trimmed.match(new RegExp(`^CONSTRAINT\\s+${ID}\\s+PRIMARY\\s+KEY\\s*\\(\\s*(${QID})`, "i"));
1721
+ if (namedPk) {
1722
+ primaryCol = bareName(namedPk[1]);
1723
+ continue;
1724
+ }
1725
+ const tablePk = trimmed.match(new RegExp(`^PRIMARY\\s+KEY\\s*\\(\\s*(${QID})`, "i"));
1726
+ if (tablePk) {
1727
+ primaryCol = bareName(tablePk[1]);
1728
+ continue;
1729
+ }
1730
+ if (/^UNIQUE\s*\(/i.test(trimmed)) continue;
1731
+ const namedFk = trimmed.match(new RegExp(
1732
+ `^CONSTRAINT\\s+(${ID})\\s+FOREIGN\\s+KEY\\s*\\(\\s*(${QID})\\s*\\)\\s+REFERENCES\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)(?:\\s+ON\\s+DELETE\\s+(\\w+(?:\\s+\\w+)?))?`,
1733
+ "i"
1734
+ ));
1735
+ if (namedFk) {
1736
+ inlineFks.push({
1737
+ constraintName: bareName(namedFk[1]),
1738
+ sourceTable: tableName,
1739
+ sourceColumn: bareName(namedFk[2]),
1740
+ targetTable: bareName(namedFk[3]),
1741
+ targetColumn: bareName(namedFk[4]),
1742
+ onDelete: namedFk[5] ?? null
1743
+ });
1518
1744
  continue;
1519
1745
  }
1520
- if (/^\s*CONSTRAINT\s/i.test(trimmed)) continue;
1521
- const colMatch = trimmed.match(/^"(\w+)"\s+(.+)/);
1746
+ const bareFk = trimmed.match(new RegExp(
1747
+ `^FOREIGN\\s+KEY\\s*\\(\\s*(${QID})\\s*\\)\\s+REFERENCES\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)(?:\\s+ON\\s+DELETE\\s+(\\w+(?:\\s+\\w+)?))?`,
1748
+ "i"
1749
+ ));
1750
+ if (bareFk) {
1751
+ inlineFks.push({
1752
+ constraintName: `${tableName}_${bareName(bareFk[1])}_fkey`,
1753
+ sourceTable: tableName,
1754
+ sourceColumn: bareName(bareFk[1]),
1755
+ targetTable: bareName(bareFk[2]),
1756
+ targetColumn: bareName(bareFk[3]),
1757
+ onDelete: bareFk[4] ?? null
1758
+ });
1759
+ continue;
1760
+ }
1761
+ if (/^CONSTRAINT\s/i.test(trimmed)) continue;
1762
+ const colMatch = trimmed.match(new RegExp(`^(${ID})\\s+(.+)`, "i"));
1522
1763
  if (!colMatch) continue;
1523
- const colName = colMatch[1];
1764
+ const colName = bareName(colMatch[1]);
1524
1765
  let rest = colMatch[2];
1766
+ const inlineRefMatch = rest.match(new RegExp(
1767
+ `\\bREFERENCES\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)(?:\\s+ON\\s+DELETE\\s+(\\w+(?:\\s+\\w+)?))?`,
1768
+ "i"
1769
+ ));
1770
+ if (inlineRefMatch) {
1771
+ inlineFks.push({
1772
+ constraintName: `${tableName}_${colName}_fkey`,
1773
+ sourceTable: tableName,
1774
+ sourceColumn: colName,
1775
+ targetTable: bareName(inlineRefMatch[1]),
1776
+ targetColumn: bareName(inlineRefMatch[2]),
1777
+ onDelete: inlineRefMatch[3] ?? null
1778
+ });
1779
+ rest = rest.replace(inlineRefMatch[0], "").trim();
1780
+ }
1525
1781
  const isNotNull = /\bNOT\s+NULL\b/i.test(rest);
1782
+ const isPrimaryKey = /\bPRIMARY\s+KEY\b/i.test(rest);
1783
+ const isUnique = /\bUNIQUE\b/i.test(rest);
1526
1784
  const defaultMatch = rest.match(/\bDEFAULT\s+(.+?)(?:\s*,?\s*$)/i);
1527
1785
  const defaultVal = defaultMatch ? defaultMatch[1].trim() : null;
1528
- let colType = rest.replace(/\bNOT\s+NULL\b/gi, "").replace(/\bDEFAULT\s+.*/gi, "").trim().replace(/,\s*$/, "").trim();
1786
+ let colType = rest.replace(/\bNOT\s+NULL\b/gi, "").replace(/\bPRIMARY\s+KEY\b/gi, "").replace(/\bUNIQUE\b/gi, "").replace(/\bDEFAULT\s+.*/gi, "").trim().replace(/,\s*$/, "").trim();
1529
1787
  columns.set(colName, {
1530
1788
  name: colName,
1531
1789
  type: colType,
1532
- nullable: !isNotNull,
1533
- primary: false,
1534
- unique: false,
1790
+ nullable: !isNotNull && !isPrimaryKey,
1791
+ primary: isPrimaryKey,
1792
+ unique: isUnique,
1535
1793
  default: defaultVal
1536
1794
  });
1795
+ if (isPrimaryKey) primaryCol = colName;
1537
1796
  }
1538
1797
  if (primaryCol && columns.has(primaryCol)) {
1539
1798
  columns.get(primaryCol).primary = true;
1540
1799
  }
1541
1800
  state.tables.set(tableName, { name: tableName, columns });
1801
+ state.fks.push(...inlineFks);
1542
1802
  }
1543
1803
  }
1804
+ function splitTopLevelCommas(body) {
1805
+ const out = [];
1806
+ let depth = 0;
1807
+ let buf = "";
1808
+ let inString = null;
1809
+ for (const ch of body) {
1810
+ if (inString) {
1811
+ buf += ch;
1812
+ if (ch === inString) inString = null;
1813
+ continue;
1814
+ }
1815
+ if (ch === "'" || ch === '"') {
1816
+ inString = ch;
1817
+ buf += ch;
1818
+ continue;
1819
+ }
1820
+ if (ch === "(") depth++;
1821
+ else if (ch === ")") depth--;
1822
+ if (ch === "," && depth === 0) {
1823
+ out.push(buf);
1824
+ buf = "";
1825
+ continue;
1826
+ }
1827
+ buf += ch;
1828
+ }
1829
+ if (buf.trim()) out.push(buf);
1830
+ return out;
1831
+ }
1544
1832
  function parseCreateEnum(sql, state) {
1545
- const re = /CREATE\s+TYPE\s+"(\w+)"\s+AS\s+ENUM\s*\(([^)]+)\)/gi;
1833
+ const re = new RegExp(
1834
+ `CREATE\\s+TYPE\\s+(${QID})\\s+AS\\s+ENUM\\s*\\(([^)]+)\\)`,
1835
+ "gi"
1836
+ );
1546
1837
  let m;
1547
1838
  while ((m = re.exec(sql)) !== null) {
1548
- const enumName = m[1];
1839
+ const enumName = bareName(m[1]);
1549
1840
  const valuesStr = m[2];
1550
1841
  const values = new Set(
1551
1842
  valuesStr.split(",").map((v) => v.trim().replace(/^'(.*)'$/, "$1")).filter(Boolean)
@@ -1554,11 +1845,14 @@ function parseCreateEnum(sql, state) {
1554
1845
  }
1555
1846
  }
1556
1847
  function parseAlterTable(sql, state) {
1557
- const addColRe = /ALTER\s+TABLE\s+"(\w+)"\s+ADD\s+COLUMN\s+"(\w+)"\s+(.+?);/gi;
1848
+ const addColRe = new RegExp(
1849
+ `ALTER\\s+TABLE\\s+(${QID})\\s+ADD\\s+COLUMN\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(${QID})\\s+(.+?);`,
1850
+ "gi"
1851
+ );
1558
1852
  let m;
1559
1853
  while ((m = addColRe.exec(sql)) !== null) {
1560
- const tableName = m[1];
1561
- const colName = m[2];
1854
+ const tableName = bareName(m[1]);
1855
+ const colName = bareName(m[2]);
1562
1856
  let rest = m[3];
1563
1857
  const table = state.tables.get(tableName);
1564
1858
  if (!table) continue;
@@ -1575,63 +1869,91 @@ function parseAlterTable(sql, state) {
1575
1869
  default: defaultVal
1576
1870
  });
1577
1871
  }
1578
- const dropColRe = /ALTER\s+TABLE\s+"(\w+)"\s+DROP\s+COLUMN\s+"(\w+)"/gi;
1872
+ const dropColRe = new RegExp(
1873
+ `ALTER\\s+TABLE\\s+(${QID})\\s+DROP\\s+COLUMN\\s+(?:IF\\s+EXISTS\\s+)?(${QID})`,
1874
+ "gi"
1875
+ );
1579
1876
  while ((m = dropColRe.exec(sql)) !== null) {
1580
- const table = state.tables.get(m[1]);
1581
- if (table) table.columns.delete(m[2]);
1877
+ const table = state.tables.get(bareName(m[1]));
1878
+ if (table) table.columns.delete(bareName(m[2]));
1582
1879
  }
1583
- const fkRe = /ALTER\s+TABLE\s+"(\w+)"\s+ADD\s+CONSTRAINT\s+"([^"]+)"\s+FOREIGN\s+KEY\s*\("(\w+)"\)\s+REFERENCES\s+"(\w+)"\("(\w+)"\)(?:\s+ON\s+DELETE\s+(\w+(?:\s+\w+)?))?/gi;
1880
+ const fkRe = new RegExp(
1881
+ `ALTER\\s+TABLE\\s+(${QID})\\s+ADD\\s+CONSTRAINT\\s+(${ID})\\s+FOREIGN\\s+KEY\\s*\\(\\s*(${QID})\\s*\\)\\s+REFERENCES\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)(?:\\s+ON\\s+DELETE\\s+(\\w+(?:\\s+\\w+)?))?`,
1882
+ "gi"
1883
+ );
1584
1884
  while ((m = fkRe.exec(sql)) !== null) {
1585
1885
  state.fks.push({
1586
- constraintName: m[2],
1587
- sourceTable: m[1],
1588
- sourceColumn: m[3],
1589
- targetTable: m[4],
1590
- targetColumn: m[5],
1886
+ constraintName: bareName(m[2]),
1887
+ sourceTable: bareName(m[1]),
1888
+ sourceColumn: bareName(m[3]),
1889
+ targetTable: bareName(m[4]),
1890
+ targetColumn: bareName(m[5]),
1591
1891
  onDelete: m[6] ?? null
1592
1892
  });
1593
1893
  }
1594
1894
  }
1595
1895
  function parseAlterEnum(sql, state) {
1596
- const re = /ALTER\s+TYPE\s+"(\w+)"\s+ADD\s+VALUE\s+'([^']+)'/gi;
1896
+ const re = new RegExp(
1897
+ `ALTER\\s+TYPE\\s+(${QID})\\s+ADD\\s+VALUE\\s+'([^']+)'`,
1898
+ "gi"
1899
+ );
1597
1900
  let m;
1598
1901
  while ((m = re.exec(sql)) !== null) {
1599
- const en = state.enums.get(m[1]);
1902
+ const en = state.enums.get(bareName(m[1]));
1600
1903
  if (en) en.values.add(m[2]);
1601
1904
  }
1602
1905
  }
1603
1906
  function parseDropTable(sql, state) {
1604
- const re = /DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?"(\w+)"/gi;
1907
+ const re = new RegExp(
1908
+ `DROP\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?(${QID})`,
1909
+ "gi"
1910
+ );
1605
1911
  let m;
1606
1912
  while ((m = re.exec(sql)) !== null) {
1607
- state.tables.delete(m[1]);
1608
- state.fks = state.fks.filter((fk) => fk.sourceTable !== m[1] && fk.targetTable !== m[1]);
1913
+ const dropped = bareName(m[1]);
1914
+ state.tables.delete(dropped);
1915
+ state.fks = state.fks.filter((fk) => fk.sourceTable !== dropped && fk.targetTable !== dropped);
1609
1916
  }
1610
1917
  }
1611
1918
  function parseUniqueIndex(sql, state) {
1612
- const re = /CREATE\s+UNIQUE\s+INDEX\s+"[^"]+"\s+ON\s+"(\w+)"\("(\w+)"\)/gi;
1919
+ const re = new RegExp(
1920
+ `CREATE\\s+UNIQUE\\s+INDEX\\s+(?:(?:IF\\s+NOT\\s+EXISTS\\s+)?(?:${ID}\\s+)?)?ON\\s+(${QID})\\s*\\(\\s*(${QID})\\s*\\)`,
1921
+ "gi"
1922
+ );
1613
1923
  let m;
1614
1924
  while ((m = re.exec(sql)) !== null) {
1615
- const table = state.tables.get(m[1]);
1616
- const col = table?.columns.get(m[2]);
1925
+ const tableName = bareName(m[1]);
1926
+ const colName = bareName(m[2]);
1927
+ const table = state.tables.get(tableName);
1928
+ const col = table?.columns.get(colName);
1617
1929
  if (col) col.unique = true;
1618
- if (!state.uniqueIndexes.has(m[1])) state.uniqueIndexes.set(m[1], /* @__PURE__ */ new Set());
1619
- state.uniqueIndexes.get(m[1]).add(m[2]);
1930
+ if (!state.uniqueIndexes.has(tableName)) state.uniqueIndexes.set(tableName, /* @__PURE__ */ new Set());
1931
+ state.uniqueIndexes.get(tableName).add(colName);
1932
+ }
1933
+ }
1934
+ function discoverMigrationFiles(migrationsDir) {
1935
+ if (!(0, import_node_fs7.existsSync)(migrationsDir)) return [];
1936
+ const out = [];
1937
+ const entries = (0, import_node_fs7.readdirSync)(migrationsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
1938
+ for (const entry of entries) {
1939
+ if (entry.isDirectory()) {
1940
+ const sqlPath = (0, import_node_path6.join)(migrationsDir, entry.name, "migration.sql");
1941
+ if ((0, import_node_fs7.existsSync)(sqlPath)) out.push(sqlPath);
1942
+ } else if (entry.isFile() && entry.name.endsWith(".sql")) {
1943
+ out.push((0, import_node_path6.join)(migrationsDir, entry.name));
1944
+ }
1620
1945
  }
1946
+ return out;
1621
1947
  }
1622
- function parseMigrations(rootDir) {
1623
- const migrationsDir = (0, import_node_path7.join)(rootDir, "prisma", "migrations");
1948
+ function parseMigrations(migrationsDir) {
1624
1949
  const state = {
1625
1950
  tables: /* @__PURE__ */ new Map(),
1626
1951
  enums: /* @__PURE__ */ new Map(),
1627
1952
  fks: [],
1628
1953
  uniqueIndexes: /* @__PURE__ */ new Map()
1629
1954
  };
1630
- if (!(0, import_node_fs7.existsSync)(migrationsDir)) return state;
1631
- const dirs = (0, import_node_fs7.readdirSync)(migrationsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
1632
- for (const dir of dirs) {
1633
- const sqlPath = (0, import_node_path7.join)(migrationsDir, dir, "migration.sql");
1634
- if (!(0, import_node_fs7.existsSync)(sqlPath)) continue;
1955
+ if (!migrationsDir) return state;
1956
+ for (const sqlPath of discoverMigrationFiles(migrationsDir)) {
1635
1957
  const sql = (0, import_node_fs7.readFileSync)(sqlPath, "utf-8");
1636
1958
  parseCreateEnum(sql, state);
1637
1959
  parseCreateTable(sql, state);
@@ -1642,9 +1964,8 @@ function parseMigrations(rootDir) {
1642
1964
  }
1643
1965
  return state;
1644
1966
  }
1645
- function loadPrismaState(rootDir) {
1646
- const schemaPath = (0, import_node_path7.join)(rootDir, "prisma", "schema.prisma");
1647
- if (!(0, import_node_fs7.existsSync)(schemaPath)) return null;
1967
+ function loadPrismaState(schemaPath) {
1968
+ if (!schemaPath || !(0, import_node_fs7.existsSync)(schemaPath)) return null;
1648
1969
  const content = (0, import_node_fs7.readFileSync)(schemaPath, "utf-8");
1649
1970
  const tables = /* @__PURE__ */ new Map();
1650
1971
  const enums = /* @__PURE__ */ new Map();
@@ -1809,14 +2130,28 @@ function verify(sqlState, prisma) {
1809
2130
  }
1810
2131
  return { contradictions, flaggedEdges };
1811
2132
  }
2133
+ function migrationsDirFor(rootDir) {
2134
+ const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
2135
+ if (!paths) return null;
2136
+ if (paths.dbConfig.kind === "prisma" || paths.dbConfig.kind === "sql-migrations") {
2137
+ return paths.dbConfig.migrationsDir;
2138
+ }
2139
+ return null;
2140
+ }
2141
+ function schemaPathFor(rootDir) {
2142
+ const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
2143
+ if (!paths) return null;
2144
+ return paths.dbConfig.kind === "prisma" ? paths.dbConfig.schemaPath : null;
2145
+ }
1812
2146
  function detect3(rootDir) {
1813
- const migrationsDir = (0, import_node_path7.join)(rootDir, "prisma", "migrations");
1814
- if (!(0, import_node_fs7.existsSync)(migrationsDir)) return false;
1815
- return (0, import_node_fs7.readdirSync)(migrationsDir, { withFileTypes: true }).some((d) => d.isDirectory() && (0, import_node_fs7.existsSync)((0, import_node_path7.join)(migrationsDir, d.name, "migration.sql")));
2147
+ const dir = migrationsDirFor(rootDir);
2148
+ if (!dir) return false;
2149
+ return discoverMigrationFiles(dir).length > 0;
1816
2150
  }
1817
2151
  function generate3(rootDir) {
1818
- const sqlState = parseMigrations(rootDir);
1819
- const prisma = loadPrismaState(rootDir);
2152
+ const migrationsDir = migrationsDirFor(rootDir);
2153
+ const sqlState = parseMigrations(migrationsDir);
2154
+ const prisma = loadPrismaState(schemaPathFor(rootDir));
1820
2155
  const prismaTableIds = prisma ? new Set(prisma.tables.keys()) : /* @__PURE__ */ new Set();
1821
2156
  const prismaEnumIds = prisma ? new Set(prisma.enums.keys()) : /* @__PURE__ */ new Set();
1822
2157
  const nodes = [];
@@ -1862,7 +2197,7 @@ function generate3(rootDir) {
1862
2197
  metadata: {
1863
2198
  generated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
1864
2199
  scope: "sql-migrations",
1865
- source: "prisma/migrations/",
2200
+ source: migrationsDir ?? "none",
1866
2201
  layer: "db",
1867
2202
  sql_tables: sqlState.tables.size,
1868
2203
  sql_enums: sqlState.enums.size,
@@ -2101,31 +2436,31 @@ var fetchResolverParser = {
2101
2436
 
2102
2437
  // src/server/graph/parsers/crosslayer/api-annotations.ts
2103
2438
  var import_node_fs8 = require("node:fs");
2104
- var import_node_path8 = require("node:path");
2439
+ var import_node_path7 = require("node:path");
2105
2440
  var API_ANNOTATION_RE = /@api\s+(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+(\/\S+)/g;
2106
2441
  function walk2(dir, exts) {
2107
2442
  if (!(0, import_node_fs8.existsSync)(dir)) return [];
2108
2443
  const results = [];
2109
2444
  for (const entry of (0, import_node_fs8.readdirSync)(dir, { withFileTypes: true })) {
2110
2445
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
2111
- const full = (0, import_node_path8.join)(dir, entry.name);
2446
+ const full = (0, import_node_path7.join)(dir, entry.name);
2112
2447
  if (entry.isDirectory()) {
2113
2448
  results.push(...walk2(full, exts));
2114
- } else if (exts.includes((0, import_node_path8.extname)(entry.name))) {
2449
+ } else if (exts.includes((0, import_node_path7.extname)(entry.name))) {
2115
2450
  results.push(full);
2116
2451
  }
2117
2452
  }
2118
2453
  return results;
2119
2454
  }
2120
2455
  function toNodeId2(srcDir, absPath) {
2121
- return (0, import_node_path8.relative)(srcDir, absPath).replace(/\\/g, "/");
2456
+ return (0, import_node_path7.relative)(srcDir, absPath).replace(/\\/g, "/");
2122
2457
  }
2123
2458
  var apiAnnotationsParser = {
2124
2459
  id: "api-annotations",
2125
2460
  layer: "crosslayer",
2126
2461
  concern: "api-binding",
2127
2462
  detect(rootDir) {
2128
- return (0, import_node_fs8.existsSync)((0, import_node_path8.join)(rootDir, "src"));
2463
+ return (0, import_node_fs8.existsSync)((0, import_node_path7.join)(rootDir, "src"));
2129
2464
  },
2130
2465
  generate(rootDir, layerOutputs) {
2131
2466
  const apiOutput = layerOutputs.get("api");
@@ -2136,7 +2471,7 @@ var apiAnnotationsParser = {
2136
2471
  const uiNodeIds = new Set(uiOutput?.nodes.map((n) => n.id) ?? []);
2137
2472
  const apiRoutes = loadApiRoutesFromOutput(apiOutput);
2138
2473
  const apiPathMap = buildApiPathMap(apiRoutes);
2139
- const srcDir = (0, import_node_path8.join)(rootDir, "src");
2474
+ const srcDir = (0, import_node_path7.join)(rootDir, "src");
2140
2475
  const files = walk2(srcDir, [".ts", ".tsx"]);
2141
2476
  const crossRefs = [];
2142
2477
  const flaggedEdges = [];
@@ -2187,7 +2522,7 @@ var apiAnnotationsParser = {
2187
2522
 
2188
2523
  // src/server/graph/parsers/crosslayer/url-literal-scanner.ts
2189
2524
  var import_node_fs9 = require("node:fs");
2190
- var import_node_path9 = require("node:path");
2525
+ var import_node_path8 = require("node:path");
2191
2526
  init_config();
2192
2527
  var URL_LITERAL_RE = /['"`](\/api\/[^'"`\s]+?)['"`]/g;
2193
2528
  function walk3(dir, exts) {
@@ -2195,17 +2530,17 @@ function walk3(dir, exts) {
2195
2530
  const results = [];
2196
2531
  for (const entry of (0, import_node_fs9.readdirSync)(dir, { withFileTypes: true })) {
2197
2532
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
2198
- const full = (0, import_node_path9.join)(dir, entry.name);
2533
+ const full = (0, import_node_path8.join)(dir, entry.name);
2199
2534
  if (entry.isDirectory()) {
2200
2535
  results.push(...walk3(full, exts));
2201
- } else if (exts.includes((0, import_node_path9.extname)(entry.name))) {
2536
+ } else if (exts.includes((0, import_node_path8.extname)(entry.name))) {
2202
2537
  results.push(full);
2203
2538
  }
2204
2539
  }
2205
2540
  return results;
2206
2541
  }
2207
2542
  function toNodeId3(srcDir, absPath) {
2208
- return (0, import_node_path9.relative)(srcDir, absPath).replace(/\\/g, "/");
2543
+ return (0, import_node_path8.relative)(srcDir, absPath).replace(/\\/g, "/");
2209
2544
  }
2210
2545
  var urlLiteralScannerParser = {
2211
2546
  id: "url-literal-scanner",
@@ -2226,7 +2561,7 @@ var urlLiteralScannerParser = {
2226
2561
  const apiPathMap = buildApiPathMap(apiRoutes);
2227
2562
  const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
2228
2563
  const srcDir = paths.srcDir;
2229
- const clientDir = (0, import_node_path9.join)(srcDir, "client");
2564
+ const clientDir = (0, import_node_path8.join)(srcDir, "client");
2230
2565
  const files = [
2231
2566
  ...walk3(clientDir, [".ts", ".tsx"]),
2232
2567
  ...walk3(paths.appDir, [".ts", ".tsx"])
@@ -2268,7 +2603,7 @@ var urlLiteralScannerParser = {
2268
2603
 
2269
2604
  // src/server/graph/parsers/static/static-values.ts
2270
2605
  var import_node_fs10 = require("node:fs");
2271
- var import_node_path10 = require("node:path");
2606
+ var import_node_path9 = require("node:path");
2272
2607
  var parseCode = null;
2273
2608
  function tryLoadTreeSitter() {
2274
2609
  if (parseCode) return true;
@@ -2301,8 +2636,8 @@ function extractEnumValues(rootDir) {
2301
2636
  const nodes = [];
2302
2637
  const edges = [];
2303
2638
  const schemaPaths = [
2304
- (0, import_node_path10.join)(rootDir, "prisma", "schema.prisma"),
2305
- (0, import_node_path10.join)(rootDir, "prisma", "schema")
2639
+ (0, import_node_path9.join)(rootDir, "prisma", "schema.prisma"),
2640
+ (0, import_node_path9.join)(rootDir, "prisma", "schema")
2306
2641
  ];
2307
2642
  let content = "";
2308
2643
  for (const p of schemaPaths) {
@@ -2313,7 +2648,7 @@ function extractEnumValues(rootDir) {
2313
2648
  content = (0, import_node_fs10.readFileSync)(p, "utf-8");
2314
2649
  } else if (stat.isDirectory()) {
2315
2650
  const files = (0, import_node_fs10.readdirSync)(p).filter((f) => f.endsWith(".prisma"));
2316
- content = files.map((f) => (0, import_node_fs10.readFileSync)((0, import_node_path10.join)(p, f), "utf-8")).join("\n");
2651
+ content = files.map((f) => (0, import_node_fs10.readFileSync)((0, import_node_path9.join)(p, f), "utf-8")).join("\n");
2317
2652
  }
2318
2653
  } catch {
2319
2654
  continue;
@@ -2469,14 +2804,14 @@ function extractSeedData(rootDir) {
2469
2804
  const nodes = [];
2470
2805
  const edges = [];
2471
2806
  const seedFiles = [
2472
- (0, import_node_path10.join)(rootDir, "prisma", "seed.ts"),
2473
- (0, import_node_path10.join)(rootDir, "prisma", "seed.js"),
2474
- (0, import_node_path10.join)(rootDir, "src", "server", "lib", "system-tags.ts")
2807
+ (0, import_node_path9.join)(rootDir, "prisma", "seed.ts"),
2808
+ (0, import_node_path9.join)(rootDir, "prisma", "seed.js"),
2809
+ (0, import_node_path9.join)(rootDir, "src", "server", "lib", "system-tags.ts")
2475
2810
  ].filter(import_node_fs10.existsSync);
2476
2811
  const useTreeSitter = tryLoadTreeSitter();
2477
2812
  for (const filePath of seedFiles) {
2478
2813
  const content = (0, import_node_fs10.readFileSync)(filePath, "utf-8");
2479
- const relPath = (0, import_node_path10.relative)(rootDir, filePath);
2814
+ const relPath = (0, import_node_path9.relative)(rootDir, filePath);
2480
2815
  const seeded = detectSeededArrays(content, relPath);
2481
2816
  let astRoot = null;
2482
2817
  if (useTreeSitter && parseCode) {
@@ -2574,7 +2909,7 @@ function walkDir(dir, exts) {
2574
2909
  const results = [];
2575
2910
  for (const entry of (0, import_node_fs10.readdirSync)(dir, { withFileTypes: true })) {
2576
2911
  if (entry.name === "node_modules" || entry.name === ".next" || entry.name === "dist") continue;
2577
- const full = (0, import_node_path10.join)(dir, entry.name);
2912
+ const full = (0, import_node_path9.join)(dir, entry.name);
2578
2913
  if (entry.isDirectory()) results.push(...walkDir(full, exts));
2579
2914
  else if (exts.some((ext) => entry.name.endsWith(ext))) results.push(full);
2580
2915
  }
@@ -2582,11 +2917,11 @@ function walkDir(dir, exts) {
2582
2917
  }
2583
2918
  function extractConstants(rootDir) {
2584
2919
  const nodes = [];
2585
- const srcDir = (0, import_node_path10.join)(rootDir, "src");
2920
+ const srcDir = (0, import_node_path9.join)(rootDir, "src");
2586
2921
  if (!(0, import_node_fs10.existsSync)(srcDir)) return { nodes };
2587
2922
  for (const filePath of walkDir(srcDir, [".ts", ".tsx"])) {
2588
2923
  const content = (0, import_node_fs10.readFileSync)(filePath, "utf-8");
2589
- const relPath = (0, import_node_path10.relative)(rootDir, filePath);
2924
+ const relPath = (0, import_node_path9.relative)(rootDir, filePath);
2590
2925
  const constArrayRe = /export\s+const\s+([A-Z][A-Z_0-9]+)\s*(?::[^=]+)?\s*=\s*\[/g;
2591
2926
  let cm;
2592
2927
  while ((cm = constArrayRe.exec(content)) !== null) {
@@ -2619,7 +2954,7 @@ function extractConstants(rootDir) {
2619
2954
  return { nodes };
2620
2955
  }
2621
2956
  function detect4(rootDir) {
2622
- return (0, import_node_fs10.existsSync)((0, import_node_path10.join)(rootDir, "prisma", "schema.prisma")) || (0, import_node_fs10.existsSync)((0, import_node_path10.join)(rootDir, "prisma", "seed.ts"));
2957
+ 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"));
2623
2958
  }
2624
2959
  function generate4(rootDir) {
2625
2960
  const enumResult = extractEnumValues(rootDir);
@@ -2695,7 +3030,7 @@ var staticValuesParser = {
2695
3030
 
2696
3031
  // src/server/graph/parsers/crosslayer/static-ref-scanner.ts
2697
3032
  var import_node_fs11 = require("node:fs");
2698
- var import_node_path11 = require("node:path");
3033
+ var import_node_path10 = require("node:path");
2699
3034
  init_config();
2700
3035
  var MIN_VALUE_LENGTH = 4;
2701
3036
  var SKIP_VALUES = /* @__PURE__ */ new Set([
@@ -2839,11 +3174,11 @@ var staticRefScannerParser = {
2839
3174
  if (!paths) return { cross_refs: [], flagged_edges: [], warnings: [] };
2840
3175
  const srcDir = paths.srcDir;
2841
3176
  const files = [
2842
- ...walkWithIgnore((0, import_node_path11.join)(srcDir, "client"), [".ts", ".tsx"]),
3177
+ ...walkWithIgnore((0, import_node_path10.join)(srcDir, "client"), [".ts", ".tsx"]),
2843
3178
  ...walkWithIgnore(paths.appDir, [".ts", ".tsx"]),
2844
- ...walkWithIgnore((0, import_node_path11.join)(srcDir, "server"), [".ts", ".tsx"]),
2845
- ...walkWithIgnore((0, import_node_path11.join)(srcDir, "lib"), [".ts", ".tsx"]),
2846
- ...walkWithIgnore((0, import_node_path11.join)(srcDir, "config"), [".ts", ".tsx"])
3179
+ ...walkWithIgnore((0, import_node_path10.join)(srcDir, "server"), [".ts", ".tsx"]),
3180
+ ...walkWithIgnore((0, import_node_path10.join)(srcDir, "lib"), [".ts", ".tsx"]),
3181
+ ...walkWithIgnore((0, import_node_path10.join)(srcDir, "config"), [".ts", ".tsx"])
2847
3182
  ];
2848
3183
  const uiOutput = layerOutputs.get("ui");
2849
3184
  const apiOutput = layerOutputs.get("api");
@@ -2861,7 +3196,7 @@ var staticRefScannerParser = {
2861
3196
  const seen = /* @__PURE__ */ new Set();
2862
3197
  let filesScanned = 0;
2863
3198
  for (const absPath of files) {
2864
- const sourceId = (0, import_node_path11.relative)(srcDir, absPath).replace(/\\/g, "/");
3199
+ const sourceId = (0, import_node_path10.relative)(srcDir, absPath).replace(/\\/g, "/");
2865
3200
  const sourceLayer = uiNodeIds.has(sourceId) ? "ui" : apiNodeIds.has(sourceId) ? "api" : null;
2866
3201
  if (!sourceLayer) continue;
2867
3202
  const content = (0, import_node_fs11.readFileSync)(absPath, "utf-8");
@@ -2980,7 +3315,7 @@ function registerBuiltins(registry, disabled) {
2980
3315
  function loadCustomParsers(registry, config, rootDir, disabled) {
2981
3316
  for (const entry of config.parsers?.custom ?? []) {
2982
3317
  try {
2983
- const absPath = (0, import_node_path12.resolve)(rootDir, entry.path);
3318
+ const absPath = (0, import_node_path11.resolve)(rootDir, entry.path);
2984
3319
  const mod = require(absPath);
2985
3320
  const parser = "default" in mod ? mod.default : mod;
2986
3321
  if (disabled.has(parser.id)) continue;
@@ -3110,7 +3445,7 @@ function applyCrossLayerResults(uiOutput, results) {
3110
3445
 
3111
3446
  // src/server/graph/core/graph-builder.ts
3112
3447
  function readGraphFromDisk(rootDir, layer) {
3113
- const filePath = (0, import_node_path13.join)(rootDir, ".launchsecure", "graphs", `${layer}.json`);
3448
+ const filePath = (0, import_node_path12.join)(rootDir, ".launchsecure", "graphs", `${layer}.json`);
3114
3449
  if (!(0, import_node_fs12.existsSync)(filePath)) return null;
3115
3450
  try {
3116
3451
  return JSON.parse((0, import_node_fs12.readFileSync)(filePath, "utf-8"));
@@ -3214,11 +3549,11 @@ function generateAll(rootDir) {
3214
3549
  init_config();
3215
3550
 
3216
3551
  // src/server/graph/core/tagger-registry.ts
3217
- var import_node_path15 = require("node:path");
3552
+ var import_node_path14 = require("node:path");
3218
3553
 
3219
3554
  // src/server/graph/taggers/module-tagger.ts
3220
3555
  var import_node_fs13 = require("node:fs");
3221
- var import_node_path14 = require("node:path");
3556
+ var import_node_path13 = require("node:path");
3222
3557
  function matchGlob(pattern, id) {
3223
3558
  const patParts = pattern.split("/");
3224
3559
  const idParts = id.split("/");
@@ -3251,13 +3586,13 @@ function detectConventionDirs(rootDir, extraConventionDirs = []) {
3251
3586
  const conventionDirs = [...CONVENTION_DIRS_BUILTIN, ...extraConventionDirs];
3252
3587
  const searchDirs = [
3253
3588
  rootDir,
3254
- (0, import_node_path14.join)(rootDir, "src"),
3255
- (0, import_node_path14.join)(rootDir, "app"),
3256
- (0, import_node_path14.join)(rootDir, "lib")
3589
+ (0, import_node_path13.join)(rootDir, "src"),
3590
+ (0, import_node_path13.join)(rootDir, "app"),
3591
+ (0, import_node_path13.join)(rootDir, "lib")
3257
3592
  ];
3258
3593
  for (const base of searchDirs) {
3259
3594
  for (const convention of conventionDirs) {
3260
- const dir = (0, import_node_path14.join)(base, convention);
3595
+ const dir = (0, import_node_path13.join)(base, convention);
3261
3596
  if (!(0, import_node_fs13.existsSync)(dir)) continue;
3262
3597
  try {
3263
3598
  const stat = (0, import_node_fs13.statSync)(dir);
@@ -3556,7 +3891,7 @@ function loadCustomTaggers(registry, config, rootDir, disabled) {
3556
3891
  for (const entry of config.taggers?.custom ?? []) {
3557
3892
  if (disabled.has(entry.id)) continue;
3558
3893
  try {
3559
- const absPath = (0, import_node_path15.resolve)(rootDir, entry.path);
3894
+ const absPath = (0, import_node_path14.resolve)(rootDir, entry.path);
3560
3895
  const mod = require(absPath);
3561
3896
  const tagger = "default" in mod ? mod.default : mod;
3562
3897
  const override = config.taggers?.trackUntagged?.[tagger.id];
@@ -3580,12 +3915,12 @@ function createTaggerRegistry(config, rootDir) {
3580
3915
 
3581
3916
  // src/server/graph/core/tag-store.ts
3582
3917
  var import_node_fs14 = require("node:fs");
3583
- var import_node_path16 = require("node:path");
3918
+ var import_node_path15 = require("node:path");
3584
3919
  var TAGS_FILENAME = "tags.json";
3585
3920
  var GRAPHS_DIR = ".launchsecure/graphs";
3586
3921
  var tagCache = /* @__PURE__ */ new Map();
3587
3922
  function tagsFilePath(rootDir) {
3588
- return (0, import_node_path16.join)(rootDir, GRAPHS_DIR, TAGS_FILENAME);
3923
+ return (0, import_node_path15.join)(rootDir, GRAPHS_DIR, TAGS_FILENAME);
3589
3924
  }
3590
3925
  function readTagStore(rootDir) {
3591
3926
  const filePath = tagsFilePath(rootDir);
@@ -3606,7 +3941,7 @@ function readTagStore(rootDir) {
3606
3941
  }
3607
3942
  function writeTagStore(rootDir, store) {
3608
3943
  const filePath = tagsFilePath(rootDir);
3609
- const dir = (0, import_node_path16.dirname)(filePath);
3944
+ const dir = (0, import_node_path15.dirname)(filePath);
3610
3945
  (0, import_node_fs14.mkdirSync)(dir, { recursive: true });
3611
3946
  const cleaned = {};
3612
3947
  for (const [nodeId, tags] of Object.entries(store)) {
@@ -3637,20 +3972,20 @@ function removeTag(rootDir, nodeId, key) {
3637
3972
  init_ts_extractor();
3638
3973
  var GRAPHS_DIR2 = ".launchsecure/graphs";
3639
3974
  function getAvailableLayers(rootDir) {
3640
- const dir = (0, import_node_path17.join)(rootDir, GRAPHS_DIR2);
3975
+ const dir = (0, import_node_path16.join)(rootDir, GRAPHS_DIR2);
3641
3976
  if (!(0, import_node_fs15.existsSync)(dir)) return [];
3642
3977
  return (0, import_node_fs15.readdirSync)(dir).filter((f) => f.endsWith(".json") && f !== "tags.json").map((f) => f.replace(".json", ""));
3643
3978
  }
3644
3979
  var graphCache = /* @__PURE__ */ new Map();
3645
3980
  var taggedCache = /* @__PURE__ */ new Map();
3646
3981
  function graphsDir(rootDir) {
3647
- return (0, import_node_path17.join)(rootDir, GRAPHS_DIR2);
3982
+ return (0, import_node_path16.join)(rootDir, GRAPHS_DIR2);
3648
3983
  }
3649
3984
  function graphFilePath(rootDir, layer) {
3650
- return (0, import_node_path17.join)(graphsDir(rootDir), `${layer}.json`);
3985
+ return (0, import_node_path16.join)(graphsDir(rootDir), `${layer}.json`);
3651
3986
  }
3652
3987
  function tagsFilePath2(rootDir) {
3653
- return (0, import_node_path17.join)(graphsDir(rootDir), "tags.json");
3988
+ return (0, import_node_path16.join)(graphsDir(rootDir), "tags.json");
3654
3989
  }
3655
3990
  function getMtimeMs(filePath) {
3656
3991
  if (!(0, import_node_fs15.existsSync)(filePath)) return 0;
@@ -3751,15 +4086,15 @@ async function generateGraph(rootDir, layer) {
3751
4086
  var import_node_child_process = require("node:child_process");
3752
4087
  var import_node_fs16 = require("node:fs");
3753
4088
  var import_node_os = require("node:os");
3754
- var import_node_path18 = require("node:path");
4089
+ var import_node_path17 = require("node:path");
3755
4090
  function lockDir(projectRoot) {
3756
4091
  if (projectRoot) {
3757
- return (0, import_node_path18.join)(projectRoot, ".launchsecure");
4092
+ return (0, import_node_path17.join)(projectRoot, ".launchsecure");
3758
4093
  }
3759
- return (0, import_node_path18.join)((0, import_node_os.homedir)(), ".launchsecure");
4094
+ return (0, import_node_path17.join)((0, import_node_os.homedir)(), ".launchsecure");
3760
4095
  }
3761
4096
  function lockPath(projectRoot) {
3762
- return (0, import_node_path18.join)(lockDir(projectRoot), "launch-chart.lock");
4097
+ return (0, import_node_path17.join)(lockDir(projectRoot), "launch-chart.lock");
3763
4098
  }
3764
4099
  var _activeProjectRoot;
3765
4100
  function readLock(projectRoot) {
@@ -3844,9 +4179,9 @@ init_config();
3844
4179
 
3845
4180
  // src/server/graph/core/audit-core.ts
3846
4181
  var import_node_fs17 = require("node:fs");
3847
- var import_node_path19 = require("node:path");
4182
+ var import_node_path18 = require("node:path");
3848
4183
  function readGraphFile(rootDir, layer) {
3849
- const filePath = (0, import_node_path19.join)(rootDir, ".launchsecure", "graphs", `${layer}.json`);
4184
+ const filePath = (0, import_node_path18.join)(rootDir, ".launchsecure", "graphs", `${layer}.json`);
3850
4185
  if (!(0, import_node_fs17.existsSync)(filePath)) return null;
3851
4186
  try {
3852
4187
  return JSON.parse((0, import_node_fs17.readFileSync)(filePath, "utf-8"));
@@ -3893,7 +4228,7 @@ function checkUnprotectedRoutes(rootDir) {
3893
4228
  const api = readGraphFile(rootDir, "api");
3894
4229
  const staticGraph = readGraphFile(rootDir, "static");
3895
4230
  if (!api) return buildReport("api", "unprotected_routes", findings);
3896
- const routePermsPath = (0, import_node_path19.join)(rootDir, "src", "config", "route-permissions.ts");
4231
+ const routePermsPath = (0, import_node_path18.join)(rootDir, "src", "config", "route-permissions.ts");
3897
4232
  let routePermsContent = "";
3898
4233
  if ((0, import_node_fs17.existsSync)(routePermsPath)) {
3899
4234
  routePermsContent = (0, import_node_fs17.readFileSync)(routePermsPath, "utf-8");
@@ -3973,7 +4308,7 @@ function checkUnenforcedPermissions(rootDir) {
3973
4308
  const staticGraph = readGraphFile(rootDir, "static");
3974
4309
  if (!staticGraph) return buildReport("static", "unenforced_permissions", findings);
3975
4310
  const permissions = staticGraph.nodes.filter((n) => n.type === "seed_permission").map((n) => ({ id: n.id, key: n.value, name: n.name }));
3976
- const routePermsPath = (0, import_node_path19.join)(rootDir, "src", "config", "route-permissions.ts");
4311
+ const routePermsPath = (0, import_node_path18.join)(rootDir, "src", "config", "route-permissions.ts");
3977
4312
  let routePermsContent = "";
3978
4313
  if ((0, import_node_fs17.existsSync)(routePermsPath)) {
3979
4314
  routePermsContent = (0, import_node_fs17.readFileSync)(routePermsPath, "utf-8");
@@ -4006,7 +4341,7 @@ function checkHardcodedValues(rootDir) {
4006
4341
  const seen = /* @__PURE__ */ new Set();
4007
4342
  for (const node of api.nodes) {
4008
4343
  if (node.type !== "endpoint") continue;
4009
- const filePath = (0, import_node_path19.join)(rootDir, "src", node.id);
4344
+ const filePath = (0, import_node_path18.join)(rootDir, "src", node.id);
4010
4345
  if (!(0, import_node_fs17.existsSync)(filePath)) continue;
4011
4346
  const content = (0, import_node_fs17.readFileSync)(filePath, "utf-8");
4012
4347
  let m;
@@ -4138,16 +4473,16 @@ var MIME_TYPES = {
4138
4473
  function findProjectRoot(startDir) {
4139
4474
  let dir = startDir;
4140
4475
  for (let i = 0; i < 8; i++) {
4141
- const graphsDir2 = import_node_path20.default.join(dir, ".launchsecure", "graphs");
4142
- if (import_node_fs18.default.existsSync(import_node_path20.default.join(graphsDir2, "ui.json")) || import_node_fs18.default.existsSync(import_node_path20.default.join(graphsDir2, "api.json")) || import_node_fs18.default.existsSync(import_node_path20.default.join(graphsDir2, "db.json"))) return dir;
4143
- const parent = import_node_path20.default.dirname(dir);
4476
+ const graphsDir2 = import_node_path19.default.join(dir, ".launchsecure", "graphs");
4477
+ 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;
4478
+ const parent = import_node_path19.default.dirname(dir);
4144
4479
  if (parent === dir) break;
4145
4480
  dir = parent;
4146
4481
  }
4147
4482
  dir = startDir;
4148
4483
  for (let i = 0; i < 8; i++) {
4149
- if (import_node_fs18.default.existsSync(import_node_path20.default.join(dir, ".git"))) return dir;
4150
- const parent = import_node_path20.default.dirname(dir);
4484
+ if (import_node_fs18.default.existsSync(import_node_path19.default.join(dir, ".git"))) return dir;
4485
+ const parent = import_node_path19.default.dirname(dir);
4151
4486
  if (parent === dir) break;
4152
4487
  dir = parent;
4153
4488
  }
@@ -4156,7 +4491,7 @@ function findProjectRoot(startDir) {
4156
4491
  function resolveRequestRoot(url, monorepoRoot, projects) {
4157
4492
  const projectParam = url.searchParams.get("project");
4158
4493
  if (!projectParam || projects.length === 0) return monorepoRoot;
4159
- const resolved = import_node_path20.default.resolve(monorepoRoot, projectParam);
4494
+ const resolved = import_node_path19.default.resolve(monorepoRoot, projectParam);
4160
4495
  if (!resolved.startsWith(monorepoRoot)) {
4161
4496
  throw new Error("Project path outside monorepo root");
4162
4497
  }
@@ -4208,14 +4543,14 @@ async function buildMergedGraph(root) {
4208
4543
  }
4209
4544
  function serveStatic(res, filePath) {
4210
4545
  if (!import_node_fs18.default.existsSync(filePath) || !import_node_fs18.default.statSync(filePath).isFile()) return false;
4211
- const ext = import_node_path20.default.extname(filePath).toLowerCase();
4546
+ const ext = import_node_path19.default.extname(filePath).toLowerCase();
4212
4547
  const mime = MIME_TYPES[ext] ?? "application/octet-stream";
4213
4548
  res.writeHead(200, { "Content-Type": mime, "Cache-Control": "no-cache" });
4214
4549
  import_node_fs18.default.createReadStream(filePath).pipe(res);
4215
4550
  return true;
4216
4551
  }
4217
4552
  function serveIndex(res, clientDir) {
4218
- const indexPath = import_node_path20.default.join(clientDir, "index.html");
4553
+ const indexPath = import_node_path19.default.join(clientDir, "index.html");
4219
4554
  if (!import_node_fs18.default.existsSync(indexPath)) {
4220
4555
  res.writeHead(500, { "Content-Type": "text/plain" });
4221
4556
  res.end(`LaunchChart client bundle not found at ${clientDir}. Run 'npm run build:chart-client'.`);
@@ -4224,14 +4559,14 @@ function serveIndex(res, clientDir) {
4224
4559
  serveStatic(res, indexPath);
4225
4560
  }
4226
4561
  function tryListen(server, port) {
4227
- return new Promise((resolve3, reject) => {
4562
+ return new Promise((resolve4, reject) => {
4228
4563
  const onError = (err) => {
4229
4564
  server.off("listening", onListening);
4230
4565
  reject(err);
4231
4566
  };
4232
4567
  const onListening = () => {
4233
4568
  server.off("error", onError);
4234
- resolve3(port);
4569
+ resolve4(port);
4235
4570
  };
4236
4571
  server.once("error", onError);
4237
4572
  server.once("listening", onListening);
@@ -4268,7 +4603,7 @@ async function startChartServer(opts = {}) {
4268
4603
  }
4269
4604
  return { port: existing.port, url: existing.url };
4270
4605
  }
4271
- const clientDir = opts.clientDir ?? import_node_path20.default.join(__dirname, "..", "chart-client");
4606
+ const clientDir = opts.clientDir ?? import_node_path19.default.join(__dirname, "..", "chart-client");
4272
4607
  const rootConfig = loadConfig(projectRoot);
4273
4608
  const projects = rootConfig.projects ?? [];
4274
4609
  const server = import_node_http.default.createServer((req, res) => {
@@ -4284,11 +4619,11 @@ async function startChartServer(opts = {}) {
4284
4619
  }
4285
4620
  if (req.method === "GET" && url2.pathname === "/api/projects") {
4286
4621
  const projectList = projects.length > 0 ? projects.map((p) => {
4287
- const absRoot = import_node_path20.default.resolve(projectRoot, p.root);
4288
- const hasGraphs = import_node_fs18.default.existsSync(import_node_path20.default.join(absRoot, ".launchsecure", "graphs"));
4289
- const hasNextConfig = import_node_fs18.default.existsSync(import_node_path20.default.join(absRoot, "next.config.ts")) || import_node_fs18.default.existsSync(import_node_path20.default.join(absRoot, "next.config.js")) || import_node_fs18.default.existsSync(import_node_path20.default.join(absRoot, "next.config.mjs"));
4622
+ const absRoot = import_node_path19.default.resolve(projectRoot, p.root);
4623
+ const hasGraphs = import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, ".launchsecure", "graphs"));
4624
+ 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"));
4290
4625
  return { name: p.name, root: p.root, hasGraphs, hasNextConfig };
4291
- }) : [{ name: import_node_path20.default.basename(projectRoot), root: ".", hasGraphs: import_node_fs18.default.existsSync(import_node_path20.default.join(projectRoot, ".launchsecure", "graphs")), hasNextConfig: true }];
4626
+ }) : [{ name: import_node_path19.default.basename(projectRoot), root: ".", hasGraphs: import_node_fs18.default.existsSync(import_node_path19.default.join(projectRoot, ".launchsecure", "graphs")), hasNextConfig: true }];
4292
4627
  res.writeHead(200, { "Content-Type": "application/json" });
4293
4628
  res.end(JSON.stringify({ projects: projectList, monorepoRoot: projectRoot }));
4294
4629
  return;
@@ -4334,18 +4669,18 @@ async function startChartServer(opts = {}) {
4334
4669
  }
4335
4670
  if (req.method === "GET" && url2.pathname === "/api/file-content") {
4336
4671
  const relPath = url2.searchParams.get("path");
4337
- if (!relPath || relPath.includes("..") || import_node_path20.default.isAbsolute(relPath)) {
4672
+ if (!relPath || relPath.includes("..") || import_node_path19.default.isAbsolute(relPath)) {
4338
4673
  res.writeHead(400, { "Content-Type": "application/json" });
4339
4674
  res.end(JSON.stringify({ error: "Invalid path" }));
4340
4675
  return;
4341
4676
  }
4342
- const filePath = import_node_path20.default.join(reqRoot, relPath);
4677
+ const filePath = import_node_path19.default.join(reqRoot, relPath);
4343
4678
  if (!filePath.startsWith(reqRoot) || !import_node_fs18.default.existsSync(filePath) || !import_node_fs18.default.statSync(filePath).isFile()) {
4344
4679
  res.writeHead(404, { "Content-Type": "application/json" });
4345
4680
  res.end(JSON.stringify({ error: "File not found" }));
4346
4681
  return;
4347
4682
  }
4348
- const ext = import_node_path20.default.extname(filePath).toLowerCase();
4683
+ const ext = import_node_path19.default.extname(filePath).toLowerCase();
4349
4684
  const langMap = { ".ts": "typescript", ".tsx": "tsx", ".js": "javascript", ".jsx": "jsx", ".prisma": "prisma", ".json": "json", ".css": "css" };
4350
4685
  const content = import_node_fs18.default.readFileSync(filePath, "utf-8");
4351
4686
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -4389,7 +4724,7 @@ async function startChartServer(opts = {}) {
4389
4724
  req.on("end", () => {
4390
4725
  try {
4391
4726
  const newConfig = JSON.parse(body);
4392
- const configPath = import_node_path20.default.join(reqRoot, ".launchchart.json");
4727
+ const configPath = import_node_path19.default.join(reqRoot, ".launchchart.json");
4393
4728
  import_node_fs18.default.writeFileSync(configPath, JSON.stringify(newConfig, null, 2) + "\n", "utf-8");
4394
4729
  res.writeHead(200, { "Content-Type": "application/json" });
4395
4730
  res.end(JSON.stringify({ ok: true }));
@@ -4423,7 +4758,7 @@ async function startChartServer(opts = {}) {
4423
4758
  const taggerConfig = JSON.parse(body);
4424
4759
  const config2 = loadConfig(reqRoot);
4425
4760
  config2.taggers = taggerConfig;
4426
- const configPath = import_node_path20.default.join(reqRoot, ".launchchart.json");
4761
+ const configPath = import_node_path19.default.join(reqRoot, ".launchchart.json");
4427
4762
  import_node_fs18.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
4428
4763
  res.writeHead(200, { "Content-Type": "application/json" });
4429
4764
  res.end(JSON.stringify({ ok: true }));
@@ -4491,16 +4826,23 @@ async function startChartServer(opts = {}) {
4491
4826
  const paths = resolveProjectPaths(reqRoot, config2);
4492
4827
  const overrides = {
4493
4828
  appDir: !!config2.paths?.appDir,
4494
- dbDir: !!config2.paths?.dbDir
4829
+ dbDir: !!config2.paths?.dbDir,
4830
+ srcRoots: !!(config2.paths?.srcRoots && config2.paths.srcRoots.length > 0)
4495
4831
  };
4832
+ const relFromRoot = (abs) => import_node_path19.default.relative(reqRoot, abs) || ".";
4496
4833
  res.writeHead(200, { "Content-Type": "application/json" });
4497
4834
  res.end(JSON.stringify({
4498
4835
  projectRoot: reqRoot,
4499
4836
  detected: paths ? {
4500
- srcDir: import_node_path20.default.relative(reqRoot, paths.srcDir) || ".",
4501
- appDir: import_node_path20.default.relative(reqRoot, paths.appDir),
4502
- apiDir: import_node_path20.default.relative(reqRoot, paths.apiDir),
4503
- dbDir: paths.dbDir ? import_node_path20.default.relative(reqRoot, paths.dbDir) : null
4837
+ srcDir: relFromRoot(paths.srcDir),
4838
+ appDir: relFromRoot(paths.appDir),
4839
+ apiDir: relFromRoot(paths.apiDir),
4840
+ dbDir: paths.dbDir ? relFromRoot(paths.dbDir) : null,
4841
+ dbKind: paths.dbConfig.kind,
4842
+ dbSchemaPath: paths.dbConfig.schemaPath ? relFromRoot(paths.dbConfig.schemaPath) : null,
4843
+ dbMigrationsDir: paths.dbConfig.migrationsDir ? relFromRoot(paths.dbConfig.migrationsDir) : null,
4844
+ srcRoots: paths.srcRoots.map(relFromRoot),
4845
+ conventionFiles: paths.conventionFiles.map(relFromRoot)
4504
4846
  } : null,
4505
4847
  overrides,
4506
4848
  isOverride: overrides.appDir
@@ -4509,8 +4851,8 @@ async function startChartServer(opts = {}) {
4509
4851
  }
4510
4852
  if (req.method === "GET" && url2.pathname === "/api/browse-dir") {
4511
4853
  const browsePath = url2.searchParams.get("path") || projectRoot;
4512
- const abs = import_node_path20.default.resolve(browsePath);
4513
- const twoUp = import_node_path20.default.resolve(projectRoot, "..", "..");
4854
+ const abs = import_node_path19.default.resolve(browsePath);
4855
+ const twoUp = import_node_path19.default.resolve(projectRoot, "..", "..");
4514
4856
  if (!abs.startsWith(twoUp)) {
4515
4857
  res.writeHead(403, { "Content-Type": "application/json" });
4516
4858
  res.end(JSON.stringify({ ok: false, error: "Path outside allowed range" }));
@@ -4519,9 +4861,9 @@ async function startChartServer(opts = {}) {
4519
4861
  try {
4520
4862
  const entries = import_node_fs18.default.readdirSync(abs, { withFileTypes: true });
4521
4863
  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();
4522
- const parent = abs !== twoUp ? import_node_path20.default.dirname(abs) : null;
4864
+ const parent = abs !== twoUp ? import_node_path19.default.dirname(abs) : null;
4523
4865
  res.writeHead(200, { "Content-Type": "application/json" });
4524
- res.end(JSON.stringify({ current: abs, parent, dirs, relative: import_node_path20.default.relative(projectRoot, abs) || "." }));
4866
+ res.end(JSON.stringify({ current: abs, parent, dirs, relative: import_node_path19.default.relative(projectRoot, abs) || "." }));
4525
4867
  } catch (err) {
4526
4868
  res.writeHead(400, { "Content-Type": "application/json" });
4527
4869
  res.end(JSON.stringify({ ok: false, error: String(err) }));
@@ -4547,7 +4889,7 @@ async function startChartServer(opts = {}) {
4547
4889
  const { projects: newProjects } = JSON.parse(body);
4548
4890
  const config2 = loadConfig(projectRoot);
4549
4891
  config2.projects = newProjects.length > 0 ? newProjects : void 0;
4550
- const configPath = import_node_path20.default.join(projectRoot, ".launchchart.json");
4892
+ const configPath = import_node_path19.default.join(projectRoot, ".launchchart.json");
4551
4893
  import_node_fs18.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
4552
4894
  projects.length = 0;
4553
4895
  if (config2.projects) projects.push(...config2.projects);
@@ -4561,7 +4903,7 @@ async function startChartServer(opts = {}) {
4561
4903
  return;
4562
4904
  }
4563
4905
  if (url2.pathname !== "/") {
4564
- const staticPath = import_node_path20.default.join(clientDir, url2.pathname);
4906
+ const staticPath = import_node_path19.default.join(clientDir, url2.pathname);
4565
4907
  if (serveStatic(res, staticPath)) return;
4566
4908
  }
4567
4909
  serveIndex(res, clientDir);