@fairfox/polly 0.7.1 → 0.7.4

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 (41) hide show
  1. package/dist/src/background/index.d.ts +1 -1
  2. package/dist/src/background/index.js +10 -3
  3. package/dist/src/background/index.js.map +9 -9
  4. package/dist/src/background/message-router.js +10 -3
  5. package/dist/src/background/message-router.js.map +8 -8
  6. package/dist/src/index.d.ts +9 -9
  7. package/dist/src/index.js +74 -67
  8. package/dist/src/index.js.map +12 -12
  9. package/dist/src/shared/adapters/chrome/context-menus.chrome.d.ts +1 -1
  10. package/dist/src/shared/adapters/chrome/tabs.chrome.d.ts +2 -2
  11. package/dist/src/shared/adapters/context-menus.adapter.d.ts +1 -1
  12. package/dist/src/shared/adapters/index.d.ts +4 -4
  13. package/dist/src/shared/adapters/index.js +9 -2
  14. package/dist/src/shared/adapters/index.js.map +6 -6
  15. package/dist/src/shared/adapters/tabs.adapter.d.ts +2 -2
  16. package/dist/src/shared/lib/context-helpers.js +10 -3
  17. package/dist/src/shared/lib/context-helpers.js.map +8 -8
  18. package/dist/src/shared/lib/message-bus.js +10 -3
  19. package/dist/src/shared/lib/message-bus.js.map +7 -7
  20. package/dist/src/shared/lib/state.js +10 -3
  21. package/dist/src/shared/lib/state.js.map +8 -8
  22. package/dist/src/shared/state/app-state.js +10 -3
  23. package/dist/src/shared/state/app-state.js.map +8 -8
  24. package/dist/tools/init/src/cli.js +17 -2
  25. package/dist/tools/init/src/cli.js.map +4 -4
  26. package/dist/tools/init/templates/pwa/package.json.template +3 -3
  27. package/dist/tools/teach/src/cli.js +2712 -2442
  28. package/dist/tools/teach/src/cli.js.map +11 -11
  29. package/dist/tools/teach/src/index.js +1379 -1379
  30. package/dist/tools/teach/src/index.js.map +10 -10
  31. package/dist/tools/test/src/adapters/index.d.ts +8 -8
  32. package/dist/tools/test/src/adapters/index.js +3 -2
  33. package/dist/tools/test/src/adapters/index.js.map +6 -6
  34. package/dist/tools/test/src/index.d.ts +3 -3
  35. package/dist/tools/test/src/index.js +3 -2
  36. package/dist/tools/test/src/index.js.map +6 -6
  37. package/dist/tools/verify/src/cli.js +190 -68
  38. package/dist/tools/verify/src/cli.js.map +8 -8
  39. package/dist/tools/visualize/src/cli.js +18 -3
  40. package/dist/tools/visualize/src/cli.js.map +4 -4
  41. package/package.json +27 -16
@@ -47,8 +47,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
47
47
  return require.apply(this, arguments);
48
48
  throw Error('Dynamic require of "' + x + '" is not supported');
49
49
  });
50
- // tools/analysis/src/types/index.ts
51
- var init_types = () => {};
52
50
 
53
51
  // node:path
54
52
  var exports_path = {};
@@ -390,6 +388,150 @@ var init_path = __esm(() => {
390
388
  path_default = posix;
391
389
  });
392
390
 
391
+ // tools/analysis/src/extract/adr.ts
392
+ var fs = (() => ({}));
393
+
394
+ class ADRExtractor {
395
+ projectRoot;
396
+ constructor(projectRoot) {
397
+ this.projectRoot = projectRoot;
398
+ }
399
+ extract() {
400
+ const adrDir = this.findADRDirectory();
401
+ if (!adrDir || !fs.existsSync(adrDir)) {
402
+ return {
403
+ adrs: [],
404
+ directory: adrDir || join(this.projectRoot, "docs", "adr")
405
+ };
406
+ }
407
+ const files = fs.readdirSync(adrDir).filter((file) => file.endsWith(".md")).map((file) => join(adrDir, file));
408
+ const adrs = [];
409
+ for (const file of files) {
410
+ try {
411
+ const adr = this.parseADR(file);
412
+ if (adr) {
413
+ adrs.push(adr);
414
+ }
415
+ } catch (error) {
416
+ if (process.env["POLLY_DEBUG"]) {
417
+ console.log(`[DEBUG] Failed to parse ADR file ${file}: ${error}`);
418
+ }
419
+ }
420
+ }
421
+ adrs.sort((a, b) => a.id.localeCompare(b.id));
422
+ return {
423
+ adrs,
424
+ directory: adrDir
425
+ };
426
+ }
427
+ findADRDirectory() {
428
+ const candidates = [
429
+ join(this.projectRoot, "docs", "adr"),
430
+ join(this.projectRoot, "docs", "architecture", "decisions"),
431
+ join(this.projectRoot, "adr"),
432
+ join(this.projectRoot, "architecture", "decisions")
433
+ ];
434
+ for (const candidate of candidates) {
435
+ if (fs.existsSync(candidate)) {
436
+ return candidate;
437
+ }
438
+ }
439
+ return null;
440
+ }
441
+ parseADR(filePath) {
442
+ const content = fs.readFileSync(filePath, "utf-8");
443
+ const fileName = basename(filePath, ".md");
444
+ const idMatch = fileName.match(/^(\d+)/);
445
+ const id = idMatch?.[1] ?? fileName;
446
+ const titleMatch = content.match(/^#\s+(.+)$/m);
447
+ const title = titleMatch?.[1]?.trim() ?? fileName;
448
+ const status = this.extractStatus(content);
449
+ const date = this.extractDate(content);
450
+ const context = this.extractSection(content, "Context");
451
+ const decision = this.extractSection(content, "Decision");
452
+ const consequences = this.extractSection(content, "Consequences");
453
+ const alternativesSection = this.extractSection(content, "Alternatives");
454
+ const alternatives = alternativesSection ? alternativesSection.split(`
455
+ `).filter((line) => line.trim().startsWith("-")).map((line) => line.replace(/^-\s*/, "").trim()) : undefined;
456
+ const links = this.extractLinks(content);
457
+ if (!context || !decision || !consequences) {
458
+ return null;
459
+ }
460
+ return {
461
+ id,
462
+ title,
463
+ status,
464
+ date,
465
+ context,
466
+ decision,
467
+ consequences,
468
+ ...alternatives && alternatives.length > 0 ? { alternatives } : {},
469
+ ...links.length > 0 ? { links } : {},
470
+ source: filePath
471
+ };
472
+ }
473
+ extractStatus(content) {
474
+ const statusMatch = content.match(/Status:\s*(\w+)/i);
475
+ if (!statusMatch)
476
+ return "accepted";
477
+ const status = statusMatch[1]?.toLowerCase();
478
+ if (status && ["proposed", "accepted", "deprecated", "superseded"].includes(status)) {
479
+ return status;
480
+ }
481
+ return "accepted";
482
+ }
483
+ extractDate(content) {
484
+ const dateMatch = content.match(/Date:\s*(\d{4}-\d{2}-\d{2})/i) || content.match(/(\d{4}-\d{2}-\d{2})/i);
485
+ if (dateMatch?.[1]) {
486
+ return dateMatch[1];
487
+ }
488
+ const isoDate = new Date().toISOString().split("T")[0];
489
+ return isoDate || new Date().toLocaleDateString("en-CA");
490
+ }
491
+ extractSection(content, sectionName) {
492
+ const regex = new RegExp(`##\\s+${sectionName}\\s*\\n([\\s\\S]*?)(?=\\n##|$)`, "i");
493
+ const match = content.match(regex);
494
+ return match?.[1]?.trim() ?? "";
495
+ }
496
+ extractLinks(content) {
497
+ const links = [];
498
+ const supersedesMatch = content.match(/Supersedes:\s*ADR-(\d+)/gi);
499
+ if (supersedesMatch) {
500
+ for (const match of supersedesMatch) {
501
+ const idMatch = match.match(/ADR-(\d+)/);
502
+ const id = idMatch?.[1];
503
+ if (id) {
504
+ links.push({
505
+ type: "supersedes",
506
+ adrId: id
507
+ });
508
+ }
509
+ }
510
+ }
511
+ const supersededByMatch = content.match(/Superseded by:\s*ADR-(\d+)/gi);
512
+ if (supersededByMatch) {
513
+ for (const match of supersededByMatch) {
514
+ const idMatch = match.match(/ADR-(\d+)/);
515
+ const id = idMatch?.[1];
516
+ if (id) {
517
+ links.push({
518
+ type: "superseded-by",
519
+ adrId: id
520
+ });
521
+ }
522
+ }
523
+ }
524
+ return links;
525
+ }
526
+ }
527
+ function extractADRs(projectRoot) {
528
+ const extractor = new ADRExtractor(projectRoot);
529
+ return extractor.extract();
530
+ }
531
+ var init_adr = __esm(() => {
532
+ init_path();
533
+ });
534
+
393
535
  // node:os
394
536
  var exports_os = {};
395
537
  __export(exports_os, {
@@ -20018,10 +20160,10 @@ var require_typescript = __commonJS((exports, module) => {
20018
20160
  function and(f, g) {
20019
20161
  return (arg) => f(arg) && g(arg);
20020
20162
  }
20021
- function or(...fs) {
20163
+ function or(...fs2) {
20022
20164
  return (...args) => {
20023
20165
  let lastResult;
20024
- for (const f of fs) {
20166
+ for (const f of fs2) {
20025
20167
  lastResult = f(...args);
20026
20168
  if (lastResult) {
20027
20169
  return lastResult;
@@ -21550,7 +21692,7 @@ ${lanes.join(`
21550
21692
  init_ts2();
21551
21693
  init_ts_performance();
21552
21694
  ((tracingEnabled2) => {
21553
- let fs;
21695
+ let fs2;
21554
21696
  let traceCount = 0;
21555
21697
  let traceFd = 0;
21556
21698
  let mode;
@@ -21559,9 +21701,9 @@ ${lanes.join(`
21559
21701
  const legend = [];
21560
21702
  function startTracing2(tracingMode, traceDir, configFilePath) {
21561
21703
  Debug.assert(!tracing, "Tracing already started");
21562
- if (fs === undefined) {
21704
+ if (fs2 === undefined) {
21563
21705
  try {
21564
- fs = (()=>{throw new Error("Cannot require module "+"fs");})();
21706
+ fs2 = (()=>{throw new Error("Cannot require module "+"fs");})();
21565
21707
  } catch (e) {
21566
21708
  throw new Error(`tracing requires having fs
21567
21709
  (original error: ${e.message || e})`);
@@ -21572,8 +21714,8 @@ ${lanes.join(`
21572
21714
  if (legendPath === undefined) {
21573
21715
  legendPath = combinePaths(traceDir, "legend.json");
21574
21716
  }
21575
- if (!fs.existsSync(traceDir)) {
21576
- fs.mkdirSync(traceDir, { recursive: true });
21717
+ if (!fs2.existsSync(traceDir)) {
21718
+ fs2.mkdirSync(traceDir, { recursive: true });
21577
21719
  }
21578
21720
  const countPart = mode === "build" ? `.${process.pid}-${++traceCount}` : mode === "server" ? `.${process.pid}` : ``;
21579
21721
  const tracePath = combinePaths(traceDir, `trace${countPart}.json`);
@@ -21583,10 +21725,10 @@ ${lanes.join(`
21583
21725
  tracePath,
21584
21726
  typesPath
21585
21727
  });
21586
- traceFd = fs.openSync(tracePath, "w");
21728
+ traceFd = fs2.openSync(tracePath, "w");
21587
21729
  tracing = tracingEnabled2;
21588
21730
  const meta = { cat: "__metadata", ph: "M", ts: 1000 * timestamp2(), pid: 1, tid: 1 };
21589
- fs.writeSync(traceFd, `[
21731
+ fs2.writeSync(traceFd, `[
21590
21732
  ` + [{ name: "process_name", args: { name: "tsc" }, ...meta }, { name: "thread_name", args: { name: "Main" }, ...meta }, { name: "TracingStartedInBrowser", ...meta, cat: "disabled-by-default-devtools.timeline" }].map((v) => JSON.stringify(v)).join(`,
21591
21733
  `));
21592
21734
  }
@@ -21594,10 +21736,10 @@ ${lanes.join(`
21594
21736
  function stopTracing() {
21595
21737
  Debug.assert(tracing, "Tracing is not in progress");
21596
21738
  Debug.assert(!!typeCatalog.length === (mode !== "server"));
21597
- fs.writeSync(traceFd, `
21739
+ fs2.writeSync(traceFd, `
21598
21740
  ]
21599
21741
  `);
21600
- fs.closeSync(traceFd);
21742
+ fs2.closeSync(traceFd);
21601
21743
  tracing = undefined;
21602
21744
  if (typeCatalog.length) {
21603
21745
  dumpTypes(typeCatalog);
@@ -21662,13 +21804,13 @@ ${lanes.join(`
21662
21804
  if (mode === "server" && phase === "checkTypes")
21663
21805
  return;
21664
21806
  mark("beginTracing");
21665
- fs.writeSync(traceFd, `,
21807
+ fs2.writeSync(traceFd, `,
21666
21808
  {"pid":1,"tid":1,"ph":"${eventType}","cat":"${phase}","ts":${time},"name":"${name}"`);
21667
21809
  if (extras)
21668
- fs.writeSync(traceFd, `,${extras}`);
21810
+ fs2.writeSync(traceFd, `,${extras}`);
21669
21811
  if (args)
21670
- fs.writeSync(traceFd, `,"args":${JSON.stringify(args)}`);
21671
- fs.writeSync(traceFd, `}`);
21812
+ fs2.writeSync(traceFd, `,"args":${JSON.stringify(args)}`);
21813
+ fs2.writeSync(traceFd, `}`);
21672
21814
  mark("endTracing");
21673
21815
  measure("Tracing", "beginTracing", "endTracing");
21674
21816
  }
@@ -21690,9 +21832,9 @@ ${lanes.join(`
21690
21832
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
21691
21833
  mark("beginDumpTypes");
21692
21834
  const typesPath = legend[legend.length - 1].typesPath;
21693
- const typesFd = fs.openSync(typesPath, "w");
21835
+ const typesFd = fs2.openSync(typesPath, "w");
21694
21836
  const recursionIdentityMap = /* @__PURE__ */ new Map;
21695
- fs.writeSync(typesFd, "[");
21837
+ fs2.writeSync(typesFd, "[");
21696
21838
  const numTypes = types2.length;
21697
21839
  for (let i2 = 0;i2 < numTypes; i2++) {
21698
21840
  const type2 = types2[i2];
@@ -21788,15 +21930,15 @@ ${lanes.join(`
21788
21930
  flags: Debug.formatTypeFlags(type2.flags).split("|"),
21789
21931
  display
21790
21932
  };
21791
- fs.writeSync(typesFd, JSON.stringify(descriptor));
21933
+ fs2.writeSync(typesFd, JSON.stringify(descriptor));
21792
21934
  if (i2 < numTypes - 1) {
21793
- fs.writeSync(typesFd, `,
21935
+ fs2.writeSync(typesFd, `,
21794
21936
  `);
21795
21937
  }
21796
21938
  }
21797
- fs.writeSync(typesFd, `]
21939
+ fs2.writeSync(typesFd, `]
21798
21940
  `);
21799
- fs.closeSync(typesFd);
21941
+ fs2.closeSync(typesFd);
21800
21942
  mark("endDumpTypes");
21801
21943
  measure("Dump types", "beginDumpTypes", "endDumpTypes");
21802
21944
  }
@@ -21804,7 +21946,7 @@ ${lanes.join(`
21804
21946
  if (!legendPath) {
21805
21947
  return;
21806
21948
  }
21807
- fs.writeFileSync(legendPath, JSON.stringify(legend));
21949
+ fs2.writeFileSync(legendPath, JSON.stringify(legend));
21808
21950
  }
21809
21951
  tracingEnabled2.dumpLegend = dumpLegend;
21810
21952
  })(tracingEnabled || (tracingEnabled = {}));
@@ -21817,7 +21959,7 @@ ${lanes.join(`
21817
21959
  return lowerCase ? name.toLowerCase() : name;
21818
21960
  }
21819
21961
  var SyntaxKind, NodeFlags, ModifierFlags, JsxFlags, RelationComparisonResult, GeneratedIdentifierFlags, TokenFlags, FlowFlags, CommentDirectiveType, OperationCanceledException, FileIncludeKind, FilePreprocessingDiagnosticsKind, EmitOnly, StructureIsReused, ExitStatus, MemberOverrideStatus, UnionReduction, ContextFlags, NodeBuilderFlags, TypeFormatFlags, SymbolFormatFlags, SymbolAccessibility, SyntheticSymbolKind, TypePredicateKind, TypeReferenceSerializationKind, SymbolFlags, EnumKind, CheckFlags, InternalSymbolName, NodeCheckFlags, TypeFlags, ObjectFlags, VarianceFlags, ElementFlags, AccessFlags, IndexFlags, JsxReferenceKind, SignatureKind, SignatureFlags, IndexKind, TypeMapKind, InferencePriority, InferenceFlags, Ternary, AssignmentDeclarationKind, DiagnosticCategory, ModuleResolutionKind, ModuleDetectionKind, WatchFileKind, WatchDirectoryKind, PollingWatchKind, ModuleKind, JsxEmit, ImportsNotUsedAsValues, NewLineKind, ScriptKind, ScriptTarget, LanguageVariant, WatchDirectoryFlags, CharacterCodes, Extension, TransformFlags, SnippetKind, EmitFlags, InternalEmitFlags, ExternalEmitHelpers, EmitHint, OuterExpressionKinds, LexicalEnvironmentFlags, BundleFileSectionKind, ListFormat, PragmaKindFlags, commentPragmas, JSDocParsingMode;
21820
- var init_types2 = __esm2({
21962
+ var init_types = __esm2({
21821
21963
  "src/compiler/types.ts"() {
21822
21964
  SyntaxKind = /* @__PURE__ */ ((SyntaxKind5) => {
21823
21965
  SyntaxKind5[SyntaxKind5["Unknown"] = 0] = "Unknown";
@@ -25138,7 +25280,7 @@ ${lanes.join(`
25138
25280
  var Diagnostics;
25139
25281
  var init_diagnosticInformationMap_generated = __esm2({
25140
25282
  "src/compiler/diagnosticInformationMap.generated.ts"() {
25141
- init_types2();
25283
+ init_types();
25142
25284
  Diagnostics = {
25143
25285
  Unterminated_string_literal: diag(1002, 1, "Unterminated_string_literal_1002", "Unterminated string literal."),
25144
25286
  Identifier_expected: diag(1003, 1, "Identifier_expected_1003", "Identifier expected."),
@@ -126749,7 +126891,7 @@ ${lanes.join(`
126749
126891
  init_performanceCore();
126750
126892
  init_perfLogger();
126751
126893
  init_tracing();
126752
- init_types2();
126894
+ init_types();
126753
126895
  init_sys();
126754
126896
  init_path2();
126755
126897
  init_diagnosticInformationMap_generated();
@@ -126859,13 +127001,13 @@ ${lanes.join(`
126859
127001
  `;
126860
127002
  }
126861
127003
  });
126862
- var init_types22 = __esm2({
127004
+ var init_types2 = __esm2({
126863
127005
  "src/jsTyping/types.ts"() {}
126864
127006
  });
126865
127007
  var init_ts_server = __esm2({
126866
127008
  "src/jsTyping/_namespaces/ts.server.ts"() {
126867
127009
  init_shared();
126868
- init_types22();
127010
+ init_types2();
126869
127011
  }
126870
127012
  });
126871
127013
  function isTypingUpToDate(cachedTyping, availableTypingVersions) {
@@ -212515,31 +212657,31 @@ interface CSSNumericArray{[Symbol.iterator]():IterableIterator<CSSNumericValue>;
212515
212657
  yield path2;
212516
212658
  }
212517
212659
  }
212518
- var fs = runtime.fs;
212660
+ var fs2 = runtime.fs;
212519
212661
 
212520
212662
  class RealFileSystemHost {
212521
212663
  async delete(path2) {
212522
212664
  try {
212523
- await fs.delete(path2);
212665
+ await fs2.delete(path2);
212524
212666
  } catch (err) {
212525
212667
  throw this.#getFileNotFoundErrorIfNecessary(err, path2);
212526
212668
  }
212527
212669
  }
212528
212670
  deleteSync(path2) {
212529
212671
  try {
212530
- fs.deleteSync(path2);
212672
+ fs2.deleteSync(path2);
212531
212673
  } catch (err) {
212532
212674
  throw this.#getFileNotFoundErrorIfNecessary(err, path2);
212533
212675
  }
212534
212676
  }
212535
212677
  readDirSync(dirPath) {
212536
212678
  try {
212537
- const entries = fs.readDirSync(dirPath);
212679
+ const entries = fs2.readDirSync(dirPath);
212538
212680
  for (const entry of entries) {
212539
212681
  entry.name = FileUtils.pathJoin(dirPath, entry.name);
212540
212682
  if (entry.isSymlink) {
212541
212683
  try {
212542
- const info = fs.statSync(entry.name);
212684
+ const info = fs2.statSync(entry.name);
212543
212685
  if (info != null) {
212544
212686
  entry.isDirectory = info.isDirectory();
212545
212687
  entry.isFile = info.isFile();
@@ -212554,84 +212696,84 @@ interface CSSNumericArray{[Symbol.iterator]():IterableIterator<CSSNumericValue>;
212554
212696
  }
212555
212697
  async readFile(filePath, encoding = "utf-8") {
212556
212698
  try {
212557
- return await fs.readFile(filePath, encoding);
212699
+ return await fs2.readFile(filePath, encoding);
212558
212700
  } catch (err) {
212559
212701
  throw this.#getFileNotFoundErrorIfNecessary(err, filePath);
212560
212702
  }
212561
212703
  }
212562
212704
  readFileSync(filePath, encoding = "utf-8") {
212563
212705
  try {
212564
- return fs.readFileSync(filePath, encoding);
212706
+ return fs2.readFileSync(filePath, encoding);
212565
212707
  } catch (err) {
212566
212708
  throw this.#getFileNotFoundErrorIfNecessary(err, filePath);
212567
212709
  }
212568
212710
  }
212569
212711
  async writeFile(filePath, fileText) {
212570
- return fs.writeFile(filePath, fileText);
212712
+ return fs2.writeFile(filePath, fileText);
212571
212713
  }
212572
212714
  writeFileSync(filePath, fileText) {
212573
- fs.writeFileSync(filePath, fileText);
212715
+ fs2.writeFileSync(filePath, fileText);
212574
212716
  }
212575
212717
  mkdir(dirPath) {
212576
- return fs.mkdir(dirPath);
212718
+ return fs2.mkdir(dirPath);
212577
212719
  }
212578
212720
  mkdirSync(dirPath) {
212579
- fs.mkdirSync(dirPath);
212721
+ fs2.mkdirSync(dirPath);
212580
212722
  }
212581
212723
  move(srcPath, destPath) {
212582
- return fs.move(srcPath, destPath);
212724
+ return fs2.move(srcPath, destPath);
212583
212725
  }
212584
212726
  moveSync(srcPath, destPath) {
212585
- fs.moveSync(srcPath, destPath);
212727
+ fs2.moveSync(srcPath, destPath);
212586
212728
  }
212587
212729
  copy(srcPath, destPath) {
212588
- return fs.copy(srcPath, destPath);
212730
+ return fs2.copy(srcPath, destPath);
212589
212731
  }
212590
212732
  copySync(srcPath, destPath) {
212591
- fs.copySync(srcPath, destPath);
212733
+ fs2.copySync(srcPath, destPath);
212592
212734
  }
212593
212735
  async fileExists(filePath) {
212594
212736
  try {
212595
- return (await fs.stat(filePath))?.isFile() ?? false;
212737
+ return (await fs2.stat(filePath))?.isFile() ?? false;
212596
212738
  } catch {
212597
212739
  return false;
212598
212740
  }
212599
212741
  }
212600
212742
  fileExistsSync(filePath) {
212601
212743
  try {
212602
- return fs.statSync(filePath)?.isFile() ?? false;
212744
+ return fs2.statSync(filePath)?.isFile() ?? false;
212603
212745
  } catch {
212604
212746
  return false;
212605
212747
  }
212606
212748
  }
212607
212749
  async directoryExists(dirPath) {
212608
212750
  try {
212609
- return (await fs.stat(dirPath))?.isDirectory() ?? false;
212751
+ return (await fs2.stat(dirPath))?.isDirectory() ?? false;
212610
212752
  } catch {
212611
212753
  return false;
212612
212754
  }
212613
212755
  }
212614
212756
  directoryExistsSync(dirPath) {
212615
212757
  try {
212616
- return fs.statSync(dirPath)?.isDirectory() ?? false;
212758
+ return fs2.statSync(dirPath)?.isDirectory() ?? false;
212617
212759
  } catch {
212618
212760
  return false;
212619
212761
  }
212620
212762
  }
212621
212763
  realpathSync(path2) {
212622
- return fs.realpathSync(path2);
212764
+ return fs2.realpathSync(path2);
212623
212765
  }
212624
212766
  getCurrentDirectory() {
212625
- return FileUtils.standardizeSlashes(fs.getCurrentDirectory());
212767
+ return FileUtils.standardizeSlashes(fs2.getCurrentDirectory());
212626
212768
  }
212627
212769
  glob(patterns) {
212628
- return fs.glob(backSlashesToForward(patterns));
212770
+ return fs2.glob(backSlashesToForward(patterns));
212629
212771
  }
212630
212772
  globSync(patterns) {
212631
- return fs.globSync(backSlashesToForward(patterns));
212773
+ return fs2.globSync(backSlashesToForward(patterns));
212632
212774
  }
212633
212775
  isCaseSensitive() {
212634
- return fs.isCaseSensitive();
212776
+ return fs2.isCaseSensitive();
212635
212777
  }
212636
212778
  #getDirectoryNotFoundErrorIfNecessary(err, path2) {
212637
212779
  return FileUtils.isNotExistsError(err) ? new exports.errors.DirectoryNotFoundError(FileUtils.getStandardizedAbsolutePath(this, path2)) : err;
@@ -235621,6 +235763,479 @@ Node text: ${this.#forgottenText}`;
235621
235763
  exports.setScopeForNode = setScopeForNode;
235622
235764
  });
235623
235765
 
235766
+ // tools/analysis/src/extract/contexts.ts
235767
+ class ContextAnalyzer {
235768
+ project;
235769
+ constructor(tsConfigPath) {
235770
+ this.project = new import_ts_morph.Project({
235771
+ tsConfigFilePath: tsConfigPath
235772
+ });
235773
+ }
235774
+ analyzeContext(contextType, entryPoint, handlers) {
235775
+ const sourceFile = this.project.getSourceFile(entryPoint);
235776
+ if (!sourceFile) {
235777
+ throw new Error(`Could not find source file: ${entryPoint}`);
235778
+ }
235779
+ const chromeAPIs = this.extractChromeAPIs(sourceFile);
235780
+ const dependencies = this.extractDependencies(sourceFile);
235781
+ const description = this.extractDescription(sourceFile);
235782
+ const components = this.isUIContext(contextType) ? this.extractComponents(sourceFile) : undefined;
235783
+ const contextHandlers = handlers.filter((h) => h.node === contextType);
235784
+ return {
235785
+ type: contextType,
235786
+ entryPoint,
235787
+ handlers: contextHandlers,
235788
+ chromeAPIs,
235789
+ externalAPIs: [],
235790
+ ...components ? { components } : {},
235791
+ dependencies,
235792
+ ...description ? { description } : {}
235793
+ };
235794
+ }
235795
+ extractChromeAPIs(sourceFile) {
235796
+ const apis = new Set;
235797
+ sourceFile.forEachDescendant((node) => {
235798
+ if (import_ts_morph.Node.isPropertyAccessExpression(node)) {
235799
+ const text = node.getText();
235800
+ this.detectAPIPattern(text, apis);
235801
+ }
235802
+ });
235803
+ return Array.from(apis).sort();
235804
+ }
235805
+ detectAPIPattern(text, apis) {
235806
+ if (text.startsWith("chrome.")) {
235807
+ const api = this.extractAPIFromPrefix(text, "chrome");
235808
+ if (api)
235809
+ apis.add(api);
235810
+ return;
235811
+ }
235812
+ if (text.startsWith("browser.")) {
235813
+ const api = this.extractAPIFromPrefix(text, "browser");
235814
+ if (api)
235815
+ apis.add(api);
235816
+ return;
235817
+ }
235818
+ if (text.includes("bus.adapters.")) {
235819
+ const api = this.extractAPIFromBusAdapter(text);
235820
+ if (api)
235821
+ apis.add(api);
235822
+ }
235823
+ }
235824
+ extractAPIFromPrefix(text, prefix) {
235825
+ const pattern = new RegExp(`^${prefix}\\.([^.(]+(?:\\.[^.(]+)?)`);
235826
+ const match = text.match(pattern);
235827
+ return match?.[1] || null;
235828
+ }
235829
+ extractAPIFromBusAdapter(text) {
235830
+ const match = text.match(/bus\.adapters\.([^.(]+)/);
235831
+ return match?.[1] || null;
235832
+ }
235833
+ extractDependencies(sourceFile) {
235834
+ const deps = [];
235835
+ for (const importDecl of sourceFile.getImportDeclarations()) {
235836
+ const moduleSpecifier = importDecl.getModuleSpecifierValue();
235837
+ deps.push(moduleSpecifier);
235838
+ }
235839
+ return deps;
235840
+ }
235841
+ extractDescription(sourceFile) {
235842
+ const firstStatement = sourceFile.getStatements()[0];
235843
+ if (!firstStatement)
235844
+ return;
235845
+ const leadingComments = firstStatement.getLeadingCommentRanges();
235846
+ if (leadingComments.length === 0)
235847
+ return;
235848
+ const comment = leadingComments[0].getText();
235849
+ const descMatch = comment.match(/@description\s+(.+?)(?:\n|$)/s);
235850
+ if (descMatch) {
235851
+ return descMatch[1].trim();
235852
+ }
235853
+ const lines = comment.split(`
235854
+ `).map((l) => l.replace(/^[\s*]+/, "").trim()).filter((l) => l && !l.startsWith("@"));
235855
+ return lines[0] || undefined;
235856
+ }
235857
+ extractComponents(sourceFile) {
235858
+ const components = [];
235859
+ sourceFile.forEachDescendant((node) => {
235860
+ this.extractFunctionComponent(node, sourceFile, components);
235861
+ this.extractArrowFunctionComponent(node, sourceFile, components);
235862
+ this.extractClassComponent(node, sourceFile, components);
235863
+ });
235864
+ return components;
235865
+ }
235866
+ extractFunctionComponent(node, sourceFile, components) {
235867
+ if (!import_ts_morph.Node.isFunctionDeclaration(node))
235868
+ return;
235869
+ const name = node.getName();
235870
+ if (!name || !this.looksLikeComponent(name, node))
235871
+ return;
235872
+ const description = this.extractJSDocDescription(node);
235873
+ components.push({
235874
+ name,
235875
+ type: "function",
235876
+ filePath: sourceFile.getFilePath(),
235877
+ line: node.getStartLineNumber(),
235878
+ props: this.extractProps(node),
235879
+ ...description ? { description } : {}
235880
+ });
235881
+ }
235882
+ extractArrowFunctionComponent(node, sourceFile, components) {
235883
+ if (!import_ts_morph.Node.isVariableDeclaration(node))
235884
+ return;
235885
+ const name = node.getName();
235886
+ const initializer = node.getInitializer();
235887
+ if (!name || !initializer)
235888
+ return;
235889
+ if (!import_ts_morph.Node.isArrowFunction(initializer) && !import_ts_morph.Node.isFunctionExpression(initializer))
235890
+ return;
235891
+ if (!this.looksLikeComponent(name, initializer))
235892
+ return;
235893
+ const description = this.extractJSDocDescription(node);
235894
+ components.push({
235895
+ name,
235896
+ type: "function",
235897
+ filePath: sourceFile.getFilePath(),
235898
+ line: node.getStartLineNumber(),
235899
+ props: this.extractProps(initializer),
235900
+ ...description ? { description } : {}
235901
+ });
235902
+ }
235903
+ extractClassComponent(node, sourceFile, components) {
235904
+ if (!import_ts_morph.Node.isClassDeclaration(node))
235905
+ return;
235906
+ const name = node.getName();
235907
+ if (!name || !this.looksLikeClassComponent(node))
235908
+ return;
235909
+ const description = this.extractJSDocDescription(node);
235910
+ components.push({
235911
+ name,
235912
+ type: "class",
235913
+ filePath: sourceFile.getFilePath(),
235914
+ line: node.getStartLineNumber(),
235915
+ props: this.extractPropsFromClass(node),
235916
+ ...description ? { description } : {}
235917
+ });
235918
+ }
235919
+ looksLikeComponent(name, node) {
235920
+ if (!/^[A-Z]/.test(name))
235921
+ return false;
235922
+ const body = node.getBody();
235923
+ if (!body)
235924
+ return false;
235925
+ let hasJSX = false;
235926
+ if (import_ts_morph.Node.isBlock(body)) {
235927
+ body.forEachDescendant((child) => {
235928
+ if (import_ts_morph.Node.isJsxElement(child) || import_ts_morph.Node.isJsxSelfClosingElement(child)) {
235929
+ hasJSX = true;
235930
+ }
235931
+ });
235932
+ } else if (import_ts_morph.Node.isJsxElement(body) || import_ts_morph.Node.isJsxSelfClosingElement(body)) {
235933
+ hasJSX = true;
235934
+ }
235935
+ return hasJSX;
235936
+ }
235937
+ looksLikeClassComponent(node) {
235938
+ const extendedTypes = node.getExtends();
235939
+ if (!extendedTypes)
235940
+ return false;
235941
+ const extendsText = extendedTypes.getText();
235942
+ return /Component|PureComponent/.test(extendsText);
235943
+ }
235944
+ extractProps(node) {
235945
+ const params = node.getParameters();
235946
+ if (params.length === 0)
235947
+ return [];
235948
+ const propsParam = params[0];
235949
+ const type2 = propsParam.getType();
235950
+ const props = [];
235951
+ for (const prop of type2.getProperties()) {
235952
+ props.push(prop.getName());
235953
+ }
235954
+ return props;
235955
+ }
235956
+ extractPropsFromClass(node) {
235957
+ const extendedTypes = node.getExtends();
235958
+ if (!extendedTypes)
235959
+ return [];
235960
+ const typeArgs = extendedTypes.getType().getTypeArguments();
235961
+ if (typeArgs.length === 0)
235962
+ return [];
235963
+ const propsType = typeArgs[0];
235964
+ const props = [];
235965
+ for (const prop of propsType.getProperties()) {
235966
+ props.push(prop.getName());
235967
+ }
235968
+ return props;
235969
+ }
235970
+ extractJSDocDescription(node) {
235971
+ const jsDocs = node.getJsDocs();
235972
+ if (jsDocs.length === 0)
235973
+ return;
235974
+ const description = jsDocs[0].getDescription().trim();
235975
+ return description || undefined;
235976
+ }
235977
+ isUIContext(contextType) {
235978
+ return ["popup", "options", "devtools"].includes(contextType);
235979
+ }
235980
+ }
235981
+ var import_ts_morph;
235982
+ var init_contexts = __esm(() => {
235983
+ import_ts_morph = __toESM(require_ts_morph(), 1);
235984
+ });
235985
+
235986
+ // tools/analysis/src/extract/flows.ts
235987
+ class FlowAnalyzer {
235988
+ project;
235989
+ handlers;
235990
+ constructor(tsConfigPath, handlers) {
235991
+ this.project = new import_ts_morph2.Project({
235992
+ tsConfigFilePath: tsConfigPath
235993
+ });
235994
+ this.handlers = handlers;
235995
+ }
235996
+ analyzeFlows() {
235997
+ const flows = [];
235998
+ const handlersByType = new Map;
235999
+ for (const handler of this.handlers) {
236000
+ if (!handlersByType.has(handler.messageType)) {
236001
+ handlersByType.set(handler.messageType, []);
236002
+ }
236003
+ handlersByType.get(handler.messageType)?.push(handler);
236004
+ }
236005
+ for (const [messageType, handlers] of handlersByType) {
236006
+ const senders = this.findMessageSenders(messageType);
236007
+ for (const sender of senders) {
236008
+ const recipients = handlers.map((h) => h.node);
236009
+ const sequence = this.buildSequence(messageType, sender, handlers);
236010
+ const flowMetadata = this.extractFlowMetadata(sender.file, sender.line);
236011
+ flows.push({
236012
+ messageType,
236013
+ from: sender.context,
236014
+ to: recipients,
236015
+ ...flowMetadata.trigger ? { trigger: flowMetadata.trigger } : {},
236016
+ ...flowMetadata.flowName ? { flowName: flowMetadata.flowName } : {},
236017
+ ...flowMetadata.description ? { description: flowMetadata.description } : {},
236018
+ sequence
236019
+ });
236020
+ }
236021
+ }
236022
+ return flows;
236023
+ }
236024
+ findMessageSenders(messageType) {
236025
+ const senders = [];
236026
+ for (const sourceFile of this.project.getSourceFiles()) {
236027
+ const filePath = sourceFile.getFilePath();
236028
+ const context = this.inferContext(filePath);
236029
+ sourceFile.forEachDescendant((node) => {
236030
+ this.processMessageSender(node, messageType, context, filePath, senders);
236031
+ });
236032
+ }
236033
+ return senders;
236034
+ }
236035
+ processMessageSender(node, messageType, context, filePath, senders) {
236036
+ if (!import_ts_morph2.Node.isCallExpression(node)) {
236037
+ return;
236038
+ }
236039
+ const expression = node.getExpression();
236040
+ if (import_ts_morph2.Node.isPropertyAccessExpression(expression)) {
236041
+ this.processPropertyAccessSender(node, expression, messageType, context, filePath, senders);
236042
+ } else if (import_ts_morph2.Node.isIdentifier(expression)) {
236043
+ this.processIdentifierSender(node, expression, messageType, context, filePath, senders);
236044
+ }
236045
+ }
236046
+ processPropertyAccessSender(node, expression, messageType, context, filePath, senders) {
236047
+ if (!import_ts_morph2.Node.isPropertyAccessExpression(expression)) {
236048
+ return;
236049
+ }
236050
+ const methodName = expression.getName();
236051
+ if (!this.isMessageSendMethod(methodName)) {
236052
+ return;
236053
+ }
236054
+ const args = node.getArguments();
236055
+ if (args.length === 0) {
236056
+ return;
236057
+ }
236058
+ const msgType = this.extractMessageTypeFromArg(args[0]);
236059
+ if (msgType === messageType) {
236060
+ senders.push({
236061
+ context,
236062
+ file: filePath,
236063
+ line: node.getStartLineNumber()
236064
+ });
236065
+ }
236066
+ }
236067
+ processIdentifierSender(node, expression, messageType, context, filePath, senders) {
236068
+ if (!import_ts_morph2.Node.isIdentifier(expression)) {
236069
+ return;
236070
+ }
236071
+ if (expression.getText() !== "postMessage") {
236072
+ return;
236073
+ }
236074
+ const args = node.getArguments();
236075
+ if (args.length === 0) {
236076
+ return;
236077
+ }
236078
+ const msgType = this.extractMessageTypeFromArg(args[0]);
236079
+ if (msgType === messageType) {
236080
+ senders.push({
236081
+ context,
236082
+ file: filePath,
236083
+ line: node.getStartLineNumber()
236084
+ });
236085
+ }
236086
+ }
236087
+ isMessageSendMethod(methodName) {
236088
+ return methodName === "send" || methodName === "emit" || methodName === "postMessage" || methodName === "broadcast";
236089
+ }
236090
+ buildSequence(messageType, sender, handlers) {
236091
+ const steps = [];
236092
+ let stepNumber = 1;
236093
+ steps.push({
236094
+ step: stepNumber++,
236095
+ action: `${sender.context}.send(${messageType})`,
236096
+ context: sender.context,
236097
+ location: {
236098
+ file: sender.file,
236099
+ line: sender.line
236100
+ }
236101
+ });
236102
+ for (const handler of handlers) {
236103
+ steps.push({
236104
+ step: stepNumber++,
236105
+ action: `${handler.node}.handle(${messageType})`,
236106
+ context: handler.node,
236107
+ location: handler.location
236108
+ });
236109
+ const subsends = this.findMessagesInHandler(handler);
236110
+ for (const subsend of subsends) {
236111
+ steps.push({
236112
+ step: stepNumber++,
236113
+ action: `${handler.node}.send(${subsend.messageType})`,
236114
+ context: handler.node,
236115
+ location: subsend.location
236116
+ });
236117
+ }
236118
+ }
236119
+ return steps;
236120
+ }
236121
+ findMessagesInHandler(handler) {
236122
+ const sends = [];
236123
+ const sourceFile = this.project.getSourceFile(handler.location.file);
236124
+ if (!sourceFile)
236125
+ return sends;
236126
+ const targetLine = handler.location.line;
236127
+ sourceFile.forEachDescendant((node) => {
236128
+ this.processSendCall(node, targetLine, handler.location.file, sends);
236129
+ });
236130
+ return sends;
236131
+ }
236132
+ processSendCall(node, targetLine, filePath, sends) {
236133
+ if (!import_ts_morph2.Node.isCallExpression(node)) {
236134
+ return;
236135
+ }
236136
+ const line = node.getStartLineNumber();
236137
+ if (!this.isNearLine(line, targetLine)) {
236138
+ return;
236139
+ }
236140
+ const expression = node.getExpression();
236141
+ if (!this.isSendOrEmitCall(expression)) {
236142
+ return;
236143
+ }
236144
+ const args = node.getArguments();
236145
+ if (args.length === 0) {
236146
+ return;
236147
+ }
236148
+ const messageType = this.extractMessageTypeFromArg(args[0]);
236149
+ if (messageType) {
236150
+ sends.push({
236151
+ messageType,
236152
+ location: { file: filePath, line }
236153
+ });
236154
+ }
236155
+ }
236156
+ isNearLine(line, targetLine) {
236157
+ return Math.abs(line - targetLine) < 20;
236158
+ }
236159
+ isSendOrEmitCall(expression) {
236160
+ if (!import_ts_morph2.Node.isPropertyAccessExpression(expression)) {
236161
+ return false;
236162
+ }
236163
+ const methodName = expression.getName();
236164
+ return methodName === "send" || methodName === "emit";
236165
+ }
236166
+ extractMessageTypeFromArg(arg) {
236167
+ if (import_ts_morph2.Node.isStringLiteral(arg)) {
236168
+ return arg.getLiteralValue();
236169
+ }
236170
+ if (import_ts_morph2.Node.isObjectLiteralExpression(arg)) {
236171
+ return this.extractMessageTypeFromObject(arg);
236172
+ }
236173
+ return;
236174
+ }
236175
+ extractMessageTypeFromObject(obj) {
236176
+ if (!import_ts_morph2.Node.isObjectLiteralExpression(obj)) {
236177
+ return;
236178
+ }
236179
+ const typeProperty = obj.getProperty("type");
236180
+ if (!typeProperty || !import_ts_morph2.Node.isPropertyAssignment(typeProperty)) {
236181
+ return;
236182
+ }
236183
+ const initializer = typeProperty.getInitializer();
236184
+ if (!initializer || !import_ts_morph2.Node.isStringLiteral(initializer)) {
236185
+ return;
236186
+ }
236187
+ return initializer.getLiteralValue();
236188
+ }
236189
+ extractFlowMetadata(filePath, lineNumber) {
236190
+ const sourceFile = this.project.getSourceFile(filePath);
236191
+ if (!sourceFile)
236192
+ return {};
236193
+ let targetNode = null;
236194
+ sourceFile.forEachDescendant((node) => {
236195
+ if (node.getStartLineNumber() === lineNumber) {
236196
+ targetNode = node;
236197
+ }
236198
+ });
236199
+ if (!targetNode)
236200
+ return {};
236201
+ const jsDocs = targetNode.getJsDocs?.() || [];
236202
+ if (jsDocs.length === 0)
236203
+ return {};
236204
+ const comment = jsDocs[0].getText();
236205
+ const flowMatch = comment.match(/@flow\s+([^\s]+)/);
236206
+ const flowName = flowMatch ? flowMatch[1] : undefined;
236207
+ const triggerMatch = comment.match(/@trigger\s+(.+?)(?:\n|$)/);
236208
+ const trigger = triggerMatch ? triggerMatch[1].trim() : undefined;
236209
+ const descMatch = comment.match(/@description\s+(.+?)(?:\n|$)/s);
236210
+ const description = descMatch ? descMatch[1].trim() : undefined;
236211
+ return { trigger, flowName, description };
236212
+ }
236213
+ inferContext(filePath) {
236214
+ const path = filePath.toLowerCase();
236215
+ const contextPatterns = [
236216
+ { context: "background", patterns: ["/background/", "\\background\\"] },
236217
+ { context: "content", patterns: ["/content/", "\\content\\"] },
236218
+ { context: "popup", patterns: ["/popup/", "\\popup\\"] },
236219
+ { context: "devtools", patterns: ["/devtools/", "\\devtools\\"] },
236220
+ { context: "options", patterns: ["/options/", "\\options\\"] },
236221
+ { context: "offscreen", patterns: ["/offscreen/", "\\offscreen\\"] },
236222
+ { context: "server", patterns: ["/server/", "\\server\\", "/server."] },
236223
+ { context: "client", patterns: ["/client/", "\\client\\", "/client."] },
236224
+ { context: "worker", patterns: ["/worker/", "\\worker\\", "service-worker"] }
236225
+ ];
236226
+ for (const { context, patterns } of contextPatterns) {
236227
+ if (patterns.some((pattern) => path.includes(pattern))) {
236228
+ return context;
236229
+ }
236230
+ }
236231
+ return "unknown";
236232
+ }
236233
+ }
236234
+ var import_ts_morph2;
236235
+ var init_flows = __esm(() => {
236236
+ import_ts_morph2 = __toESM(require_ts_morph(), 1);
236237
+ });
236238
+
235624
236239
  // tools/analysis/src/extract/relationships.ts
235625
236240
  class RelationshipExtractor {
235626
236241
  extractFromHandler(handlerNode, sourceFile, handlerName) {
@@ -235635,16 +236250,16 @@ class RelationshipExtractor {
235635
236250
  });
235636
236251
  }
235637
236252
  processDescendantNode(descendant, sourceFile, handlerName, relationships, visited) {
235638
- if (import_ts_morph.Node.isCallExpression(descendant)) {
236253
+ if (import_ts_morph3.Node.isCallExpression(descendant)) {
235639
236254
  this.processCallExpression(descendant, sourceFile, handlerName, relationships, visited);
235640
236255
  }
235641
- if (import_ts_morph.Node.isAwaitExpression(descendant)) {
236256
+ if (import_ts_morph3.Node.isAwaitExpression(descendant)) {
235642
236257
  const rel = this.extractFromDatabaseCall(descendant, handlerName);
235643
236258
  if (rel) {
235644
236259
  relationships.push(rel);
235645
236260
  }
235646
236261
  }
235647
- if (import_ts_morph.Node.isCallExpression(descendant) && descendant.getExpression().getText() === "fetch") {
236262
+ if (import_ts_morph3.Node.isCallExpression(descendant) && descendant.getExpression().getText() === "fetch") {
235648
236263
  const rel = this.extractFromFetchCall(descendant, handlerName);
235649
236264
  if (rel) {
235650
236265
  relationships.push(rel);
@@ -235653,11 +236268,11 @@ class RelationshipExtractor {
235653
236268
  }
235654
236269
  processCallExpression(callExpr, sourceFile, handlerName, relationships, visited) {
235655
236270
  const expr = callExpr.getExpression();
235656
- if (import_ts_morph.Node.isIdentifier(expr)) {
236271
+ if (import_ts_morph3.Node.isIdentifier(expr)) {
235657
236272
  this.processIdentifierCall(expr, sourceFile, handlerName, relationships, visited);
235658
236273
  return;
235659
236274
  }
235660
- if (import_ts_morph.Node.isPropertyAccessExpression(expr)) {
236275
+ if (import_ts_morph3.Node.isPropertyAccessExpression(expr)) {
235661
236276
  const rel2 = this.extractFromPropertyAccess(expr, handlerName);
235662
236277
  if (rel2) {
235663
236278
  relationships.push(rel2);
@@ -235738,7 +236353,7 @@ class RelationshipExtractor {
235738
236353
  }
235739
236354
  let functionName = exprText;
235740
236355
  let targetComponent = null;
235741
- if (import_ts_morph.Node.isPropertyAccessExpression(expr)) {
236356
+ if (import_ts_morph3.Node.isPropertyAccessExpression(expr)) {
235742
236357
  const objectExpr = expr.getExpression();
235743
236358
  const objectName = objectExpr.getText();
235744
236359
  const methodName = expr.getName();
@@ -235763,13 +236378,13 @@ class RelationshipExtractor {
235763
236378
  };
235764
236379
  }
235765
236380
  extractFromPropertyAccess(propAccess, handlerName) {
235766
- if (!import_ts_morph.Node.isPropertyAccessExpression(propAccess)) {
236381
+ if (!import_ts_morph3.Node.isPropertyAccessExpression(propAccess)) {
235767
236382
  return null;
235768
236383
  }
235769
236384
  const fullChain = propAccess.getText();
235770
236385
  const methodName = propAccess.getName();
235771
236386
  let rootObject = propAccess.getExpression();
235772
- while (import_ts_morph.Node.isPropertyAccessExpression(rootObject)) {
236387
+ while (import_ts_morph3.Node.isPropertyAccessExpression(rootObject)) {
235773
236388
  rootObject = rootObject.getExpression();
235774
236389
  }
235775
236390
  const objectName = rootObject.getText();
@@ -235787,11 +236402,11 @@ class RelationshipExtractor {
235787
236402
  };
235788
236403
  }
235789
236404
  extractFromDatabaseCall(awaitExpr, handlerName) {
235790
- if (!import_ts_morph.Node.isAwaitExpression(awaitExpr)) {
236405
+ if (!import_ts_morph3.Node.isAwaitExpression(awaitExpr)) {
235791
236406
  return null;
235792
236407
  }
235793
236408
  const innerExpr = awaitExpr.getExpression();
235794
- if (!import_ts_morph.Node.isCallExpression(innerExpr)) {
236409
+ if (!import_ts_morph3.Node.isCallExpression(innerExpr)) {
235795
236410
  return null;
235796
236411
  }
235797
236412
  const callExpr = innerExpr.getExpression().getText();
@@ -235975,7 +236590,7 @@ class RelationshipExtractor {
235975
236590
  const variableDecl = moduleSpecifier.getVariableDeclaration(functionName);
235976
236591
  if (variableDecl) {
235977
236592
  const initializer = variableDecl.getInitializer();
235978
- if (initializer && (import_ts_morph.Node.isArrowFunction(initializer) || import_ts_morph.Node.isFunctionExpression(initializer))) {
236593
+ if (initializer && (import_ts_morph3.Node.isArrowFunction(initializer) || import_ts_morph3.Node.isFunctionExpression(initializer))) {
235979
236594
  return {
235980
236595
  functionDecl: initializer,
235981
236596
  sourceFile: moduleSpecifier
@@ -236009,9 +236624,9 @@ class RelationshipExtractor {
236009
236624
  return unique;
236010
236625
  }
236011
236626
  }
236012
- var import_ts_morph;
236627
+ var import_ts_morph3;
236013
236628
  var init_relationships = __esm(() => {
236014
- import_ts_morph = __toESM(require_ts_morph(), 1);
236629
+ import_ts_morph3 = __toESM(require_ts_morph(), 1);
236015
236630
  });
236016
236631
 
236017
236632
  // tools/analysis/src/extract/handlers.ts
@@ -236020,7 +236635,7 @@ class HandlerExtractor {
236020
236635
  typeGuardCache;
236021
236636
  relationshipExtractor;
236022
236637
  constructor(tsConfigPath) {
236023
- this.project = new import_ts_morph2.Project({
236638
+ this.project = new import_ts_morph4.Project({
236024
236639
  tsConfigFilePath: tsConfigPath
236025
236640
  });
236026
236641
  this.typeGuardCache = new WeakMap;
@@ -236086,25 +236701,25 @@ class HandlerExtractor {
236086
236701
  return handlers;
236087
236702
  }
236088
236703
  processNodeForHandlers(node, context, filePath, handlers) {
236089
- if (import_ts_morph2.Node.isCallExpression(node)) {
236704
+ if (import_ts_morph4.Node.isCallExpression(node)) {
236090
236705
  this.processCallExpressionHandler(node, context, filePath, handlers);
236091
236706
  }
236092
- if (import_ts_morph2.Node.isSwitchStatement(node)) {
236707
+ if (import_ts_morph4.Node.isSwitchStatement(node)) {
236093
236708
  const switchHandlers = this.extractSwitchCaseHandlers(node, context, filePath);
236094
236709
  handlers.push(...switchHandlers);
236095
236710
  }
236096
- if (import_ts_morph2.Node.isVariableDeclaration(node)) {
236711
+ if (import_ts_morph4.Node.isVariableDeclaration(node)) {
236097
236712
  const mapHandlers = this.extractHandlerMapPattern(node, context, filePath);
236098
236713
  handlers.push(...mapHandlers);
236099
236714
  }
236100
- if (import_ts_morph2.Node.isIfStatement(node) && !this.isElseIfStatement(node)) {
236715
+ if (import_ts_morph4.Node.isIfStatement(node) && !this.isElseIfStatement(node)) {
236101
236716
  const typeGuardHandlers = this.extractTypeGuardHandlers(node, context, filePath);
236102
236717
  handlers.push(...typeGuardHandlers);
236103
236718
  }
236104
236719
  }
236105
236720
  processCallExpressionHandler(node, context, filePath, handlers) {
236106
236721
  const expression = node.getExpression();
236107
- if (import_ts_morph2.Node.isPropertyAccessExpression(expression)) {
236722
+ if (import_ts_morph4.Node.isPropertyAccessExpression(expression)) {
236108
236723
  const methodName = expression.getName();
236109
236724
  if (methodName === "on" || methodName === "addEventListener") {
236110
236725
  const handler = this.extractHandler(node, context, filePath);
@@ -236116,7 +236731,7 @@ class HandlerExtractor {
236116
236731
  }
236117
236732
  isElseIfStatement(node) {
236118
236733
  const parent = node.getParent();
236119
- return parent !== undefined && import_ts_morph2.Node.isIfStatement(parent);
236734
+ return parent !== undefined && import_ts_morph4.Node.isIfStatement(parent);
236120
236735
  }
236121
236736
  extractHandler(callExpr, context, filePath) {
236122
236737
  const args = callExpr.getArguments();
@@ -236125,9 +236740,9 @@ class HandlerExtractor {
236125
236740
  }
236126
236741
  const messageTypeArg = args[0];
236127
236742
  let messageType = null;
236128
- if (import_ts_morph2.Node.isStringLiteral(messageTypeArg)) {
236743
+ if (import_ts_morph4.Node.isStringLiteral(messageTypeArg)) {
236129
236744
  messageType = messageTypeArg.getLiteralValue();
236130
- } else if (import_ts_morph2.Node.isTemplateExpression(messageTypeArg)) {
236745
+ } else if (import_ts_morph4.Node.isTemplateExpression(messageTypeArg)) {
236131
236746
  messageType = messageTypeArg.getText().replace(/[`'"]/g, "");
236132
236747
  }
236133
236748
  if (!messageType) {
@@ -236137,7 +236752,7 @@ class HandlerExtractor {
236137
236752
  const assignments = [];
236138
236753
  const preconditions = [];
236139
236754
  const postconditions = [];
236140
- if (import_ts_morph2.Node.isArrowFunction(handlerArg) || import_ts_morph2.Node.isFunctionExpression(handlerArg)) {
236755
+ if (import_ts_morph4.Node.isArrowFunction(handlerArg) || import_ts_morph4.Node.isFunctionExpression(handlerArg)) {
236141
236756
  this.extractAssignments(handlerArg, assignments);
236142
236757
  this.extractVerificationConditions(handlerArg, preconditions, postconditions);
236143
236758
  this.checkAsyncMutations(handlerArg, messageType);
@@ -236146,7 +236761,7 @@ class HandlerExtractor {
236146
236761
  const sourceFile = callExpr.getSourceFile();
236147
236762
  const handlerName = `${messageType}_handler`;
236148
236763
  let relationships;
236149
- if (import_ts_morph2.Node.isArrowFunction(handlerArg) || import_ts_morph2.Node.isFunctionExpression(handlerArg)) {
236764
+ if (import_ts_morph4.Node.isArrowFunction(handlerArg) || import_ts_morph4.Node.isFunctionExpression(handlerArg)) {
236150
236765
  const detectedRelationships = this.relationshipExtractor.extractFromHandler(handlerArg, sourceFile, handlerName);
236151
236766
  if (detectedRelationships.length > 0) {
236152
236767
  relationships = detectedRelationships;
@@ -236167,16 +236782,16 @@ class HandlerExtractor {
236167
236782
  }
236168
236783
  extractAssignments(funcNode, assignments) {
236169
236784
  funcNode.forEachDescendant((node) => {
236170
- if (import_ts_morph2.Node.isBinaryExpression(node)) {
236785
+ if (import_ts_morph4.Node.isBinaryExpression(node)) {
236171
236786
  this.extractBinaryExpressionAssignment(node, assignments);
236172
236787
  }
236173
- if (import_ts_morph2.Node.isCallExpression(node)) {
236788
+ if (import_ts_morph4.Node.isCallExpression(node)) {
236174
236789
  this.extractArrayMutationAssignment(node, assignments);
236175
236790
  }
236176
236791
  });
236177
236792
  }
236178
236793
  extractBinaryExpressionAssignment(node, assignments) {
236179
- if (!import_ts_morph2.Node.isBinaryExpression(node))
236794
+ if (!import_ts_morph4.Node.isBinaryExpression(node))
236180
236795
  return;
236181
236796
  const operator = node.getOperatorToken().getText();
236182
236797
  if (operator === "=") {
@@ -236186,18 +236801,18 @@ class HandlerExtractor {
236186
236801
  }
236187
236802
  }
236188
236803
  extractSimpleOrElementAccessAssignment(node, assignments) {
236189
- if (!import_ts_morph2.Node.isBinaryExpression(node))
236804
+ if (!import_ts_morph4.Node.isBinaryExpression(node))
236190
236805
  return;
236191
236806
  const left = node.getLeft();
236192
236807
  const right = node.getRight();
236193
- if (import_ts_morph2.Node.isPropertyAccessExpression(left)) {
236808
+ if (import_ts_morph4.Node.isPropertyAccessExpression(left)) {
236194
236809
  this.extractPropertyAccessAssignment(left, right, assignments);
236195
- } else if (import_ts_morph2.Node.isElementAccessExpression(left)) {
236810
+ } else if (import_ts_morph4.Node.isElementAccessExpression(left)) {
236196
236811
  this.extractElementAccessAssignment(left, right, assignments);
236197
236812
  }
236198
236813
  }
236199
236814
  extractPropertyAccessAssignment(left, right, assignments) {
236200
- if (!import_ts_morph2.Node.isPropertyAccessExpression(left))
236815
+ if (!import_ts_morph4.Node.isPropertyAccessExpression(left))
236201
236816
  return;
236202
236817
  const fieldPath = this.getPropertyPath(left);
236203
236818
  if (fieldPath.startsWith("state.")) {
@@ -236209,10 +236824,10 @@ class HandlerExtractor {
236209
236824
  }
236210
236825
  }
236211
236826
  extractElementAccessAssignment(left, right, assignments) {
236212
- if (!import_ts_morph2.Node.isElementAccessExpression(left))
236827
+ if (!import_ts_morph4.Node.isElementAccessExpression(left))
236213
236828
  return;
236214
236829
  const expr = left.getExpression();
236215
- if (!import_ts_morph2.Node.isPropertyAccessExpression(expr))
236830
+ if (!import_ts_morph4.Node.isPropertyAccessExpression(expr))
236216
236831
  return;
236217
236832
  const fieldPath = this.getPropertyPath(expr);
236218
236833
  if (!fieldPath.startsWith("state."))
@@ -236222,7 +236837,7 @@ class HandlerExtractor {
236222
236837
  const index = indexExpr ? indexExpr.getText() : "0";
236223
236838
  const value = this.extractValue(right);
236224
236839
  if (value !== undefined) {
236225
- const tlaIndex = this.isNumericLiteral(index) ? (Number.parseInt(index) + 1).toString() : `${index} + 1`;
236840
+ const tlaIndex = this.isNumericLiteral(index) ? (Number.parseInt(index, 10) + 1).toString() : `${index} + 1`;
236226
236841
  assignments.push({
236227
236842
  field: `${field}[${tlaIndex}]`,
236228
236843
  value
@@ -236230,12 +236845,12 @@ class HandlerExtractor {
236230
236845
  }
236231
236846
  }
236232
236847
  extractCompoundAssignment(node, assignments) {
236233
- if (!import_ts_morph2.Node.isBinaryExpression(node))
236848
+ if (!import_ts_morph4.Node.isBinaryExpression(node))
236234
236849
  return;
236235
236850
  const operator = node.getOperatorToken().getText();
236236
236851
  const left = node.getLeft();
236237
236852
  const right = node.getRight();
236238
- if (import_ts_morph2.Node.isPropertyAccessExpression(left)) {
236853
+ if (import_ts_morph4.Node.isPropertyAccessExpression(left)) {
236239
236854
  const fieldPath = this.getPropertyPath(left);
236240
236855
  if (fieldPath.startsWith("state.")) {
236241
236856
  const field = fieldPath.substring(6);
@@ -236249,14 +236864,14 @@ class HandlerExtractor {
236249
236864
  }
236250
236865
  }
236251
236866
  extractArrayMutationAssignment(node, assignments) {
236252
- if (!import_ts_morph2.Node.isCallExpression(node))
236867
+ if (!import_ts_morph4.Node.isCallExpression(node))
236253
236868
  return;
236254
236869
  const expr = node.getExpression();
236255
- if (!import_ts_morph2.Node.isPropertyAccessExpression(expr))
236870
+ if (!import_ts_morph4.Node.isPropertyAccessExpression(expr))
236256
236871
  return;
236257
236872
  const methodName = expr.getName();
236258
236873
  const object = expr.getExpression();
236259
- if (import_ts_morph2.Node.isPropertyAccessExpression(object)) {
236874
+ if (import_ts_morph4.Node.isPropertyAccessExpression(object)) {
236260
236875
  const fieldPath = this.getPropertyPath(object);
236261
236876
  if (fieldPath.startsWith("state.")) {
236262
236877
  const field = fieldPath.substring(6);
@@ -236288,13 +236903,13 @@ class HandlerExtractor {
236288
236903
  }
236289
236904
  }
236290
236905
  checkAsyncMutations(funcNode, messageType) {
236291
- const isAsync = funcNode.hasModifier?.(import_ts_morph2.SyntaxKind.AsyncKeyword) || funcNode.getModifiers?.()?.some((m) => m.getKind() === import_ts_morph2.SyntaxKind.AsyncKeyword);
236906
+ const isAsync = funcNode.hasModifier?.(import_ts_morph4.SyntaxKind.AsyncKeyword) || funcNode.getModifiers?.()?.some((m) => m.getKind() === import_ts_morph4.SyntaxKind.AsyncKeyword);
236292
236907
  if (!isAsync) {
236293
236908
  return;
236294
236909
  }
236295
236910
  const awaitExpressions = [];
236296
236911
  funcNode.forEachDescendant((node) => {
236297
- if (import_ts_morph2.Node.isAwaitExpression(node)) {
236912
+ if (import_ts_morph4.Node.isAwaitExpression(node)) {
236298
236913
  awaitExpressions.push(node);
236299
236914
  }
236300
236915
  });
@@ -236308,10 +236923,10 @@ class HandlerExtractor {
236308
236923
  }
236309
236924
  const firstAwaitPos = awaitExpressions[0].getStart();
236310
236925
  funcNode.forEachDescendant((node) => {
236311
- if (import_ts_morph2.Node.isBinaryExpression(node)) {
236926
+ if (import_ts_morph4.Node.isBinaryExpression(node)) {
236312
236927
  this.checkBinaryExpressionMutation(node, firstAwaitPos, mutations);
236313
236928
  }
236314
- if (import_ts_morph2.Node.isCallExpression(node)) {
236929
+ if (import_ts_morph4.Node.isCallExpression(node)) {
236315
236930
  this.checkCallExpressionMutation(node, firstAwaitPos, mutations);
236316
236931
  }
236317
236932
  });
@@ -236324,19 +236939,19 @@ class HandlerExtractor {
236324
236939
  }
236325
236940
  extractVerificationConditions(funcNode, preconditions, postconditions) {
236326
236941
  const body = funcNode.getBody();
236327
- const statements = import_ts_morph2.Node.isBlock(body) ? body.getStatements() : [body];
236942
+ const statements = import_ts_morph4.Node.isBlock(body) ? body.getStatements() : [body];
236328
236943
  for (const statement of statements) {
236329
236944
  this.processStatementForConditions(statement, preconditions, postconditions);
236330
236945
  }
236331
236946
  }
236332
236947
  processStatementForConditions(statement, preconditions, postconditions) {
236333
- if (!import_ts_morph2.Node.isExpressionStatement(statement))
236948
+ if (!import_ts_morph4.Node.isExpressionStatement(statement))
236334
236949
  return;
236335
236950
  const expr = statement.getExpression();
236336
- if (!import_ts_morph2.Node.isCallExpression(expr))
236951
+ if (!import_ts_morph4.Node.isCallExpression(expr))
236337
236952
  return;
236338
236953
  const callee = expr.getExpression();
236339
- if (!import_ts_morph2.Node.isIdentifier(callee))
236954
+ if (!import_ts_morph4.Node.isIdentifier(callee))
236340
236955
  return;
236341
236956
  const functionName = callee.getText();
236342
236957
  if (functionName === "requires") {
@@ -236359,7 +236974,7 @@ class HandlerExtractor {
236359
236974
  const conditionArg = args[0];
236360
236975
  const expression = conditionArg.getText();
236361
236976
  let message;
236362
- if (args.length >= 2 && import_ts_morph2.Node.isStringLiteral(args[1])) {
236977
+ if (args.length >= 2 && import_ts_morph4.Node.isStringLiteral(args[1])) {
236363
236978
  message = args[1].getLiteralValue();
236364
236979
  }
236365
236980
  const line = callExpr.getStartLineNumber();
@@ -236376,29 +236991,29 @@ class HandlerExtractor {
236376
236991
  getPropertyPath(node) {
236377
236992
  const parts = [];
236378
236993
  let current = node;
236379
- while (import_ts_morph2.Node.isPropertyAccessExpression(current)) {
236994
+ while (import_ts_morph4.Node.isPropertyAccessExpression(current)) {
236380
236995
  parts.unshift(current.getName());
236381
236996
  current = current.getExpression();
236382
236997
  }
236383
- if (import_ts_morph2.Node.isIdentifier(current)) {
236998
+ if (import_ts_morph4.Node.isIdentifier(current)) {
236384
236999
  parts.unshift(current.getText());
236385
237000
  }
236386
237001
  return parts.join(".");
236387
237002
  }
236388
237003
  extractValue(node) {
236389
- if (import_ts_morph2.Node.isStringLiteral(node)) {
237004
+ if (import_ts_morph4.Node.isStringLiteral(node)) {
236390
237005
  return node.getLiteralValue();
236391
237006
  }
236392
- if (import_ts_morph2.Node.isNumericLiteral(node)) {
237007
+ if (import_ts_morph4.Node.isNumericLiteral(node)) {
236393
237008
  return node.getLiteralValue();
236394
237009
  }
236395
- if (node.getKind() === import_ts_morph2.SyntaxKind.TrueKeyword) {
237010
+ if (node.getKind() === import_ts_morph4.SyntaxKind.TrueKeyword) {
236396
237011
  return true;
236397
237012
  }
236398
- if (node.getKind() === import_ts_morph2.SyntaxKind.FalseKeyword) {
237013
+ if (node.getKind() === import_ts_morph4.SyntaxKind.FalseKeyword) {
236399
237014
  return false;
236400
237015
  }
236401
- if (node.getKind() === import_ts_morph2.SyntaxKind.NullKeyword) {
237016
+ if (node.getKind() === import_ts_morph4.SyntaxKind.NullKeyword) {
236402
237017
  return null;
236403
237018
  }
236404
237019
  return;
@@ -236416,10 +237031,10 @@ class HandlerExtractor {
236416
237031
  }
236417
237032
  const caseClauses = switchNode.getClauses();
236418
237033
  for (const clause of caseClauses) {
236419
- if (import_ts_morph2.Node.isCaseClause(clause)) {
237034
+ if (import_ts_morph4.Node.isCaseClause(clause)) {
236420
237035
  const caseExpr = clause.getExpression();
236421
237036
  let messageType = null;
236422
- if (import_ts_morph2.Node.isStringLiteral(caseExpr)) {
237037
+ if (import_ts_morph4.Node.isStringLiteral(caseExpr)) {
236423
237038
  messageType = caseExpr.getLiteralValue();
236424
237039
  }
236425
237040
  if (messageType) {
@@ -236456,14 +237071,14 @@ class HandlerExtractor {
236456
237071
  return handlers;
236457
237072
  }
236458
237073
  isHandlerMapInitializer(initializer, varDecl) {
236459
- if (!initializer || !import_ts_morph2.Node.isObjectLiteralExpression(initializer)) {
237074
+ if (!initializer || !import_ts_morph4.Node.isObjectLiteralExpression(initializer)) {
236460
237075
  return false;
236461
237076
  }
236462
237077
  const varName = varDecl.getName().toLowerCase();
236463
237078
  return /(handler|listener|callback|event)s?/.test(varName);
236464
237079
  }
236465
237080
  extractHandlerFromProperty(prop, context, filePath) {
236466
- if (!import_ts_morph2.Node.isPropertyAssignment(prop)) {
237081
+ if (!import_ts_morph4.Node.isPropertyAssignment(prop)) {
236467
237082
  return null;
236468
237083
  }
236469
237084
  const nameNode = prop.getNameNode();
@@ -236482,10 +237097,10 @@ class HandlerExtractor {
236482
237097
  };
236483
237098
  }
236484
237099
  getMessageTypeFromPropertyName(nameNode) {
236485
- if (import_ts_morph2.Node.isStringLiteral(nameNode)) {
237100
+ if (import_ts_morph4.Node.isStringLiteral(nameNode)) {
236486
237101
  return nameNode.getLiteralValue();
236487
237102
  }
236488
- if (import_ts_morph2.Node.isIdentifier(nameNode)) {
237103
+ if (import_ts_morph4.Node.isIdentifier(nameNode)) {
236489
237104
  return nameNode.getText();
236490
237105
  }
236491
237106
  return null;
@@ -236530,7 +237145,7 @@ class HandlerExtractor {
236530
237145
  this.debugLogFoundHandler(handler);
236531
237146
  }
236532
237147
  const elseStatement = ifStatement.getElseStatement();
236533
- if (elseStatement && import_ts_morph2.Node.isIfStatement(elseStatement)) {
237148
+ if (elseStatement && import_ts_morph4.Node.isIfStatement(elseStatement)) {
236534
237149
  ifStatement = elseStatement;
236535
237150
  } else {
236536
237151
  break;
@@ -236551,11 +237166,11 @@ class HandlerExtractor {
236551
237166
  try {
236552
237167
  const ifStmt = ifNode;
236553
237168
  const condition = ifStmt.getExpression();
236554
- if (!import_ts_morph2.Node.isCallExpression(condition)) {
237169
+ if (!import_ts_morph4.Node.isCallExpression(condition)) {
236555
237170
  return null;
236556
237171
  }
236557
237172
  const funcExpr = condition.getExpression();
236558
- const funcName = import_ts_morph2.Node.isIdentifier(funcExpr) ? funcExpr.getText() : undefined;
237173
+ const funcName = import_ts_morph4.Node.isIdentifier(funcExpr) ? funcExpr.getText() : undefined;
236559
237174
  this.debugLogProcessingFunction(funcName);
236560
237175
  const messageType = this.resolveMessageType(funcExpr, funcName, typeGuards);
236561
237176
  if (!messageType) {
@@ -236595,7 +237210,7 @@ class HandlerExtractor {
236595
237210
  return guardType;
236596
237211
  }
236597
237212
  }
236598
- if (import_ts_morph2.Node.isIdentifier(funcExpr)) {
237213
+ if (import_ts_morph4.Node.isIdentifier(funcExpr)) {
236599
237214
  this.debugLogTryingImportResolution(funcName);
236600
237215
  return this.resolveImportedTypeGuard(funcExpr) ?? undefined;
236601
237216
  }
@@ -236633,7 +237248,7 @@ class HandlerExtractor {
236633
237248
  return;
236634
237249
  }
236635
237250
  const returnTypeNode = node.getReturnTypeNode();
236636
- if (!returnTypeNode || !import_ts_morph2.Node.isTypePredicate(returnTypeNode)) {
237251
+ if (!returnTypeNode || !import_ts_morph4.Node.isTypePredicate(returnTypeNode)) {
236637
237252
  return;
236638
237253
  }
236639
237254
  const functionName = this.extractFunctionName(node);
@@ -236646,17 +237261,17 @@ class HandlerExtractor {
236646
237261
  }
236647
237262
  }
236648
237263
  extractFunctionName(node) {
236649
- if (import_ts_morph2.Node.isFunctionDeclaration(node)) {
237264
+ if (import_ts_morph4.Node.isFunctionDeclaration(node)) {
236650
237265
  return node.getName();
236651
237266
  }
236652
- if (import_ts_morph2.Node.isFunctionExpression(node) || import_ts_morph2.Node.isArrowFunction(node)) {
237267
+ if (import_ts_morph4.Node.isFunctionExpression(node) || import_ts_morph4.Node.isArrowFunction(node)) {
236653
237268
  return this.extractFunctionNameFromVariable(node);
236654
237269
  }
236655
237270
  return;
236656
237271
  }
236657
237272
  extractFunctionNameFromVariable(node) {
236658
237273
  const parent = node.getParent();
236659
- if (import_ts_morph2.Node.isVariableDeclaration(parent)) {
237274
+ if (import_ts_morph4.Node.isVariableDeclaration(parent)) {
236660
237275
  return parent.getName();
236661
237276
  }
236662
237277
  return;
@@ -236716,10 +237331,10 @@ class HandlerExtractor {
236716
237331
  return this.extractFromFunctionBody(def, funcName);
236717
237332
  }
236718
237333
  isFunctionNode(node) {
236719
- return import_ts_morph2.Node.isFunctionDeclaration(node) || import_ts_morph2.Node.isFunctionExpression(node) || import_ts_morph2.Node.isArrowFunction(node);
237334
+ return import_ts_morph4.Node.isFunctionDeclaration(node) || import_ts_morph4.Node.isFunctionExpression(node) || import_ts_morph4.Node.isArrowFunction(node);
236720
237335
  }
236721
237336
  extractFromTypePredicate(returnTypeNode, funcName) {
236722
- if (!returnTypeNode || !import_ts_morph2.Node.isTypePredicate(returnTypeNode)) {
237337
+ if (!returnTypeNode || !import_ts_morph4.Node.isTypePredicate(returnTypeNode)) {
236723
237338
  return null;
236724
237339
  }
236725
237340
  const typeNode = returnTypeNode.getTypeNode();
@@ -236758,7 +237373,7 @@ class HandlerExtractor {
236758
237373
  const returnType = def.getReturnType().getText();
236759
237374
  console.log(`[DEBUG] Function ${funcName} return type (resolved): ${returnType}`);
236760
237375
  console.log(`[DEBUG] Has return type node: ${!!returnTypeNode}`);
236761
- console.log(`[DEBUG] Is type predicate node: ${returnTypeNode && import_ts_morph2.Node.isTypePredicate(returnTypeNode)}`);
237376
+ console.log(`[DEBUG] Is type predicate node: ${returnTypeNode && import_ts_morph4.Node.isTypePredicate(returnTypeNode)}`);
236762
237377
  }
236763
237378
  }
236764
237379
  debugLogTypePredicateResolution(funcName, messageType) {
@@ -236836,15 +237451,15 @@ class HandlerExtractor {
236836
237451
  return null;
236837
237452
  }
236838
237453
  checkBinaryExpressionMutation(node, firstAwaitPos, mutations) {
236839
- if (!import_ts_morph2.Node.isBinaryExpression(node))
237454
+ if (!import_ts_morph4.Node.isBinaryExpression(node))
236840
237455
  return;
236841
237456
  const operator = node.getOperatorToken().getText();
236842
237457
  if (operator !== "=" && !["+=", "-=", "*=", "/=", "%="].includes(operator))
236843
237458
  return;
236844
237459
  const left = node.getLeft();
236845
- if (!import_ts_morph2.Node.isPropertyAccessExpression(left) && !import_ts_morph2.Node.isElementAccessExpression(left))
237460
+ if (!import_ts_morph4.Node.isPropertyAccessExpression(left) && !import_ts_morph4.Node.isElementAccessExpression(left))
236846
237461
  return;
236847
- const fieldPath = import_ts_morph2.Node.isPropertyAccessExpression(left) ? this.getPropertyPath(left) : this.getPropertyPath(left.getExpression());
237462
+ const fieldPath = import_ts_morph4.Node.isPropertyAccessExpression(left) ? this.getPropertyPath(left) : this.getPropertyPath(left.getExpression());
236848
237463
  if (fieldPath.startsWith("state.")) {
236849
237464
  const field = fieldPath.substring(6);
236850
237465
  const line = node.getStartLineNumber();
@@ -236853,14 +237468,14 @@ class HandlerExtractor {
236853
237468
  }
236854
237469
  }
236855
237470
  checkCallExpressionMutation(node, firstAwaitPos, mutations) {
236856
- if (!import_ts_morph2.Node.isCallExpression(node))
237471
+ if (!import_ts_morph4.Node.isCallExpression(node))
236857
237472
  return;
236858
237473
  const expr = node.getExpression();
236859
- if (!import_ts_morph2.Node.isPropertyAccessExpression(expr))
237474
+ if (!import_ts_morph4.Node.isPropertyAccessExpression(expr))
236860
237475
  return;
236861
237476
  const methodName = expr.getName();
236862
237477
  const object = expr.getExpression();
236863
- if (!import_ts_morph2.Node.isPropertyAccessExpression(object))
237478
+ if (!import_ts_morph4.Node.isPropertyAccessExpression(object))
236864
237479
  return;
236865
237480
  const fieldPath = this.getPropertyPath(object);
236866
237481
  if (!fieldPath.startsWith("state."))
@@ -236877,442 +237492,252 @@ function extractHandlers(tsConfigPath) {
236877
237492
  const extractor = new HandlerExtractor(tsConfigPath);
236878
237493
  return extractor.extractHandlers();
236879
237494
  }
236880
- var import_ts_morph2;
237495
+ var import_ts_morph4;
236881
237496
  var init_handlers = __esm(() => {
236882
237497
  init_relationships();
236883
- import_ts_morph2 = __toESM(require_ts_morph(), 1);
237498
+ import_ts_morph4 = __toESM(require_ts_morph(), 1);
236884
237499
  });
236885
237500
 
236886
- // tools/analysis/src/extract/types.ts
236887
- class TypeExtractor {
237501
+ // tools/analysis/src/extract/integrations.ts
237502
+ class IntegrationAnalyzer {
236888
237503
  project;
236889
237504
  constructor(tsConfigPath) {
236890
- this.project = new import_ts_morph3.Project({
237505
+ this.project = new import_ts_morph5.Project({
236891
237506
  tsConfigFilePath: tsConfigPath
236892
237507
  });
236893
237508
  }
236894
- async analyzeCodebase(stateFilePath) {
236895
- const stateType = stateFilePath ? this.extractStateType(stateFilePath) : this.findStateType();
236896
- const messageTypes = this.findMessageTypes();
236897
- const fields = stateType ? this.analyzeFields(stateType) : [];
236898
- const handlerAnalysis = this.extractHandlerAnalysis();
236899
- const validMessageTypes = this.filterAndLogMessageTypes(messageTypes, handlerAnalysis.messageTypes);
236900
- const validHandlers = this.filterAndLogHandlers(handlerAnalysis.handlers);
236901
- return {
236902
- stateType,
236903
- messageTypes: validMessageTypes,
236904
- fields,
236905
- handlers: validHandlers
236906
- };
236907
- }
236908
- extractHandlerAnalysis() {
236909
- const configFilePath = this.project.getCompilerOptions()["configFilePath"];
236910
- const tsConfigPath = typeof configFilePath === "string" ? configFilePath : "tsconfig.json";
236911
- const handlerExtractor = new HandlerExtractor(tsConfigPath);
236912
- return handlerExtractor.extractHandlers();
236913
- }
236914
- filterAndLogMessageTypes(messageTypes, handlerMessageTypes) {
236915
- const allMessageTypes = Array.from(new Set([...messageTypes, ...handlerMessageTypes]));
236916
- const validMessageTypes = [];
236917
- const invalidMessageTypes = [];
236918
- for (const msgType of allMessageTypes) {
236919
- if (this.isValidTLAIdentifier(msgType)) {
236920
- validMessageTypes.push(msgType);
236921
- } else {
236922
- invalidMessageTypes.push(msgType);
236923
- }
237509
+ analyzeIntegrations() {
237510
+ const integrations = new Map;
237511
+ const fetchCalls = this.findFetchCalls();
237512
+ for (const call of fetchCalls) {
237513
+ this.addOrMergeIntegration(integrations, this.createAPIIntegration(call));
236924
237514
  }
236925
- this.logInvalidMessageTypes(invalidMessageTypes);
236926
- return validMessageTypes;
236927
- }
236928
- logInvalidMessageTypes(invalidMessageTypes) {
236929
- if (invalidMessageTypes.length === 0 || !process.env["POLLY_DEBUG"])
236930
- return;
236931
- console.log(`[WARN] Filtered out ${invalidMessageTypes.length} invalid message type(s):`);
236932
- for (const invalid of invalidMessageTypes) {
236933
- console.log(`[WARN] - "${invalid}" (not a valid TLA+ identifier)`);
237515
+ const websockets = this.findWebSockets();
237516
+ for (const ws of websockets) {
237517
+ this.addOrMergeIntegration(integrations, this.createWebSocketIntegration(ws));
236934
237518
  }
236935
- }
236936
- filterAndLogHandlers(handlers) {
236937
- const validHandlers = handlers.filter((h) => this.isValidTLAIdentifier(h.messageType));
236938
- this.logInvalidHandlers(handlers, validHandlers);
236939
- return validHandlers;
236940
- }
236941
- logInvalidHandlers(allHandlers, validHandlers) {
236942
- const filteredHandlerCount = allHandlers.length - validHandlers.length;
236943
- if (filteredHandlerCount === 0 || !process.env["POLLY_DEBUG"])
236944
- return;
236945
- console.log(`[WARN] Filtered out ${filteredHandlerCount} handler(s) with invalid message types:`);
236946
- for (const handler of allHandlers) {
236947
- if (!this.isValidTLAIdentifier(handler.messageType)) {
236948
- console.log(`[WARN] - Handler for "${handler.messageType}" at ${handler.location.file}:${handler.location.line}`);
236949
- }
237519
+ const externalScripts = this.findExternalScripts();
237520
+ for (const script of externalScripts) {
237521
+ this.addOrMergeIntegration(integrations, script);
236950
237522
  }
237523
+ return Array.from(integrations.values());
236951
237524
  }
236952
- isValidTLAIdentifier(s) {
236953
- if (!s || s.length === 0) {
236954
- return false;
237525
+ findFetchCalls() {
237526
+ const calls = [];
237527
+ for (const sourceFile of this.project.getSourceFiles()) {
237528
+ sourceFile.forEachDescendant((node) => {
237529
+ this.processFetchCall(node, sourceFile, calls);
237530
+ });
236955
237531
  }
236956
- return /^[a-zA-Z][a-zA-Z0-9_]*$/.test(s);
237532
+ return calls;
236957
237533
  }
236958
- extractStateType(filePath) {
236959
- const sourceFile = this.project.getSourceFile(filePath);
236960
- if (!sourceFile) {
236961
- return null;
236962
- }
236963
- const typeAlias = sourceFile.getTypeAlias("AppState") || sourceFile.getTypeAlias("State") || sourceFile.getTypeAliases()[0];
236964
- if (!typeAlias) {
236965
- return null;
237534
+ processFetchCall(node, sourceFile, calls) {
237535
+ if (!import_ts_morph5.Node.isCallExpression(node)) {
237536
+ return;
236966
237537
  }
236967
- const type2 = typeAlias.getType();
236968
- return this.convertType(type2, typeAlias.getName());
236969
- }
236970
- findStateType() {
236971
- const stateFiles = this.project.getSourceFiles("**/state*.ts");
236972
- for (const file of stateFiles) {
236973
- const typeAlias = file.getTypeAlias("AppState") || file.getTypeAlias("State");
236974
- if (typeAlias) {
236975
- const type2 = typeAlias.getType();
236976
- return this.convertType(type2, typeAlias.getName());
236977
- }
237538
+ const expression = node.getExpression();
237539
+ if (!this.isFetchCall(expression)) {
237540
+ return;
236978
237541
  }
236979
- return null;
236980
- }
236981
- findMessageTypes() {
236982
- const messageTypes = [];
236983
- const warnings = [];
236984
- const messageFiles = this.project.getSourceFiles("**/message*.ts");
236985
- for (const file of messageFiles) {
236986
- for (const typeAlias of file.getTypeAliases()) {
236987
- const extractedTypes = this.extractMessageTypesFromType(typeAlias.getType(), typeAlias.getName(), file, warnings);
236988
- messageTypes.push(...extractedTypes);
236989
- }
237542
+ const args = node.getArguments();
237543
+ if (args.length === 0) {
237544
+ return;
236990
237545
  }
236991
- if (warnings.length > 0 && process.env["POLLY_DEBUG"]) {
236992
- console.log("[WARN] Message type extraction warnings:");
236993
- for (const warning of warnings) {
236994
- console.log(`[WARN] ${warning}`);
236995
- }
237546
+ const url = this.extractURLFromArg(args[0]);
237547
+ if (!url) {
237548
+ return;
236996
237549
  }
236997
- return [...new Set(messageTypes)];
237550
+ const method = this.extractMethodFromOptions(args);
237551
+ const description = this.extractJSDocDescription(node);
237552
+ calls.push({
237553
+ url,
237554
+ method,
237555
+ file: sourceFile.getFilePath(),
237556
+ line: node.getStartLineNumber(),
237557
+ description
237558
+ });
236998
237559
  }
236999
- extractMessageTypesFromType(type2, typeName, sourceFile, warnings) {
237000
- if (type2.isUnion()) {
237001
- return this.extractFromUnionType(type2, typeName, sourceFile, warnings);
237002
- }
237003
- if (type2.getAliasSymbol()) {
237004
- return this.extractFromTypeAlias(type2, typeName, sourceFile, warnings);
237005
- }
237006
- if (type2.isConditionalType?.()) {
237007
- return this.extractFromConditionalType(type2, warnings);
237008
- }
237009
- if (type2.getText().includes("[K in ")) {
237010
- return this.extractFromMappedType(type2, sourceFile, warnings);
237560
+ isFetchCall(expression) {
237561
+ return import_ts_morph5.Node.isIdentifier(expression) && expression.getText() === "fetch";
237562
+ }
237563
+ extractURLFromArg(urlArg) {
237564
+ if (import_ts_morph5.Node.isStringLiteral(urlArg)) {
237565
+ return urlArg.getLiteralValue();
237011
237566
  }
237012
- if (type2.getText().includes("`")) {
237013
- this.warnTemplateLiteral(type2, warnings);
237014
- return [];
237567
+ if (import_ts_morph5.Node.isTemplateExpression(urlArg)) {
237568
+ return this.extractBaseURL(urlArg.getText());
237015
237569
  }
237016
- return [];
237570
+ return null;
237017
237571
  }
237018
- extractFromUnionType(type2, typeName, sourceFile, warnings) {
237019
- const messageTypes = [];
237020
- const unionTypes = type2.getUnionTypes();
237021
- for (const unionType of unionTypes) {
237022
- const extracted = this.extractFromUnionMember(unionType, typeName, sourceFile, warnings);
237023
- messageTypes.push(...extracted);
237572
+ extractMethodFromOptions(args) {
237573
+ const defaultMethod = "GET";
237574
+ if (args.length <= 1) {
237575
+ return defaultMethod;
237024
237576
  }
237025
- return messageTypes;
237026
- }
237027
- extractFromUnionMember(unionType, typeName, sourceFile, warnings) {
237028
- if (unionType.isObject()) {
237029
- return this.extractFromDiscriminatedUnion(unionType, sourceFile, warnings);
237577
+ const optionsArg = args[1];
237578
+ if (!import_ts_morph5.Node.isObjectLiteralExpression(optionsArg)) {
237579
+ return defaultMethod;
237030
237580
  }
237031
- if (unionType.isStringLiteral()) {
237032
- return [unionType.getLiteralValue()];
237581
+ const methodProp = optionsArg.getProperty("method");
237582
+ if (!methodProp || !import_ts_morph5.Node.isPropertyAssignment(methodProp)) {
237583
+ return defaultMethod;
237033
237584
  }
237034
- if (unionType.getAliasSymbol()) {
237035
- const aliasedType = unionType.getAliasSymbol()?.getDeclaredType();
237036
- if (aliasedType) {
237037
- return this.extractMessageTypesFromType(aliasedType, typeName, sourceFile, warnings);
237038
- }
237585
+ const initializer = methodProp.getInitializer();
237586
+ if (!initializer || !import_ts_morph5.Node.isStringLiteral(initializer)) {
237587
+ return defaultMethod;
237039
237588
  }
237040
- return [];
237589
+ return initializer.getLiteralValue().toUpperCase();
237041
237590
  }
237042
- extractFromDiscriminatedUnion(unionType, sourceFile, warnings) {
237043
- const typeProperty = unionType.getProperty("type");
237044
- if (!typeProperty) {
237045
- return [];
237046
- }
237047
- const typeType = typeProperty.getTypeAtLocation(sourceFile);
237048
- if (typeType.isStringLiteral()) {
237049
- return [typeType.getLiteralValue()];
237050
- }
237051
- if (typeType.getText().includes("`")) {
237052
- warnings.push(`Template literal type in discriminant: ${typeType.getText()} - may be unbounded`);
237591
+ findWebSockets() {
237592
+ const websockets = [];
237593
+ for (const sourceFile of this.project.getSourceFiles()) {
237594
+ sourceFile.forEachDescendant((node) => {
237595
+ if (import_ts_morph5.Node.isNewExpression(node)) {
237596
+ const expression = node.getExpression();
237597
+ if (import_ts_morph5.Node.isIdentifier(expression) && expression.getText() === "WebSocket") {
237598
+ const args = node.getArguments();
237599
+ if (args.length > 0 && import_ts_morph5.Node.isStringLiteral(args[0])) {
237600
+ const url = args[0].getLiteralValue();
237601
+ const description = this.extractJSDocDescription(node);
237602
+ websockets.push({
237603
+ url,
237604
+ file: sourceFile.getFilePath(),
237605
+ line: node.getStartLineNumber(),
237606
+ description
237607
+ });
237608
+ }
237609
+ }
237610
+ }
237611
+ });
237053
237612
  }
237054
- return [];
237613
+ return websockets;
237055
237614
  }
237056
- extractFromTypeAlias(type2, typeName, sourceFile, warnings) {
237057
- const aliasedType = type2.getAliasSymbol()?.getDeclaredType();
237058
- if (!aliasedType || aliasedType === type2) {
237059
- return [];
237615
+ findExternalScripts() {
237616
+ const scripts = [];
237617
+ const seen = new Set;
237618
+ for (const sourceFile of this.project.getSourceFiles()) {
237619
+ this.extractExternalImportsFromFile(sourceFile, scripts, seen);
237060
237620
  }
237061
- return this.extractMessageTypesFromType(aliasedType, typeName, sourceFile, warnings);
237621
+ return scripts;
237062
237622
  }
237063
- extractFromConditionalType(type2, warnings) {
237064
- warnings.push(`Conditional type detected: ${type2.getText()} - extracting conservatively`);
237065
- const messageTypes = [];
237066
- const typeText = type2.getText();
237067
- const parts = typeText.split("?");
237068
- if (parts.length < 2) {
237069
- return messageTypes;
237070
- }
237071
- const branches = parts[1].split(":");
237072
- for (const branch of branches) {
237073
- const extracted = this.extractStringLiteralFromBranch(branch);
237074
- if (extracted) {
237075
- messageTypes.push(extracted);
237623
+ extractExternalImportsFromFile(sourceFile, scripts, seen) {
237624
+ for (const importDecl of sourceFile.getImportDeclarations()) {
237625
+ const moduleSpecifier = importDecl.getModuleSpecifierValue();
237626
+ if (this.isExternalImport(moduleSpecifier)) {
237627
+ const packageName = this.extractPackageName(moduleSpecifier);
237628
+ if (packageName && !seen.has(packageName)) {
237629
+ seen.add(packageName);
237630
+ scripts.push(this.createExternalScriptIntegration(packageName, sourceFile));
237631
+ }
237076
237632
  }
237077
237633
  }
237078
- return messageTypes;
237079
237634
  }
237080
- extractStringLiteralFromBranch(branch) {
237081
- const trimmed = branch.trim();
237082
- const match = trimmed.match(/^["'](\w+)["']$/);
237083
- return match?.[1] ?? null;
237635
+ isExternalImport(moduleSpecifier) {
237636
+ return !moduleSpecifier.startsWith(".") && !moduleSpecifier.startsWith("/");
237084
237637
  }
237085
- extractFromMappedType(type2, sourceFile, warnings) {
237086
- warnings.push(`Mapped type detected: ${type2.getText()} - attempting to extract keys`);
237087
- const match = type2.getText().match(/\[K in ([^\]]+)\]/);
237088
- if (!match?.[1]) {
237089
- return [];
237090
- }
237091
- const keyTypeName = match[1].trim();
237092
- const keyTypeAlias = sourceFile.getTypeAlias?.(keyTypeName);
237093
- if (!keyTypeAlias) {
237094
- return [];
237638
+ extractPackageName(moduleSpecifier) {
237639
+ if (moduleSpecifier.startsWith("@")) {
237640
+ return moduleSpecifier.split("/").slice(0, 2).join("/");
237095
237641
  }
237096
- const keyType = keyTypeAlias.getType();
237097
- return this.extractMessageTypesFromType(keyType, keyTypeName, sourceFile, warnings);
237098
- }
237099
- warnTemplateLiteral(type2, warnings) {
237100
- warnings.push(`Template literal type: ${type2.getText()} - this creates an unbounded set and cannot be fully extracted`);
237642
+ return moduleSpecifier.split("/")[0];
237101
237643
  }
237102
- convertType(type2, name) {
237103
- const nullable = type2.isNullable();
237104
- if (type2.isBoolean() || type2.isBooleanLiteral()) {
237105
- return { name, kind: "boolean", nullable };
237106
- }
237107
- if (type2.isUnion()) {
237108
- return this.convertUnionType(type2, name, nullable);
237109
- }
237110
- if (type2.isString() || type2.isStringLiteral()) {
237111
- return { name, kind: "string", nullable };
237112
- }
237113
- if (type2.isNumber() || type2.isNumberLiteral()) {
237114
- return { name, kind: "number", nullable };
237115
- }
237116
- if (type2.isArray()) {
237117
- return this.convertArrayType(type2, name, nullable);
237118
- }
237119
- const collectionType = this.tryConvertCollectionType(type2, name, nullable);
237120
- if (collectionType) {
237121
- return collectionType;
237122
- }
237123
- if (type2.isObject()) {
237124
- return this.convertObjectType(type2, name, nullable);
237125
- }
237126
- if (type2.isNull()) {
237127
- return { name, kind: "null", nullable: true };
237128
- }
237129
- return { name, kind: "unknown", nullable };
237644
+ createExternalScriptIntegration(packageName, sourceFile) {
237645
+ return {
237646
+ type: "external-script",
237647
+ name: packageName,
237648
+ technology: "npm package",
237649
+ usedIn: [sourceFile.getFilePath()],
237650
+ description: `External dependency: ${packageName}`
237651
+ };
237130
237652
  }
237131
- convertUnionType(type2, name, nullable) {
237132
- const unionTypes = type2.getUnionTypes();
237133
- const allStringLiterals = unionTypes.every((t) => t.isStringLiteral());
237134
- if (allStringLiterals) {
237135
- const enumValues = unionTypes.map((t) => t.getLiteralValue());
237136
- return { name, kind: "enum", nullable, enumValues };
237137
- }
237138
- const nonNullTypes = unionTypes.filter((t) => !t.isNull() && !t.isUndefined());
237139
- if (nonNullTypes.length === 1) {
237140
- const firstType = nonNullTypes[0];
237141
- if (firstType) {
237142
- const baseType = this.convertType(firstType, name);
237143
- return { ...baseType, nullable: true };
237144
- }
237145
- }
237653
+ createAPIIntegration(call) {
237654
+ const baseURL = this.extractBaseURL(call.url);
237655
+ const name = this.inferAPIName(baseURL);
237146
237656
  return {
237657
+ type: "api",
237147
237658
  name,
237148
- kind: "union",
237149
- nullable,
237150
- unionTypes: unionTypes.map((t, i2) => this.convertType(t, `${name}_${i2}`))
237659
+ technology: "REST API",
237660
+ url: baseURL,
237661
+ usedIn: [call.file],
237662
+ description: call.description || `External API: ${name}`,
237663
+ calls: [
237664
+ {
237665
+ method: call.method,
237666
+ endpoint: call.url,
237667
+ location: {
237668
+ file: call.file,
237669
+ line: call.line
237670
+ },
237671
+ description: call.description
237672
+ }
237673
+ ]
237151
237674
  };
237152
237675
  }
237153
- convertArrayType(type2, name, nullable) {
237154
- const elementType = type2.getArrayElementType();
237676
+ createWebSocketIntegration(ws) {
237677
+ const name = this.inferAPIName(ws.url);
237155
237678
  return {
237679
+ type: "websocket",
237156
237680
  name,
237157
- kind: "array",
237158
- nullable,
237159
- elementType: elementType ? this.convertType(elementType, `${name}_element`) : { name: "unknown", kind: "unknown", nullable: false }
237681
+ technology: "WebSocket",
237682
+ url: ws.url,
237683
+ usedIn: [ws.file],
237684
+ description: ws.description || `WebSocket connection: ${name}`
237160
237685
  };
237161
237686
  }
237162
- tryConvertCollectionType(type2, name, nullable) {
237163
- const symbol = type2.getSymbol();
237164
- if (!symbol)
237165
- return null;
237166
- const symbolName = symbol.getName();
237167
- if (symbolName === "Map") {
237168
- const typeArgs = type2.getTypeArguments();
237169
- return {
237170
- name,
237171
- kind: "map",
237172
- nullable,
237173
- valueType: typeArgs?.[1] ? this.convertType(typeArgs[1], `${name}_value`) : undefined
237174
- };
237175
- }
237176
- if (symbolName === "Set") {
237177
- const typeArgs = type2.getTypeArguments();
237178
- return {
237179
- name,
237180
- kind: "set",
237181
- nullable,
237182
- elementType: typeArgs?.[0] ? this.convertType(typeArgs[0], `${name}_element`) : undefined
237183
- };
237184
- }
237185
- return null;
237186
- }
237187
- convertObjectType(type2, name, nullable) {
237188
- const properties = {};
237189
- const sourceFile = this.project.getSourceFiles()[0];
237190
- if (sourceFile) {
237191
- for (const prop of type2.getProperties()) {
237192
- const propName = prop.getName();
237193
- const propType = prop.getTypeAtLocation(sourceFile);
237194
- properties[propName] = this.convertType(propType, propName);
237195
- }
237687
+ extractBaseURL(url) {
237688
+ let cleanUrl = url.replace(/\$\{[^}]+\}/g, "");
237689
+ cleanUrl = cleanUrl.replace(/`/g, "");
237690
+ try {
237691
+ const parsed = new URL(cleanUrl);
237692
+ return `${parsed.protocol}//${parsed.host}`;
237693
+ } catch {
237694
+ const match = cleanUrl.match(/https?:\/\/([^/]+)/);
237695
+ return match ? match[0] : cleanUrl;
237196
237696
  }
237197
- return { name, kind: "object", nullable, properties };
237198
237697
  }
237199
- analyzeFields(stateType, prefix = "") {
237200
- const fields = [];
237201
- if (stateType.kind === "object" && stateType.properties) {
237202
- for (const [key, propType] of Object.entries(stateType.properties)) {
237203
- const path = prefix ? `${prefix}.${key}` : key;
237204
- if (propType.kind === "object") {
237205
- fields.push(...this.analyzeFields(propType, path));
237206
- } else {
237207
- const analysis = this.analyzeField(path, propType);
237208
- fields.push(analysis);
237209
- }
237698
+ inferAPIName(url) {
237699
+ try {
237700
+ const parsed = new URL(url);
237701
+ const hostname2 = parsed.hostname;
237702
+ const cleanHost = hostname2.replace(/^www\./, "");
237703
+ const parts = cleanHost.split(".");
237704
+ if (parts.length > 0 && parts[0]) {
237705
+ return `${parts[0].charAt(0).toUpperCase() + parts[0].slice(1)} API`;
237210
237706
  }
237211
- }
237212
- return fields;
237213
- }
237214
- analyzeField(path, type2) {
237215
- const analysis = {
237216
- path,
237217
- type: type2,
237218
- confidence: "low",
237219
- evidence: [],
237220
- suggestions: [],
237221
- bounds: {}
237222
- };
237223
- if (type2.kind === "boolean")
237224
- return this.analyzeBooleanField(analysis);
237225
- if (type2.kind === "enum")
237226
- return this.analyzeEnumField(analysis, type2);
237227
- if (type2.kind === "array")
237228
- return this.analyzeArrayField(analysis);
237229
- if (type2.kind === "number")
237230
- return this.analyzeNumberField(analysis);
237231
- if (type2.kind === "string")
237232
- return this.analyzeStringField(analysis);
237233
- if (type2.kind === "map" || type2.kind === "set")
237234
- return this.analyzeMapSetField(analysis);
237235
- return analysis;
237236
- }
237237
- analyzeBooleanField(analysis) {
237238
- analysis.confidence = "high";
237239
- analysis.evidence.push("Boolean type - auto-configured");
237240
- return analysis;
237707
+ } catch {}
237708
+ return "External API";
237241
237709
  }
237242
- analyzeEnumField(analysis, type2) {
237243
- if (type2.enumValues) {
237244
- analysis.confidence = "high";
237245
- analysis.evidence.push(`Enum with ${type2.enumValues.length} values`);
237246
- if (analysis.bounds) {
237247
- analysis.bounds.values = type2.enumValues;
237710
+ addOrMergeIntegration(map, integration) {
237711
+ const key = `${integration.type}:${integration.name}`;
237712
+ if (map.has(key)) {
237713
+ const existing = map.get(key);
237714
+ if (!existing)
237715
+ return;
237716
+ existing.usedIn = [...new Set([...existing.usedIn, ...integration.usedIn])];
237717
+ if (integration.calls && existing.calls) {
237718
+ existing.calls.push(...integration.calls);
237719
+ } else if (integration.calls) {
237720
+ existing.calls = integration.calls;
237248
237721
  }
237722
+ } else {
237723
+ map.set(key, integration);
237249
237724
  }
237250
- return analysis;
237251
- }
237252
- analyzeArrayField(analysis) {
237253
- analysis.confidence = "low";
237254
- analysis.suggestions.push("Choose maxLength: 5 (fast), 10 (balanced), or 20 (thorough)");
237255
- if (analysis.bounds) {
237256
- analysis.bounds.maxLength = undefined;
237257
- }
237258
- const foundBound = this.findArrayBound();
237259
- if (foundBound && analysis.bounds) {
237260
- analysis.confidence = "medium";
237261
- analysis.evidence.push(`Found array check: ${foundBound.evidence}`);
237262
- analysis.bounds.maxLength = foundBound.value;
237263
- }
237264
- return analysis;
237265
- }
237266
- analyzeNumberField(analysis) {
237267
- analysis.confidence = "low";
237268
- analysis.suggestions.push("Provide min and max values based on your application logic");
237269
- if (analysis.bounds) {
237270
- analysis.bounds.min = undefined;
237271
- analysis.bounds.max = undefined;
237272
- }
237273
- const foundBound = this.findNumberBound();
237274
- if (foundBound && analysis.bounds) {
237275
- analysis.confidence = "high";
237276
- analysis.evidence.push(`Found comparison: ${foundBound.evidence}`);
237277
- analysis.bounds = { ...analysis.bounds, ...foundBound.bounds };
237278
- }
237279
- return analysis;
237280
- }
237281
- analyzeStringField(analysis) {
237282
- analysis.confidence = "low";
237283
- analysis.suggestions.push('Provide 2-3 example values: ["value1", "value2", "value3"]', "Or use { abstract: true } for symbolic verification");
237284
- if (analysis.bounds) {
237285
- analysis.bounds.values = undefined;
237286
- }
237287
- return analysis;
237288
- }
237289
- analyzeMapSetField(analysis) {
237290
- analysis.confidence = "low";
237291
- analysis.suggestions.push("Provide maxSize (recommended: 3-5)");
237292
- if (analysis.bounds) {
237293
- analysis.bounds.maxSize = undefined;
237294
- }
237295
- return analysis;
237296
237725
  }
237297
- findArrayBound() {
237298
- return null;
237299
- }
237300
- findNumberBound() {
237301
- return null;
237726
+ extractJSDocDescription(node) {
237727
+ const jsDocs = node.getJsDocs?.() || [];
237728
+ if (jsDocs.length === 0)
237729
+ return;
237730
+ const comment = jsDocs[0].getDescription().trim();
237731
+ return comment || undefined;
237302
237732
  }
237303
237733
  }
237304
- async function analyzeCodebase(options) {
237305
- const extractor = new TypeExtractor(options.tsConfigPath);
237306
- return extractor.analyzeCodebase(options.stateFilePath);
237307
- }
237308
- var import_ts_morph3;
237309
- var init_types2 = __esm(() => {
237310
- init_handlers();
237311
- import_ts_morph3 = __toESM(require_ts_morph(), 1);
237734
+ var import_ts_morph5;
237735
+ var init_integrations = __esm(() => {
237736
+ import_ts_morph5 = __toESM(require_ts_morph(), 1);
237312
237737
  });
237313
237738
 
237314
237739
  // tools/analysis/src/extract/manifest.ts
237315
- var fs = (() => ({}));
237740
+ var fs2 = (() => ({}));
237316
237741
 
237317
237742
  class ManifestParser {
237318
237743
  manifestPath;
@@ -237321,7 +237746,7 @@ class ManifestParser {
237321
237746
  constructor(projectRoot, optional = false) {
237322
237747
  this.baseDir = projectRoot;
237323
237748
  this.manifestPath = join(projectRoot, "manifest.json");
237324
- if (!fs.existsSync(this.manifestPath)) {
237749
+ if (!fs2.existsSync(this.manifestPath)) {
237325
237750
  if (optional) {
237326
237751
  this.manifestData = null;
237327
237752
  return;
@@ -237329,7 +237754,7 @@ class ManifestParser {
237329
237754
  throw new Error(`manifest.json not found at ${this.manifestPath}`);
237330
237755
  }
237331
237756
  try {
237332
- const content = fs.readFileSync(this.manifestPath, "utf-8");
237757
+ const content = fs2.readFileSync(this.manifestPath, "utf-8");
237333
237758
  this.manifestData = JSON.parse(content);
237334
237759
  } catch (error) {
237335
237760
  throw new Error(`Failed to parse manifest.json: ${error}`);
@@ -237505,27 +237930,27 @@ class ManifestParser {
237505
237930
  join(this.baseDir, "src", manifestPath.replace(/\.js$/, ".tsx"))
237506
237931
  ];
237507
237932
  for (const candidate of candidates) {
237508
- if (fs.existsSync(candidate)) {
237933
+ if (fs2.existsSync(candidate)) {
237509
237934
  return candidate;
237510
237935
  }
237511
237936
  }
237512
237937
  return join(this.baseDir, manifestPath);
237513
237938
  }
237514
237939
  findAssociatedJS(htmlPath) {
237515
- if (!fs.existsSync(htmlPath)) {
237940
+ if (!fs2.existsSync(htmlPath)) {
237516
237941
  return null;
237517
237942
  }
237518
- const html = fs.readFileSync(htmlPath, "utf-8");
237943
+ const html = fs2.readFileSync(htmlPath, "utf-8");
237519
237944
  const scriptMatch = html.match(/<script[^>]+src=["']([^"']+)["']/i);
237520
237945
  if (scriptMatch?.[1]) {
237521
237946
  const scriptPath = scriptMatch[1];
237522
237947
  const fullPath = resolve(dirname(htmlPath), scriptPath);
237523
- if (fs.existsSync(fullPath))
237948
+ if (fs2.existsSync(fullPath))
237524
237949
  return fullPath;
237525
- if (fs.existsSync(fullPath.replace(/\.js$/, ".ts"))) {
237950
+ if (fs2.existsSync(fullPath.replace(/\.js$/, ".ts"))) {
237526
237951
  return fullPath.replace(/\.js$/, ".ts");
237527
237952
  }
237528
- if (fs.existsSync(fullPath.replace(/\.js$/, ".tsx"))) {
237953
+ if (fs2.existsSync(fullPath.replace(/\.js$/, ".tsx"))) {
237529
237954
  return fullPath.replace(/\.js$/, ".tsx");
237530
237955
  }
237531
237956
  }
@@ -237540,7 +237965,7 @@ class ManifestParser {
237540
237965
  join(dir, "index.js")
237541
237966
  ];
237542
237967
  for (const candidate of candidates) {
237543
- if (fs.existsSync(candidate)) {
237968
+ if (fs2.existsSync(candidate)) {
237544
237969
  return candidate;
237545
237970
  }
237546
237971
  }
@@ -237559,861 +237984,6 @@ var init_manifest = __esm(() => {
237559
237984
  init_path();
237560
237985
  });
237561
237986
 
237562
- // tools/analysis/src/extract/contexts.ts
237563
- class ContextAnalyzer {
237564
- project;
237565
- constructor(tsConfigPath) {
237566
- this.project = new import_ts_morph4.Project({
237567
- tsConfigFilePath: tsConfigPath
237568
- });
237569
- }
237570
- analyzeContext(contextType, entryPoint, handlers) {
237571
- const sourceFile = this.project.getSourceFile(entryPoint);
237572
- if (!sourceFile) {
237573
- throw new Error(`Could not find source file: ${entryPoint}`);
237574
- }
237575
- const chromeAPIs = this.extractChromeAPIs(sourceFile);
237576
- const dependencies = this.extractDependencies(sourceFile);
237577
- const description = this.extractDescription(sourceFile);
237578
- const components = this.isUIContext(contextType) ? this.extractComponents(sourceFile) : undefined;
237579
- const contextHandlers = handlers.filter((h) => h.node === contextType);
237580
- return {
237581
- type: contextType,
237582
- entryPoint,
237583
- handlers: contextHandlers,
237584
- chromeAPIs,
237585
- externalAPIs: [],
237586
- ...components ? { components } : {},
237587
- dependencies,
237588
- ...description ? { description } : {}
237589
- };
237590
- }
237591
- extractChromeAPIs(sourceFile) {
237592
- const apis = new Set;
237593
- sourceFile.forEachDescendant((node) => {
237594
- if (import_ts_morph4.Node.isPropertyAccessExpression(node)) {
237595
- const text = node.getText();
237596
- this.detectAPIPattern(text, apis);
237597
- }
237598
- });
237599
- return Array.from(apis).sort();
237600
- }
237601
- detectAPIPattern(text, apis) {
237602
- if (text.startsWith("chrome.")) {
237603
- const api = this.extractAPIFromPrefix(text, "chrome");
237604
- if (api)
237605
- apis.add(api);
237606
- return;
237607
- }
237608
- if (text.startsWith("browser.")) {
237609
- const api = this.extractAPIFromPrefix(text, "browser");
237610
- if (api)
237611
- apis.add(api);
237612
- return;
237613
- }
237614
- if (text.includes("bus.adapters.")) {
237615
- const api = this.extractAPIFromBusAdapter(text);
237616
- if (api)
237617
- apis.add(api);
237618
- }
237619
- }
237620
- extractAPIFromPrefix(text, prefix) {
237621
- const pattern = new RegExp(`^${prefix}\\.([^.(]+(?:\\.[^.(]+)?)`);
237622
- const match = text.match(pattern);
237623
- return match?.[1] || null;
237624
- }
237625
- extractAPIFromBusAdapter(text) {
237626
- const match = text.match(/bus\.adapters\.([^.(]+)/);
237627
- return match?.[1] || null;
237628
- }
237629
- extractDependencies(sourceFile) {
237630
- const deps = [];
237631
- for (const importDecl of sourceFile.getImportDeclarations()) {
237632
- const moduleSpecifier = importDecl.getModuleSpecifierValue();
237633
- deps.push(moduleSpecifier);
237634
- }
237635
- return deps;
237636
- }
237637
- extractDescription(sourceFile) {
237638
- const firstStatement = sourceFile.getStatements()[0];
237639
- if (!firstStatement)
237640
- return;
237641
- const leadingComments = firstStatement.getLeadingCommentRanges();
237642
- if (leadingComments.length === 0)
237643
- return;
237644
- const comment = leadingComments[0].getText();
237645
- const descMatch = comment.match(/@description\s+(.+?)(?:\n|$)/s);
237646
- if (descMatch) {
237647
- return descMatch[1].trim();
237648
- }
237649
- const lines = comment.split(`
237650
- `).map((l) => l.replace(/^[\s*]+/, "").trim()).filter((l) => l && !l.startsWith("@"));
237651
- return lines[0] || undefined;
237652
- }
237653
- extractComponents(sourceFile) {
237654
- const components = [];
237655
- sourceFile.forEachDescendant((node) => {
237656
- this.extractFunctionComponent(node, sourceFile, components);
237657
- this.extractArrowFunctionComponent(node, sourceFile, components);
237658
- this.extractClassComponent(node, sourceFile, components);
237659
- });
237660
- return components;
237661
- }
237662
- extractFunctionComponent(node, sourceFile, components) {
237663
- if (!import_ts_morph4.Node.isFunctionDeclaration(node))
237664
- return;
237665
- const name = node.getName();
237666
- if (!name || !this.looksLikeComponent(name, node))
237667
- return;
237668
- const description = this.extractJSDocDescription(node);
237669
- components.push({
237670
- name,
237671
- type: "function",
237672
- filePath: sourceFile.getFilePath(),
237673
- line: node.getStartLineNumber(),
237674
- props: this.extractProps(node),
237675
- ...description ? { description } : {}
237676
- });
237677
- }
237678
- extractArrowFunctionComponent(node, sourceFile, components) {
237679
- if (!import_ts_morph4.Node.isVariableDeclaration(node))
237680
- return;
237681
- const name = node.getName();
237682
- const initializer = node.getInitializer();
237683
- if (!name || !initializer)
237684
- return;
237685
- if (!import_ts_morph4.Node.isArrowFunction(initializer) && !import_ts_morph4.Node.isFunctionExpression(initializer))
237686
- return;
237687
- if (!this.looksLikeComponent(name, initializer))
237688
- return;
237689
- const description = this.extractJSDocDescription(node);
237690
- components.push({
237691
- name,
237692
- type: "function",
237693
- filePath: sourceFile.getFilePath(),
237694
- line: node.getStartLineNumber(),
237695
- props: this.extractProps(initializer),
237696
- ...description ? { description } : {}
237697
- });
237698
- }
237699
- extractClassComponent(node, sourceFile, components) {
237700
- if (!import_ts_morph4.Node.isClassDeclaration(node))
237701
- return;
237702
- const name = node.getName();
237703
- if (!name || !this.looksLikeClassComponent(node))
237704
- return;
237705
- const description = this.extractJSDocDescription(node);
237706
- components.push({
237707
- name,
237708
- type: "class",
237709
- filePath: sourceFile.getFilePath(),
237710
- line: node.getStartLineNumber(),
237711
- props: this.extractPropsFromClass(node),
237712
- ...description ? { description } : {}
237713
- });
237714
- }
237715
- looksLikeComponent(name, node) {
237716
- if (!/^[A-Z]/.test(name))
237717
- return false;
237718
- const body = node.getBody();
237719
- if (!body)
237720
- return false;
237721
- let hasJSX = false;
237722
- if (import_ts_morph4.Node.isBlock(body)) {
237723
- body.forEachDescendant((child) => {
237724
- if (import_ts_morph4.Node.isJsxElement(child) || import_ts_morph4.Node.isJsxSelfClosingElement(child)) {
237725
- hasJSX = true;
237726
- }
237727
- });
237728
- } else if (import_ts_morph4.Node.isJsxElement(body) || import_ts_morph4.Node.isJsxSelfClosingElement(body)) {
237729
- hasJSX = true;
237730
- }
237731
- return hasJSX;
237732
- }
237733
- looksLikeClassComponent(node) {
237734
- const extendedTypes = node.getExtends();
237735
- if (!extendedTypes)
237736
- return false;
237737
- const extendsText = extendedTypes.getText();
237738
- return /Component|PureComponent/.test(extendsText);
237739
- }
237740
- extractProps(node) {
237741
- const params = node.getParameters();
237742
- if (params.length === 0)
237743
- return [];
237744
- const propsParam = params[0];
237745
- const type2 = propsParam.getType();
237746
- const props = [];
237747
- for (const prop of type2.getProperties()) {
237748
- props.push(prop.getName());
237749
- }
237750
- return props;
237751
- }
237752
- extractPropsFromClass(node) {
237753
- const extendedTypes = node.getExtends();
237754
- if (!extendedTypes)
237755
- return [];
237756
- const typeArgs = extendedTypes.getType().getTypeArguments();
237757
- if (typeArgs.length === 0)
237758
- return [];
237759
- const propsType = typeArgs[0];
237760
- const props = [];
237761
- for (const prop of propsType.getProperties()) {
237762
- props.push(prop.getName());
237763
- }
237764
- return props;
237765
- }
237766
- extractJSDocDescription(node) {
237767
- const jsDocs = node.getJsDocs();
237768
- if (jsDocs.length === 0)
237769
- return;
237770
- const description = jsDocs[0].getDescription().trim();
237771
- return description || undefined;
237772
- }
237773
- isUIContext(contextType) {
237774
- return ["popup", "options", "devtools"].includes(contextType);
237775
- }
237776
- }
237777
- var import_ts_morph4;
237778
- var init_contexts = __esm(() => {
237779
- import_ts_morph4 = __toESM(require_ts_morph(), 1);
237780
- });
237781
-
237782
- // tools/analysis/src/extract/flows.ts
237783
- class FlowAnalyzer {
237784
- project;
237785
- handlers;
237786
- constructor(tsConfigPath, handlers) {
237787
- this.project = new import_ts_morph5.Project({
237788
- tsConfigFilePath: tsConfigPath
237789
- });
237790
- this.handlers = handlers;
237791
- }
237792
- analyzeFlows() {
237793
- const flows = [];
237794
- const handlersByType = new Map;
237795
- for (const handler of this.handlers) {
237796
- if (!handlersByType.has(handler.messageType)) {
237797
- handlersByType.set(handler.messageType, []);
237798
- }
237799
- handlersByType.get(handler.messageType)?.push(handler);
237800
- }
237801
- for (const [messageType, handlers] of handlersByType) {
237802
- const senders = this.findMessageSenders(messageType);
237803
- for (const sender of senders) {
237804
- const recipients = handlers.map((h) => h.node);
237805
- const sequence = this.buildSequence(messageType, sender, handlers);
237806
- const flowMetadata = this.extractFlowMetadata(sender.file, sender.line);
237807
- flows.push({
237808
- messageType,
237809
- from: sender.context,
237810
- to: recipients,
237811
- ...flowMetadata.trigger ? { trigger: flowMetadata.trigger } : {},
237812
- ...flowMetadata.flowName ? { flowName: flowMetadata.flowName } : {},
237813
- ...flowMetadata.description ? { description: flowMetadata.description } : {},
237814
- sequence
237815
- });
237816
- }
237817
- }
237818
- return flows;
237819
- }
237820
- findMessageSenders(messageType) {
237821
- const senders = [];
237822
- for (const sourceFile of this.project.getSourceFiles()) {
237823
- const filePath = sourceFile.getFilePath();
237824
- const context = this.inferContext(filePath);
237825
- sourceFile.forEachDescendant((node) => {
237826
- this.processMessageSender(node, messageType, context, filePath, senders);
237827
- });
237828
- }
237829
- return senders;
237830
- }
237831
- processMessageSender(node, messageType, context, filePath, senders) {
237832
- if (!import_ts_morph5.Node.isCallExpression(node)) {
237833
- return;
237834
- }
237835
- const expression = node.getExpression();
237836
- if (import_ts_morph5.Node.isPropertyAccessExpression(expression)) {
237837
- this.processPropertyAccessSender(node, expression, messageType, context, filePath, senders);
237838
- } else if (import_ts_morph5.Node.isIdentifier(expression)) {
237839
- this.processIdentifierSender(node, expression, messageType, context, filePath, senders);
237840
- }
237841
- }
237842
- processPropertyAccessSender(node, expression, messageType, context, filePath, senders) {
237843
- if (!import_ts_morph5.Node.isPropertyAccessExpression(expression)) {
237844
- return;
237845
- }
237846
- const methodName = expression.getName();
237847
- if (!this.isMessageSendMethod(methodName)) {
237848
- return;
237849
- }
237850
- const args = node.getArguments();
237851
- if (args.length === 0) {
237852
- return;
237853
- }
237854
- const msgType = this.extractMessageTypeFromArg(args[0]);
237855
- if (msgType === messageType) {
237856
- senders.push({
237857
- context,
237858
- file: filePath,
237859
- line: node.getStartLineNumber()
237860
- });
237861
- }
237862
- }
237863
- processIdentifierSender(node, expression, messageType, context, filePath, senders) {
237864
- if (!import_ts_morph5.Node.isIdentifier(expression)) {
237865
- return;
237866
- }
237867
- if (expression.getText() !== "postMessage") {
237868
- return;
237869
- }
237870
- const args = node.getArguments();
237871
- if (args.length === 0) {
237872
- return;
237873
- }
237874
- const msgType = this.extractMessageTypeFromArg(args[0]);
237875
- if (msgType === messageType) {
237876
- senders.push({
237877
- context,
237878
- file: filePath,
237879
- line: node.getStartLineNumber()
237880
- });
237881
- }
237882
- }
237883
- isMessageSendMethod(methodName) {
237884
- return methodName === "send" || methodName === "emit" || methodName === "postMessage" || methodName === "broadcast";
237885
- }
237886
- buildSequence(messageType, sender, handlers) {
237887
- const steps = [];
237888
- let stepNumber = 1;
237889
- steps.push({
237890
- step: stepNumber++,
237891
- action: `${sender.context}.send(${messageType})`,
237892
- context: sender.context,
237893
- location: {
237894
- file: sender.file,
237895
- line: sender.line
237896
- }
237897
- });
237898
- for (const handler of handlers) {
237899
- steps.push({
237900
- step: stepNumber++,
237901
- action: `${handler.node}.handle(${messageType})`,
237902
- context: handler.node,
237903
- location: handler.location
237904
- });
237905
- const subsends = this.findMessagesInHandler(handler);
237906
- for (const subsend of subsends) {
237907
- steps.push({
237908
- step: stepNumber++,
237909
- action: `${handler.node}.send(${subsend.messageType})`,
237910
- context: handler.node,
237911
- location: subsend.location
237912
- });
237913
- }
237914
- }
237915
- return steps;
237916
- }
237917
- findMessagesInHandler(handler) {
237918
- const sends = [];
237919
- const sourceFile = this.project.getSourceFile(handler.location.file);
237920
- if (!sourceFile)
237921
- return sends;
237922
- const targetLine = handler.location.line;
237923
- sourceFile.forEachDescendant((node) => {
237924
- this.processSendCall(node, targetLine, handler.location.file, sends);
237925
- });
237926
- return sends;
237927
- }
237928
- processSendCall(node, targetLine, filePath, sends) {
237929
- if (!import_ts_morph5.Node.isCallExpression(node)) {
237930
- return;
237931
- }
237932
- const line = node.getStartLineNumber();
237933
- if (!this.isNearLine(line, targetLine)) {
237934
- return;
237935
- }
237936
- const expression = node.getExpression();
237937
- if (!this.isSendOrEmitCall(expression)) {
237938
- return;
237939
- }
237940
- const args = node.getArguments();
237941
- if (args.length === 0) {
237942
- return;
237943
- }
237944
- const messageType = this.extractMessageTypeFromArg(args[0]);
237945
- if (messageType) {
237946
- sends.push({
237947
- messageType,
237948
- location: { file: filePath, line }
237949
- });
237950
- }
237951
- }
237952
- isNearLine(line, targetLine) {
237953
- return Math.abs(line - targetLine) < 20;
237954
- }
237955
- isSendOrEmitCall(expression) {
237956
- if (!import_ts_morph5.Node.isPropertyAccessExpression(expression)) {
237957
- return false;
237958
- }
237959
- const methodName = expression.getName();
237960
- return methodName === "send" || methodName === "emit";
237961
- }
237962
- extractMessageTypeFromArg(arg) {
237963
- if (import_ts_morph5.Node.isStringLiteral(arg)) {
237964
- return arg.getLiteralValue();
237965
- }
237966
- if (import_ts_morph5.Node.isObjectLiteralExpression(arg)) {
237967
- return this.extractMessageTypeFromObject(arg);
237968
- }
237969
- return;
237970
- }
237971
- extractMessageTypeFromObject(obj) {
237972
- if (!import_ts_morph5.Node.isObjectLiteralExpression(obj)) {
237973
- return;
237974
- }
237975
- const typeProperty = obj.getProperty("type");
237976
- if (!typeProperty || !import_ts_morph5.Node.isPropertyAssignment(typeProperty)) {
237977
- return;
237978
- }
237979
- const initializer = typeProperty.getInitializer();
237980
- if (!initializer || !import_ts_morph5.Node.isStringLiteral(initializer)) {
237981
- return;
237982
- }
237983
- return initializer.getLiteralValue();
237984
- }
237985
- extractFlowMetadata(filePath, lineNumber) {
237986
- const sourceFile = this.project.getSourceFile(filePath);
237987
- if (!sourceFile)
237988
- return {};
237989
- let targetNode = null;
237990
- sourceFile.forEachDescendant((node) => {
237991
- if (node.getStartLineNumber() === lineNumber) {
237992
- targetNode = node;
237993
- }
237994
- });
237995
- if (!targetNode)
237996
- return {};
237997
- const jsDocs = targetNode.getJsDocs?.() || [];
237998
- if (jsDocs.length === 0)
237999
- return {};
238000
- const comment = jsDocs[0].getText();
238001
- const flowMatch = comment.match(/@flow\s+([^\s]+)/);
238002
- const flowName = flowMatch ? flowMatch[1] : undefined;
238003
- const triggerMatch = comment.match(/@trigger\s+(.+?)(?:\n|$)/);
238004
- const trigger = triggerMatch ? triggerMatch[1].trim() : undefined;
238005
- const descMatch = comment.match(/@description\s+(.+?)(?:\n|$)/s);
238006
- const description = descMatch ? descMatch[1].trim() : undefined;
238007
- return { trigger, flowName, description };
238008
- }
238009
- inferContext(filePath) {
238010
- const path = filePath.toLowerCase();
238011
- const contextPatterns = [
238012
- { context: "background", patterns: ["/background/", "\\background\\"] },
238013
- { context: "content", patterns: ["/content/", "\\content\\"] },
238014
- { context: "popup", patterns: ["/popup/", "\\popup\\"] },
238015
- { context: "devtools", patterns: ["/devtools/", "\\devtools\\"] },
238016
- { context: "options", patterns: ["/options/", "\\options\\"] },
238017
- { context: "offscreen", patterns: ["/offscreen/", "\\offscreen\\"] },
238018
- { context: "server", patterns: ["/server/", "\\server\\", "/server."] },
238019
- { context: "client", patterns: ["/client/", "\\client\\", "/client."] },
238020
- { context: "worker", patterns: ["/worker/", "\\worker\\", "service-worker"] }
238021
- ];
238022
- for (const { context, patterns } of contextPatterns) {
238023
- if (patterns.some((pattern) => path.includes(pattern))) {
238024
- return context;
238025
- }
238026
- }
238027
- return "unknown";
238028
- }
238029
- }
238030
- var import_ts_morph5;
238031
- var init_flows = __esm(() => {
238032
- import_ts_morph5 = __toESM(require_ts_morph(), 1);
238033
- });
238034
-
238035
- // tools/analysis/src/extract/integrations.ts
238036
- class IntegrationAnalyzer {
238037
- project;
238038
- constructor(tsConfigPath) {
238039
- this.project = new import_ts_morph6.Project({
238040
- tsConfigFilePath: tsConfigPath
238041
- });
238042
- }
238043
- analyzeIntegrations() {
238044
- const integrations = new Map;
238045
- const fetchCalls = this.findFetchCalls();
238046
- for (const call of fetchCalls) {
238047
- this.addOrMergeIntegration(integrations, this.createAPIIntegration(call));
238048
- }
238049
- const websockets = this.findWebSockets();
238050
- for (const ws of websockets) {
238051
- this.addOrMergeIntegration(integrations, this.createWebSocketIntegration(ws));
238052
- }
238053
- const externalScripts = this.findExternalScripts();
238054
- for (const script of externalScripts) {
238055
- this.addOrMergeIntegration(integrations, script);
238056
- }
238057
- return Array.from(integrations.values());
238058
- }
238059
- findFetchCalls() {
238060
- const calls = [];
238061
- for (const sourceFile of this.project.getSourceFiles()) {
238062
- sourceFile.forEachDescendant((node) => {
238063
- this.processFetchCall(node, sourceFile, calls);
238064
- });
238065
- }
238066
- return calls;
238067
- }
238068
- processFetchCall(node, sourceFile, calls) {
238069
- if (!import_ts_morph6.Node.isCallExpression(node)) {
238070
- return;
238071
- }
238072
- const expression = node.getExpression();
238073
- if (!this.isFetchCall(expression)) {
238074
- return;
238075
- }
238076
- const args = node.getArguments();
238077
- if (args.length === 0) {
238078
- return;
238079
- }
238080
- const url = this.extractURLFromArg(args[0]);
238081
- if (!url) {
238082
- return;
238083
- }
238084
- const method = this.extractMethodFromOptions(args);
238085
- const description = this.extractJSDocDescription(node);
238086
- calls.push({
238087
- url,
238088
- method,
238089
- file: sourceFile.getFilePath(),
238090
- line: node.getStartLineNumber(),
238091
- description
238092
- });
238093
- }
238094
- isFetchCall(expression) {
238095
- return import_ts_morph6.Node.isIdentifier(expression) && expression.getText() === "fetch";
238096
- }
238097
- extractURLFromArg(urlArg) {
238098
- if (import_ts_morph6.Node.isStringLiteral(urlArg)) {
238099
- return urlArg.getLiteralValue();
238100
- }
238101
- if (import_ts_morph6.Node.isTemplateExpression(urlArg)) {
238102
- return this.extractBaseURL(urlArg.getText());
238103
- }
238104
- return null;
238105
- }
238106
- extractMethodFromOptions(args) {
238107
- const defaultMethod = "GET";
238108
- if (args.length <= 1) {
238109
- return defaultMethod;
238110
- }
238111
- const optionsArg = args[1];
238112
- if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) {
238113
- return defaultMethod;
238114
- }
238115
- const methodProp = optionsArg.getProperty("method");
238116
- if (!methodProp || !import_ts_morph6.Node.isPropertyAssignment(methodProp)) {
238117
- return defaultMethod;
238118
- }
238119
- const initializer = methodProp.getInitializer();
238120
- if (!initializer || !import_ts_morph6.Node.isStringLiteral(initializer)) {
238121
- return defaultMethod;
238122
- }
238123
- return initializer.getLiteralValue().toUpperCase();
238124
- }
238125
- findWebSockets() {
238126
- const websockets = [];
238127
- for (const sourceFile of this.project.getSourceFiles()) {
238128
- sourceFile.forEachDescendant((node) => {
238129
- if (import_ts_morph6.Node.isNewExpression(node)) {
238130
- const expression = node.getExpression();
238131
- if (import_ts_morph6.Node.isIdentifier(expression) && expression.getText() === "WebSocket") {
238132
- const args = node.getArguments();
238133
- if (args.length > 0 && import_ts_morph6.Node.isStringLiteral(args[0])) {
238134
- const url = args[0].getLiteralValue();
238135
- const description = this.extractJSDocDescription(node);
238136
- websockets.push({
238137
- url,
238138
- file: sourceFile.getFilePath(),
238139
- line: node.getStartLineNumber(),
238140
- description
238141
- });
238142
- }
238143
- }
238144
- }
238145
- });
238146
- }
238147
- return websockets;
238148
- }
238149
- findExternalScripts() {
238150
- const scripts = [];
238151
- const seen = new Set;
238152
- for (const sourceFile of this.project.getSourceFiles()) {
238153
- this.extractExternalImportsFromFile(sourceFile, scripts, seen);
238154
- }
238155
- return scripts;
238156
- }
238157
- extractExternalImportsFromFile(sourceFile, scripts, seen) {
238158
- for (const importDecl of sourceFile.getImportDeclarations()) {
238159
- const moduleSpecifier = importDecl.getModuleSpecifierValue();
238160
- if (this.isExternalImport(moduleSpecifier)) {
238161
- const packageName = this.extractPackageName(moduleSpecifier);
238162
- if (packageName && !seen.has(packageName)) {
238163
- seen.add(packageName);
238164
- scripts.push(this.createExternalScriptIntegration(packageName, sourceFile));
238165
- }
238166
- }
238167
- }
238168
- }
238169
- isExternalImport(moduleSpecifier) {
238170
- return !moduleSpecifier.startsWith(".") && !moduleSpecifier.startsWith("/");
238171
- }
238172
- extractPackageName(moduleSpecifier) {
238173
- if (moduleSpecifier.startsWith("@")) {
238174
- return moduleSpecifier.split("/").slice(0, 2).join("/");
238175
- }
238176
- return moduleSpecifier.split("/")[0];
238177
- }
238178
- createExternalScriptIntegration(packageName, sourceFile) {
238179
- return {
238180
- type: "external-script",
238181
- name: packageName,
238182
- technology: "npm package",
238183
- usedIn: [sourceFile.getFilePath()],
238184
- description: `External dependency: ${packageName}`
238185
- };
238186
- }
238187
- createAPIIntegration(call) {
238188
- const baseURL = this.extractBaseURL(call.url);
238189
- const name = this.inferAPIName(baseURL);
238190
- return {
238191
- type: "api",
238192
- name,
238193
- technology: "REST API",
238194
- url: baseURL,
238195
- usedIn: [call.file],
238196
- description: call.description || `External API: ${name}`,
238197
- calls: [
238198
- {
238199
- method: call.method,
238200
- endpoint: call.url,
238201
- location: {
238202
- file: call.file,
238203
- line: call.line
238204
- },
238205
- description: call.description
238206
- }
238207
- ]
238208
- };
238209
- }
238210
- createWebSocketIntegration(ws) {
238211
- const name = this.inferAPIName(ws.url);
238212
- return {
238213
- type: "websocket",
238214
- name,
238215
- technology: "WebSocket",
238216
- url: ws.url,
238217
- usedIn: [ws.file],
238218
- description: ws.description || `WebSocket connection: ${name}`
238219
- };
238220
- }
238221
- extractBaseURL(url) {
238222
- let cleanUrl = url.replace(/\$\{[^}]+\}/g, "");
238223
- cleanUrl = cleanUrl.replace(/`/g, "");
238224
- try {
238225
- const parsed = new URL(cleanUrl);
238226
- return `${parsed.protocol}//${parsed.host}`;
238227
- } catch {
238228
- const match = cleanUrl.match(/https?:\/\/([^/]+)/);
238229
- return match ? match[0] : cleanUrl;
238230
- }
238231
- }
238232
- inferAPIName(url) {
238233
- try {
238234
- const parsed = new URL(url);
238235
- const hostname2 = parsed.hostname;
238236
- const cleanHost = hostname2.replace(/^www\./, "");
238237
- const parts = cleanHost.split(".");
238238
- if (parts.length > 0 && parts[0]) {
238239
- return `${parts[0].charAt(0).toUpperCase() + parts[0].slice(1)} API`;
238240
- }
238241
- } catch {}
238242
- return "External API";
238243
- }
238244
- addOrMergeIntegration(map, integration) {
238245
- const key = `${integration.type}:${integration.name}`;
238246
- if (map.has(key)) {
238247
- const existing = map.get(key);
238248
- if (!existing)
238249
- return;
238250
- existing.usedIn = [...new Set([...existing.usedIn, ...integration.usedIn])];
238251
- if (integration.calls && existing.calls) {
238252
- existing.calls.push(...integration.calls);
238253
- } else if (integration.calls) {
238254
- existing.calls = integration.calls;
238255
- }
238256
- } else {
238257
- map.set(key, integration);
238258
- }
238259
- }
238260
- extractJSDocDescription(node) {
238261
- const jsDocs = node.getJsDocs?.() || [];
238262
- if (jsDocs.length === 0)
238263
- return;
238264
- const comment = jsDocs[0].getDescription().trim();
238265
- return comment || undefined;
238266
- }
238267
- }
238268
- var import_ts_morph6;
238269
- var init_integrations = __esm(() => {
238270
- import_ts_morph6 = __toESM(require_ts_morph(), 1);
238271
- });
238272
-
238273
- // tools/analysis/src/extract/adr.ts
238274
- var fs2 = (() => ({}));
238275
-
238276
- class ADRExtractor {
238277
- projectRoot;
238278
- constructor(projectRoot) {
238279
- this.projectRoot = projectRoot;
238280
- }
238281
- extract() {
238282
- const adrDir = this.findADRDirectory();
238283
- if (!adrDir || !fs2.existsSync(adrDir)) {
238284
- return {
238285
- adrs: [],
238286
- directory: adrDir || join(this.projectRoot, "docs", "adr")
238287
- };
238288
- }
238289
- const files = fs2.readdirSync(adrDir).filter((file) => file.endsWith(".md")).map((file) => join(adrDir, file));
238290
- const adrs = [];
238291
- for (const file of files) {
238292
- try {
238293
- const adr2 = this.parseADR(file);
238294
- if (adr2) {
238295
- adrs.push(adr2);
238296
- }
238297
- } catch (error) {
238298
- if (process.env["POLLY_DEBUG"]) {
238299
- console.log(`[DEBUG] Failed to parse ADR file ${file}: ${error}`);
238300
- }
238301
- }
238302
- }
238303
- adrs.sort((a, b) => a.id.localeCompare(b.id));
238304
- return {
238305
- adrs,
238306
- directory: adrDir
238307
- };
238308
- }
238309
- findADRDirectory() {
238310
- const candidates = [
238311
- join(this.projectRoot, "docs", "adr"),
238312
- join(this.projectRoot, "docs", "architecture", "decisions"),
238313
- join(this.projectRoot, "adr"),
238314
- join(this.projectRoot, "architecture", "decisions")
238315
- ];
238316
- for (const candidate of candidates) {
238317
- if (fs2.existsSync(candidate)) {
238318
- return candidate;
238319
- }
238320
- }
238321
- return null;
238322
- }
238323
- parseADR(filePath) {
238324
- const content = fs2.readFileSync(filePath, "utf-8");
238325
- const fileName = basename(filePath, ".md");
238326
- const idMatch = fileName.match(/^(\d+)/);
238327
- const id = idMatch?.[1] ?? fileName;
238328
- const titleMatch = content.match(/^#\s+(.+)$/m);
238329
- const title = titleMatch?.[1]?.trim() ?? fileName;
238330
- const status = this.extractStatus(content);
238331
- const date = this.extractDate(content);
238332
- const context = this.extractSection(content, "Context");
238333
- const decision = this.extractSection(content, "Decision");
238334
- const consequences = this.extractSection(content, "Consequences");
238335
- const alternativesSection = this.extractSection(content, "Alternatives");
238336
- const alternatives = alternativesSection ? alternativesSection.split(`
238337
- `).filter((line) => line.trim().startsWith("-")).map((line) => line.replace(/^-\s*/, "").trim()) : undefined;
238338
- const links = this.extractLinks(content);
238339
- if (!context || !decision || !consequences) {
238340
- return null;
238341
- }
238342
- return {
238343
- id,
238344
- title,
238345
- status,
238346
- date,
238347
- context,
238348
- decision,
238349
- consequences,
238350
- ...alternatives && alternatives.length > 0 ? { alternatives } : {},
238351
- ...links.length > 0 ? { links } : {},
238352
- source: filePath
238353
- };
238354
- }
238355
- extractStatus(content) {
238356
- const statusMatch = content.match(/Status:\s*(\w+)/i);
238357
- if (!statusMatch)
238358
- return "accepted";
238359
- const status = statusMatch[1]?.toLowerCase();
238360
- if (status && ["proposed", "accepted", "deprecated", "superseded"].includes(status)) {
238361
- return status;
238362
- }
238363
- return "accepted";
238364
- }
238365
- extractDate(content) {
238366
- const dateMatch = content.match(/Date:\s*(\d{4}-\d{2}-\d{2})/i) || content.match(/(\d{4}-\d{2}-\d{2})/i);
238367
- if (dateMatch?.[1]) {
238368
- return dateMatch[1];
238369
- }
238370
- const isoDate = new Date().toISOString().split("T")[0];
238371
- return isoDate || new Date().toLocaleDateString("en-CA");
238372
- }
238373
- extractSection(content, sectionName) {
238374
- const regex = new RegExp(`##\\s+${sectionName}\\s*\\n([\\s\\S]*?)(?=\\n##|$)`, "i");
238375
- const match = content.match(regex);
238376
- return match?.[1]?.trim() ?? "";
238377
- }
238378
- extractLinks(content) {
238379
- const links = [];
238380
- const supersedesMatch = content.match(/Supersedes:\s*ADR-(\d+)/gi);
238381
- if (supersedesMatch) {
238382
- for (const match of supersedesMatch) {
238383
- const idMatch = match.match(/ADR-(\d+)/);
238384
- const id = idMatch?.[1];
238385
- if (id) {
238386
- links.push({
238387
- type: "supersedes",
238388
- adrId: id
238389
- });
238390
- }
238391
- }
238392
- }
238393
- const supersededByMatch = content.match(/Superseded by:\s*ADR-(\d+)/gi);
238394
- if (supersededByMatch) {
238395
- for (const match of supersededByMatch) {
238396
- const idMatch = match.match(/ADR-(\d+)/);
238397
- const id = idMatch?.[1];
238398
- if (id) {
238399
- links.push({
238400
- type: "superseded-by",
238401
- adrId: id
238402
- });
238403
- }
238404
- }
238405
- }
238406
- return links;
238407
- }
238408
- }
238409
- function extractADRs(projectRoot) {
238410
- const extractor = new ADRExtractor(projectRoot);
238411
- return extractor.extract();
238412
- }
238413
- var init_adr = __esm(() => {
238414
- init_path();
238415
- });
238416
-
238417
237987
  // tools/analysis/src/extract/project-detector.ts
238418
237988
  var exports_project_detector = {};
238419
237989
  __export(exports_project_detector, {
@@ -239052,18 +238622,448 @@ var init_architecture = __esm(() => {
239052
238622
  init_manifest();
239053
238623
  });
239054
238624
 
238625
+ // tools/analysis/src/extract/types.ts
238626
+ class TypeExtractor {
238627
+ project;
238628
+ constructor(tsConfigPath) {
238629
+ this.project = new import_ts_morph6.Project({
238630
+ tsConfigFilePath: tsConfigPath
238631
+ });
238632
+ }
238633
+ async analyzeCodebase(stateFilePath) {
238634
+ const stateType = stateFilePath ? this.extractStateType(stateFilePath) : this.findStateType();
238635
+ const messageTypes = this.findMessageTypes();
238636
+ const fields = stateType ? this.analyzeFields(stateType) : [];
238637
+ const handlerAnalysis = this.extractHandlerAnalysis();
238638
+ const validMessageTypes = this.filterAndLogMessageTypes(messageTypes, handlerAnalysis.messageTypes);
238639
+ const validHandlers = this.filterAndLogHandlers(handlerAnalysis.handlers);
238640
+ return {
238641
+ stateType,
238642
+ messageTypes: validMessageTypes,
238643
+ fields,
238644
+ handlers: validHandlers
238645
+ };
238646
+ }
238647
+ extractHandlerAnalysis() {
238648
+ const configFilePath = this.project.getCompilerOptions()["configFilePath"];
238649
+ const tsConfigPath = typeof configFilePath === "string" ? configFilePath : "tsconfig.json";
238650
+ const handlerExtractor = new HandlerExtractor(tsConfigPath);
238651
+ return handlerExtractor.extractHandlers();
238652
+ }
238653
+ filterAndLogMessageTypes(messageTypes, handlerMessageTypes) {
238654
+ const allMessageTypes = Array.from(new Set([...messageTypes, ...handlerMessageTypes]));
238655
+ const validMessageTypes = [];
238656
+ const invalidMessageTypes = [];
238657
+ for (const msgType of allMessageTypes) {
238658
+ if (this.isValidTLAIdentifier(msgType)) {
238659
+ validMessageTypes.push(msgType);
238660
+ } else {
238661
+ invalidMessageTypes.push(msgType);
238662
+ }
238663
+ }
238664
+ this.logInvalidMessageTypes(invalidMessageTypes);
238665
+ return validMessageTypes;
238666
+ }
238667
+ logInvalidMessageTypes(invalidMessageTypes) {
238668
+ if (invalidMessageTypes.length === 0 || !process.env["POLLY_DEBUG"])
238669
+ return;
238670
+ console.log(`[WARN] Filtered out ${invalidMessageTypes.length} invalid message type(s):`);
238671
+ for (const invalid of invalidMessageTypes) {
238672
+ console.log(`[WARN] - "${invalid}" (not a valid TLA+ identifier)`);
238673
+ }
238674
+ }
238675
+ filterAndLogHandlers(handlers) {
238676
+ const validHandlers = handlers.filter((h) => this.isValidTLAIdentifier(h.messageType));
238677
+ this.logInvalidHandlers(handlers, validHandlers);
238678
+ return validHandlers;
238679
+ }
238680
+ logInvalidHandlers(allHandlers, validHandlers) {
238681
+ const filteredHandlerCount = allHandlers.length - validHandlers.length;
238682
+ if (filteredHandlerCount === 0 || !process.env["POLLY_DEBUG"])
238683
+ return;
238684
+ console.log(`[WARN] Filtered out ${filteredHandlerCount} handler(s) with invalid message types:`);
238685
+ for (const handler of allHandlers) {
238686
+ if (!this.isValidTLAIdentifier(handler.messageType)) {
238687
+ console.log(`[WARN] - Handler for "${handler.messageType}" at ${handler.location.file}:${handler.location.line}`);
238688
+ }
238689
+ }
238690
+ }
238691
+ isValidTLAIdentifier(s) {
238692
+ if (!s || s.length === 0) {
238693
+ return false;
238694
+ }
238695
+ return /^[a-zA-Z][a-zA-Z0-9_]*$/.test(s);
238696
+ }
238697
+ extractStateType(filePath) {
238698
+ const sourceFile = this.project.getSourceFile(filePath);
238699
+ if (!sourceFile) {
238700
+ return null;
238701
+ }
238702
+ const typeAlias = sourceFile.getTypeAlias("AppState") || sourceFile.getTypeAlias("State") || sourceFile.getTypeAliases()[0];
238703
+ if (!typeAlias) {
238704
+ return null;
238705
+ }
238706
+ const type2 = typeAlias.getType();
238707
+ return this.convertType(type2, typeAlias.getName());
238708
+ }
238709
+ findStateType() {
238710
+ const stateFiles = this.project.getSourceFiles("**/state*.ts");
238711
+ for (const file of stateFiles) {
238712
+ const typeAlias = file.getTypeAlias("AppState") || file.getTypeAlias("State");
238713
+ if (typeAlias) {
238714
+ const type2 = typeAlias.getType();
238715
+ return this.convertType(type2, typeAlias.getName());
238716
+ }
238717
+ }
238718
+ return null;
238719
+ }
238720
+ findMessageTypes() {
238721
+ const messageTypes = [];
238722
+ const warnings = [];
238723
+ const messageFiles = this.project.getSourceFiles("**/message*.ts");
238724
+ for (const file of messageFiles) {
238725
+ for (const typeAlias of file.getTypeAliases()) {
238726
+ const extractedTypes = this.extractMessageTypesFromType(typeAlias.getType(), typeAlias.getName(), file, warnings);
238727
+ messageTypes.push(...extractedTypes);
238728
+ }
238729
+ }
238730
+ if (warnings.length > 0 && process.env["POLLY_DEBUG"]) {
238731
+ console.log("[WARN] Message type extraction warnings:");
238732
+ for (const warning of warnings) {
238733
+ console.log(`[WARN] ${warning}`);
238734
+ }
238735
+ }
238736
+ return [...new Set(messageTypes)];
238737
+ }
238738
+ extractMessageTypesFromType(type2, typeName, sourceFile, warnings) {
238739
+ if (type2.isUnion()) {
238740
+ return this.extractFromUnionType(type2, typeName, sourceFile, warnings);
238741
+ }
238742
+ if (type2.getAliasSymbol()) {
238743
+ return this.extractFromTypeAlias(type2, typeName, sourceFile, warnings);
238744
+ }
238745
+ if (type2.isConditionalType?.()) {
238746
+ return this.extractFromConditionalType(type2, warnings);
238747
+ }
238748
+ if (type2.getText().includes("[K in ")) {
238749
+ return this.extractFromMappedType(type2, sourceFile, warnings);
238750
+ }
238751
+ if (type2.getText().includes("`")) {
238752
+ this.warnTemplateLiteral(type2, warnings);
238753
+ return [];
238754
+ }
238755
+ return [];
238756
+ }
238757
+ extractFromUnionType(type2, typeName, sourceFile, warnings) {
238758
+ const messageTypes = [];
238759
+ const unionTypes = type2.getUnionTypes();
238760
+ for (const unionType of unionTypes) {
238761
+ const extracted = this.extractFromUnionMember(unionType, typeName, sourceFile, warnings);
238762
+ messageTypes.push(...extracted);
238763
+ }
238764
+ return messageTypes;
238765
+ }
238766
+ extractFromUnionMember(unionType, typeName, sourceFile, warnings) {
238767
+ if (unionType.isObject()) {
238768
+ return this.extractFromDiscriminatedUnion(unionType, sourceFile, warnings);
238769
+ }
238770
+ if (unionType.isStringLiteral()) {
238771
+ return [unionType.getLiteralValue()];
238772
+ }
238773
+ if (unionType.getAliasSymbol()) {
238774
+ const aliasedType = unionType.getAliasSymbol()?.getDeclaredType();
238775
+ if (aliasedType) {
238776
+ return this.extractMessageTypesFromType(aliasedType, typeName, sourceFile, warnings);
238777
+ }
238778
+ }
238779
+ return [];
238780
+ }
238781
+ extractFromDiscriminatedUnion(unionType, sourceFile, warnings) {
238782
+ const typeProperty = unionType.getProperty("type");
238783
+ if (!typeProperty) {
238784
+ return [];
238785
+ }
238786
+ const typeType = typeProperty.getTypeAtLocation(sourceFile);
238787
+ if (typeType.isStringLiteral()) {
238788
+ return [typeType.getLiteralValue()];
238789
+ }
238790
+ if (typeType.getText().includes("`")) {
238791
+ warnings.push(`Template literal type in discriminant: ${typeType.getText()} - may be unbounded`);
238792
+ }
238793
+ return [];
238794
+ }
238795
+ extractFromTypeAlias(type2, typeName, sourceFile, warnings) {
238796
+ const aliasedType = type2.getAliasSymbol()?.getDeclaredType();
238797
+ if (!aliasedType || aliasedType === type2) {
238798
+ return [];
238799
+ }
238800
+ return this.extractMessageTypesFromType(aliasedType, typeName, sourceFile, warnings);
238801
+ }
238802
+ extractFromConditionalType(type2, warnings) {
238803
+ warnings.push(`Conditional type detected: ${type2.getText()} - extracting conservatively`);
238804
+ const messageTypes = [];
238805
+ const typeText = type2.getText();
238806
+ const parts = typeText.split("?");
238807
+ if (parts.length < 2) {
238808
+ return messageTypes;
238809
+ }
238810
+ const branches = parts[1].split(":");
238811
+ for (const branch of branches) {
238812
+ const extracted = this.extractStringLiteralFromBranch(branch);
238813
+ if (extracted) {
238814
+ messageTypes.push(extracted);
238815
+ }
238816
+ }
238817
+ return messageTypes;
238818
+ }
238819
+ extractStringLiteralFromBranch(branch) {
238820
+ const trimmed = branch.trim();
238821
+ const match = trimmed.match(/^["'](\w+)["']$/);
238822
+ return match?.[1] ?? null;
238823
+ }
238824
+ extractFromMappedType(type2, sourceFile, warnings) {
238825
+ warnings.push(`Mapped type detected: ${type2.getText()} - attempting to extract keys`);
238826
+ const match = type2.getText().match(/\[K in ([^\]]+)\]/);
238827
+ if (!match?.[1]) {
238828
+ return [];
238829
+ }
238830
+ const keyTypeName = match[1].trim();
238831
+ const keyTypeAlias = sourceFile.getTypeAlias?.(keyTypeName);
238832
+ if (!keyTypeAlias) {
238833
+ return [];
238834
+ }
238835
+ const keyType = keyTypeAlias.getType();
238836
+ return this.extractMessageTypesFromType(keyType, keyTypeName, sourceFile, warnings);
238837
+ }
238838
+ warnTemplateLiteral(type2, warnings) {
238839
+ warnings.push(`Template literal type: ${type2.getText()} - this creates an unbounded set and cannot be fully extracted`);
238840
+ }
238841
+ convertType(type2, name) {
238842
+ const nullable = type2.isNullable();
238843
+ if (type2.isBoolean() || type2.isBooleanLiteral()) {
238844
+ return { name, kind: "boolean", nullable };
238845
+ }
238846
+ if (type2.isUnion()) {
238847
+ return this.convertUnionType(type2, name, nullable);
238848
+ }
238849
+ if (type2.isString() || type2.isStringLiteral()) {
238850
+ return { name, kind: "string", nullable };
238851
+ }
238852
+ if (type2.isNumber() || type2.isNumberLiteral()) {
238853
+ return { name, kind: "number", nullable };
238854
+ }
238855
+ if (type2.isArray()) {
238856
+ return this.convertArrayType(type2, name, nullable);
238857
+ }
238858
+ const collectionType = this.tryConvertCollectionType(type2, name, nullable);
238859
+ if (collectionType) {
238860
+ return collectionType;
238861
+ }
238862
+ if (type2.isObject()) {
238863
+ return this.convertObjectType(type2, name, nullable);
238864
+ }
238865
+ if (type2.isNull()) {
238866
+ return { name, kind: "null", nullable: true };
238867
+ }
238868
+ return { name, kind: "unknown", nullable };
238869
+ }
238870
+ convertUnionType(type2, name, nullable) {
238871
+ const unionTypes = type2.getUnionTypes();
238872
+ const allStringLiterals = unionTypes.every((t) => t.isStringLiteral());
238873
+ if (allStringLiterals) {
238874
+ const enumValues = unionTypes.map((t) => t.getLiteralValue());
238875
+ return { name, kind: "enum", nullable, enumValues };
238876
+ }
238877
+ const nonNullTypes = unionTypes.filter((t) => !t.isNull() && !t.isUndefined());
238878
+ if (nonNullTypes.length === 1) {
238879
+ const firstType = nonNullTypes[0];
238880
+ if (firstType) {
238881
+ const baseType = this.convertType(firstType, name);
238882
+ return { ...baseType, nullable: true };
238883
+ }
238884
+ }
238885
+ return {
238886
+ name,
238887
+ kind: "union",
238888
+ nullable,
238889
+ unionTypes: unionTypes.map((t, i2) => this.convertType(t, `${name}_${i2}`))
238890
+ };
238891
+ }
238892
+ convertArrayType(type2, name, nullable) {
238893
+ const elementType = type2.getArrayElementType();
238894
+ return {
238895
+ name,
238896
+ kind: "array",
238897
+ nullable,
238898
+ elementType: elementType ? this.convertType(elementType, `${name}_element`) : { name: "unknown", kind: "unknown", nullable: false }
238899
+ };
238900
+ }
238901
+ tryConvertCollectionType(type2, name, nullable) {
238902
+ const symbol = type2.getSymbol();
238903
+ if (!symbol)
238904
+ return null;
238905
+ const symbolName = symbol.getName();
238906
+ if (symbolName === "Map") {
238907
+ const typeArgs = type2.getTypeArguments();
238908
+ return {
238909
+ name,
238910
+ kind: "map",
238911
+ nullable,
238912
+ valueType: typeArgs?.[1] ? this.convertType(typeArgs[1], `${name}_value`) : undefined
238913
+ };
238914
+ }
238915
+ if (symbolName === "Set") {
238916
+ const typeArgs = type2.getTypeArguments();
238917
+ return {
238918
+ name,
238919
+ kind: "set",
238920
+ nullable,
238921
+ elementType: typeArgs?.[0] ? this.convertType(typeArgs[0], `${name}_element`) : undefined
238922
+ };
238923
+ }
238924
+ return null;
238925
+ }
238926
+ convertObjectType(type2, name, nullable) {
238927
+ const properties = {};
238928
+ const sourceFile = this.project.getSourceFiles()[0];
238929
+ if (sourceFile) {
238930
+ for (const prop of type2.getProperties()) {
238931
+ const propName = prop.getName();
238932
+ const propType = prop.getTypeAtLocation(sourceFile);
238933
+ properties[propName] = this.convertType(propType, propName);
238934
+ }
238935
+ }
238936
+ return { name, kind: "object", nullable, properties };
238937
+ }
238938
+ analyzeFields(stateType, prefix = "") {
238939
+ const fields = [];
238940
+ if (stateType.kind === "object" && stateType.properties) {
238941
+ for (const [key, propType] of Object.entries(stateType.properties)) {
238942
+ const path = prefix ? `${prefix}.${key}` : key;
238943
+ if (propType.kind === "object") {
238944
+ fields.push(...this.analyzeFields(propType, path));
238945
+ } else {
238946
+ const analysis = this.analyzeField(path, propType);
238947
+ fields.push(analysis);
238948
+ }
238949
+ }
238950
+ }
238951
+ return fields;
238952
+ }
238953
+ analyzeField(path, type2) {
238954
+ const analysis = {
238955
+ path,
238956
+ type: type2,
238957
+ confidence: "low",
238958
+ evidence: [],
238959
+ suggestions: [],
238960
+ bounds: {}
238961
+ };
238962
+ if (type2.kind === "boolean")
238963
+ return this.analyzeBooleanField(analysis);
238964
+ if (type2.kind === "enum")
238965
+ return this.analyzeEnumField(analysis, type2);
238966
+ if (type2.kind === "array")
238967
+ return this.analyzeArrayField(analysis);
238968
+ if (type2.kind === "number")
238969
+ return this.analyzeNumberField(analysis);
238970
+ if (type2.kind === "string")
238971
+ return this.analyzeStringField(analysis);
238972
+ if (type2.kind === "map" || type2.kind === "set")
238973
+ return this.analyzeMapSetField(analysis);
238974
+ return analysis;
238975
+ }
238976
+ analyzeBooleanField(analysis) {
238977
+ analysis.confidence = "high";
238978
+ analysis.evidence.push("Boolean type - auto-configured");
238979
+ return analysis;
238980
+ }
238981
+ analyzeEnumField(analysis, type2) {
238982
+ if (type2.enumValues) {
238983
+ analysis.confidence = "high";
238984
+ analysis.evidence.push(`Enum with ${type2.enumValues.length} values`);
238985
+ if (analysis.bounds) {
238986
+ analysis.bounds.values = type2.enumValues;
238987
+ }
238988
+ }
238989
+ return analysis;
238990
+ }
238991
+ analyzeArrayField(analysis) {
238992
+ analysis.confidence = "low";
238993
+ analysis.suggestions.push("Choose maxLength: 5 (fast), 10 (balanced), or 20 (thorough)");
238994
+ if (analysis.bounds) {
238995
+ analysis.bounds.maxLength = undefined;
238996
+ }
238997
+ const foundBound = this.findArrayBound();
238998
+ if (foundBound && analysis.bounds) {
238999
+ analysis.confidence = "medium";
239000
+ analysis.evidence.push(`Found array check: ${foundBound.evidence}`);
239001
+ analysis.bounds.maxLength = foundBound.value;
239002
+ }
239003
+ return analysis;
239004
+ }
239005
+ analyzeNumberField(analysis) {
239006
+ analysis.confidence = "low";
239007
+ analysis.suggestions.push("Provide min and max values based on your application logic");
239008
+ if (analysis.bounds) {
239009
+ analysis.bounds.min = undefined;
239010
+ analysis.bounds.max = undefined;
239011
+ }
239012
+ const foundBound = this.findNumberBound();
239013
+ if (foundBound && analysis.bounds) {
239014
+ analysis.confidence = "high";
239015
+ analysis.evidence.push(`Found comparison: ${foundBound.evidence}`);
239016
+ analysis.bounds = { ...analysis.bounds, ...foundBound.bounds };
239017
+ }
239018
+ return analysis;
239019
+ }
239020
+ analyzeStringField(analysis) {
239021
+ analysis.confidence = "low";
239022
+ analysis.suggestions.push('Provide 2-3 example values: ["value1", "value2", "value3"]', "Or use { abstract: true } for symbolic verification");
239023
+ if (analysis.bounds) {
239024
+ analysis.bounds.values = undefined;
239025
+ }
239026
+ return analysis;
239027
+ }
239028
+ analyzeMapSetField(analysis) {
239029
+ analysis.confidence = "low";
239030
+ analysis.suggestions.push("Provide maxSize (recommended: 3-5)");
239031
+ if (analysis.bounds) {
239032
+ analysis.bounds.maxSize = undefined;
239033
+ }
239034
+ return analysis;
239035
+ }
239036
+ findArrayBound() {
239037
+ return null;
239038
+ }
239039
+ findNumberBound() {
239040
+ return null;
239041
+ }
239042
+ }
239043
+ async function analyzeCodebase(options) {
239044
+ const extractor = new TypeExtractor(options.tsConfigPath);
239045
+ return extractor.analyzeCodebase(options.stateFilePath);
239046
+ }
239047
+ var import_ts_morph6;
239048
+ var init_types = __esm(() => {
239049
+ init_handlers();
239050
+ import_ts_morph6 = __toESM(require_ts_morph(), 1);
239051
+ });
239052
+
239055
239053
  // tools/analysis/src/extract/index.ts
239056
239054
  var init_extract = __esm(() => {
239057
- init_types2();
239058
- init_handlers();
239059
- init_manifest();
239055
+ init_adr();
239056
+ init_architecture();
239060
239057
  init_contexts();
239061
239058
  init_flows();
239059
+ init_handlers();
239062
239060
  init_integrations();
239063
- init_adr();
239064
- init_architecture();
239061
+ init_manifest();
239065
239062
  init_project_detector();
239063
+ init_types();
239066
239064
  });
239065
+ // tools/analysis/src/types/index.ts
239066
+ var init_types2 = () => {};
239067
239067
 
239068
239068
  // tools/analysis/src/index.ts
239069
239069
  var exports_src = {};
@@ -239086,8 +239086,8 @@ __export(exports_src, {
239086
239086
  ADRExtractor: () => ADRExtractor
239087
239087
  });
239088
239088
  var init_src = __esm(() => {
239089
- init_types();
239090
239089
  init_extract();
239090
+ init_types2();
239091
239091
  });
239092
239092
 
239093
239093
  // tools/visualize/src/types/structurizr.ts
@@ -240436,4 +240436,4 @@ export {
240436
240436
  generateTeachingMaterial
240437
240437
  };
240438
240438
 
240439
- //# debugId=59D49291B64A354564756E2164756E21
240439
+ //# debugId=E1CBC64B9CEE404064756E2164756E21