@launchsecure/launch-kit 0.0.23 → 0.0.25

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 (60) hide show
  1. package/dist/deck-client/assets/{_baseUniq-DsfOm3t_.js → _baseUniq-2gclQXo7.js} +1 -1
  2. package/dist/deck-client/assets/{arc-NJuvkBv1.js → arc-DcMY5Wm0.js} +1 -1
  3. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-BgrcgZs0.js → architectureDiagram-Q4EWVU46-B8iirmmJ.js} +1 -1
  4. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-C3XoLi15.js → blockDiagram-DXYQGD6D-B4JBLjmJ.js} +1 -1
  5. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-FX2PjLfb.js → c4Diagram-AHTNJAMY-CojrJAk8.js} +1 -1
  6. package/dist/deck-client/assets/channel-ERh5jKXV.js +1 -0
  7. package/dist/deck-client/assets/{chunk-4BX2VUAB-D0aqsJV0.js → chunk-4BX2VUAB-Bmb_BMDo.js} +1 -1
  8. package/dist/deck-client/assets/{chunk-4TB4RGXK-7qRCCAgK.js → chunk-4TB4RGXK-CumBy8qe.js} +1 -1
  9. package/dist/deck-client/assets/{chunk-55IACEB6-DfHG-iqb.js → chunk-55IACEB6-Ka8Hb1wD.js} +1 -1
  10. package/dist/deck-client/assets/{chunk-EDXVE4YY-DrR52j3B.js → chunk-EDXVE4YY-B3sIPiQo.js} +1 -1
  11. package/dist/deck-client/assets/{chunk-FMBD7UC4-D5KSGATB.js → chunk-FMBD7UC4-C1tYkaqu.js} +1 -1
  12. package/dist/deck-client/assets/{chunk-OYMX7WX6-M7hsLRNU.js → chunk-OYMX7WX6-D7Wacbky.js} +1 -1
  13. package/dist/deck-client/assets/{chunk-QZHKN3VN-1ynAWO2m.js → chunk-QZHKN3VN-ChXI0vO3.js} +1 -1
  14. package/dist/deck-client/assets/{chunk-YZCP3GAM-S2-nGw3D.js → chunk-YZCP3GAM-BXhiqf8u.js} +1 -1
  15. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-CMi1Gaev.js +1 -0
  16. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-CMi1Gaev.js +1 -0
  17. package/dist/deck-client/assets/clone-DfWhlD4X.js +1 -0
  18. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-BcMwozS2.js → cose-bilkent-S5V4N54A-Bqp3p68D.js} +1 -1
  19. package/dist/deck-client/assets/{dagre-KV5264BT-DtKMhl_1.js → dagre-KV5264BT-BS-rtyhZ.js} +1 -1
  20. package/dist/deck-client/assets/{diagram-5BDNPKRD-1plH69us.js → diagram-5BDNPKRD-BIrj9YGI.js} +1 -1
  21. package/dist/deck-client/assets/{diagram-G4DWMVQ6-D_o-BHO3.js → diagram-G4DWMVQ6-noHWPIg4.js} +1 -1
  22. package/dist/deck-client/assets/{diagram-MMDJMWI5-ClZ1LIx6.js → diagram-MMDJMWI5-C2qHxvqV.js} +1 -1
  23. package/dist/deck-client/assets/{diagram-TYMM5635-B8dKHfRh.js → diagram-TYMM5635-BytnGQr-.js} +1 -1
  24. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-CY2aCH7-.js → erDiagram-SMLLAGMA-BfK5m2YQ.js} +1 -1
  25. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-DZZWHti8.js → flowDiagram-DWJPFMVM-Cq925G1Z.js} +1 -1
  26. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-OwGGa6Lu.js → ganttDiagram-T4ZO3ILL-DhhHPAmj.js} +1 -1
  27. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-GKyWD4Qt.js → gitGraphDiagram-UUTBAWPF-B3Lc0h9q.js} +1 -1
  28. package/dist/deck-client/assets/{graph-CORzYQdB.js → graph-RTawgVWm.js} +1 -1
  29. package/dist/deck-client/assets/{index-hiIpM7EP.js → index-BfIfJXmS.js} +3 -3
  30. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-DmgqJCcF.js → infoDiagram-42DDH7IO-BlR584kX.js} +1 -1
  31. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-D-1v7knu.js → ishikawaDiagram-UXIWVN3A-DygKoNGY.js} +1 -1
  32. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-CYrGQE7b.js → journeyDiagram-VCZTEJTY-BnaiYp9N.js} +1 -1
  33. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-BJFDWiH-.js → kanban-definition-6JOO6SKY-BQBUBzJC.js} +1 -1
  34. package/dist/deck-client/assets/{layout-BTFFcaxF.js → layout-DeZ8HI1T.js} +1 -1
  35. package/dist/deck-client/assets/{linear-DAbl6COS.js → linear-C6roLi_9.js} +1 -1
  36. package/dist/deck-client/assets/{min-oWHBrFBm.js → min-CbUksbuI.js} +1 -1
  37. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-BTCB0VLO.js → mindmap-definition-QFDTVHPH-iNxV62yN.js} +1 -1
  38. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-CUZChWNA.js → pieDiagram-DEJITSTG-DHVA0jaG.js} +1 -1
  39. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-4M1Um_e4.js → quadrantDiagram-34T5L4WZ-DBeKKLUQ.js} +1 -1
  40. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-DLzQZ0B3.js → requirementDiagram-MS252O5E-CBwITx7p.js} +1 -1
  41. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-DcNgzV3E.js → sankeyDiagram-XADWPNL6-BtE-1YTU.js} +1 -1
  42. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-CAcI2vC9.js → sequenceDiagram-FGHM5R23-DN96yPP2.js} +1 -1
  43. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-CntjTTm5.js → stateDiagram-FHFEXIEX-VUkKC2uJ.js} +1 -1
  44. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CA0IjulK.js +1 -0
  45. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-D8zrit4U.js → timeline-definition-GMOUNBTQ-oUeZhRns.js} +1 -1
  46. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-C4SuFPgo.js → vennDiagram-DHZGUBPP-D87fK90n.js} +1 -1
  47. package/dist/deck-client/assets/wardley-RL74JXVD-DYbYcpDp.js +162 -0
  48. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-kj73r6f-.js → wardleyDiagram-NUSXRM2D-Ca_i0QRA.js} +1 -1
  49. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-CC_d_Ey3.js → xychartDiagram-5P7HB3ND-CUOJVIvq.js} +1 -1
  50. package/dist/deck-client/index.html +1 -1
  51. package/dist/server/chart-serve.js +368 -27
  52. package/dist/server/cli.js +368 -27
  53. package/dist/server/graph-mcp-entry.js +368 -27
  54. package/package.json +1 -1
  55. package/dist/deck-client/assets/channel-ChQjD1T1.js +0 -1
  56. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-B_9iqK1S.js +0 -1
  57. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-B_9iqK1S.js +0 -1
  58. package/dist/deck-client/assets/clone-BYt1AMfz.js +0 -1
  59. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-YiaphOU_.js +0 -1
  60. package/dist/deck-client/assets/wardley-RL74JXVD-B3F-Olcq.js +0 -162
@@ -3733,11 +3733,14 @@ var init_config = __esm({
3733
3733
  var ts_extractor_exports = {};
3734
3734
  __export(ts_extractor_exports, {
3735
3735
  classifyFile: () => classifyFile,
3736
+ classifyRouteAgainstMiddleware: () => classifyRouteAgainstMiddleware,
3736
3737
  createQuery: () => createQuery,
3737
3738
  extractAuthWrappersTS: () => extractAuthWrappersTS,
3738
3739
  extractDbCallsTS: () => extractDbCallsTS,
3739
3740
  extractDeep: () => extractDeep,
3741
+ extractMiddlewareAuthTS: () => extractMiddlewareAuthTS,
3740
3742
  initTreeSitter: () => initTreeSitter,
3743
+ middlewarePatternToRegex: () => middlewarePatternToRegex,
3741
3744
  parseCodeTS: () => parseCodeTS,
3742
3745
  parseFileTS: () => parseFileTS,
3743
3746
  setExtractorConfig: () => setExtractorConfig
@@ -4131,6 +4134,180 @@ function extractAuthWrappersTS(absPath) {
4131
4134
  }
4132
4135
  return wrappers;
4133
4136
  }
4137
+ function inferIntentFromName(name) {
4138
+ for (const re of EXEMPT_NAME_PATTERNS) {
4139
+ if (re.test(name)) return { intent: "exempt", hint: `name "${name}" matches /${re.source}/` };
4140
+ }
4141
+ for (const re of PROTECT_NAME_PATTERNS) {
4142
+ if (re.test(name)) return { intent: "protect", hint: `name "${name}" matches /${re.source}/` };
4143
+ }
4144
+ return { intent: "ambiguous", hint: `name "${name}" has no exempt/protect signal` };
4145
+ }
4146
+ function looksLikeRoutePattern(s) {
4147
+ return s.startsWith("/") && !s.startsWith("//");
4148
+ }
4149
+ function collectStringsFromArray(arrNode) {
4150
+ const out = [];
4151
+ for (const child of arrNode.children) {
4152
+ if (child.type !== "string") continue;
4153
+ const frag = childOfType(child, "string_fragment");
4154
+ if (frag) out.push(frag.text);
4155
+ }
4156
+ return out;
4157
+ }
4158
+ function findArrayInValue(valueNode) {
4159
+ if (!valueNode) return null;
4160
+ if (valueNode.type === "array") return valueNode;
4161
+ if (valueNode.type === "call_expression") {
4162
+ const args = childOfType(valueNode, "arguments");
4163
+ if (!args) return null;
4164
+ for (const c of args.children) {
4165
+ if (c.type === "array") return c;
4166
+ }
4167
+ }
4168
+ return null;
4169
+ }
4170
+ function extractMatcherFromDeclarator(decl) {
4171
+ const nameNode = childOfType(decl, "identifier");
4172
+ if (!nameNode) return null;
4173
+ let valueNode;
4174
+ for (const c of decl.children) {
4175
+ if (c.type === "identifier" || c.type === "=" || c.type === "type_annotation") continue;
4176
+ valueNode = c;
4177
+ }
4178
+ const arr = findArrayInValue(valueNode);
4179
+ if (!arr) return null;
4180
+ const strings = collectStringsFromArray(arr);
4181
+ const routes = strings.filter(looksLikeRoutePattern);
4182
+ if (routes.length === 0) return null;
4183
+ const { intent, hint } = inferIntentFromName(nameNode.text);
4184
+ return { name: nameNode.text, patterns: routes, intent, hint };
4185
+ }
4186
+ function extractMatchersFromObject(objNode) {
4187
+ const out = [];
4188
+ for (const pair of childrenOfType(objNode, "pair")) {
4189
+ const key = childOfType(pair, "property_identifier");
4190
+ if (!key) continue;
4191
+ const arr = pair.children.find((c) => c.type === "array");
4192
+ if (!arr) continue;
4193
+ const routes = collectStringsFromArray(arr).filter(looksLikeRoutePattern);
4194
+ if (routes.length === 0) continue;
4195
+ const { intent, hint } = inferIntentFromName(key.text);
4196
+ out.push({ name: key.text, patterns: routes, intent, hint });
4197
+ }
4198
+ return out;
4199
+ }
4200
+ function detectFallthroughProtect(root) {
4201
+ const text = root.text;
4202
+ const signals = [
4203
+ /\bauth\.protect\s*\(/,
4204
+ /\bauth\(\)\.protect\s*\(/,
4205
+ /\bredirect\s*\(\s*['"`]\/(sign-?in|log-?in|auth)/i,
4206
+ /\bNextResponse\.redirect\s*\(/,
4207
+ /\bthrow\s+new\s+\w*Unauthorized/i
4208
+ ];
4209
+ return signals.some((re) => re.test(text));
4210
+ }
4211
+ function extractMiddlewareAuthTS(absPath) {
4212
+ if (!require("node:fs").existsSync(absPath)) return null;
4213
+ const tree = parseSource(absPath);
4214
+ const root = tree.rootNode;
4215
+ const matchers = [];
4216
+ for (const stmt of root.children) {
4217
+ if (stmt.type !== "lexical_declaration" && stmt.type !== "variable_declaration") continue;
4218
+ for (const decl of childrenOfType(stmt, "variable_declarator")) {
4219
+ const m = extractMatcherFromDeclarator(decl);
4220
+ if (m) matchers.push(m);
4221
+ let valueNode;
4222
+ for (const c of decl.children) {
4223
+ if (c.type === "identifier" || c.type === "=" || c.type === "type_annotation") continue;
4224
+ valueNode = c;
4225
+ }
4226
+ if (valueNode?.type === "object") {
4227
+ for (const inner of extractMatchersFromObject(valueNode)) matchers.push(inner);
4228
+ }
4229
+ }
4230
+ }
4231
+ for (const stmt of root.children) {
4232
+ if (stmt.type !== "export_statement") continue;
4233
+ const decl = childOfType(stmt, "lexical_declaration") ?? childOfType(stmt, "variable_declaration");
4234
+ if (!decl) continue;
4235
+ for (const d of childrenOfType(decl, "variable_declarator")) {
4236
+ let valueNode;
4237
+ for (const c of d.children) {
4238
+ if (c.type === "identifier" || c.type === "=" || c.type === "type_annotation") continue;
4239
+ valueNode = c;
4240
+ }
4241
+ if (valueNode?.type === "object") {
4242
+ for (const inner of extractMatchersFromObject(valueNode)) matchers.push(inner);
4243
+ }
4244
+ const m = extractMatcherFromDeclarator(d);
4245
+ if (m && !matchers.some((x) => x.name === m.name && x.patterns.join() === m.patterns.join())) {
4246
+ matchers.push(m);
4247
+ }
4248
+ }
4249
+ }
4250
+ return {
4251
+ file: absPath,
4252
+ matchers,
4253
+ hasFallthroughProtect: detectFallthroughProtect(root)
4254
+ };
4255
+ }
4256
+ function middlewarePatternToRegex(pattern) {
4257
+ if (/\(\?\!/.test(pattern)) return null;
4258
+ if (pattern.startsWith("(")) return null;
4259
+ let src = "^";
4260
+ let i = 0;
4261
+ while (i < pattern.length) {
4262
+ const ch = pattern[i];
4263
+ if (ch === "(" && pattern.slice(i, i + 4) === "(.*)") {
4264
+ src += ".*";
4265
+ i += 4;
4266
+ continue;
4267
+ }
4268
+ if (ch === ":") {
4269
+ i++;
4270
+ while (i < pattern.length && /[a-zA-Z0-9_]/.test(pattern[i])) i++;
4271
+ src += "[^/]+";
4272
+ continue;
4273
+ }
4274
+ if (ch === "*") {
4275
+ i++;
4276
+ while (i < pattern.length && /[a-zA-Z0-9_]/.test(pattern[i])) i++;
4277
+ if (pattern[i] === "?") {
4278
+ i++;
4279
+ src += ".*";
4280
+ continue;
4281
+ }
4282
+ src += ".+";
4283
+ continue;
4284
+ }
4285
+ if (/[.\\+?^${}()|[\]]/.test(ch)) {
4286
+ src += "\\" + ch;
4287
+ i++;
4288
+ continue;
4289
+ }
4290
+ src += ch;
4291
+ i++;
4292
+ }
4293
+ src += "$";
4294
+ try {
4295
+ return new RegExp(src);
4296
+ } catch {
4297
+ return null;
4298
+ }
4299
+ }
4300
+ function classifyRouteAgainstMiddleware(routePath, info) {
4301
+ for (const m of info.matchers) {
4302
+ if (m.intent === "ambiguous") continue;
4303
+ for (const pat of m.patterns) {
4304
+ const re = middlewarePatternToRegex(pat);
4305
+ if (!re) continue;
4306
+ if (re.test(routePath)) return { intent: m.intent, matcher: m.name, pattern: pat };
4307
+ }
4308
+ }
4309
+ return null;
4310
+ }
4134
4311
  function trunc(s, max = 120) {
4135
4312
  return s.length > max ? s.slice(0, max) + "..." : s;
4136
4313
  }
@@ -4287,7 +4464,7 @@ function extractDeep(absPath) {
4287
4464
  }
4288
4465
  return { elements, stateVars, conditions, variables, responses, params };
4289
4466
  }
4290
- var import_node_fs4, import_node_path4, tsxLanguage, parserInstance, initPromise, initialized, queriesDir, queryCache, PRISMA_MUTATION_METHODS_BUILTIN, SUPABASE_MUTATION_METHODS_BUILTIN, DB_IDENTIFIERS_FALLBACK, extraDbIdentifiers, extraMutationMethods, INLINE_AUTH_IMPORTS;
4467
+ var import_node_fs4, import_node_path4, tsxLanguage, parserInstance, initPromise, initialized, queriesDir, queryCache, PRISMA_MUTATION_METHODS_BUILTIN, SUPABASE_MUTATION_METHODS_BUILTIN, DB_IDENTIFIERS_FALLBACK, extraDbIdentifiers, extraMutationMethods, INLINE_AUTH_IMPORTS, EXEMPT_NAME_PATTERNS, PROTECT_NAME_PATTERNS;
4291
4468
  var init_ts_extractor = __esm({
4292
4469
  "src/server/graph/core/ts-extractor.ts"() {
4293
4470
  "use strict";
@@ -4326,6 +4503,29 @@ var init_ts_extractor = __esm({
4326
4503
  { module: /^@auth\//, helpers: ["auth"] },
4327
4504
  { module: /^@supabase\/auth-helpers/, helpers: ["createServerClient"] }
4328
4505
  ];
4506
+ EXEMPT_NAME_PATTERNS = [
4507
+ /^is_?public/i,
4508
+ /^public_?routes?/i,
4509
+ /^public_?paths?/i,
4510
+ /^whitelist/i,
4511
+ /^allowlist/i,
4512
+ /^unauthenticated/i,
4513
+ /^anonymous/i,
4514
+ /^guest/i,
4515
+ /^skip_?auth/i,
4516
+ /^bypass/i
4517
+ ];
4518
+ PROTECT_NAME_PATTERNS = [
4519
+ /^is_?protected/i,
4520
+ /^protected_?routes?/i,
4521
+ /^protected_?paths?/i,
4522
+ /^require_?auth/i,
4523
+ /^auth_?required/i,
4524
+ /^private_?routes?/i,
4525
+ /^is_?admin/i,
4526
+ /^admin_?routes?/i,
4527
+ /^secured/i
4528
+ ];
4329
4529
  }
4330
4530
  });
4331
4531
 
@@ -7048,15 +7248,9 @@ var import_node_fs2 = require("node:fs");
7048
7248
  var import_node_path2 = require("node:path");
7049
7249
  var DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
7050
7250
  "node_modules",
7051
- ".git",
7052
- ".next",
7053
- ".launchsecure",
7054
- ".claude",
7055
7251
  "dist",
7056
7252
  "build",
7057
7253
  "out",
7058
- ".turbo",
7059
- ".vercel",
7060
7254
  "coverage"
7061
7255
  ]);
7062
7256
  function walk(dir, exts) {
@@ -7078,6 +7272,7 @@ function walkWithIgnore(dir, exts, opts = {}) {
7078
7272
  const skip = opts.extraIgnore ? /* @__PURE__ */ new Set([...DEFAULT_IGNORE_DIRS, ...opts.extraIgnore]) : DEFAULT_IGNORE_DIRS;
7079
7273
  for (const entry of (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true })) {
7080
7274
  if (entry.isDirectory()) {
7275
+ if (entry.name.startsWith(".")) continue;
7081
7276
  if (skip.has(entry.name)) continue;
7082
7277
  results.push(...walkWithIgnore((0, import_node_path2.join)(dir, entry.name), exts, opts));
7083
7278
  } else if (exts.includes((0, import_node_path2.extname)(entry.name))) {
@@ -7829,43 +8024,169 @@ function generate(rootDir) {
7829
8024
  nodeIdSet.add(externalId);
7830
8025
  uiEdges.push(...edgesFromThis);
7831
8026
  }
8027
+ const tablesByFile = /* @__PURE__ */ new Map();
8028
+ const allDbNodes = [...apiNodes, ...uiNodes];
8029
+ for (const node of allDbNodes) {
8030
+ const calls = node._dbCalls;
8031
+ if (!calls || calls.length === 0) continue;
8032
+ const map = /* @__PURE__ */ new Map();
8033
+ for (const c of calls) {
8034
+ const key = `${c.kind}:${c.model}:${c.isMutation ? "m" : "r"}`;
8035
+ if (!map.has(key)) {
8036
+ map.set(key, { model: c.model, method: c.method, isMutation: c.isMutation, kind: c.kind, via: [] });
8037
+ }
8038
+ }
8039
+ tablesByFile.set(node.id, map);
8040
+ }
8041
+ const reverseRuntimeImports = /* @__PURE__ */ new Map();
8042
+ for (const edge of uiEdges) {
8043
+ if (edge.type !== "imports" && edge.type !== "renders") continue;
8044
+ if (!reverseRuntimeImports.has(edge.target)) {
8045
+ reverseRuntimeImports.set(edge.target, /* @__PURE__ */ new Set());
8046
+ }
8047
+ reverseRuntimeImports.get(edge.target).add(edge.source);
8048
+ }
8049
+ let changed = true;
8050
+ let iterations = 0;
8051
+ while (changed && iterations < 50) {
8052
+ changed = false;
8053
+ iterations++;
8054
+ for (const [target, tableMap] of [...tablesByFile]) {
8055
+ const importers = reverseRuntimeImports.get(target);
8056
+ if (!importers) continue;
8057
+ for (const importer of importers) {
8058
+ if (importer === target) continue;
8059
+ let importerMap = tablesByFile.get(importer);
8060
+ if (!importerMap) {
8061
+ importerMap = /* @__PURE__ */ new Map();
8062
+ tablesByFile.set(importer, importerMap);
8063
+ }
8064
+ for (const [key, call] of tableMap) {
8065
+ if (importerMap.has(key)) continue;
8066
+ importerMap.set(key, {
8067
+ model: call.model,
8068
+ method: null,
8069
+ isMutation: call.isMutation,
8070
+ kind: call.kind,
8071
+ via: [...call.via, target]
8072
+ });
8073
+ changed = true;
8074
+ }
8075
+ }
8076
+ }
8077
+ }
8078
+ for (const node of apiNodes) {
8079
+ const map = tablesByFile.get(node.id);
8080
+ if (!map) continue;
8081
+ node.db_models = [...new Set([...map.values()].map((c) => c.model))];
8082
+ node.db_operations = [...new Set(
8083
+ [...map.values()].filter((c) => c.via.length === 0 && c.method).map((c) => `${c.model}.${c.method}`)
8084
+ )];
8085
+ node.mutates = [...map.values()].some((c) => c.isMutation);
8086
+ }
7832
8087
  const apiCrossRefs = [];
7833
8088
  for (const node of apiNodes) {
7834
- const dbCalls = node._dbCalls;
7835
- if (!dbCalls) continue;
7836
- const seenModels = /* @__PURE__ */ new Set();
7837
- for (const call of dbCalls) {
8089
+ const map = tablesByFile.get(node.id);
8090
+ if (!map) {
8091
+ delete node._dbCalls;
8092
+ continue;
8093
+ }
8094
+ const seenTargets = /* @__PURE__ */ new Set();
8095
+ for (const call of map.values()) {
7838
8096
  const target = call.kind === "sql" ? call.model : camelToPascal(call.model);
7839
- if (seenModels.has(target)) continue;
7840
- seenModels.add(target);
7841
- apiCrossRefs.push({
8097
+ if (seenTargets.has(target)) continue;
8098
+ seenTargets.add(target);
8099
+ const isTransitive = call.via.length > 0;
8100
+ const ref = {
7842
8101
  source: node.id,
7843
8102
  target,
7844
- type: call.isMutation ? "mutates" : "reads",
8103
+ type: call.isMutation ? isTransitive ? "mutates_via" : "mutates" : isTransitive ? "reads_via" : "reads",
7845
8104
  layer: "db"
7846
- });
8105
+ };
8106
+ if (isTransitive) ref.via = call.via;
8107
+ apiCrossRefs.push(ref);
7847
8108
  }
7848
8109
  delete node._dbCalls;
7849
8110
  }
7850
8111
  const uiCrossRefs = [];
7851
8112
  for (const node of uiNodes) {
7852
- const dbCalls = node._dbCalls;
7853
- if (!dbCalls) continue;
7854
- const seenModels = /* @__PURE__ */ new Set();
7855
- for (const call of dbCalls) {
8113
+ const map = tablesByFile.get(node.id);
8114
+ if (!map) {
8115
+ if (node._dbCalls) delete node._dbCalls;
8116
+ continue;
8117
+ }
8118
+ const seenTargets = /* @__PURE__ */ new Set();
8119
+ for (const call of map.values()) {
7856
8120
  const target = call.kind === "sql" ? call.model : camelToPascal(call.model);
7857
- if (seenModels.has(target)) continue;
7858
- seenModels.add(target);
7859
- uiCrossRefs.push({
8121
+ if (seenTargets.has(target)) continue;
8122
+ seenTargets.add(target);
8123
+ const isTransitive = call.via.length > 0;
8124
+ const ref = {
7860
8125
  source: node.id,
7861
8126
  target,
7862
- type: call.isMutation ? "mutates" : "reads",
8127
+ type: call.isMutation ? isTransitive ? "mutates_via" : "mutates" : isTransitive ? "reads_via" : "reads",
7863
8128
  layer: "db"
7864
- });
8129
+ };
8130
+ if (isTransitive) ref.via = call.via;
8131
+ uiCrossRefs.push(ref);
7865
8132
  }
7866
- delete node._dbCalls;
8133
+ if (node._dbCalls) delete node._dbCalls;
7867
8134
  }
7868
8135
  uiCrossRefs.sort((a, b) => a.source.localeCompare(b.source) || a.target.localeCompare(b.target));
8136
+ const middlewareInfos = [];
8137
+ for (const conv of paths.conventionFiles) {
8138
+ if (!/middleware\.tsx?$/.test(conv)) continue;
8139
+ try {
8140
+ const info = extractMiddlewareAuthTS(conv);
8141
+ if (info && info.matchers.length > 0) middlewareInfos.push(info);
8142
+ } catch {
8143
+ }
8144
+ }
8145
+ if (middlewareInfos.length > 0) {
8146
+ let setAuth2 = function(node, newTags, replaceAll) {
8147
+ const existing = node.auth ?? [];
8148
+ const meaningful = existing.filter((a) => a !== "public");
8149
+ const merged = replaceAll ? newTags : [.../* @__PURE__ */ new Set([...newTags, ...meaningful])];
8150
+ node.auth = merged.length > 0 ? merged : ["public"];
8151
+ }, applyMiddleware2 = function(node, routePath) {
8152
+ let resolved = null;
8153
+ let label = "";
8154
+ let hasAnyExemptMatcher = false;
8155
+ let hasAnyFallthrough = false;
8156
+ for (const info of middlewareInfos) {
8157
+ if (info.hasFallthroughProtect) hasAnyFallthrough = true;
8158
+ if (info.matchers.some((m) => m.intent === "exempt")) hasAnyExemptMatcher = true;
8159
+ const c = classifyRouteAgainstMiddleware(routePath, info);
8160
+ if (!c) continue;
8161
+ if (!resolved) {
8162
+ resolved = c.intent;
8163
+ label = c.matcher;
8164
+ }
8165
+ }
8166
+ if (resolved === "exempt") {
8167
+ setAuth2(node, ["public"], true);
8168
+ return;
8169
+ }
8170
+ if (resolved === "protect") {
8171
+ setAuth2(node, [`middleware:${label}`], false);
8172
+ return;
8173
+ }
8174
+ if (hasAnyExemptMatcher && hasAnyFallthrough) {
8175
+ setAuth2(node, ["middleware-protected"], false);
8176
+ }
8177
+ };
8178
+ var setAuth = setAuth2, applyMiddleware = applyMiddleware2;
8179
+ for (const node of apiNodes) {
8180
+ const routePath = node.path;
8181
+ if (!routePath) continue;
8182
+ applyMiddleware2(node, routePath);
8183
+ }
8184
+ for (const node of uiNodes) {
8185
+ const route = node.route;
8186
+ if (!route) continue;
8187
+ applyMiddleware2(node, route);
8188
+ }
8189
+ }
7869
8190
  const apiNodeIds = new Set(apiNodes.map((n) => n.id));
7870
8191
  const apiEdges = [];
7871
8192
  const uiOnlyEdges = [];
@@ -9741,7 +10062,7 @@ var staticRefScannerParser = {
9741
10062
  },
9742
10063
  generate(rootDir, layerOutputs) {
9743
10064
  const staticOutput = layerOutputs.get("static");
9744
- if (!staticOutput || staticOutput.nodes.length === 0) {
10065
+ if (!staticOutput || !Array.isArray(staticOutput.nodes) || staticOutput.nodes.length === 0) {
9745
10066
  return { cross_refs: [], flagged_edges: [], warnings: [] };
9746
10067
  }
9747
10068
  const valueLookup = /* @__PURE__ */ new Map();
@@ -10690,6 +11011,26 @@ async function generateGraph(rootDir, layer) {
10690
11011
  invalidateCache(filePath);
10691
11012
  invalidateTaggedCache(rootDir, result.layer);
10692
11013
  }
11014
+ if (!layer) {
11015
+ const producedLayers = new Set(results.map((r) => r.layer));
11016
+ try {
11017
+ for (const f of (0, import_node_fs15.readdirSync)(dir)) {
11018
+ if (!f.endsWith(".json") || f === "tags.json") continue;
11019
+ const layerName = f.replace(/\.json$/, "");
11020
+ if (producedLayers.has(layerName)) continue;
11021
+ const orphan = (0, import_node_path16.join)(dir, f);
11022
+ try {
11023
+ (0, import_node_fs15.unlinkSync)(orphan);
11024
+ invalidateCache(orphan);
11025
+ invalidateTaggedCache(rootDir, layerName);
11026
+ process.stderr.write(`[launch-chart] removed orphan layer file: ${f} (no parser produced ${layerName} this run)
11027
+ `);
11028
+ } catch {
11029
+ }
11030
+ }
11031
+ } catch {
11032
+ }
11033
+ }
10693
11034
  return results;
10694
11035
  }
10695
11036