truecourse 0.6.6 → 0.6.7-next.1

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 (3) hide show
  1. package/cli.mjs +1324 -288
  2. package/package.json +1 -1
  3. package/server.mjs +1343 -315
package/server.mjs CHANGED
@@ -19092,13 +19092,13 @@ var require_broadcast_operator = __commonJS({
19092
19092
  * @return a Promise that will be fulfilled when all clients have acknowledged the event
19093
19093
  */
19094
19094
  emitWithAck(ev, ...args) {
19095
- return new Promise((resolve8, reject) => {
19095
+ return new Promise((resolve9, reject) => {
19096
19096
  args.push((err, responses) => {
19097
19097
  if (err) {
19098
19098
  err.responses = responses;
19099
19099
  return reject(err);
19100
19100
  } else {
19101
- return resolve8(responses);
19101
+ return resolve9(responses);
19102
19102
  }
19103
19103
  });
19104
19104
  this.emit(ev, ...args);
@@ -19452,12 +19452,12 @@ var require_socket2 = __commonJS({
19452
19452
  */
19453
19453
  emitWithAck(ev, ...args) {
19454
19454
  const withErr = this.flags.timeout !== void 0;
19455
- return new Promise((resolve8, reject) => {
19455
+ return new Promise((resolve9, reject) => {
19456
19456
  args.push((arg1, arg2) => {
19457
19457
  if (withErr) {
19458
- return arg1 ? reject(arg1) : resolve8(arg2);
19458
+ return arg1 ? reject(arg1) : resolve9(arg2);
19459
19459
  } else {
19460
- return resolve8(arg1);
19460
+ return resolve9(arg1);
19461
19461
  }
19462
19462
  });
19463
19463
  this.emit(ev, ...args);
@@ -20527,13 +20527,13 @@ var require_namespace = __commonJS({
20527
20527
  * @return a Promise that will be fulfilled when all servers have acknowledged the event
20528
20528
  */
20529
20529
  serverSideEmitWithAck(ev, ...args) {
20530
- return new Promise((resolve8, reject) => {
20530
+ return new Promise((resolve9, reject) => {
20531
20531
  args.push((err, responses) => {
20532
20532
  if (err) {
20533
20533
  err.responses = responses;
20534
20534
  return reject(err);
20535
20535
  } else {
20536
- return resolve8(responses);
20536
+ return resolve9(responses);
20537
20537
  }
20538
20538
  });
20539
20539
  this.serverSideEmit(ev, ...args);
@@ -21494,7 +21494,7 @@ var require_cluster_adapter = __commonJS({
21494
21494
  return localSockets;
21495
21495
  }
21496
21496
  const requestId = randomId();
21497
- return new Promise((resolve8, reject) => {
21497
+ return new Promise((resolve9, reject) => {
21498
21498
  const timeout = setTimeout(() => {
21499
21499
  const storedRequest2 = this.requests.get(requestId);
21500
21500
  if (storedRequest2) {
@@ -21504,7 +21504,7 @@ var require_cluster_adapter = __commonJS({
21504
21504
  }, opts.flags.timeout || DEFAULT_TIMEOUT);
21505
21505
  const storedRequest = {
21506
21506
  type: MessageType.FETCH_SOCKETS,
21507
- resolve: resolve8,
21507
+ resolve: resolve9,
21508
21508
  timeout,
21509
21509
  current: 0,
21510
21510
  expected: expectedResponseCount,
@@ -21715,7 +21715,7 @@ var require_cluster_adapter = __commonJS({
21715
21715
  return localSockets;
21716
21716
  }
21717
21717
  const requestId = randomId();
21718
- return new Promise((resolve8, reject) => {
21718
+ return new Promise((resolve9, reject) => {
21719
21719
  const timeout = setTimeout(() => {
21720
21720
  const storedRequest2 = this.customRequests.get(requestId);
21721
21721
  if (storedRequest2) {
@@ -21725,7 +21725,7 @@ var require_cluster_adapter = __commonJS({
21725
21725
  }, opts.flags.timeout || DEFAULT_TIMEOUT);
21726
21726
  const storedRequest = {
21727
21727
  type: MessageType.FETCH_SOCKETS,
21728
- resolve: resolve8,
21728
+ resolve: resolve9,
21729
21729
  timeout,
21730
21730
  missingUids: /* @__PURE__ */ new Set([...this.nodesMap.keys()]),
21731
21731
  responses: localSockets
@@ -22552,13 +22552,13 @@ var require_dist2 = __commonJS({
22552
22552
  this.engine.close();
22553
22553
  (0, uws_1.restoreAdapter)();
22554
22554
  if (this.httpServer) {
22555
- return new Promise((resolve8) => {
22555
+ return new Promise((resolve9) => {
22556
22556
  this.httpServer.close((err) => {
22557
22557
  fn && fn(err);
22558
22558
  if (err) {
22559
22559
  debug2("server was not running");
22560
22560
  }
22561
- resolve8();
22561
+ resolve9();
22562
22562
  });
22563
22563
  });
22564
22564
  } else {
@@ -22914,7 +22914,7 @@ function popLogger() {
22914
22914
  async function closeLogger() {
22915
22915
  while (stack.length > 0) {
22916
22916
  const sink = stack.pop();
22917
- await new Promise((resolve8) => sink.stream.end(resolve8));
22917
+ await new Promise((resolve9) => sink.stream.end(resolve9));
22918
22918
  }
22919
22919
  }
22920
22920
  function currentSink() {
@@ -28154,6 +28154,12 @@ function loadTcIgnore(startDir) {
28154
28154
  if (rel === "" || rel.startsWith(".."))
28155
28155
  return false;
28156
28156
  return ig.ignores(rel);
28157
+ },
28158
+ reincludes(absPath) {
28159
+ const rel = path3.relative(root, path3.resolve(absPath)).split(path3.sep).join("/");
28160
+ if (rel === "" || rel.startsWith(".."))
28161
+ return false;
28162
+ return ig.test(rel).unignored;
28157
28163
  }
28158
28164
  };
28159
28165
  }
@@ -33100,10 +33106,10 @@ var require_raw_body = __commonJS({
33100
33106
  if (done) {
33101
33107
  return readStream(stream, encoding, length, limit, wrap(done));
33102
33108
  }
33103
- return new Promise(function executor(resolve8, reject) {
33109
+ return new Promise(function executor(resolve9, reject) {
33104
33110
  readStream(stream, encoding, length, limit, function onRead(err, buf) {
33105
33111
  if (err) return reject(err);
33106
- resolve8(buf);
33112
+ resolve9(buf);
33107
33113
  });
33108
33114
  });
33109
33115
  }
@@ -37941,7 +37947,7 @@ var require_view = __commonJS({
37941
37947
  var basename2 = path52.basename;
37942
37948
  var extname = path52.extname;
37943
37949
  var join9 = path52.join;
37944
- var resolve8 = path52.resolve;
37950
+ var resolve9 = path52.resolve;
37945
37951
  module.exports = View;
37946
37952
  function View(name, options) {
37947
37953
  var opts = options || {};
@@ -37975,7 +37981,7 @@ var require_view = __commonJS({
37975
37981
  debug2('lookup "%s"', name);
37976
37982
  for (var i = 0; i < roots.length && !path53; i++) {
37977
37983
  var root = roots[i];
37978
- var loc = resolve8(root, name);
37984
+ var loc = resolve9(root, name);
37979
37985
  var dir = dirname8(loc);
37980
37986
  var file = basename2(loc);
37981
37987
  path53 = this.resolve(dir, file);
@@ -37986,7 +37992,7 @@ var require_view = __commonJS({
37986
37992
  debug2('render "%s"', this.path);
37987
37993
  this.engine(this.path, options, callback);
37988
37994
  };
37989
- View.prototype.resolve = function resolve9(dir, file) {
37995
+ View.prototype.resolve = function resolve10(dir, file) {
37990
37996
  var ext2 = this.ext;
37991
37997
  var path53 = join9(dir, file);
37992
37998
  var stat = tryStat(path53);
@@ -38512,7 +38518,7 @@ var require_send = __commonJS({
38512
38518
  var extname = path52.extname;
38513
38519
  var join9 = path52.join;
38514
38520
  var normalize2 = path52.normalize;
38515
- var resolve8 = path52.resolve;
38521
+ var resolve9 = path52.resolve;
38516
38522
  var sep3 = path52.sep;
38517
38523
  var BYTES_RANGE_REGEXP = /^ *bytes=/;
38518
38524
  var MAX_MAXAGE = 60 * 60 * 24 * 365 * 1e3;
@@ -38549,7 +38555,7 @@ var require_send = __commonJS({
38549
38555
  this._maxage = opts.maxAge || opts.maxage;
38550
38556
  this._maxage = typeof this._maxage === "string" ? ms(this._maxage) : Number(this._maxage);
38551
38557
  this._maxage = !isNaN(this._maxage) ? Math.min(Math.max(0, this._maxage), MAX_MAXAGE) : 0;
38552
- this._root = opts.root ? resolve8(opts.root) : null;
38558
+ this._root = opts.root ? resolve9(opts.root) : null;
38553
38559
  if (!this._root && opts.from) {
38554
38560
  this.from(opts.from);
38555
38561
  }
@@ -38573,7 +38579,7 @@ var require_send = __commonJS({
38573
38579
  return this;
38574
38580
  }, "send.index: pass index as option");
38575
38581
  SendStream.prototype.root = function root(path53) {
38576
- this._root = resolve8(String(path53));
38582
+ this._root = resolve9(String(path53));
38577
38583
  debug2("root %s", this._root);
38578
38584
  return this;
38579
38585
  };
@@ -38737,7 +38743,7 @@ var require_send = __commonJS({
38737
38743
  return res;
38738
38744
  }
38739
38745
  parts = normalize2(path53).split(sep3);
38740
- path53 = resolve8(path53);
38746
+ path53 = resolve9(path53);
38741
38747
  }
38742
38748
  if (containsDotFile(parts)) {
38743
38749
  var access = this._dotfiles;
@@ -40016,7 +40022,7 @@ var require_application = __commonJS({
40016
40022
  var deprecate = require_depd()("express");
40017
40023
  var flatten = require_array_flatten();
40018
40024
  var merge = require_utils_merge();
40019
- var resolve8 = __require("path").resolve;
40025
+ var resolve9 = __require("path").resolve;
40020
40026
  var setPrototypeOf = require_setprototypeof();
40021
40027
  var hasOwnProperty = Object.prototype.hasOwnProperty;
40022
40028
  var slice3 = Array.prototype.slice;
@@ -40055,7 +40061,7 @@ var require_application = __commonJS({
40055
40061
  this.mountpath = "/";
40056
40062
  this.locals.settings = this.settings;
40057
40063
  this.set("view", View);
40058
- this.set("views", resolve8("views"));
40064
+ this.set("views", resolve9("views"));
40059
40065
  this.set("jsonp callback name", "callback");
40060
40066
  if (env === "production") {
40061
40067
  this.enable("view cache");
@@ -40483,7 +40489,7 @@ var require_response = __commonJS({
40483
40489
  var send = require_send();
40484
40490
  var extname = path52.extname;
40485
40491
  var mime = send.mime;
40486
- var resolve8 = path52.resolve;
40492
+ var resolve9 = path52.resolve;
40487
40493
  var vary = require_vary();
40488
40494
  var res = Object.create(http.ServerResponse.prototype);
40489
40495
  module.exports = res;
@@ -40742,7 +40748,7 @@ var require_response = __commonJS({
40742
40748
  }
40743
40749
  opts = Object.create(opts);
40744
40750
  opts.headers = headers;
40745
- var fullPath = !opts.root ? resolve8(path53) : path53;
40751
+ var fullPath = !opts.root ? resolve9(path53) : path53;
40746
40752
  return this.sendFile(fullPath, opts, done);
40747
40753
  };
40748
40754
  res.contentType = res.type = function contentType(type) {
@@ -41008,7 +41014,7 @@ var require_serve_static = __commonJS({
41008
41014
  var encodeUrl = require_encodeurl();
41009
41015
  var escapeHtml = require_escape_html();
41010
41016
  var parseUrl = require_parseurl();
41011
- var resolve8 = __require("path").resolve;
41017
+ var resolve9 = __require("path").resolve;
41012
41018
  var send = require_send();
41013
41019
  var url = __require("url");
41014
41020
  module.exports = serveStatic;
@@ -41028,7 +41034,7 @@ var require_serve_static = __commonJS({
41028
41034
  throw new TypeError("option setHeaders must be function");
41029
41035
  }
41030
41036
  opts.maxage = opts.maxage || opts.maxAge || 0;
41031
- opts.root = resolve8(root);
41037
+ opts.root = resolve9(root);
41032
41038
  var onDirectory = redirect ? createRedirectDirectoryListener() : createNotFoundDirectoryListener();
41033
41039
  return function serveStatic2(req, res, next) {
41034
41040
  if (req.method !== "GET" && req.method !== "HEAD") {
@@ -41762,6 +41768,14 @@ function parseFile(filePath, code, language) {
41762
41768
  throw new Error(`Failed to parse file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
41763
41769
  }
41764
41770
  }
41771
+ function withParsedTree(filePath, code, language, use) {
41772
+ const tree = parseFile(filePath, code, language);
41773
+ try {
41774
+ return use(tree);
41775
+ } finally {
41776
+ tree.delete();
41777
+ }
41778
+ }
41765
41779
  var _require, BUNDLED_WASM_DIR, GRAMMAR_WASM, languageCache, parserCache, initPromise, initialized;
41766
41780
  var init_parser = __esm({
41767
41781
  "packages/analyzer/dist/parser.js"() {
@@ -44621,53 +44635,15 @@ async function analyzeFile(filePath) {
44621
44635
  }
44622
44636
  try {
44623
44637
  const content = await readFile(filePath, "utf-8");
44624
- const tree = parseFile(filePath, content, language);
44625
- let functions, classes, imports, exports;
44626
- switch (language) {
44627
- case "typescript":
44628
- case "tsx":
44629
- functions = extractTypeScriptFunctions(tree, filePath, language);
44630
- classes = extractTypeScriptClasses(tree, filePath, language);
44631
- imports = extractTypeScriptImports(tree, filePath, language);
44632
- exports = extractTypeScriptExports(tree, filePath, language);
44633
- break;
44634
- case "javascript":
44635
- functions = extractJavaScriptFunctions(tree, filePath);
44636
- classes = extractJavaScriptClasses(tree, filePath);
44637
- imports = extractJavaScriptImports(tree, filePath);
44638
- exports = extractJavaScriptExports(tree, filePath);
44639
- break;
44640
- case "python":
44641
- functions = extractPythonFunctions(tree, filePath);
44642
- classes = extractPythonClasses(tree, filePath);
44643
- imports = extractPythonImports(tree, filePath);
44644
- exports = extractPythonExports(tree, filePath);
44645
- break;
44646
- default:
44647
- throw new Error(`Unsupported language: ${language}`);
44648
- }
44649
- const functionContext = buildFunctionContext(functions, classes);
44650
- const calls = extractCalls(tree, filePath, language, functionContext);
44651
- const httpCalls = extractHttpCalls(tree, filePath, language, functions, classes);
44652
- const { routes: routeRegistrations, mounts: routerMounts } = extractRouteRegistrations(tree, filePath, language);
44653
- return {
44654
- filePath,
44655
- language,
44656
- functions,
44657
- classes,
44658
- imports,
44659
- exports,
44660
- calls,
44661
- httpCalls,
44662
- ...routeRegistrations.length > 0 ? { routeRegistrations } : {},
44663
- ...routerMounts.length > 0 ? { routerMounts } : {}
44664
- };
44638
+ return withParsedTree(filePath, content, language, (tree) => buildFileAnalysis(tree, filePath, language));
44665
44639
  } catch (error) {
44666
44640
  throw new Error(`Failed to analyze file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
44667
44641
  }
44668
44642
  }
44669
44643
  function analyzeFileContent(filePath, content, language) {
44670
- const tree = parseFile(filePath, content, language);
44644
+ return withParsedTree(filePath, content, language, (tree) => buildFileAnalysis(tree, filePath, language));
44645
+ }
44646
+ function buildFileAnalysis(tree, filePath, language) {
44671
44647
  let functions, classes, imports, exports;
44672
44648
  switch (language) {
44673
44649
  case "typescript":
@@ -44683,6 +44659,12 @@ function analyzeFileContent(filePath, content, language) {
44683
44659
  imports = extractJavaScriptImports(tree, filePath);
44684
44660
  exports = extractJavaScriptExports(tree, filePath);
44685
44661
  break;
44662
+ case "python":
44663
+ functions = extractPythonFunctions(tree, filePath);
44664
+ classes = extractPythonClasses(tree, filePath);
44665
+ imports = extractPythonImports(tree, filePath);
44666
+ exports = extractPythonExports(tree, filePath);
44667
+ break;
44686
44668
  default:
44687
44669
  throw new Error(`Unsupported language: ${language}`);
44688
44670
  }
@@ -49398,11 +49380,6 @@ var init_entities = __esm({
49398
49380
  init_patterns();
49399
49381
  init_parser();
49400
49382
  TypeScriptEntityDetector = class {
49401
- // Lazy: do not call getParser() at construction time — callers may
49402
- // instantiate this before initParsers() has completed.
49403
- get parser() {
49404
- return getParser("typescript");
49405
- }
49406
49383
  shouldScanFile(filePath) {
49407
49384
  if (!/\.(ts|tsx|js|jsx)$/.test(filePath)) {
49408
49385
  return false;
@@ -49417,25 +49394,24 @@ var init_entities = __esm({
49417
49394
  return hasEntityDir || hasEntityPattern;
49418
49395
  }
49419
49396
  detectEntities(sourceCode, filePath, service) {
49420
- const tree = this.parser.parse(sourceCode);
49421
- const entities = [];
49422
- if (!tree)
49423
- return entities;
49424
- const classNodes = this.findClassDeclarations(tree.rootNode);
49425
- for (const classNode of classNodes) {
49426
- const entity = this.detectEntityFromClass(classNode, sourceCode, filePath, service);
49427
- if (entity) {
49428
- entities.push(entity);
49397
+ return withParsedTree(filePath, sourceCode, "typescript", (tree) => {
49398
+ const entities = [];
49399
+ const classNodes = this.findClassDeclarations(tree.rootNode);
49400
+ for (const classNode of classNodes) {
49401
+ const entity = this.detectEntityFromClass(classNode, sourceCode, filePath, service);
49402
+ if (entity) {
49403
+ entities.push(entity);
49404
+ }
49429
49405
  }
49430
- }
49431
- const interfaceNodes = this.findInterfaceDeclarations(tree.rootNode);
49432
- for (const interfaceNode of interfaceNodes) {
49433
- const entity = this.detectEntityFromInterface(interfaceNode, sourceCode, filePath, service);
49434
- if (entity) {
49435
- entities.push(entity);
49406
+ const interfaceNodes = this.findInterfaceDeclarations(tree.rootNode);
49407
+ for (const interfaceNode of interfaceNodes) {
49408
+ const entity = this.detectEntityFromInterface(interfaceNode, sourceCode, filePath, service);
49409
+ if (entity) {
49410
+ entities.push(entity);
49411
+ }
49436
49412
  }
49437
- }
49438
- return entities;
49413
+ return entities;
49414
+ });
49439
49415
  }
49440
49416
  findClassDeclarations(node2) {
49441
49417
  const classes = [];
@@ -49803,14 +49779,14 @@ var init_lsp_client = __esm({
49803
49779
  this.sendNotification("exit", null);
49804
49780
  } catch {
49805
49781
  }
49806
- await new Promise((resolve8) => {
49782
+ await new Promise((resolve9) => {
49807
49783
  const timeout = setTimeout(() => {
49808
49784
  this.process?.kill("SIGKILL");
49809
- resolve8();
49785
+ resolve9();
49810
49786
  }, 2e3);
49811
49787
  this.process.on("exit", () => {
49812
49788
  clearTimeout(timeout);
49813
- resolve8();
49789
+ resolve9();
49814
49790
  });
49815
49791
  });
49816
49792
  this.process = null;
@@ -49953,9 +49929,9 @@ var init_lsp_client = __esm({
49953
49929
  // Internal: JSON-RPC transport
49954
49930
  // -------------------------------------------------------------------------
49955
49931
  sendRequest(method, params) {
49956
- return new Promise((resolve8, reject) => {
49932
+ return new Promise((resolve9, reject) => {
49957
49933
  const id = ++this.requestId;
49958
- this.pendingRequests.set(id, { resolve: resolve8, reject });
49934
+ this.pendingRequests.set(id, { resolve: resolve9, reject });
49959
49935
  const msg = { jsonrpc: "2.0", id, method, params };
49960
49936
  this.process.stdin.write(encodeMessage(msg));
49961
49937
  });
@@ -50012,7 +49988,7 @@ var init_lsp_client = __esm({
50012
49988
  */
50013
49989
  waitForDiagnostics(fileCount) {
50014
49990
  const waitMs = Math.min(Math.max(fileCount * 100, 1e3), 1e4);
50015
- return new Promise((resolve8) => setTimeout(resolve8, waitMs));
49991
+ return new Promise((resolve9) => setTimeout(resolve9, waitMs));
50016
49992
  }
50017
49993
  };
50018
49994
  }
@@ -78018,6 +77994,9 @@ var init_no_self_compare = __esm({
78018
77994
  });
78019
77995
 
78020
77996
  // packages/analyzer/dist/rules/bugs/visitors/javascript/duplicate-class-members.js
77997
+ function reportDuplicate(ruleKey, child, filePath, sourceCode, name) {
77998
+ return makeViolation(ruleKey, child, filePath, "high", "Duplicate class member", `Member \`${name}\` is defined more than once \u2014 the later definition silently overwrites the earlier one.`, sourceCode, "Remove the duplicate member or rename one of them.");
77999
+ }
78021
78000
  var duplicateClassMembersVisitor;
78022
78001
  var init_duplicate_class_members = __esm({
78023
78002
  "packages/analyzer/dist/rules/bugs/visitors/javascript/duplicate-class-members.js"() {
@@ -78031,18 +78010,37 @@ var init_duplicate_class_members = __esm({
78031
78010
  visit(node2, filePath, sourceCode) {
78032
78011
  const seen = /* @__PURE__ */ new Map();
78033
78012
  for (const child of node2.namedChildren) {
78034
- let name = null;
78035
- if (child.type === "method_definition" || child.type === "public_field_definition" || child.type === "field_definition") {
78036
- const nameNode = child.childForFieldName("name");
78037
- if (nameNode)
78038
- name = nameNode.text;
78013
+ if (child.type !== "method_definition" && child.type !== "public_field_definition" && child.type !== "field_definition")
78014
+ continue;
78015
+ const nameNode = child.childForFieldName("name");
78016
+ if (!nameNode)
78017
+ continue;
78018
+ const name = nameNode.text;
78019
+ let isStatic = false;
78020
+ let accessor = null;
78021
+ for (let i = 0; i < child.childCount; i++) {
78022
+ const c = child.child(i);
78023
+ if (!c)
78024
+ continue;
78025
+ if (c.type === "static")
78026
+ isStatic = true;
78027
+ else if (c.type === "get")
78028
+ accessor = "get";
78029
+ else if (c.type === "set")
78030
+ accessor = "set";
78031
+ }
78032
+ const kind = accessor ?? (child.type === "method_definition" ? "method" : "field");
78033
+ const key2 = `${isStatic ? "static" : "instance"}:${name}`;
78034
+ const existing = seen.get(key2);
78035
+ if (!existing) {
78036
+ seen.set(key2, { kinds: /* @__PURE__ */ new Set([kind]) });
78037
+ continue;
78039
78038
  }
78040
- if (name) {
78041
- if (seen.has(name)) {
78042
- return makeViolation(this.ruleKey, child, filePath, "high", "Duplicate class member", `Member \`${name}\` is defined more than once \u2014 the later definition silently overwrites the earlier one.`, sourceCode, "Remove the duplicate member or rename one of them.");
78043
- }
78044
- seen.set(name, child);
78039
+ const isGetSetPair = existing.kinds.size === 1 && existing.kinds.has("get") && kind === "set" || existing.kinds.size === 1 && existing.kinds.has("set") && kind === "get";
78040
+ if (!isGetSetPair) {
78041
+ return reportDuplicate(this.ruleKey, child, filePath, sourceCode, name);
78045
78042
  }
78043
+ existing.kinds.add(kind);
78046
78044
  }
78047
78045
  return null;
78048
78046
  }
@@ -80389,7 +80387,9 @@ var init_element_overwrite = __esm({
80389
80387
  hasAwait2 = true;
80390
80388
  }
80391
80389
  const isRefCurrent = left.type === "member_expression" && indexOrProp.text === "current";
80392
- if (!wasRead && !(isRefCurrent && hasAwait2)) {
80390
+ const right = expr.childForFieldName("right");
80391
+ const readsSelfInRhs = right ? right.text.includes(left.text) : false;
80392
+ if (!wasRead && !readsSelfInRhs && !(isRefCurrent && hasAwait2)) {
80393
80393
  return makeViolation(this.ruleKey, expr, filePath, "high", "Element overwritten before read", `\`${key2}\` is assigned again before being read \u2014 the first assignment has no effect.`, sourceCode, "Remove the first assignment or use the value before overwriting it.");
80394
80394
  }
80395
80395
  }
@@ -81753,6 +81753,17 @@ var init_promise_executor_return = __esm({
81753
81753
  });
81754
81754
 
81755
81755
  // packages/analyzer/dist/rules/bugs/visitors/javascript/empty-pattern.js
81756
+ function isFunctionParameter(node2) {
81757
+ const parent = node2.parent;
81758
+ if (!parent)
81759
+ return false;
81760
+ if (parent.type === "formal_parameters")
81761
+ return true;
81762
+ if (parent.type === "required_parameter" || parent.type === "optional_parameter") {
81763
+ return parent.parent?.type === "formal_parameters";
81764
+ }
81765
+ return false;
81766
+ }
81756
81767
  var emptyPatternVisitor;
81757
81768
  var init_empty_pattern = __esm({
81758
81769
  "packages/analyzer/dist/rules/bugs/visitors/javascript/empty-pattern.js"() {
@@ -81766,6 +81777,8 @@ var init_empty_pattern = __esm({
81766
81777
  visit(node2, filePath, sourceCode) {
81767
81778
  const bindings = node2.namedChildren.filter((c) => c.type !== "comment");
81768
81779
  if (bindings.length === 0) {
81780
+ if (node2.type === "object_pattern" && isFunctionParameter(node2))
81781
+ return null;
81769
81782
  const kind = node2.type === "object_pattern" ? "{}" : "[]";
81770
81783
  return makeViolation(this.ruleKey, node2, filePath, "medium", "Empty destructuring pattern", `Empty destructuring pattern \`${kind}\` does not bind any variables.`, sourceCode, "Add variable bindings to the destructuring pattern or remove it entirely.");
81771
81784
  }
@@ -83317,6 +83330,15 @@ var init_contradictory_non_null_coalescing = __esm({
83317
83330
  });
83318
83331
 
83319
83332
  // packages/analyzer/dist/rules/bugs/visitors/javascript/empty-object-type.js
83333
+ function isReactPropsParameter(typeAnnotation, filePath) {
83334
+ if (!/\.(tsx|jsx)$/i.test(filePath))
83335
+ return false;
83336
+ const param = typeAnnotation.parent;
83337
+ if (param?.type !== "required_parameter" && param?.type !== "optional_parameter")
83338
+ return false;
83339
+ const pattern = param.childForFieldName("pattern");
83340
+ return pattern?.text === "props";
83341
+ }
83320
83342
  var emptyObjectTypeVisitor;
83321
83343
  var init_empty_object_type = __esm({
83322
83344
  "packages/analyzer/dist/rules/bugs/visitors/javascript/empty-object-type.js"() {
@@ -83341,7 +83363,7 @@ var init_empty_object_type = __esm({
83341
83363
  };
83342
83364
  if (node2.type === "type_annotation") {
83343
83365
  const emptyObj = checkForEmptyObject(node2);
83344
- if (emptyObj) {
83366
+ if (emptyObj && !isReactPropsParameter(node2, filePath)) {
83345
83367
  return makeViolation(this.ruleKey, emptyObj, filePath, "high", "Empty object type {}", "`{}` type matches everything except `null` and `undefined` \u2014 this is rarely intentional. Use `object` for non-primitive objects or `Record<string, unknown>` for generic objects.", sourceCode, "Replace `{}` with `object`, `Record<string, unknown>`, or a specific type.");
83346
83368
  }
83347
83369
  }
@@ -99934,6 +99956,12 @@ var init_prefer_immediate_return = __esm({
99934
99956
  const nameNode = decl.childForFieldName("name");
99935
99957
  if (nameNode?.text !== retName)
99936
99958
  return null;
99959
+ const valueNode = decl.childForFieldName("value");
99960
+ if (valueNode) {
99961
+ const lineSpan = valueNode.endPosition.row - valueNode.startPosition.row + 1;
99962
+ if (lineSpan > 10)
99963
+ return null;
99964
+ }
99937
99965
  let usageCount = 0;
99938
99966
  function countUsages2(n) {
99939
99967
  if (n.type === "identifier" && n.text === retName) {
@@ -102079,6 +102107,14 @@ function isDirectCallbackArg(fn) {
102079
102107
  p = p.parent;
102080
102108
  return p?.type === "arguments" ? p : null;
102081
102109
  }
102110
+ function isTrivialCallback(fn) {
102111
+ const body = fn.childForFieldName("body");
102112
+ if (!body)
102113
+ return false;
102114
+ if (body.type !== "statement_block")
102115
+ return true;
102116
+ return body.namedChildren.length === 0;
102117
+ }
102082
102118
  function findEnclosingFn(start) {
102083
102119
  let n = start;
102084
102120
  while (n) {
@@ -102100,6 +102136,8 @@ var init_deep_callback_nesting = __esm({
102100
102136
  languages: ["typescript", "tsx", "javascript"],
102101
102137
  nodeTypes: ["function_expression", "arrow_function"],
102102
102138
  visit(node2, filePath, sourceCode) {
102139
+ if (isTrivialCallback(node2))
102140
+ return null;
102103
102141
  let depth = 0;
102104
102142
  let current = node2;
102105
102143
  while (true) {
@@ -102218,6 +102256,10 @@ var init_default_parameter_position = __esm({
102218
102256
  const isOptional = param.type === "optional_parameter" || param.text.includes("?");
102219
102257
  if (isOptional)
102220
102258
  continue;
102259
+ const patternNode = param.childForFieldName("pattern") ?? param.namedChildren[0];
102260
+ const isDestructured = param.type === "object_pattern" || param.type === "array_pattern" || patternNode?.type === "object_pattern" || patternNode?.type === "array_pattern";
102261
+ if (isDestructured)
102262
+ continue;
102221
102263
  const nameNode = param.childForFieldName("pattern") ?? param.childForFieldName("name") ?? param.namedChildren[0];
102222
102264
  const name = nameNode?.text ?? "parameter";
102223
102265
  return makeViolation(this.ruleKey, param, filePath, "low", "Default parameter not last", `Required parameter \`${name}\` appears after a default parameter. Default parameters should come last.`, sourceCode, "Move default parameters to the end of the parameter list.");
@@ -102963,7 +103005,8 @@ var init_public_static_readonly = __esm({
102963
103005
  const isStatic = member.children.some((c) => c.type === "static");
102964
103006
  const isPublic = !member.children.some((c) => c.type === "accessibility_modifier" && (c.text === "private" || c.text === "protected"));
102965
103007
  const isReadonly = member.children.some((c) => c.type === "readonly");
102966
- if (isStatic && isPublic && !isReadonly) {
103008
+ const hasInitializer = member.childForFieldName("value") != null;
103009
+ if (isStatic && isPublic && !isReadonly && hasInitializer) {
102967
103010
  const nameNode = member.childForFieldName("name");
102968
103011
  const name = nameNode?.text ?? "field";
102969
103012
  return makeViolation(this.ruleKey, member, filePath, "medium", "Mutable public static field", `Public static field \`${name}\` is not \`readonly\`. Static public fields that are constants should be readonly.`, sourceCode, `Add the \`readonly\` modifier: \`public static readonly ${name}\`.`);
@@ -108414,11 +108457,18 @@ var init_confusing_void_expression = __esm({
108414
108457
  }
108415
108458
  parent = parent.parent;
108416
108459
  }
108417
- if (containingFn?.type === "arrow_function") {
108460
+ const isContainingFnVoid = () => {
108461
+ if (!containingFn)
108462
+ return false;
108418
108463
  const fnReturnType = typeQuery.getReturnType(filePath, containingFn.startPosition.row, containingFn.startPosition.column, containingFn.endPosition.row, containingFn.endPosition.column);
108419
- if (fnReturnType && /^(void|undefined|Promise<\s*(void|undefined)\s*>)$/.test(fnReturnType)) {
108464
+ return !!fnReturnType && /^(void|undefined|Promise<\s*(void|undefined)\s*>)$/.test(fnReturnType);
108465
+ };
108466
+ if (containingFn?.type === "arrow_function") {
108467
+ if (isContainingFnVoid())
108468
+ return null;
108469
+ } else if (value.type === "await_expression" && (containingFn?.type === "function_declaration" || containingFn?.type === "function_expression" || containingFn?.type === "method_definition" || containingFn?.type === "function")) {
108470
+ if (isContainingFnVoid())
108420
108471
  return null;
108421
- }
108422
108472
  }
108423
108473
  return makeViolation(this.ruleKey, node2, filePath, "low", "Returning void expression", `Returning a void expression \u2014 \`${value.text.slice(0, 40)}\` returns \`undefined\`. This is confusing because it looks like the return value matters.`, sourceCode, "Put the expression on its own line and use a bare `return` statement.");
108424
108474
  }
@@ -108604,6 +108654,32 @@ var init_unnecessary_type_parameter = __esm({
108604
108654
  });
108605
108655
 
108606
108656
  // packages/analyzer/dist/rules/code-quality/visitors/javascript/prefer-this-return-type.js
108657
+ function collectMethodReturns(body) {
108658
+ const returns = [];
108659
+ const NESTED_SCOPES = /* @__PURE__ */ new Set([
108660
+ "function_declaration",
108661
+ "function_expression",
108662
+ "function",
108663
+ "arrow_function",
108664
+ "generator_function",
108665
+ "generator_function_declaration",
108666
+ "method_definition",
108667
+ "class_declaration",
108668
+ "class"
108669
+ ]);
108670
+ const walk11 = (n) => {
108671
+ for (const child of n.namedChildren) {
108672
+ if (child.type === "return_statement") {
108673
+ returns.push(child);
108674
+ }
108675
+ if (!NESTED_SCOPES.has(child.type)) {
108676
+ walk11(child);
108677
+ }
108678
+ }
108679
+ };
108680
+ walk11(body);
108681
+ return returns;
108682
+ }
108607
108683
  function findEnclosingClass(node2) {
108608
108684
  let current = node2.parent;
108609
108685
  while (current) {
@@ -108656,6 +108732,23 @@ var init_prefer_this_return_type = __esm({
108656
108732
  if (!typeText)
108657
108733
  return null;
108658
108734
  if (typeText === className) {
108735
+ const body = node2.childForFieldName("body");
108736
+ if (!body)
108737
+ return null;
108738
+ const returns = collectMethodReturns(body);
108739
+ if (returns.length === 0)
108740
+ return null;
108741
+ let returnsThis = false;
108742
+ for (const ret of returns) {
108743
+ const arg = ret.namedChildren[0];
108744
+ if (arg && arg.type === "this") {
108745
+ returnsThis = true;
108746
+ } else {
108747
+ return null;
108748
+ }
108749
+ }
108750
+ if (!returnsThis)
108751
+ return null;
108659
108752
  return makeViolation(this.ruleKey, returnTypeNode, filePath, "low", "Method should return `this` instead of class name", `Method \`${methodName}\` returns \`${className}\` but should return \`this\` to support subclass chaining.`, sourceCode, `Change the return type from \`${className}\` to \`this\`.`);
108660
108753
  }
108661
108754
  return null;
@@ -122403,12 +122496,32 @@ var init_regex_in_loop = __esm({
122403
122496
  });
122404
122497
 
122405
122498
  // packages/analyzer/dist/rules/performance/visitors/javascript/spread-in-reduce.js
122406
- function containsSpread(node2) {
122407
- if (node2.type === "spread_element")
122408
- return true;
122499
+ function getAccumulatorName(callback) {
122500
+ if (callback.type !== "arrow_function" && callback.type !== "function_expression" && callback.type !== "function") {
122501
+ return null;
122502
+ }
122503
+ const params = callback.childForFieldName("parameters");
122504
+ if (!params)
122505
+ return null;
122506
+ const first2 = params.namedChildren.find((c) => c.type !== "comment");
122507
+ if (!first2)
122508
+ return null;
122509
+ if (first2.type === "identifier")
122510
+ return first2.text;
122511
+ const pattern = first2.childForFieldName("pattern");
122512
+ if (pattern && pattern.type === "identifier")
122513
+ return pattern.text;
122514
+ return null;
122515
+ }
122516
+ function spreadsIdentifier(node2, name) {
122517
+ if (node2.type === "spread_element") {
122518
+ const arg = node2.namedChildren[0];
122519
+ if (arg && arg.type === "identifier" && arg.text === name)
122520
+ return true;
122521
+ }
122409
122522
  for (let i = 0; i < node2.childCount; i++) {
122410
122523
  const child = node2.child(i);
122411
- if (child && containsSpread(child))
122524
+ if (child && spreadsIdentifier(child, name))
122412
122525
  return true;
122413
122526
  }
122414
122527
  return false;
@@ -122435,7 +122548,10 @@ var init_spread_in_reduce = __esm({
122435
122548
  const callback = args.namedChildren[0];
122436
122549
  if (!callback)
122437
122550
  return null;
122438
- if (containsSpread(callback)) {
122551
+ const accName = getAccumulatorName(callback);
122552
+ if (!accName)
122553
+ return null;
122554
+ if (spreadsIdentifier(callback, accName)) {
122439
122555
  return makeViolation(this.ruleKey, node2, filePath, "medium", "Spread operator in reduce callback", "Using spread in a reduce callback creates a new copy on every iteration, resulting in O(n^2) time complexity.", sourceCode, "Use Object.assign() or direct mutation of the accumulator instead of spread.");
122440
122556
  }
122441
122557
  return null;
@@ -122505,6 +122621,8 @@ var init_sync_fs_in_request_handler = __esm({
122505
122621
  return null;
122506
122622
  if (SEED_FILE_PATH_PATTERN2.test(filePath))
122507
122623
  return null;
122624
+ if (sourceCode.startsWith("#!"))
122625
+ return null;
122508
122626
  if (!isInsideAsyncFunctionOrHandler(node2))
122509
122627
  return null;
122510
122628
  const enclosingName = findEnclosingFunctionName(node2);
@@ -122685,6 +122803,100 @@ var init_large_bundle_import = __esm({
122685
122803
  });
122686
122804
 
122687
122805
  // packages/analyzer/dist/rules/performance/visitors/javascript/json-parse-in-loop.js
122806
+ function isDynamicPerIteration(arg, callNode) {
122807
+ switch (arg.type) {
122808
+ case "parenthesized_expression": {
122809
+ const inner = arg.namedChildren[0];
122810
+ return inner ? isDynamicPerIteration(inner, callNode) : false;
122811
+ }
122812
+ case "call_expression":
122813
+ case "subscript_expression":
122814
+ case "template_string":
122815
+ return true;
122816
+ case "ternary_expression": {
122817
+ const cons = arg.childForFieldName("consequence");
122818
+ const alt = arg.childForFieldName("alternative");
122819
+ return !!cons && isDynamicPerIteration(cons, callNode) || !!alt && isDynamicPerIteration(alt, callNode);
122820
+ }
122821
+ case "binary_expression": {
122822
+ const left = arg.childForFieldName("left");
122823
+ const right = arg.childForFieldName("right");
122824
+ return !!left && isDynamicPerIteration(left, callNode) || !!right && isDynamicPerIteration(right, callNode);
122825
+ }
122826
+ case "identifier":
122827
+ return isLoopBoundIdentifier(arg.text, callNode);
122828
+ case "member_expression": {
122829
+ const inner = arg.childForFieldName("object");
122830
+ if (!inner)
122831
+ return false;
122832
+ if (inner.type === "identifier")
122833
+ return isLoopBoundIdentifier(inner.text, callNode);
122834
+ return isDynamicPerIteration(inner, callNode);
122835
+ }
122836
+ default:
122837
+ return false;
122838
+ }
122839
+ }
122840
+ function isLoopBoundIdentifier(varName, callNode) {
122841
+ let current = callNode.parent;
122842
+ let innermostLoop = null;
122843
+ while (current) {
122844
+ if (current.type === "for_in_statement" || current.type === "for_of_statement") {
122845
+ const left = current.childForFieldName("left");
122846
+ if (left && containsIdentifierExact(left, varName))
122847
+ return true;
122848
+ if (!innermostLoop)
122849
+ innermostLoop = current;
122850
+ }
122851
+ if (current.type === "for_statement") {
122852
+ const init = current.childForFieldName("initializer");
122853
+ if (init && containsIdentifierExact(init, varName))
122854
+ return true;
122855
+ if (!innermostLoop)
122856
+ innermostLoop = current;
122857
+ }
122858
+ if (current.type === "while_statement" || current.type === "do_statement") {
122859
+ if (!innermostLoop)
122860
+ innermostLoop = current;
122861
+ }
122862
+ if (current.type === "arrow_function" || current.type === "function_expression" || current.type === "function") {
122863
+ const params = current.childForFieldName("parameters");
122864
+ if (params && containsIdentifierExact(params, varName)) {
122865
+ const callParent = current.parent?.parent;
122866
+ if (callParent?.type === "call_expression")
122867
+ return true;
122868
+ }
122869
+ }
122870
+ current = current.parent;
122871
+ }
122872
+ if (innermostLoop) {
122873
+ const body = innermostLoop.childForFieldName("body");
122874
+ if (body && containsBindingInBlock(body, varName))
122875
+ return true;
122876
+ }
122877
+ return false;
122878
+ }
122879
+ function containsBindingInBlock(node2, varName) {
122880
+ if (node2.type === "lexical_declaration" || node2.type === "variable_declaration") {
122881
+ for (const decl of node2.namedChildren) {
122882
+ if (decl.type === "variable_declarator") {
122883
+ const name = decl.childForFieldName("name");
122884
+ if (name && containsIdentifierExact(name, varName))
122885
+ return true;
122886
+ }
122887
+ }
122888
+ return false;
122889
+ }
122890
+ if (node2.type === "function_declaration" || node2.type === "arrow_function" || node2.type === "function" || node2.type === "function_expression" || node2.type === "method_definition" || node2.type === "class_declaration") {
122891
+ return false;
122892
+ }
122893
+ for (let i = 0; i < node2.childCount; i++) {
122894
+ const child = node2.child(i);
122895
+ if (child && containsBindingInBlock(child, varName))
122896
+ return true;
122897
+ }
122898
+ return false;
122899
+ }
122688
122900
  var jsonParseInLoopVisitor;
122689
122901
  var init_json_parse_in_loop = __esm({
122690
122902
  "packages/analyzer/dist/rules/performance/visitors/javascript/json-parse-in-loop.js"() {
@@ -122709,64 +122921,9 @@ var init_json_parse_in_loop = __esm({
122709
122921
  if (!isInsideLoop(node2))
122710
122922
  return null;
122711
122923
  const args = node2.childForFieldName("arguments");
122712
- if (args) {
122713
- const firstArg = args.namedChildren[0];
122714
- if (firstArg) {
122715
- if (firstArg.type === "call_expression")
122716
- return null;
122717
- if (firstArg.type === "subscript_expression")
122718
- return null;
122719
- if (firstArg.type === "template_string")
122720
- return null;
122721
- if (firstArg.type === "identifier") {
122722
- const varName = firstArg.text;
122723
- let current = node2.parent;
122724
- while (current) {
122725
- if (current.type === "for_in_statement" || current.type === "for_of_statement") {
122726
- const left = current.childForFieldName("left");
122727
- if (left && containsIdentifierExact(left, varName))
122728
- return null;
122729
- }
122730
- if (current.type === "for_statement") {
122731
- const init = current.childForFieldName("initializer");
122732
- if (init && containsIdentifierExact(init, varName))
122733
- return null;
122734
- }
122735
- if (current.type === "arrow_function" || current.type === "function_expression" || current.type === "function") {
122736
- const params = current.childForFieldName("parameters");
122737
- if (params && containsIdentifierExact(params, varName)) {
122738
- const callParent = current.parent?.parent;
122739
- if (callParent?.type === "call_expression")
122740
- return null;
122741
- }
122742
- }
122743
- current = current.parent;
122744
- }
122745
- }
122746
- if (firstArg.type === "member_expression") {
122747
- const innerObj = firstArg.childForFieldName("object");
122748
- if (innerObj?.type === "identifier") {
122749
- let current = node2.parent;
122750
- while (current) {
122751
- if (current.type === "for_in_statement" || current.type === "for_of_statement") {
122752
- const left = current.childForFieldName("left");
122753
- if (left && containsIdentifierExact(left, innerObj.text))
122754
- return null;
122755
- }
122756
- if (current.type === "arrow_function" || current.type === "function_expression" || current.type === "function") {
122757
- const params = current.childForFieldName("parameters");
122758
- if (params && containsIdentifierExact(params, innerObj.text)) {
122759
- const callParent = current.parent?.parent;
122760
- if (callParent?.type === "call_expression")
122761
- return null;
122762
- }
122763
- }
122764
- current = current.parent;
122765
- }
122766
- }
122767
- }
122768
- }
122769
- }
122924
+ const firstArg = args?.namedChildren[0];
122925
+ if (firstArg && isDynamicPerIteration(firstArg, node2))
122926
+ return null;
122770
122927
  return makeViolation(this.ruleKey, node2, filePath, "medium", `JSON.${prop.text}() inside loop`, `JSON.${prop.text}() is expensive and calling it inside a loop degrades performance. Move it outside the loop if possible.`, sourceCode, `Cache the result of JSON.${prop.text}() outside the loop.`);
122771
122928
  }
122772
122929
  };
@@ -123099,6 +123256,19 @@ var init_missing_usememo_expensive = __esm({
123099
123256
  return null;
123100
123257
  }
123101
123258
  }
123259
+ if (prop.text === "reduce") {
123260
+ const args = node2.childForFieldName("arguments");
123261
+ const callback = args?.namedChild(0);
123262
+ if (callback?.type === "arrow_function" || callback?.type === "function_expression" || callback?.type === "function") {
123263
+ const body = callback.childForFieldName("body");
123264
+ let inspect = body;
123265
+ if (inspect?.type === "parenthesized_expression") {
123266
+ inspect = inspect.namedChildren[0] ?? inspect;
123267
+ }
123268
+ if (inspect?.type === "binary_expression")
123269
+ return null;
123270
+ }
123271
+ }
123102
123272
  const enclosingFn = findEnclosingFunctionNode(node2);
123103
123273
  if (!enclosingFn)
123104
123274
  return null;
@@ -124094,6 +124264,37 @@ var init_python9 = __esm({
124094
124264
  });
124095
124265
 
124096
124266
  // packages/analyzer/dist/rules/reliability/visitors/javascript/catch-without-error-type.js
124267
+ function narrowsViaTypeGuard(node2, paramName2) {
124268
+ if (node2.type === "call_expression") {
124269
+ const fn = node2.childForFieldName("function");
124270
+ let calleeName = null;
124271
+ if (fn?.type === "identifier")
124272
+ calleeName = fn.text;
124273
+ else if (fn?.type === "member_expression")
124274
+ calleeName = fn.childForFieldName("property")?.text ?? null;
124275
+ if (calleeName && TYPE_GUARD_NAME.test(calleeName)) {
124276
+ const args = node2.childForFieldName("arguments");
124277
+ if (args && referencesBinding(args, paramName2))
124278
+ return true;
124279
+ }
124280
+ }
124281
+ for (let i = 0; i < node2.childCount; i++) {
124282
+ const ch = node2.child(i);
124283
+ if (ch && narrowsViaTypeGuard(ch, paramName2))
124284
+ return true;
124285
+ }
124286
+ return false;
124287
+ }
124288
+ function referencesBinding(node2, name) {
124289
+ if (node2.type === "identifier" && node2.text === name)
124290
+ return true;
124291
+ for (let i = 0; i < node2.childCount; i++) {
124292
+ const ch = node2.child(i);
124293
+ if (ch && referencesBinding(ch, name))
124294
+ return true;
124295
+ }
124296
+ return false;
124297
+ }
124097
124298
  function hasBranchingConstruct(node2) {
124098
124299
  if (node2.type === "if_statement" || node2.type === "switch_statement" || node2.type === "ternary_expression")
124099
124300
  return true;
@@ -124106,7 +124307,7 @@ function hasBranchingConstruct(node2) {
124106
124307
  }
124107
124308
  return false;
124108
124309
  }
124109
- var catchWithoutErrorTypeVisitor;
124310
+ var catchWithoutErrorTypeVisitor, TYPE_GUARD_NAME;
124110
124311
  var init_catch_without_error_type = __esm({
124111
124312
  "packages/analyzer/dist/rules/reliability/visitors/javascript/catch-without-error-type.js"() {
124112
124313
  "use strict";
@@ -124134,9 +124335,13 @@ var init_catch_without_error_type = __esm({
124134
124335
  return null;
124135
124336
  if (!hasBranchingConstruct(body))
124136
124337
  return null;
124338
+ const paramName2 = param.type === "identifier" ? param.text : null;
124339
+ if (paramName2 && narrowsViaTypeGuard(body, paramName2))
124340
+ return null;
124137
124341
  return makeViolation(this.ruleKey, node2, filePath, "medium", "Catch without error type discrimination", "Catch block does not check or narrow the error type. Different error types may need different handling.", sourceCode, "Use instanceof checks or type guards in the catch block to handle specific error types.");
124138
124342
  }
124139
124343
  };
124344
+ TYPE_GUARD_NAME = /^(is|has|assert)[A-Z]/;
124140
124345
  }
124141
124346
  });
124142
124347
 
@@ -129378,7 +129583,8 @@ __export(dist_exports, {
129378
129583
  shouldExtractEntities: () => shouldExtractEntities,
129379
129584
  toLayerDetectionResults: () => toLayerDetectionResults,
129380
129585
  traceFlows: () => traceFlows,
129381
- walkAstWithVisitors: () => walkAstWithVisitors
129586
+ walkAstWithVisitors: () => walkAstWithVisitors,
129587
+ withParsedTree: () => withParsedTree
129382
129588
  });
129383
129589
  async function analyzeRepository(rootPath) {
129384
129590
  await initParsers();
@@ -129558,12 +129764,12 @@ var require_isexe = __commonJS({
129558
129764
  if (typeof Promise !== "function") {
129559
129765
  throw new TypeError("callback not provided");
129560
129766
  }
129561
- return new Promise(function(resolve8, reject) {
129767
+ return new Promise(function(resolve9, reject) {
129562
129768
  isexe(path52, options || {}, function(er, is) {
129563
129769
  if (er) {
129564
129770
  reject(er);
129565
129771
  } else {
129566
- resolve8(is);
129772
+ resolve9(is);
129567
129773
  }
129568
129774
  });
129569
129775
  });
@@ -129629,27 +129835,27 @@ var require_which = __commonJS({
129629
129835
  opt = {};
129630
129836
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
129631
129837
  const found = [];
129632
- const step = (i) => new Promise((resolve8, reject) => {
129838
+ const step = (i) => new Promise((resolve9, reject) => {
129633
129839
  if (i === pathEnv.length)
129634
- return opt.all && found.length ? resolve8(found) : reject(getNotFoundError(cmd));
129840
+ return opt.all && found.length ? resolve9(found) : reject(getNotFoundError(cmd));
129635
129841
  const ppRaw = pathEnv[i];
129636
129842
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
129637
129843
  const pCmd = path52.join(pathPart, cmd);
129638
129844
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
129639
- resolve8(subStep(p, i, 0));
129845
+ resolve9(subStep(p, i, 0));
129640
129846
  });
129641
- const subStep = (p, i, ii) => new Promise((resolve8, reject) => {
129847
+ const subStep = (p, i, ii) => new Promise((resolve9, reject) => {
129642
129848
  if (ii === pathExt.length)
129643
- return resolve8(step(i + 1));
129849
+ return resolve9(step(i + 1));
129644
129850
  const ext2 = pathExt[ii];
129645
129851
  isexe(p + ext2, { pathExt: pathExtExe }, (er, is) => {
129646
129852
  if (!er && is) {
129647
129853
  if (opt.all)
129648
129854
  found.push(p + ext2);
129649
129855
  else
129650
- return resolve8(p + ext2);
129856
+ return resolve9(p + ext2);
129651
129857
  }
129652
- return resolve8(subStep(p, i, ii + 1));
129858
+ return resolve9(subStep(p, i, ii + 1));
129653
129859
  });
129654
129860
  });
129655
129861
  return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
@@ -130054,27 +130260,27 @@ function pLimit(concurrency) {
130054
130260
  activeCount--;
130055
130261
  resumeNext();
130056
130262
  };
130057
- const run = async (function_, resolve8, arguments_) => {
130263
+ const run = async (function_, resolve9, arguments_) => {
130058
130264
  const result = (async () => function_(...arguments_))();
130059
- resolve8(result);
130265
+ resolve9(result);
130060
130266
  try {
130061
130267
  await result;
130062
130268
  } catch {
130063
130269
  }
130064
130270
  next();
130065
130271
  };
130066
- const enqueue = (function_, resolve8, reject, arguments_) => {
130272
+ const enqueue = (function_, resolve9, reject, arguments_) => {
130067
130273
  const queueItem = { reject };
130068
130274
  new Promise((internalResolve) => {
130069
130275
  queueItem.run = internalResolve;
130070
130276
  queue.enqueue(queueItem);
130071
- }).then(run.bind(void 0, function_, resolve8, arguments_));
130277
+ }).then(run.bind(void 0, function_, resolve9, arguments_));
130072
130278
  if (activeCount < concurrency) {
130073
130279
  resumeNext();
130074
130280
  }
130075
130281
  };
130076
- const generator = (function_, ...arguments_) => new Promise((resolve8, reject) => {
130077
- enqueue(function_, resolve8, reject, arguments_);
130282
+ const generator = (function_, ...arguments_) => new Promise((resolve9, reject) => {
130283
+ enqueue(function_, resolve9, reject, arguments_);
130078
130284
  });
130079
130285
  Object.defineProperties(generator, {
130080
130286
  activeCount: {
@@ -132572,7 +132778,7 @@ var init_cli_provider = __esm({
132572
132778
  jsonSchemaStr,
132573
132779
  ...opts?.extraArgs ?? []
132574
132780
  ];
132575
- return new Promise((resolve8, reject) => {
132781
+ return new Promise((resolve9, reject) => {
132576
132782
  const child = (0, import_cross_spawn.default)(this.binaryName, args, {
132577
132783
  env: this.getCleanEnv(),
132578
132784
  stdio: ["pipe", "pipe", "pipe"],
@@ -132620,7 +132826,7 @@ var init_cli_provider = __esm({
132620
132826
  reject(new Error(`[CLI] ${this.binaryName} exited with code ${code}: ${detail}`));
132621
132827
  return;
132622
132828
  }
132623
- resolve8(stdout);
132829
+ resolve9(stdout);
132624
132830
  });
132625
132831
  child.on("error", (err) => {
132626
132832
  clearTimeout(timer);
@@ -135815,10 +136021,10 @@ var require_axios = __commonJS({
135815
136021
  this.__CANCEL__ = true;
135816
136022
  }
135817
136023
  };
135818
- function settle(resolve8, reject, response) {
136024
+ function settle(resolve9, reject, response) {
135819
136025
  const validateStatus = response.config.validateStatus;
135820
136026
  if (!response.status || !validateStatus || validateStatus(response.status)) {
135821
- resolve8(response);
136027
+ resolve9(response);
135822
136028
  } else {
135823
136029
  reject(new AxiosError("Request failed with status code " + response.status, [AxiosError.ERR_BAD_REQUEST, AxiosError.ERR_BAD_RESPONSE][Math.floor(response.status / 100) - 4], response.config, response.request, response));
135824
136030
  }
@@ -136461,7 +136667,7 @@ var require_axios = __commonJS({
136461
136667
  }
136462
136668
  var isHttpAdapterSupported = typeof process !== "undefined" && utils$1.kindOf(process) === "process";
136463
136669
  var wrapAsync = (asyncExecutor) => {
136464
- return new Promise((resolve8, reject) => {
136670
+ return new Promise((resolve9, reject) => {
136465
136671
  let onDone;
136466
136672
  let isDone;
136467
136673
  const done = (value, isRejected) => {
@@ -136471,7 +136677,7 @@ var require_axios = __commonJS({
136471
136677
  };
136472
136678
  const _resolve = (value) => {
136473
136679
  done(value);
136474
- resolve8(value);
136680
+ resolve9(value);
136475
136681
  };
136476
136682
  const _reject = (reason) => {
136477
136683
  done(reason, true);
@@ -136532,7 +136738,7 @@ var require_axios = __commonJS({
136532
136738
  }
136533
136739
  };
136534
136740
  var httpAdapter = isHttpAdapterSupported && function httpAdapter2(config2) {
136535
- return wrapAsync(async function dispatchHttpRequest(resolve8, reject, onDone) {
136741
+ return wrapAsync(async function dispatchHttpRequest(resolve9, reject, onDone) {
136536
136742
  let {
136537
136743
  data,
136538
136744
  lookup,
@@ -136624,7 +136830,7 @@ var require_axios = __commonJS({
136624
136830
  }
136625
136831
  let convertedData;
136626
136832
  if (method !== "GET") {
136627
- return settle(resolve8, reject, {
136833
+ return settle(resolve9, reject, {
136628
136834
  status: 405,
136629
136835
  statusText: "method not allowed",
136630
136836
  headers: {},
@@ -136646,7 +136852,7 @@ var require_axios = __commonJS({
136646
136852
  } else if (responseType === "stream") {
136647
136853
  convertedData = stream.Readable.from(convertedData);
136648
136854
  }
136649
- return settle(resolve8, reject, {
136855
+ return settle(resolve9, reject, {
136650
136856
  data: convertedData,
136651
136857
  status: 200,
136652
136858
  statusText: "OK",
@@ -136841,7 +137047,7 @@ var require_axios = __commonJS({
136841
137047
  };
136842
137048
  if (responseType === "stream") {
136843
137049
  response.data = responseStream;
136844
- settle(resolve8, reject, response);
137050
+ settle(resolve9, reject, response);
136845
137051
  } else {
136846
137052
  const responseBuffer = [];
136847
137053
  let totalResponseBytes = 0;
@@ -136879,7 +137085,7 @@ var require_axios = __commonJS({
136879
137085
  } catch (err) {
136880
137086
  return reject(AxiosError.from(err, null, config2, response.request, response));
136881
137087
  }
136882
- settle(resolve8, reject, response);
137088
+ settle(resolve9, reject, response);
136883
137089
  });
136884
137090
  }
136885
137091
  abortEmitter.once("abort", (err) => {
@@ -137117,7 +137323,7 @@ var require_axios = __commonJS({
137117
137323
  };
137118
137324
  var isXHRAdapterSupported = typeof XMLHttpRequest !== "undefined";
137119
137325
  var xhrAdapter = isXHRAdapterSupported && function(config2) {
137120
- return new Promise(function dispatchXhrRequest(resolve8, reject) {
137326
+ return new Promise(function dispatchXhrRequest(resolve9, reject) {
137121
137327
  const _config = resolveConfig(config2);
137122
137328
  let requestData = _config.data;
137123
137329
  const requestHeaders = AxiosHeaders.from(_config.headers).normalize();
@@ -137153,7 +137359,7 @@ var require_axios = __commonJS({
137153
137359
  request
137154
137360
  };
137155
137361
  settle(function _resolve(value) {
137156
- resolve8(value);
137362
+ resolve9(value);
137157
137363
  done();
137158
137364
  }, function _reject(err) {
137159
137365
  reject(err);
@@ -137524,8 +137730,8 @@ var require_axios = __commonJS({
137524
137730
  responseType = responseType || "text";
137525
137731
  let responseData = await resolvers[utils$1.findKey(resolvers, responseType) || "text"](response, config2);
137526
137732
  !isStreamResponse && unsubscribe && unsubscribe();
137527
- return await new Promise((resolve8, reject) => {
137528
- settle(resolve8, reject, {
137733
+ return await new Promise((resolve9, reject) => {
137734
+ settle(resolve9, reject, {
137529
137735
  data: responseData,
137530
137736
  headers: AxiosHeaders.from(response.headers),
137531
137737
  status: response.status,
@@ -137894,8 +138100,8 @@ var require_axios = __commonJS({
137894
138100
  throw new TypeError("executor must be a function.");
137895
138101
  }
137896
138102
  let resolvePromise;
137897
- this.promise = new Promise(function promiseExecutor(resolve8) {
137898
- resolvePromise = resolve8;
138103
+ this.promise = new Promise(function promiseExecutor(resolve9) {
138104
+ resolvePromise = resolve9;
137899
138105
  });
137900
138106
  const token = this;
137901
138107
  this.promise.then((cancel) => {
@@ -137908,9 +138114,9 @@ var require_axios = __commonJS({
137908
138114
  });
137909
138115
  this.promise.then = (onfulfilled) => {
137910
138116
  let _resolve;
137911
- const promise = new Promise((resolve8) => {
137912
- token.subscribe(resolve8);
137913
- _resolve = resolve8;
138117
+ const promise = new Promise((resolve9) => {
138118
+ token.subscribe(resolve9);
138119
+ _resolve = resolve9;
137914
138120
  }).then(onfulfilled);
137915
138121
  promise.cancel = function reject() {
137916
138122
  token.unsubscribe(_resolve);
@@ -145925,7 +146131,7 @@ function emitAnalysisCanceled(repoId) {
145925
146131
  io3.to(`repo:${repoId}`).emit("analysis:canceled", { repoId });
145926
146132
  }
145927
146133
  function createSocketLlmEstimateHandler(repoId) {
145928
- return (estimate) => new Promise((resolve8) => {
146134
+ return (estimate) => new Promise((resolve9) => {
145929
146135
  const io3 = getIO();
145930
146136
  const room = `repo:${repoId}`;
145931
146137
  io3.to(room).emit("analysis:llm-estimate", {
@@ -145939,13 +146145,13 @@ function createSocketLlmEstimateHandler(repoId) {
145939
146145
  });
145940
146146
  const timeout = setTimeout(() => {
145941
146147
  cleanup();
145942
- resolve8(true);
146148
+ resolve9(true);
145943
146149
  }, 6e4);
145944
146150
  function onProceed(data) {
145945
146151
  if (data.repoId !== repoId) return;
145946
146152
  cleanup();
145947
146153
  io3.to(room).emit("analysis:llm-resolved", { repoId, proceed: data.proceed });
145948
- resolve8(data.proceed);
146154
+ resolve9(data.proceed);
145949
146155
  }
145950
146156
  function cleanup() {
145951
146157
  clearTimeout(timeout);
@@ -145959,7 +146165,7 @@ function createSocketLlmEstimateHandler(repoId) {
145959
146165
  });
145960
146166
  }
145961
146167
  function createSocketStashConfirmHandler(repoId) {
145962
- return (info) => new Promise((resolve8) => {
146168
+ return (info) => new Promise((resolve9) => {
145963
146169
  const io3 = getIO();
145964
146170
  const room = `repo:${repoId}`;
145965
146171
  io3.to(room).emit("analysis:stash-confirm-request", {
@@ -145970,7 +146176,7 @@ function createSocketStashConfirmHandler(repoId) {
145970
146176
  function onResponse(data) {
145971
146177
  if (data.repoId !== repoId) return;
145972
146178
  cleanup();
145973
- resolve8(data.choice);
146179
+ resolve9(data.choice);
145974
146180
  }
145975
146181
  function cleanup() {
145976
146182
  for (const [, socket] of io3.sockets.sockets) {
@@ -152776,12 +152982,12 @@ async function runViolationPipeline(input) {
152776
152982
  const fileContents = /* @__PURE__ */ new Map();
152777
152983
  const totalToScan = filesToScan.length;
152778
152984
  let scanned = 0;
152779
- for (const { filePath, resolve: resolve8 } of filesToScan) {
152985
+ for (const { filePath, resolve: resolve9 } of filesToScan) {
152780
152986
  try {
152781
152987
  const lang = detectLanguage(filePath);
152782
152988
  if (!lang)
152783
152989
  continue;
152784
- const absPath = resolve8 ? path12.resolve(repoPath, filePath) : path12.isAbsolute(filePath) ? filePath : path12.join(repoPath, filePath);
152990
+ const absPath = resolve9 ? path12.resolve(repoPath, filePath) : path12.isAbsolute(filePath) ? filePath : path12.join(repoPath, filePath);
152785
152991
  if (!fs11.existsSync(absPath))
152786
152992
  continue;
152787
152993
  const content = fs11.readFileSync(absPath, "utf-8");
@@ -152886,18 +153092,17 @@ async function runViolationPipeline(input) {
152886
153092
  const DETAIL_UPDATE_MS = 100;
152887
153093
  let lastYieldMs = Date.now();
152888
153094
  let lastDetailMs = lastYieldMs;
152889
- for (const { filePath, resolve: resolve8 } of filesToScan) {
153095
+ for (const { filePath, resolve: resolve9 } of filesToScan) {
152890
153096
  try {
152891
153097
  const lang = detectLanguage(filePath);
152892
153098
  if (!lang)
152893
153099
  continue;
152894
- const absPath = resolve8 ? path12.resolve(repoPath, filePath) : path12.isAbsolute(filePath) ? filePath : path12.join(repoPath, filePath);
153100
+ const absPath = resolve9 ? path12.resolve(repoPath, filePath) : path12.isAbsolute(filePath) ? filePath : path12.join(repoPath, filePath);
152895
153101
  const key2 = changedFileSet ? absPath : filePath;
152896
153102
  const fc = fileContents.get(key2);
152897
153103
  if (!fc)
152898
153104
  continue;
152899
- const tree = parseFile(changedFileSet ? filePath : filePath, fc.content, lang);
152900
- const codeRuleViolations = checkCodeRules(tree, changedFileSet ? absPath : filePath, fc.content, enabledCodeRules, lang, typeQuery, schemaIndex);
153105
+ const codeRuleViolations = withParsedTree(filePath, fc.content, lang, (tree) => checkCodeRules(tree, changedFileSet ? absPath : filePath, fc.content, enabledCodeRules, lang, typeQuery, schemaIndex));
152901
153106
  allCodeViolations.push(...codeRuleViolations);
152902
153107
  } catch {
152903
153108
  }
@@ -155047,14 +155252,14 @@ async function addSourceContext(frames) {
155047
155252
  return frames;
155048
155253
  }
155049
155254
  function getContextLinesFromFile(path52, ranges, output) {
155050
- return new Promise((resolve8) => {
155255
+ return new Promise((resolve9) => {
155051
155256
  const stream = createReadStream(path52);
155052
155257
  const lineReaded = createInterface({
155053
155258
  input: stream
155054
155259
  });
155055
155260
  function destroyStreamAndResolve() {
155056
155261
  stream.destroy();
155057
- resolve8();
155262
+ resolve9();
155058
155263
  }
155059
155264
  let lineNumber = 0;
155060
155265
  let currentRangeIndex = 0;
@@ -157652,15 +157857,15 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
157652
157857
  if (this.featureFlagsPoller === void 0) {
157653
157858
  return false;
157654
157859
  }
157655
- return new Promise((resolve8) => {
157860
+ return new Promise((resolve9) => {
157656
157861
  const timeout = setTimeout(() => {
157657
157862
  cleanup();
157658
- resolve8(false);
157863
+ resolve9(false);
157659
157864
  }, timeoutMs);
157660
157865
  const cleanup = this._events.on("localEvaluationFlagsLoaded", (count) => {
157661
157866
  clearTimeout(timeout);
157662
157867
  cleanup();
157663
- resolve8(count > 0);
157868
+ resolve9(count > 0);
157664
157869
  });
157665
157870
  });
157666
157871
  }
@@ -158123,7 +158328,7 @@ function readToolVersion() {
158123
158328
  if (cachedVersion)
158124
158329
  return cachedVersion;
158125
158330
  if (true) {
158126
- cachedVersion = "0.6.6";
158331
+ cachedVersion = "0.6.7-next.1";
158127
158332
  return cachedVersion;
158128
158333
  }
158129
158334
  try {
@@ -160312,6 +160517,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
160312
160517
  ".cache",
160313
160518
  "coverage"
160314
160519
  ]);
160520
+ var SKIP_DIR_PROBE = "__tc_skipdir_probe__.md";
160315
160521
  var PREVIEW_LINE_LIMIT = 200;
160316
160522
  function discoverDocs(rootDir, opts = {}) {
160317
160523
  const previewLines = opts.previewLines ?? PREVIEW_LINE_LIMIT;
@@ -160326,11 +160532,12 @@ function discoverDocs(rootDir, opts = {}) {
160326
160532
  }
160327
160533
  entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
160328
160534
  for (const entry of entries) {
160329
- if (SKIP_DIRS.has(entry.name))
160535
+ const full = path18.join(dir, entry.name);
160536
+ if (SKIP_DIRS.has(entry.name) && !(entry.isDirectory() && tcIgnore.reincludes(path18.join(full, SKIP_DIR_PROBE)))) {
160330
160537
  continue;
160538
+ }
160331
160539
  if (entry.name.startsWith(".") && entry.name !== ".")
160332
160540
  continue;
160333
- const full = path18.join(dir, entry.name);
160334
160541
  if (tcIgnore.ignores(full))
160335
160542
  continue;
160336
160543
  if (entry.isDirectory()) {
@@ -160520,7 +160727,7 @@ function stripCodeFences(text) {
160520
160727
  }
160521
160728
  function cliTransport(opts = {}) {
160522
160729
  const bin = opts.bin ?? resolveClaudeBinary();
160523
- return (req) => new Promise((resolve8, reject) => {
160730
+ return (req) => new Promise((resolve9, reject) => {
160524
160731
  const modelArgs = [];
160525
160732
  if (req.model)
160526
160733
  modelArgs.push("--model", req.model);
@@ -160565,7 +160772,7 @@ function cliTransport(opts = {}) {
160565
160772
  reject(new Error("claude returned no text"));
160566
160773
  return;
160567
160774
  }
160568
- resolve8(text);
160775
+ resolve9(text);
160569
160776
  } catch (e) {
160570
160777
  reject(e instanceof Error ? e : new Error(String(e)));
160571
160778
  }
@@ -162315,14 +162522,14 @@ async function explainConflicts(repoRoot, conflicts2, opts = {}) {
162315
162522
  opts.onStart?.(conflicts2.length);
162316
162523
  let active = 0;
162317
162524
  let cursor = 0;
162318
- await new Promise((resolve8) => {
162525
+ await new Promise((resolve9) => {
162319
162526
  const launch = () => {
162320
162527
  while (active < concurrency && cursor < conflicts2.length) {
162321
162528
  const conflict = conflicts2[cursor++];
162322
162529
  if (conflict.explanation) {
162323
162530
  opts.onDone?.();
162324
162531
  if (cursor >= conflicts2.length && active === 0)
162325
- resolve8();
162532
+ resolve9();
162326
162533
  continue;
162327
162534
  }
162328
162535
  active++;
@@ -162330,13 +162537,13 @@ async function explainConflicts(repoRoot, conflicts2, opts = {}) {
162330
162537
  opts.onDone?.();
162331
162538
  active--;
162332
162539
  if (cursor >= conflicts2.length && active === 0)
162333
- resolve8();
162540
+ resolve9();
162334
162541
  else
162335
162542
  launch();
162336
162543
  });
162337
162544
  }
162338
162545
  if (cursor >= conflicts2.length && active === 0)
162339
- resolve8();
162546
+ resolve9();
162340
162547
  };
162341
162548
  launch();
162342
162549
  });
@@ -162569,7 +162776,7 @@ async function resolveConflicts(repoRoot, conflicts2, opts = {}) {
162569
162776
  const out = [];
162570
162777
  let cursor = 0;
162571
162778
  let active = 0;
162572
- await new Promise((resolve8) => {
162779
+ await new Promise((resolve9) => {
162573
162780
  const launch = () => {
162574
162781
  while (active < concurrency && cursor < conflicts2.length) {
162575
162782
  const conflict = conflicts2[cursor++];
@@ -162588,13 +162795,13 @@ async function resolveConflicts(repoRoot, conflicts2, opts = {}) {
162588
162795
  }).finally(() => {
162589
162796
  active--;
162590
162797
  if (cursor >= conflicts2.length && active === 0)
162591
- resolve8();
162798
+ resolve9();
162592
162799
  else
162593
162800
  launch();
162594
162801
  });
162595
162802
  }
162596
162803
  if (cursor >= conflicts2.length && active === 0)
162597
- resolve8();
162804
+ resolve9();
162598
162805
  };
162599
162806
  launch();
162600
162807
  });
@@ -162814,7 +163021,7 @@ async function filterByRelevance(repoRoot, docs, opts = {}) {
162814
163021
  const verdicts = /* @__PURE__ */ new Map();
162815
163022
  let cursor = 0;
162816
163023
  let active = 0;
162817
- await new Promise((resolve8) => {
163024
+ await new Promise((resolve9) => {
162818
163025
  const launch = () => {
162819
163026
  while (active < concurrency && cursor < docs.length) {
162820
163027
  const doc = docs[cursor++];
@@ -162822,7 +163029,7 @@ async function filterByRelevance(repoRoot, docs, opts = {}) {
162822
163029
  verdicts.set(doc.path, { path: doc.path, include: true, reason: "manual include" });
162823
163030
  markDone();
162824
163031
  if (cursor >= docs.length && active === 0)
162825
- resolve8();
163032
+ resolve9();
162826
163033
  continue;
162827
163034
  }
162828
163035
  active++;
@@ -162838,13 +163045,13 @@ async function filterByRelevance(repoRoot, docs, opts = {}) {
162838
163045
  markDone();
162839
163046
  active--;
162840
163047
  if (cursor >= docs.length && active === 0)
162841
- resolve8();
163048
+ resolve9();
162842
163049
  else
162843
163050
  launch();
162844
163051
  });
162845
163052
  }
162846
163053
  if (cursor >= docs.length && active === 0)
162847
- resolve8();
163054
+ resolve9();
162848
163055
  };
162849
163056
  launch();
162850
163057
  });
@@ -164292,6 +164499,27 @@ not narrow the set.
164292
164499
  substitute for \`immutable\`. If a derived/server-computed field is also frozen
164293
164500
  after creation, emit BOTH \`computed-at\` AND \`immutable\`.
164294
164501
 
164502
+ **NEVER infer \`immutable\` from what a field IS \u2014 only from what the spec SAYS
164503
+ about changing it.** The following do NOT imply immutability, alone or combined:
164504
+
164505
+ - The field is required (\`Required? yes\` in a field table).
164506
+ - The field is an identifier (\`id\`, \`uuid\`, "identifier of this X").
164507
+ - The field is a timestamp ("when the event happened", \`occurred\`, \`createdAt\`).
164508
+ - The field is client-provided or server-assigned (provenance is not mutability).
164509
+
164510
+ Counter-example \u2014 a field table like:
164511
+
164512
+ | Name | Type | Required? | Description |
164513
+ | -------- | ------ | --------- | ---------------------------------------- |
164514
+ | occurred | String | yes | When the event happened |
164515
+ | id | String | yes | Client-provided identifier of this event |
164516
+
164517
+ asserts NOTHING about mutability. Both fields get plain \`field occurred: string {}\`
164518
+ / \`field id: string {}\` \u2014 no \`immutable\`, even though an id "sounds" immutable.
164519
+ Emitting un-stated \`immutable\` creates a false drift against every codebase whose
164520
+ model simply doesn't freeze the field. If the spec is silent on mutability, the
164521
+ contract is silent on mutability.
164522
+
164295
164523
  # Enum extraction \u2014 required whenever spec defines an enum
164296
164524
 
164297
164525
  Whenever the spec text contains any of:
@@ -164314,6 +164542,27 @@ data document. If \`Entity:Customer\` references \`Enum:LoyaltyTier\` and the sa
164314
164542
  slice contains "LoyaltyTier values: standard, silver, gold", emit BOTH the entity
164315
164543
  fragment AND the enum fragment.
164316
164544
 
164545
+ **An enum is a closed set of DATA VALUES the code compares against** (the
164546
+ string/number literals a field is set to or checked against) \u2014 NOT a catalog of
164547
+ code symbols a caller picks between. Before emitting, check what the listed items
164548
+ ARE. Emit nothing when the list is:
164549
+
164550
+ - **Implementation / plugin classes** \u2014 e.g. "run launchers: \`DefaultRunLauncher\`,
164551
+ \`DockerRunLauncher\`, \`K8sRunLauncher\`", storage backends, compute-log managers.
164552
+ These are swappable extension points (a user can add their own); the items are
164553
+ class names, not a closed value set.
164554
+ - **API functions, decorators, or methods** \u2014 e.g. "asset decorators: \`asset\`,
164555
+ \`multi_asset\`, \`graph_asset\`". These are symbols a user calls, not values.
164556
+ - **An incidental excerpt** \u2014 a few items quoted inside a troubleshooting,
164557
+ example, or how-to passage ("relevant event types: \u2026") rather than a
164558
+ definitional "the valid values of X are \u2026". A subset mentioned in passing is
164559
+ not an enum definition.
164560
+
164561
+ Discriminator: if the items are **names of code symbols** (classes / functions a
164562
+ caller selects), do NOT emit an enum. Only emit when the items are **literal data
164563
+ values** the code stores or compares against (config keys, status strings, tag
164564
+ keys, numeric codes).
164565
+
164317
164566
  **Never reference an enum without defining it.** Every \`Enum:X\` identifier you
164318
164567
  emit (in \`field: Enum:X\` or \`states Enum:X\`) MUST have a matching \`enum X { \u2026 }\`
164319
164568
  artifact somewhere in the same slice (or you must assume another slice provides
@@ -164643,6 +164892,42 @@ Don't emit constants whose value is a function call, expression, or
164643
164892
  external reference \u2014 only literal values (strings, numbers, booleans,
164644
164893
  arrays of literals, flat object literals of literals).
164645
164894
 
164895
+ **Don't extract constants from customization examples.** A constant asserts
164896
+ "the code MUST hold this value". A doc that shows users how to CUSTOMIZE or
164897
+ OVERRIDE something is asserting the opposite \u2014 "you can put whatever value
164898
+ you want here" \u2014 and its example values are illustrations, not obligations.
164899
+ Skip a candidate value when EITHER signal is present:
164900
+
164901
+ 1. **Context signal**: the page title or enclosing section heading describes
164902
+ a customization/override mechanism \u2014 "Customize \u2026", "Override \u2026",
164903
+ "\u2026 template", "\u2026 variables", "Configure your own \u2026", "Example
164904
+ configuration".
164905
+ 2. **Shape signal**: the value sits inside a JSON-schema-style variables
164906
+ block, i.e. an object of the form
164907
+ \`"<name>": { "title": \u2026, "description": \u2026, "default": <value>, "type": \u2026 }\`.
164908
+ That quadruple is a schema DECLARING an overridable variable; the
164909
+ \`"default"\` there is a starting point the user is expected to change.
164910
+
164911
+ Counter-example \u2014 a page titled "Customize Base Job Templates" containing:
164912
+
164913
+ \`\`\`json
164914
+ "cpu_request": {
164915
+ "title": "CPU Request",
164916
+ "description": "CPU allocation to request for this pod",
164917
+ "default": "100m",
164918
+ "type": "string"
164919
+ }
164920
+ \`\`\`
164921
+
164922
+ emits NO constant. Both signals fire: the page is a customization guide, and
164923
+ the value is a variables-schema \`default\`. Extracting
164924
+ \`constant cpu_request { expected-value "100m" }\` would demand every codebase
164925
+ hard-code the example \u2014 a guaranteed false drift.
164926
+
164927
+ By contrast, prose like "the scheduler polls every 15 seconds" or "the API
164928
+ version is \`v2\`" in a behavioral spec section IS an assertion about the
164929
+ system \u2014 extract those normally.
164930
+
164646
164931
  # ArchitectureDecision \u2014 extract when the spec/ADR fixes a platform choice
164647
164932
 
164648
164933
  A spec or ADR that records a system-wide technology choice \u2014 "we use
@@ -168850,6 +169135,77 @@ function extractPluginStyleRoutesFromFile(filePath, source, tree) {
168850
169135
 
168851
169136
  // packages/contract-verifier/dist/extractor/operation-fastapi.js
168852
169137
  var HTTP_METHODS4 = /* @__PURE__ */ new Set(["get", "post", "put", "delete", "patch"]);
169138
+ function extractStarletteRoutesFromFile(filePath, source, tree) {
169139
+ const out = [];
169140
+ walk(tree.rootNode, (node2) => {
169141
+ if (node2.type !== "call")
169142
+ return;
169143
+ const fn = node2.childForFieldName("function");
169144
+ if (fn?.type !== "identifier" || source.slice(fn.startIndex, fn.endIndex) !== "Route")
169145
+ return;
169146
+ const args = node2.childForFieldName("arguments");
169147
+ if (!args)
169148
+ return;
169149
+ const first2 = args.namedChild(0);
169150
+ if (first2?.type !== "string")
169151
+ return;
169152
+ const rawPath = pyStr(first2, source);
169153
+ if (!rawPath.startsWith("/"))
169154
+ return;
169155
+ const second = args.namedChild(1);
169156
+ if (!second || second.type === "keyword_argument")
169157
+ return;
169158
+ const methods = readStarletteMethods(args, source);
169159
+ const identities = starletteIdentitiesForPath(rawPath);
169160
+ const line = node2.startPosition.row + 1;
169161
+ for (const method of methods) {
169162
+ for (const path52 of identities) {
169163
+ out.push({
169164
+ identity: `${method} ${path52}`,
169165
+ contract: {
169166
+ protocol: "http",
169167
+ method,
169168
+ path: path52,
169169
+ responses: [],
169170
+ tags: []
169171
+ },
169172
+ filePath,
169173
+ declarationLine: line,
169174
+ observed: { queryParams: [], numericClamps: [], hasClampCall: false }
169175
+ });
169176
+ }
169177
+ }
169178
+ });
169179
+ return out;
169180
+ }
169181
+ function readStarletteMethods(args, source) {
169182
+ for (let i = 0; i < args.namedChildCount; i++) {
169183
+ const a = args.namedChild(i);
169184
+ if (a?.type !== "keyword_argument")
169185
+ continue;
169186
+ const name = a.childForFieldName("name");
169187
+ if (!name || source.slice(name.startIndex, name.endIndex) !== "methods")
169188
+ continue;
169189
+ const value = a.childForFieldName("value");
169190
+ if (value?.type !== "list")
169191
+ return ["GET"];
169192
+ const methods = [];
169193
+ for (let j = 0; j < value.namedChildCount; j++) {
169194
+ const el = value.namedChild(j);
169195
+ if (el?.type === "string")
169196
+ methods.push(pyStr(el, source).toUpperCase());
169197
+ }
169198
+ return methods.length > 0 ? methods : ["GET"];
169199
+ }
169200
+ return ["GET"];
169201
+ }
169202
+ function starletteIdentitiesForPath(rawPath) {
169203
+ const catchAll = rawPath.match(/^(\/.*\/)\{\w+:path\}$/);
169204
+ if (catchAll) {
169205
+ return [catchAll[1].replace(/\{(\w+):[^}]+\}/g, "{$1}")];
169206
+ }
169207
+ return [rawPath.replace(/\{(\w+):[^}]+\}/g, "{$1}")];
169208
+ }
168853
169209
  function extractFastApiOperationsFromFile(filePath, source, tree) {
168854
169210
  const routers = collectRouters(tree.rootNode, source);
168855
169211
  const stringVars = collectStringVars(tree.rootNode, source);
@@ -172359,11 +172715,81 @@ function extractIdProperty(obj) {
172359
172715
  return null;
172360
172716
  }
172361
172717
 
172718
+ // packages/contract-verifier/dist/extractor/py-string-resolver.js
172719
+ var MAX_DEPTH3 = 4;
172720
+ function collectStringConstantTable(rootNode, source) {
172721
+ const table = /* @__PURE__ */ new Map();
172722
+ for (let i = 0; i < rootNode.namedChildCount; i++) {
172723
+ const stmt = rootNode.namedChild(i);
172724
+ if (stmt?.type !== "expression_statement")
172725
+ continue;
172726
+ const assign = stmt.namedChild(0);
172727
+ if (assign?.type !== "assignment")
172728
+ continue;
172729
+ const left = assign.childForFieldName("left");
172730
+ if (left?.type !== "identifier")
172731
+ continue;
172732
+ const right = assign.childForFieldName("right");
172733
+ if (right?.type !== "string")
172734
+ continue;
172735
+ const name = source.slice(left.startIndex, left.endIndex);
172736
+ if (table.has(name))
172737
+ continue;
172738
+ table.set(name, right);
172739
+ }
172740
+ return table;
172741
+ }
172742
+ function stringValue(node2, source, table) {
172743
+ return resolve8(node2, source, table, 0);
172744
+ }
172745
+ function resolve8(node2, source, table, depth) {
172746
+ if (node2.type !== "string")
172747
+ return null;
172748
+ if (depth > MAX_DEPTH3)
172749
+ return null;
172750
+ let content = "";
172751
+ let sawContent = false;
172752
+ for (let i = 0; i < node2.namedChildCount; i++) {
172753
+ const c = node2.namedChild(i);
172754
+ if (!c)
172755
+ continue;
172756
+ if (c.type === "string_content") {
172757
+ content += source.slice(c.startIndex, c.endIndex);
172758
+ sawContent = true;
172759
+ continue;
172760
+ }
172761
+ if (c.type === "interpolation") {
172762
+ if (c.namedChildren.some((g) => g?.type === "format_specifier"))
172763
+ return null;
172764
+ const expr = c.namedChild(0);
172765
+ if (expr?.type !== "identifier" || !table)
172766
+ return null;
172767
+ const referent = table.get(source.slice(expr.startIndex, expr.endIndex));
172768
+ if (!referent)
172769
+ return null;
172770
+ const resolved = resolve8(referent, source, table, depth + 1);
172771
+ if (resolved === null)
172772
+ return null;
172773
+ content += resolved;
172774
+ sawContent = true;
172775
+ continue;
172776
+ }
172777
+ if (c.type === "format_specifier")
172778
+ return null;
172779
+ }
172780
+ if (sawContent)
172781
+ return content;
172782
+ const raw = source.slice(node2.startIndex, node2.endIndex);
172783
+ const m = raw.match(/^[a-zA-Z]*('''|"""|'|")([\s\S]*)\1$/);
172784
+ return m ? m[2] : null;
172785
+ }
172786
+
172362
172787
  // packages/contract-verifier/dist/extractor/enum/py-enums.js
172363
172788
  var ENUM_CONVENTION_NAME2 = /^(?:VALID|ALLOWED|KNOWN|ENUM)_/i;
172364
172789
  var ENUM_CONVENTION_SUFFIX2 = /_(?:VALUES|SET|CLASSIFICATIONS|STATUSES|KINDS|TYPES|OPTIONS|CHOICES)$/i;
172365
172790
  function extractPyEnumsFromFile(filePath, source, tree) {
172366
172791
  const out = [];
172792
+ const stringTable = collectStringConstantTable(tree.rootNode, source);
172367
172793
  walk7(tree.rootNode, (node2) => {
172368
172794
  if (node2.type === "class_definition") {
172369
172795
  const decl = extractEnumClass(node2, filePath, source);
@@ -172379,6 +172805,305 @@ function extractPyEnumsFromFile(filePath, source, tree) {
172379
172805
  }
172380
172806
  return true;
172381
172807
  });
172808
+ out.push(...synthesizeInstanceRegistryEnum(tree.rootNode, filePath, source));
172809
+ out.push(...synthesizeDiscriminatedUnionEnum(tree.rootNode, filePath, source));
172810
+ out.push(...synthesizeConstantClusterEnums(tree.rootNode, filePath, source, stringTable));
172811
+ return out;
172812
+ }
172813
+ function synthesizeDiscriminatedUnionEnum(root, filePath, source) {
172814
+ const discriminatorOf = /* @__PURE__ */ new Map();
172815
+ walk7(root, (node2) => {
172816
+ if (node2.type !== "class_definition")
172817
+ return true;
172818
+ const className = textOfField(node2, "name", source);
172819
+ const body = node2.childForFieldName("body");
172820
+ if (!className || !body)
172821
+ return true;
172822
+ for (let i = 0; i < body.namedChildCount; i++) {
172823
+ const stmt = body.namedChild(i);
172824
+ if (stmt?.type !== "expression_statement")
172825
+ continue;
172826
+ const assign = stmt.namedChild(0);
172827
+ if (assign?.type !== "assignment")
172828
+ continue;
172829
+ const left = assign.childForFieldName("left");
172830
+ const typeAnn = assign.childForFieldName("type");
172831
+ if (left?.type !== "identifier" || !typeAnn)
172832
+ continue;
172833
+ if (source.slice(left.startIndex, left.endIndex) !== "type")
172834
+ continue;
172835
+ const lit = literalStringOfAnnotation(typeAnn, source);
172836
+ if (lit !== null)
172837
+ discriminatorOf.set(className, lit);
172838
+ }
172839
+ return true;
172840
+ });
172841
+ if (discriminatorOf.size === 0)
172842
+ return [];
172843
+ const out = [];
172844
+ for (let i = 0; i < root.namedChildCount; i++) {
172845
+ const stmt = root.namedChild(i);
172846
+ if (stmt?.type !== "expression_statement")
172847
+ continue;
172848
+ const assign = stmt.namedChild(0);
172849
+ if (assign?.type !== "assignment")
172850
+ continue;
172851
+ const left = assign.childForFieldName("left");
172852
+ if (left?.type !== "identifier")
172853
+ continue;
172854
+ const aliasName = source.slice(left.startIndex, left.endIndex);
172855
+ const right = assign.childForFieldName("right");
172856
+ if (!right)
172857
+ continue;
172858
+ const memberClasses = unionMemberIdentifiers(right, source);
172859
+ if (memberClasses.length === 0)
172860
+ continue;
172861
+ const values = memberClasses.map((m) => discriminatorOf.get(m)).filter((v) => v !== void 0);
172862
+ if (values.length < 3)
172863
+ continue;
172864
+ out.push(mkEnum2(aliasName, values, "py-discriminated-union", root, filePath));
172865
+ }
172866
+ return out;
172867
+ }
172868
+ function literalStringOfAnnotation(ann, source) {
172869
+ let node2 = ann;
172870
+ if (node2.type === "type")
172871
+ node2 = node2.namedChild(0);
172872
+ if (!node2 || node2.type !== "generic_type" && node2.type !== "subscript")
172873
+ return null;
172874
+ const base = node2.namedChild(0);
172875
+ if (!base || !source.slice(base.startIndex, base.endIndex).endsWith("Literal"))
172876
+ return null;
172877
+ const strings = collectStringsDeep(node2, source);
172878
+ return strings.length === 1 ? strings[0] : null;
172879
+ }
172880
+ function collectStringsDeep(node2, source) {
172881
+ const out = [];
172882
+ const visit = (n) => {
172883
+ if (n.type === "string") {
172884
+ const v = stringValue(n, source);
172885
+ if (v !== null)
172886
+ out.push(v);
172887
+ return;
172888
+ }
172889
+ for (let i = 0; i < n.namedChildCount; i++) {
172890
+ const c = n.namedChild(i);
172891
+ if (c)
172892
+ visit(c);
172893
+ }
172894
+ };
172895
+ visit(node2);
172896
+ return out;
172897
+ }
172898
+ function unionMemberIdentifiers(node2, source) {
172899
+ if (node2.type === "subscript") {
172900
+ const value = node2.childForFieldName("value");
172901
+ if (value && source.slice(value.startIndex, value.endIndex).endsWith("Union")) {
172902
+ const out = [];
172903
+ for (let i = 0; i < node2.namedChildCount; i++) {
172904
+ const c = node2.namedChild(i);
172905
+ if (c && c !== value)
172906
+ collectTypeIdentifiers(c, out, source);
172907
+ }
172908
+ return out;
172909
+ }
172910
+ return [];
172911
+ }
172912
+ if (node2.type === "binary_operator") {
172913
+ const text = source.slice(node2.startIndex, node2.endIndex);
172914
+ if (text.includes("|")) {
172915
+ const out = [];
172916
+ collectTypeIdentifiers(node2, out, source);
172917
+ return out;
172918
+ }
172919
+ }
172920
+ return [];
172921
+ }
172922
+ function collectTypeIdentifiers(node2, out, source) {
172923
+ if (node2.type === "identifier") {
172924
+ out.push(source.slice(node2.startIndex, node2.endIndex));
172925
+ return;
172926
+ }
172927
+ for (let i = 0; i < node2.namedChildCount; i++) {
172928
+ const c = node2.namedChild(i);
172929
+ if (c)
172930
+ collectTypeIdentifiers(c, out, source);
172931
+ }
172932
+ }
172933
+ var MIN_PREFIX = 6;
172934
+ var MIN_CLUSTER_MEMBERS = 3;
172935
+ var MAX_CLUSTER_MEMBERS = 200;
172936
+ function synthesizeConstantClusterEnums(root, filePath, source, stringTable) {
172937
+ const named = [];
172938
+ for (const [name, node2] of stringTable) {
172939
+ const v = stringValue(node2, source, stringTable);
172940
+ if (v === null)
172941
+ continue;
172942
+ named.push({ name, value: v });
172943
+ }
172944
+ if (named.length < MIN_CLUSTER_MEMBERS)
172945
+ return [];
172946
+ named.sort((a, b) => a.value < b.value ? -1 : a.value > b.value ? 1 : 0);
172947
+ const clusters = [];
172948
+ let i = 0;
172949
+ while (i < named.length) {
172950
+ let j = i + 1;
172951
+ let prefix = named[i].value;
172952
+ while (j < named.length) {
172953
+ const next = commonPrefix(prefix, named[j].value);
172954
+ if (next.length < MIN_PREFIX)
172955
+ break;
172956
+ prefix = next;
172957
+ j++;
172958
+ }
172959
+ if (j - i >= MIN_CLUSTER_MEMBERS && j - i <= MAX_CLUSTER_MEMBERS && prefix.length >= MIN_PREFIX) {
172960
+ clusters.push({ prefix, members: named.slice(i, j) });
172961
+ }
172962
+ i = j === i + 1 ? i + 1 : j;
172963
+ }
172964
+ const out = [];
172965
+ for (const cluster of clusters) {
172966
+ const realMembers = cluster.members.filter((m) => m.value.length > cluster.prefix.length);
172967
+ if (realMembers.length < MIN_CLUSTER_MEMBERS)
172968
+ continue;
172969
+ let prefix = realMembers[0].value;
172970
+ for (let k = 1; k < realMembers.length; k++) {
172971
+ prefix = commonPrefix(prefix, realMembers[k].value);
172972
+ if (prefix.length < MIN_PREFIX)
172973
+ break;
172974
+ }
172975
+ if (prefix.length < MIN_PREFIX)
172976
+ continue;
172977
+ const trailingDelim = /[/\-_.:]$/.test(prefix);
172978
+ const allCleanTails = trailingDelim || realMembers.every((m) => /^[A-Za-z0-9_\-./:]+$/.test(m.value.slice(prefix.length)));
172979
+ if (!allCleanTails)
172980
+ continue;
172981
+ const trimmedName = prefix.replace(/[^A-Za-z0-9]+$/, "") || prefix;
172982
+ const firstNode = stringTable.get(realMembers[0].name);
172983
+ const lastNode = stringTable.get(realMembers[realMembers.length - 1].name);
172984
+ out.push({
172985
+ name: trimmedName,
172986
+ values: [...new Set(realMembers.map((m) => m.value))].sort(),
172987
+ shape: "py-constant-cluster",
172988
+ source: {
172989
+ filePath,
172990
+ lineStart: firstNode ? firstNode.startPosition.row + 1 : 1,
172991
+ lineEnd: lastNode ? lastNode.endPosition.row + 1 : 1
172992
+ }
172993
+ });
172994
+ }
172995
+ return out;
172996
+ }
172997
+ function commonPrefix(a, b) {
172998
+ const len = Math.min(a.length, b.length);
172999
+ let i = 0;
173000
+ while (i < len && a.charCodeAt(i) === b.charCodeAt(i))
173001
+ i++;
173002
+ return a.slice(0, i);
173003
+ }
173004
+ function synthesizeInstanceRegistryEnum(root, filePath, source) {
173005
+ const baseOf = /* @__PURE__ */ new Map();
173006
+ for (let i = 0; i < root.namedChildCount; i++) {
173007
+ const child = root.namedChild(i);
173008
+ const node2 = child?.type === "class_definition" ? child : child?.type === "decorated_definition" ? child.childForFieldName("definition") : null;
173009
+ if (node2?.type !== "class_definition")
173010
+ continue;
173011
+ const name = textOfField(node2, "name", source);
173012
+ const supers = node2.childForFieldName("superclasses");
173013
+ if (!name || !supers)
173014
+ continue;
173015
+ const first2 = supers.namedChild(0);
173016
+ if (first2?.type === "identifier")
173017
+ baseOf.set(name, source.slice(first2.startIndex, first2.endIndex));
173018
+ }
173019
+ const subclassCount = /* @__PURE__ */ new Map();
173020
+ for (const base of baseOf.values())
173021
+ subclassCount.set(base, (subclassCount.get(base) ?? 0) + 1);
173022
+ const categoricalBases = new Set([...subclassCount].filter(([, n]) => n >= 2).map(([b]) => b));
173023
+ if (categoricalBases.size === 0)
173024
+ return [];
173025
+ const resolveBase = (cls) => {
173026
+ let cur = cls;
173027
+ for (let hops = 0; hops < 8; hops++) {
173028
+ if (categoricalBases.has(cur))
173029
+ return cur;
173030
+ const next = baseOf.get(cur);
173031
+ if (!next)
173032
+ return null;
173033
+ cur = next;
173034
+ }
173035
+ return null;
173036
+ };
173037
+ const group = /* @__PURE__ */ new Map();
173038
+ const memberBase = /* @__PURE__ */ new Map();
173039
+ const deferred2 = [];
173040
+ for (let i = 0; i < root.namedChildCount; i++) {
173041
+ const stmt = root.namedChild(i);
173042
+ if (stmt?.type !== "expression_statement")
173043
+ continue;
173044
+ const assign = stmt.namedChild(0);
173045
+ if (assign?.type !== "assignment")
173046
+ continue;
173047
+ if (assign.childForFieldName("type"))
173048
+ continue;
173049
+ const left = assign.childForFieldName("left");
173050
+ if (left?.type !== "identifier")
173051
+ continue;
173052
+ const name = source.slice(left.startIndex, left.endIndex);
173053
+ if (!/^[A-Z][A-Z0-9_]*$/.test(name))
173054
+ continue;
173055
+ const right = assign.childForFieldName("right");
173056
+ if (!right)
173057
+ continue;
173058
+ if (right.type === "call") {
173059
+ const fn = right.childForFieldName("function");
173060
+ if (fn?.type !== "identifier")
173061
+ continue;
173062
+ const ctor = source.slice(fn.startIndex, fn.endIndex);
173063
+ const base = resolveBase(ctor);
173064
+ if (base) {
173065
+ if (!group.has(base))
173066
+ group.set(base, []);
173067
+ group.get(base).push(name);
173068
+ memberBase.set(name, base);
173069
+ }
173070
+ } else {
173071
+ deferred2.push({ name, rhs: right });
173072
+ }
173073
+ }
173074
+ for (const { name, rhs } of deferred2) {
173075
+ const ids = collectIdentifiers3(rhs, source);
173076
+ if (ids.length === 0)
173077
+ continue;
173078
+ const bases = new Set(ids.map((id) => memberBase.get(id)).filter((b) => !!b));
173079
+ if (bases.size === 1) {
173080
+ const base = [...bases][0];
173081
+ group.get(base).push(name);
173082
+ memberBase.set(name, base);
173083
+ }
173084
+ }
173085
+ const out = [];
173086
+ for (const [base, members] of group) {
173087
+ if (members.length < 3)
173088
+ continue;
173089
+ out.push(mkEnum2(base, members, "py-instance-registry", root, filePath));
173090
+ }
173091
+ return out;
173092
+ }
173093
+ function collectIdentifiers3(node2, source) {
173094
+ const out = [];
173095
+ const visit = (n) => {
173096
+ if (n.type === "identifier") {
173097
+ out.push(source.slice(n.startIndex, n.endIndex));
173098
+ return;
173099
+ }
173100
+ for (let i = 0; i < n.namedChildCount; i++) {
173101
+ const c = n.namedChild(i);
173102
+ if (c)
173103
+ visit(c);
173104
+ }
173105
+ };
173106
+ visit(node2);
172382
173107
  return out;
172383
173108
  }
172384
173109
  function extractEnumClass(node2, filePath, source) {
@@ -172455,6 +173180,22 @@ function extractAssignmentEnum(node2, filePath, source) {
172455
173180
  }
172456
173181
  return null;
172457
173182
  }
173183
+ if (right.type === "set" || right.type === "list") {
173184
+ const attrs = attributeSetValues(right, source);
173185
+ if (attrs) {
173186
+ return mkEnum2(name, attrs, right.type === "set" ? "py-set" : "py-list", node2, filePath);
173187
+ }
173188
+ }
173189
+ const diff = parseSetDifference(right, source);
173190
+ if (diff) {
173191
+ return {
173192
+ name,
173193
+ values: [],
173194
+ shape: "py-set-difference",
173195
+ source: { filePath, lineStart: node2.startPosition.row + 1, lineEnd: node2.endPosition.row + 1 },
173196
+ unresolved: diff
173197
+ };
173198
+ }
172458
173199
  if (!nameLooksLikeEnumConst2(name))
172459
173200
  return null;
172460
173201
  if (right.type === "set" || right.type === "list") {
@@ -172481,6 +173222,60 @@ function extractAssignmentEnum(node2, filePath, source) {
172481
173222
  function nameLooksLikeEnumConst2(name) {
172482
173223
  return ENUM_CONVENTION_NAME2.test(name) || ENUM_CONVENTION_SUFFIX2.test(name);
172483
173224
  }
173225
+ function attributeSetValues(node2, source) {
173226
+ const out = [];
173227
+ for (let i = 0; i < node2.namedChildCount; i++) {
173228
+ const c = node2.namedChild(i);
173229
+ if (!c)
173230
+ continue;
173231
+ if (c.type !== "attribute")
173232
+ return null;
173233
+ const attr = c.childForFieldName("attribute");
173234
+ if (!attr)
173235
+ return null;
173236
+ out.push(source.slice(attr.startIndex, attr.endIndex));
173237
+ }
173238
+ return out.length >= 2 ? out : null;
173239
+ }
173240
+ function parseSetDifference(node2, source) {
173241
+ let n = node2;
173242
+ if (n.type === "call") {
173243
+ const fn = n.childForFieldName("function");
173244
+ const fnName = fn ? source.slice(fn.startIndex, fn.endIndex) : "";
173245
+ if (/^(list|set|frozenset|tuple)$/.test(fnName)) {
173246
+ const inner = n.childForFieldName("arguments")?.namedChild(0);
173247
+ if (inner)
173248
+ n = inner;
173249
+ }
173250
+ }
173251
+ if (n.type !== "binary_operator")
173252
+ return null;
173253
+ if (!source.slice(n.startIndex, n.endIndex).includes("-"))
173254
+ return null;
173255
+ const left = n.childForFieldName("left");
173256
+ const right = n.childForFieldName("right");
173257
+ if (!left || !right)
173258
+ return null;
173259
+ const base = enumOperandName(left, source);
173260
+ const minus = enumOperandName(right, source);
173261
+ return base && minus ? { base, minus } : null;
173262
+ }
173263
+ function enumOperandName(node2, source) {
173264
+ let n = node2;
173265
+ if (n.type === "call") {
173266
+ const inner = n.childForFieldName("arguments")?.namedChild(0);
173267
+ if (inner)
173268
+ n = inner;
173269
+ }
173270
+ if (n.type === "identifier")
173271
+ return source.slice(n.startIndex, n.endIndex);
173272
+ if (n.type === "attribute") {
173273
+ const a = n.childForFieldName("attribute");
173274
+ if (a)
173275
+ return source.slice(a.startIndex, a.endIndex);
173276
+ }
173277
+ return null;
173278
+ }
172484
173279
  function collectStringChildren(node2, source) {
172485
173280
  const out = [];
172486
173281
  for (let i = 0; i < node2.namedChildCount; i++) {
@@ -172493,24 +173288,6 @@ function collectStringChildren(node2, source) {
172493
173288
  }
172494
173289
  return out;
172495
173290
  }
172496
- function stringValue(node2, source) {
172497
- let content = "";
172498
- let sawContent = false;
172499
- for (let i = 0; i < node2.namedChildCount; i++) {
172500
- const c = node2.namedChild(i);
172501
- if (c?.type === "string_content") {
172502
- content += source.slice(c.startIndex, c.endIndex);
172503
- sawContent = true;
172504
- } else if (c?.type === "interpolation" || c?.type === "format_specifier") {
172505
- return null;
172506
- }
172507
- }
172508
- if (sawContent)
172509
- return content;
172510
- const raw = source.slice(node2.startIndex, node2.endIndex);
172511
- const m = raw.match(/^[a-zA-Z]*('''|"""|'|")([\s\S]*)\1$/);
172512
- return m ? m[2] : null;
172513
- }
172514
173291
  function textOfField(node2, field, source) {
172515
173292
  const c = node2.childForFieldName(field);
172516
173293
  return c ? source.slice(c.startIndex, c.endIndex) : "";
@@ -172567,8 +173344,27 @@ async function extractEnumsFromDir(rootDir) {
172567
173344
  source: { filePath: entries[0].filePath, lineStart: 1, lineEnd: 1 }
172568
173345
  });
172569
173346
  }
172570
- const seen = /* @__PURE__ */ new Map();
173347
+ const norm = (s) => s.replace(/[^A-Za-z0-9]/g, "").toLowerCase();
173348
+ const valuesByName = /* @__PURE__ */ new Map();
173349
+ for (const e of raw) {
173350
+ if (e.shape !== "py-set-difference")
173351
+ valuesByName.set(norm(e.name), e.values);
173352
+ }
172571
173353
  for (const e of raw) {
173354
+ if (e.shape !== "py-set-difference" || !e.unresolved)
173355
+ continue;
173356
+ const baseVals = valuesByName.get(norm(e.unresolved.base));
173357
+ const minusVals = valuesByName.get(norm(e.unresolved.minus));
173358
+ if (baseVals && minusVals) {
173359
+ const minusSet = new Set(minusVals.map((v) => v.replace(/[^A-Za-z0-9]/g, "").toLowerCase()));
173360
+ e.values = [...new Set(baseVals.filter((v) => !minusSet.has(v.replace(/[^A-Za-z0-9]/g, "").toLowerCase())))].sort();
173361
+ e.shape = "py-set";
173362
+ delete e.unresolved;
173363
+ }
173364
+ }
173365
+ const resolved = raw.filter((e) => e.shape !== "py-set-difference");
173366
+ const seen = /* @__PURE__ */ new Map();
173367
+ for (const e of resolved) {
172572
173368
  const key2 = `${e.name}|${e.values.join(",")}`;
172573
173369
  if (!seen.has(key2))
172574
173370
  seen.set(key2, e);
@@ -173226,6 +174022,13 @@ function walk9(node2, visit) {
173226
174022
  var UNPARSEABLE2 = Symbol("unparseable");
173227
174023
  function extractPyConstantsFromFile(filePath, source, tree) {
173228
174024
  const out = [];
174025
+ const stringTable = collectStringConstantTable(tree.rootNode, source);
174026
+ walk10(tree.rootNode, (node2) => {
174027
+ if (node2.type === "class_definition") {
174028
+ out.push(...extractSettingsFields(node2, source, filePath, stringTable));
174029
+ }
174030
+ return true;
174031
+ });
173229
174032
  walk10(tree.rootNode, (node2) => {
173230
174033
  if (node2.type !== "assignment")
173231
174034
  return true;
@@ -173235,23 +174038,169 @@ function extractPyConstantsFromFile(filePath, source, tree) {
173235
174038
  const right = node2.childForFieldName("right");
173236
174039
  if (!right)
173237
174040
  return true;
173238
- const value = parseLiteral(right, source);
173239
- if (value === UNPARSEABLE2)
174041
+ const name = source.slice(left.startIndex, left.endIndex);
174042
+ const pos = { filePath, lineStart: node2.startPosition.row + 1, lineEnd: node2.endPosition.row + 1 };
174043
+ const value = parseLiteral(right, source, stringTable);
174044
+ if (value !== UNPARSEABLE2) {
174045
+ out.push({ name, value, shape: "const-literal", source: pos });
174046
+ return true;
174047
+ }
174048
+ if (!node2.childForFieldName("type"))
174049
+ return true;
174050
+ if (right.type !== "call")
174051
+ return true;
174052
+ const fn = right.childForFieldName("function");
174053
+ if (!fn)
173240
174054
  return true;
174055
+ if (!source.slice(fn.startIndex, fn.endIndex).endsWith("Field"))
174056
+ return true;
174057
+ const callArgs = right.childForFieldName("arguments");
174058
+ if (!callArgs)
174059
+ return true;
174060
+ let defaultVal = UNPARSEABLE2;
174061
+ const aliasStrings = [];
174062
+ for (let i = 0; i < callArgs.namedChildCount; i++) {
174063
+ const arg = callArgs.namedChild(i);
174064
+ if (arg?.type !== "keyword_argument")
174065
+ continue;
174066
+ const kwName = arg.childForFieldName("name");
174067
+ const kwVal = arg.childForFieldName("value");
174068
+ if (!kwName || !kwVal)
174069
+ continue;
174070
+ const kw = source.slice(kwName.startIndex, kwName.endIndex);
174071
+ if (kw === "default" && defaultVal === UNPARSEABLE2) {
174072
+ defaultVal = parseLiteral(kwVal, source, stringTable);
174073
+ } else if (kw === "validation_alias") {
174074
+ aliasStrings.push(...extractAliasChoiceStrings(kwVal, source, stringTable));
174075
+ }
174076
+ }
174077
+ if (defaultVal === UNPARSEABLE2)
174078
+ return true;
174079
+ out.push({ name, value: defaultVal, shape: "const-literal", source: pos });
174080
+ for (const alias of aliasStrings) {
174081
+ out.push({ name: alias, value: defaultVal, shape: "const-literal", source: pos });
174082
+ }
174083
+ return true;
174084
+ });
174085
+ return out;
174086
+ }
174087
+ function extractSettingsFields(classNode, source, filePath, stringTable) {
174088
+ const body = classNode.childForFieldName("body");
174089
+ if (!body)
174090
+ return [];
174091
+ const scope = deriveSettingsScope(body, source, stringTable);
174092
+ if (scope === null)
174093
+ return [];
174094
+ const out = [];
174095
+ for (let i = 0; i < body.namedChildCount; i++) {
174096
+ const stmt = body.namedChild(i);
174097
+ if (stmt?.type !== "expression_statement")
174098
+ continue;
174099
+ const assign = stmt.namedChild(0);
174100
+ if (assign?.type !== "assignment")
174101
+ continue;
174102
+ if (!assign.childForFieldName("type"))
174103
+ continue;
174104
+ const left = assign.childForFieldName("left");
174105
+ if (left?.type !== "identifier")
174106
+ continue;
174107
+ const fieldName = source.slice(left.startIndex, left.endIndex);
174108
+ if (fieldName === "model_config")
174109
+ continue;
174110
+ const right = assign.childForFieldName("right");
174111
+ if (!right)
174112
+ continue;
174113
+ const value = fieldDefaultValue(right, source, stringTable);
174114
+ if (value === UNPARSEABLE2)
174115
+ continue;
174116
+ const name = `${scope}_${fieldName}`.toUpperCase();
173241
174117
  out.push({
173242
- name: source.slice(left.startIndex, left.endIndex),
174118
+ name,
173243
174119
  value,
173244
- shape: "const-literal",
173245
- source: { filePath, lineStart: node2.startPosition.row + 1, lineEnd: node2.endPosition.row + 1 }
174120
+ shape: "settings-field",
174121
+ source: { filePath, lineStart: assign.startPosition.row + 1, lineEnd: assign.endPosition.row + 1 }
173246
174122
  });
173247
- return true;
173248
- });
174123
+ }
173249
174124
  return out;
173250
174125
  }
173251
- function parseLiteral(node2, source) {
174126
+ function deriveSettingsScope(body, source, stringTable) {
174127
+ for (let i = 0; i < body.namedChildCount; i++) {
174128
+ const stmt = body.namedChild(i);
174129
+ if (stmt?.type !== "expression_statement")
174130
+ continue;
174131
+ const assign = stmt.namedChild(0);
174132
+ if (assign?.type !== "assignment")
174133
+ continue;
174134
+ const left = assign.childForFieldName("left");
174135
+ if (left?.type !== "identifier")
174136
+ continue;
174137
+ if (source.slice(left.startIndex, left.endIndex) !== "model_config")
174138
+ continue;
174139
+ const right = assign.childForFieldName("right");
174140
+ if (right?.type !== "call")
174141
+ continue;
174142
+ const args = right.childForFieldName("arguments");
174143
+ if (!args)
174144
+ continue;
174145
+ for (let j = 0; j < args.namedChildCount; j++) {
174146
+ const arg = args.namedChild(j);
174147
+ if (arg?.type !== "keyword_argument")
174148
+ continue;
174149
+ const kw = arg.childForFieldName("name");
174150
+ const val = arg.childForFieldName("value");
174151
+ if (kw && val && source.slice(kw.startIndex, kw.endIndex) === "env_prefix" && val.type === "string") {
174152
+ const prefix = stringValue(val, source, stringTable);
174153
+ if (prefix)
174154
+ return prefix.replace(/_+$/, "").toLowerCase();
174155
+ }
174156
+ }
174157
+ for (let j = 0; j < args.namedChildCount; j++) {
174158
+ const arg = args.namedChild(j);
174159
+ if (arg?.type !== "tuple")
174160
+ continue;
174161
+ const parts = [];
174162
+ for (let k = 0; k < arg.namedChildCount; k++) {
174163
+ const el = arg.namedChild(k);
174164
+ if (el?.type === "string") {
174165
+ const v = stringValue(el, source, stringTable);
174166
+ if (v)
174167
+ parts.push(v);
174168
+ }
174169
+ }
174170
+ if (parts.length > 0)
174171
+ return parts.join("_").toLowerCase();
174172
+ }
174173
+ }
174174
+ return null;
174175
+ }
174176
+ function fieldDefaultValue(right, source, stringTable) {
174177
+ const direct = parseLiteral(right, source, stringTable);
174178
+ if (direct !== UNPARSEABLE2)
174179
+ return direct;
174180
+ if (right.type !== "call")
174181
+ return UNPARSEABLE2;
174182
+ const fn = right.childForFieldName("function");
174183
+ if (!fn || !source.slice(fn.startIndex, fn.endIndex).endsWith("Field"))
174184
+ return UNPARSEABLE2;
174185
+ const args = right.childForFieldName("arguments");
174186
+ if (!args)
174187
+ return UNPARSEABLE2;
174188
+ for (let i = 0; i < args.namedChildCount; i++) {
174189
+ const arg = args.namedChild(i);
174190
+ if (arg?.type !== "keyword_argument")
174191
+ continue;
174192
+ const kw = arg.childForFieldName("name");
174193
+ const val = arg.childForFieldName("value");
174194
+ if (kw && val && source.slice(kw.startIndex, kw.endIndex) === "default") {
174195
+ return parseLiteral(val, source, stringTable);
174196
+ }
174197
+ }
174198
+ return UNPARSEABLE2;
174199
+ }
174200
+ function parseLiteral(node2, source, stringTable) {
173252
174201
  switch (node2.type) {
173253
174202
  case "string": {
173254
- const v = stringValue2(node2, source);
174203
+ const v = stringValue(node2, source, stringTable);
173255
174204
  return v === null ? UNPARSEABLE2 : v;
173256
174205
  }
173257
174206
  case "integer":
@@ -173275,7 +174224,7 @@ function parseLiteral(node2, source) {
173275
174224
  const c = node2.namedChild(i);
173276
174225
  if (!c)
173277
174226
  continue;
173278
- const v = parseLiteral(c, source);
174227
+ const v = parseLiteral(c, source, stringTable);
173279
174228
  if (v === UNPARSEABLE2)
173280
174229
  return UNPARSEABLE2;
173281
174230
  items.push(v);
@@ -173294,12 +174243,12 @@ function parseLiteral(node2, source) {
173294
174243
  continue;
173295
174244
  let key2 = null;
173296
174245
  if (keyNode.type === "string")
173297
- key2 = stringValue2(keyNode, source);
174246
+ key2 = stringValue(keyNode, source, stringTable);
173298
174247
  else if (keyNode.type === "identifier")
173299
174248
  key2 = source.slice(keyNode.startIndex, keyNode.endIndex);
173300
174249
  if (key2 === null)
173301
174250
  return UNPARSEABLE2;
173302
- const v = parseLiteral(valNode, source);
174251
+ const v = parseLiteral(valNode, source, stringTable);
173303
174252
  if (v === UNPARSEABLE2)
173304
174253
  return UNPARSEABLE2;
173305
174254
  obj[key2] = v;
@@ -173310,23 +174259,28 @@ function parseLiteral(node2, source) {
173310
174259
  return UNPARSEABLE2;
173311
174260
  }
173312
174261
  }
173313
- function stringValue2(node2, source) {
173314
- let content = "";
173315
- let sawContent = false;
173316
- for (let i = 0; i < node2.namedChildCount; i++) {
173317
- const c = node2.namedChild(i);
173318
- if (c?.type === "string_content") {
173319
- content += source.slice(c.startIndex, c.endIndex);
173320
- sawContent = true;
173321
- } else if (c?.type === "interpolation") {
173322
- return null;
174262
+ function extractAliasChoiceStrings(node2, source, stringTable) {
174263
+ if (node2.type !== "call")
174264
+ return [];
174265
+ const fn = node2.childForFieldName("function");
174266
+ if (!fn)
174267
+ return [];
174268
+ const fnText = source.slice(fn.startIndex, fn.endIndex);
174269
+ if (!fnText.endsWith("AliasChoices"))
174270
+ return [];
174271
+ const args = node2.childForFieldName("arguments");
174272
+ if (!args)
174273
+ return [];
174274
+ const result = [];
174275
+ for (let i = 0; i < args.namedChildCount; i++) {
174276
+ const c = args.namedChild(i);
174277
+ if (c?.type === "string") {
174278
+ const v = stringValue(c, source, stringTable);
174279
+ if (v !== null)
174280
+ result.push(v);
173323
174281
  }
173324
174282
  }
173325
- if (sawContent)
173326
- return content;
173327
- const raw = source.slice(node2.startIndex, node2.endIndex);
173328
- const m = raw.match(/^[a-zA-Z]*('''|"""|'|")([\s\S]*)\1$/);
173329
- return m ? m[2] : null;
174283
+ return result;
173330
174284
  }
173331
174285
  function walk10(node2, visit) {
173332
174286
  const recurse = visit(node2);
@@ -173779,7 +174733,7 @@ function readTransitionMap(node2, s) {
173779
174733
  for (const elem of v.namedChildren) {
173780
174734
  if (elem.type !== "string")
173781
174735
  return null;
173782
- const text = stringValue3(elem, s);
174736
+ const text = stringValue2(elem, s);
173783
174737
  if (text === null)
173784
174738
  return null;
173785
174739
  tos.push(text);
@@ -173818,7 +174772,7 @@ function readFieldAssignment(node2, s) {
173818
174772
  return null;
173819
174773
  if (rhs.type !== "string")
173820
174774
  return null;
173821
- const value = stringValue3(rhs, s);
174775
+ const value = stringValue2(rhs, s);
173822
174776
  if (value === null)
173823
174777
  return null;
173824
174778
  return {
@@ -173925,13 +174879,13 @@ function matchPair(member, literal, s) {
173925
174879
  return [{
173926
174880
  receiver: s.source.slice(obj.startIndex, obj.endIndex),
173927
174881
  field: s.source.slice(prop.startIndex, prop.endIndex),
173928
- value: stringValue3(literal, s) ?? ""
174882
+ value: stringValue2(literal, s) ?? ""
173929
174883
  }];
173930
174884
  }
173931
174885
  function stripQuotes(str) {
173932
174886
  return str.replace(/^['"]|['"]$/g, "");
173933
174887
  }
173934
- function stringValue3(node2, s) {
174888
+ function stringValue2(node2, s) {
173935
174889
  if (node2.type !== "string")
173936
174890
  return null;
173937
174891
  const childType = s.lang === "python" ? "string_content" : "string_fragment";
@@ -174928,7 +175882,11 @@ async function extractOperationsFromDir(rootDir) {
174928
175882
  await eachParsedSource(rootDir, (s) => {
174929
175883
  if (s.lang !== "python")
174930
175884
  return;
174931
- for (const op of extractFastApiOperationsFromFile(s.filePath, s.source, s.tree)) {
175885
+ const pythonOps = [
175886
+ ...extractFastApiOperationsFromFile(s.filePath, s.source, s.tree),
175887
+ ...extractStarletteRoutesFromFile(s.filePath, s.source, s.tree)
175888
+ ];
175889
+ for (const op of pythonOps) {
174932
175890
  if (!seen.has(op.identity)) {
174933
175891
  expressOps.push(op);
174934
175892
  seen.add(op.identity);
@@ -176071,7 +177029,9 @@ import { randomUUID as randomUUID19 } from "node:crypto";
176071
177029
  function compareEnum(input) {
176072
177030
  const { ref, contract, codeEnums } = input;
176073
177031
  const drifts = [];
176074
- const nameMatches = matchByName(contract, codeEnums, ref.identity);
177032
+ const matched = matchByName(contract, codeEnums, ref.identity);
177033
+ const valueOnlyMatch = matched.byName.length === 0;
177034
+ const nameMatches = matched.byName.length > 0 ? matched.byName : bestValueMatch(contract, matched.byValue);
176075
177035
  if (nameMatches.length === 0) {
176076
177036
  drifts.push({
176077
177037
  id: randomUUID19(),
@@ -176087,21 +177047,37 @@ function compareEnum(input) {
176087
177047
  codeSide: "<no match>"
176088
177048
  });
176089
177049
  } else {
177050
+ const specNorm = new Map(contract.values.map((v) => [normalizeValue(v), v]));
177051
+ const cloudOnlyValues = new Set((contract.triggerSubsets ?? []).filter((s) => isEnvironmentGatedSubset(s.name)).flatMap((s) => s.values.map(normalizeValue)));
177052
+ const byName = /* @__PURE__ */ new Map();
176090
177053
  for (const m of nameMatches) {
176091
- const specNorm = new Map(contract.values.map((v) => [normalizeValue(v), v]));
176092
- const codeNorm = new Map(m.values.map((v) => [normalizeValue(v), v]));
176093
- const missing = contract.values.filter((v) => !codeNorm.has(normalizeValue(v)));
176094
- const extra = m.values.filter((v) => !specNorm.has(normalizeValue(v)));
177054
+ const key2 = normalizeName(m.name);
177055
+ if (!byName.has(key2))
177056
+ byName.set(key2, { values: /* @__PURE__ */ new Set(), repr: m });
177057
+ const g = byName.get(key2);
177058
+ for (const v of m.values)
177059
+ g.values.add(normalizeValue(v));
177060
+ }
177061
+ const specNormSet = new Set(contract.values.map(normalizeValue));
177062
+ for (const g of byName.values()) {
177063
+ if (valueOnlyMatch && isStrictSubset(g.values, specNormSet))
177064
+ continue;
177065
+ const missing = contract.values.filter((v) => !g.values.has(normalizeValue(v)) && !cloudOnlyValues.has(normalizeValue(v)));
176095
177066
  for (const v of missing) {
176096
- drifts.push(mkValueDrift(ref, "missing-value", v, m, contract.values));
177067
+ drifts.push(mkValueDrift(ref, "missing-value", v, g.repr, contract.values));
176097
177068
  }
177069
+ }
177070
+ for (const m of nameMatches) {
177071
+ if (isSynthesizedEnumShape(m.shape))
177072
+ continue;
177073
+ const extra = m.values.filter((v) => !specNorm.has(normalizeValue(v)));
176098
177074
  for (const v of extra) {
176099
177075
  drifts.push(mkValueDrift(ref, "extra-value", v, m, contract.values));
176100
177076
  }
176101
177077
  }
176102
177078
  }
176103
177079
  for (const subset of contract.triggerSubsets ?? []) {
176104
- const subsetMatches = matchSubsetByName(subset.name, codeEnums);
177080
+ const subsetMatches = matchSubsetByName(subset.name, subset.values, codeEnums);
176105
177081
  if (subsetMatches.length === 0) {
176106
177082
  drifts.push({
176107
177083
  id: randomUUID19(),
@@ -176142,13 +177118,17 @@ function normalizeName(s) {
176142
177118
  }
176143
177119
  return n;
176144
177120
  }
177121
+ function isEnvironmentGatedSubset(name) {
177122
+ return normalizeName(name) === "cloudonly";
177123
+ }
176145
177124
  function matchByName(contract, codeEnums, specName) {
176146
177125
  const target = normalizeName(specName);
176147
- const out = [];
177126
+ const byName = [];
177127
+ const byValue = [];
176148
177128
  for (const e of codeEnums) {
176149
177129
  const codeName = normalizeName(e.name);
176150
177130
  if (codeName === target) {
176151
- out.push(e);
177131
+ byName.push(e);
176152
177132
  continue;
176153
177133
  }
176154
177134
  if (codeName.includes(target) || target.includes(codeName)) {
@@ -176159,7 +177139,7 @@ function matchByName(contract, codeEnums, specName) {
176159
177139
  }
176160
177140
  }
176161
177141
  if (valueSetOverlap(contract.values, e.values) >= 0.5) {
176162
- out.push(e);
177142
+ byName.push(e);
176163
177143
  continue;
176164
177144
  }
176165
177145
  }
@@ -176168,21 +177148,40 @@ function matchByName(contract, codeEnums, specName) {
176168
177148
  const overlap = valueSetOverlap(contract.values, e.values);
176169
177149
  const sizeDiff = Math.abs(contract.values.length - e.values.length);
176170
177150
  if (overlap >= 0.6 && sizeDiff <= 2) {
176171
- out.push(e);
177151
+ byValue.push(e);
176172
177152
  }
176173
177153
  } else if (minLen >= 2) {
176174
177154
  const overlap = valueSetOverlap(contract.values, e.values);
176175
177155
  const sizeDiff = Math.abs(contract.values.length - e.values.length);
176176
177156
  if (overlap === 1 && sizeDiff === 0) {
176177
- out.push(e);
177157
+ byValue.push(e);
176178
177158
  }
176179
177159
  }
176180
177160
  }
176181
- return out;
177161
+ return { byName, byValue };
177162
+ }
177163
+ function bestValueMatch(contract, byValue) {
177164
+ if (byValue.length <= 1)
177165
+ return byValue;
177166
+ let best = -1;
177167
+ for (const e of byValue)
177168
+ best = Math.max(best, valueSetOverlap(contract.values, e.values));
177169
+ return byValue.filter((e) => valueSetOverlap(contract.values, e.values) === best);
177170
+ }
177171
+ function isSynthesizedEnumShape(shape) {
177172
+ return shape === "sibling-id-literal" || shape === "py-instance-registry" || shape === "py-discriminated-union" || shape === "py-constant-cluster";
176182
177173
  }
176183
177174
  function normalizeValue(v) {
176184
177175
  return v.replace(/[^A-Za-z0-9]/g, "").toLowerCase();
176185
177176
  }
177177
+ function isStrictSubset(codeValues, specNorm) {
177178
+ if (codeValues.size === 0 || codeValues.size >= specNorm.size)
177179
+ return false;
177180
+ for (const v of codeValues)
177181
+ if (!specNorm.has(v))
177182
+ return false;
177183
+ return true;
177184
+ }
176186
177185
  function valueSetOverlap(a, b) {
176187
177186
  const aSet = new Set(a.map(normalizeValue));
176188
177187
  const bSet = new Set(b.map(normalizeValue));
@@ -176190,9 +177189,21 @@ function valueSetOverlap(a, b) {
176190
177189
  const union = (/* @__PURE__ */ new Set([...aSet, ...bSet])).size;
176191
177190
  return union === 0 ? 0 : intersection / union;
176192
177191
  }
176193
- function matchSubsetByName(subsetName, codeEnums) {
177192
+ function matchSubsetByName(subsetName, subsetValues, codeEnums) {
176194
177193
  const target = normalizeName(subsetName);
176195
- return codeEnums.filter((e) => normalizeName(e.name) === target);
177194
+ const out = [];
177195
+ for (const e of codeEnums) {
177196
+ const codeName = normalizeName(e.name);
177197
+ if (codeName === target) {
177198
+ out.push(e);
177199
+ continue;
177200
+ }
177201
+ if (codeName.includes(target) || target.includes(codeName)) {
177202
+ if (valueSetOverlap(subsetValues, e.values) >= 0.5)
177203
+ out.push(e);
177204
+ }
177205
+ }
177206
+ return out;
176196
177207
  }
176197
177208
  function mkValueDrift(ref, kind, value, codeEnum, specValues) {
176198
177209
  const severity = kind === "missing-value" ? "high" : "medium";
@@ -176306,6 +177317,21 @@ function compareNamedConstant(input) {
176306
177317
  return false;
176307
177318
  });
176308
177319
  }
177320
+ if (matches.length === 0) {
177321
+ matches = codeConstants.filter((c) => {
177322
+ if (c.shape !== "settings-field")
177323
+ return false;
177324
+ const codeName = normalizeName2(c.name);
177325
+ if (codeName.length < 8 || !target.endsWith(codeName))
177326
+ return false;
177327
+ return contract.expectedValue === void 0 || deepEqual(
177328
+ contract.expectedValue,
177329
+ c.value,
177330
+ /*allowExtraCodeKeys*/
177331
+ true
177332
+ );
177333
+ });
177334
+ }
176309
177335
  if (matches.length === 0) {
176310
177336
  return [{
176311
177337
  id: randomUUID21(),
@@ -176491,6 +177517,8 @@ async function verify(opts) {
176491
177517
  if (!code2) {
176492
177518
  if (st === "planned" || st === "deferred" || st === "out-of-scope")
176493
177519
  continue;
177520
+ if (/^[A-Z]+ https?:\/\//.test(artifact.ref.identity))
177521
+ continue;
176494
177522
  drifts.push({
176495
177523
  id: cryptoRandomId(),
176496
177524
  type: "contract-drift",
@@ -178887,7 +179915,7 @@ async function main() {
178887
179915
  const app = createApp();
178888
179916
  const httpServer = createServer(app);
178889
179917
  setupSocket(httpServer);
178890
- await new Promise((resolve8, reject) => {
179918
+ await new Promise((resolve9, reject) => {
178891
179919
  httpServer.on("error", (err) => {
178892
179920
  if (err.code === "EADDRINUSE") {
178893
179921
  reject(new Error(
@@ -178918,7 +179946,7 @@ Stop it first, or set PORT to use a different port.`
178918
179946
  ""
178919
179947
  ]);
178920
179948
  log.info(`[Server] Listening on port ${port}`);
178921
- resolve8();
179949
+ resolve9();
178922
179950
  });
178923
179951
  });
178924
179952
  async function shutdown() {