@fairfox/polly 0.5.3 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/cli/polly.js +9 -9
  2. package/dist/cli/polly.js.map +4 -4
  3. package/dist/cli/template-utils.js +3 -3
  4. package/dist/cli/template-utils.js.map +3 -3
  5. package/dist/vendor/verify/src/cli.js +115 -72
  6. package/dist/vendor/verify/src/cli.js.map +10 -10
  7. package/dist/vendor/verify/src/public-api.d.ts +41 -0
  8. package/dist/vendor/verify/src/public-api.js +26 -0
  9. package/dist/vendor/verify/src/public-api.js.map +10 -0
  10. package/dist/vendor/visualize/src/cli.js +123 -105
  11. package/dist/vendor/visualize/src/cli.js.map +13 -13
  12. package/package.json +6 -4
  13. package/templates/pwa/build.ts.template +37 -37
  14. package/templates/pwa/server.ts.template +53 -53
  15. package/templates/pwa/src/service-worker.ts.template +131 -135
  16. package/templates/pwa/src/shared-worker.ts.template +114 -109
  17. /package/dist/{background → src/background}/api-client.d.ts +0 -0
  18. /package/dist/{background → src/background}/context-menu.d.ts +0 -0
  19. /package/dist/{background → src/background}/index.d.ts +0 -0
  20. /package/dist/{background → src/background}/log-store.d.ts +0 -0
  21. /package/dist/{background → src/background}/message-router.d.ts +0 -0
  22. /package/dist/{background → src/background}/offscreen-manager.d.ts +0 -0
  23. /package/dist/{index.d.ts → src/index.d.ts} +0 -0
  24. /package/dist/{shared → src/shared}/adapters/chrome/context-menus.chrome.d.ts +0 -0
  25. /package/dist/{shared → src/shared}/adapters/chrome/offscreen.chrome.d.ts +0 -0
  26. /package/dist/{shared → src/shared}/adapters/chrome/runtime.chrome.d.ts +0 -0
  27. /package/dist/{shared → src/shared}/adapters/chrome/storage.chrome.d.ts +0 -0
  28. /package/dist/{shared → src/shared}/adapters/chrome/tabs.chrome.d.ts +0 -0
  29. /package/dist/{shared → src/shared}/adapters/chrome/window.chrome.d.ts +0 -0
  30. /package/dist/{shared → src/shared}/adapters/context-menus.adapter.d.ts +0 -0
  31. /package/dist/{shared → src/shared}/adapters/fetch.adapter.d.ts +0 -0
  32. /package/dist/{shared → src/shared}/adapters/index.d.ts +0 -0
  33. /package/dist/{shared → src/shared}/adapters/logger.adapter.d.ts +0 -0
  34. /package/dist/{shared → src/shared}/adapters/offscreen.adapter.d.ts +0 -0
  35. /package/dist/{shared → src/shared}/adapters/runtime.adapter.d.ts +0 -0
  36. /package/dist/{shared → src/shared}/adapters/storage.adapter.d.ts +0 -0
  37. /package/dist/{shared → src/shared}/adapters/tabs.adapter.d.ts +0 -0
  38. /package/dist/{shared → src/shared}/adapters/window.adapter.d.ts +0 -0
  39. /package/dist/{shared → src/shared}/lib/context-helpers.d.ts +0 -0
  40. /package/dist/{shared → src/shared}/lib/context-specific-helpers.d.ts +0 -0
  41. /package/dist/{shared → src/shared}/lib/errors.d.ts +0 -0
  42. /package/dist/{shared → src/shared}/lib/handler-execution-tracker.d.ts +0 -0
  43. /package/dist/{shared → src/shared}/lib/message-bus.d.ts +0 -0
  44. /package/dist/{shared → src/shared}/lib/state.d.ts +0 -0
  45. /package/dist/{shared → src/shared}/lib/test-helpers.d.ts +0 -0
  46. /package/dist/{shared → src/shared}/state/app-state.d.ts +0 -0
  47. /package/dist/{shared → src/shared}/types/messages.d.ts +0 -0
@@ -68,27 +68,27 @@ class ProjectDetector {
68
68
  if (background) {
69
69
  const file = background.service_worker || background.scripts?.[0] || background.page;
70
70
  if (file) {
71
- entryPoints.background = this.findSourceFile(file);
71
+ entryPoints["background"] = this.findSourceFile(file);
72
72
  }
73
73
  }
74
74
  const contentScripts = manifest.content_scripts;
75
75
  if (contentScripts && contentScripts.length > 0) {
76
76
  const firstScript = contentScripts[0].js?.[0];
77
77
  if (firstScript) {
78
- entryPoints.content = this.findSourceFile(firstScript);
78
+ entryPoints["content"] = this.findSourceFile(firstScript);
79
79
  }
80
80
  }
81
81
  const popup = manifest.action?.default_popup || manifest.browser_action?.default_popup;
82
82
  if (popup) {
83
83
  const jsFile = this.findAssociatedJS(path3.join(this.projectRoot, popup));
84
84
  if (jsFile)
85
- entryPoints.popup = jsFile;
85
+ entryPoints["popup"] = jsFile;
86
86
  }
87
87
  const options = manifest.options_ui?.page || manifest.options_page;
88
88
  if (options) {
89
89
  const jsFile = this.findAssociatedJS(path3.join(this.projectRoot, options));
90
90
  if (jsFile)
91
- entryPoints.options = jsFile;
91
+ entryPoints["options"] = jsFile;
92
92
  }
93
93
  return {
94
94
  type: "chrome-extension",
@@ -112,7 +112,7 @@ class ProjectDetector {
112
112
  for (const candidate of swCandidates) {
113
113
  const fullPath = path3.join(this.projectRoot, candidate);
114
114
  if (fs3.existsSync(fullPath)) {
115
- entryPoints.worker = fullPath;
115
+ entryPoints["worker"] = fullPath;
116
116
  break;
117
117
  }
118
118
  }
@@ -126,7 +126,7 @@ class ProjectDetector {
126
126
  for (const candidate of clientCandidates) {
127
127
  const fullPath = path3.join(this.projectRoot, candidate);
128
128
  if (fs3.existsSync(fullPath)) {
129
- entryPoints.client = fullPath;
129
+ entryPoints["client"] = fullPath;
130
130
  break;
131
131
  }
132
132
  }
@@ -156,7 +156,7 @@ class ProjectDetector {
156
156
  for (const candidate of mainCandidates) {
157
157
  const fullPath = path3.join(this.projectRoot, candidate);
158
158
  if (fs3.existsSync(fullPath) || fs3.existsSync(fullPath.replace(/\.js$/, ".ts"))) {
159
- entryPoints.main = fs3.existsSync(fullPath) ? fullPath : fullPath.replace(/\.js$/, ".ts");
159
+ entryPoints["main"] = fs3.existsSync(fullPath) ? fullPath : fullPath.replace(/\.js$/, ".ts");
160
160
  break;
161
161
  }
162
162
  }
@@ -169,7 +169,7 @@ class ProjectDetector {
169
169
  for (const candidate of rendererCandidates) {
170
170
  const fullPath = path3.join(this.projectRoot, candidate);
171
171
  if (fs3.existsSync(fullPath)) {
172
- entryPoints.renderer = fullPath;
172
+ entryPoints["renderer"] = fullPath;
173
173
  break;
174
174
  }
175
175
  }
@@ -207,13 +207,13 @@ class ProjectDetector {
207
207
  const scoredServers = this.scoreServerCandidates(serverCandidates);
208
208
  if (scoredServers.length > 0) {
209
209
  const best = scoredServers[0];
210
- entryPoints.server = best.path;
210
+ entryPoints["server"] = best.path;
211
211
  if (best.hasWebSocket) {
212
- contextMapping.server = "WebSocket Server";
212
+ contextMapping["server"] = "WebSocket Server";
213
213
  } else if (best.hasHTTP) {
214
- contextMapping.server = "HTTP Server";
214
+ contextMapping["server"] = "HTTP Server";
215
215
  } else {
216
- contextMapping.server = "Server";
216
+ contextMapping["server"] = "Server";
217
217
  }
218
218
  }
219
219
  const clientCandidates = [
@@ -227,8 +227,8 @@ class ProjectDetector {
227
227
  for (const candidate of clientCandidates) {
228
228
  const fullPath = path3.join(this.projectRoot, candidate);
229
229
  if (fs3.existsSync(fullPath)) {
230
- entryPoints.client = fullPath;
231
- contextMapping.client = "Client";
230
+ entryPoints["client"] = fullPath;
231
+ contextMapping["client"] = "Client";
232
232
  break;
233
233
  }
234
234
  }
@@ -367,7 +367,7 @@ class ProjectDetector {
367
367
  for (const candidate of commonEntries) {
368
368
  const fullPath = path3.join(this.projectRoot, candidate);
369
369
  if (fs3.existsSync(fullPath)) {
370
- entryPoints.main = fullPath;
370
+ entryPoints["main"] = fullPath;
371
371
  break;
372
372
  }
373
373
  }
@@ -512,14 +512,14 @@ class ManifestParser {
512
512
  if (background) {
513
513
  const entryFile = background.files[0];
514
514
  if (entryFile) {
515
- entryPoints.background = this.findSourceFile(entryFile);
515
+ entryPoints["background"] = this.findSourceFile(entryFile);
516
516
  }
517
517
  }
518
518
  const contentScripts = this.parseContentScripts();
519
519
  if (contentScripts && contentScripts.length > 0) {
520
- const firstScript = contentScripts[0].js[0];
520
+ const firstScript = contentScripts[0]?.js[0];
521
521
  if (firstScript) {
522
- entryPoints.content = this.findSourceFile(firstScript);
522
+ entryPoints["content"] = this.findSourceFile(firstScript);
523
523
  }
524
524
  }
525
525
  const popup = this.parsePopup();
@@ -527,7 +527,7 @@ class ManifestParser {
527
527
  const htmlPath = path.join(this.baseDir, popup.html);
528
528
  const jsPath = this.findAssociatedJS(htmlPath);
529
529
  if (jsPath) {
530
- entryPoints.popup = jsPath;
530
+ entryPoints["popup"] = jsPath;
531
531
  }
532
532
  }
533
533
  const options = this.parseOptions();
@@ -535,7 +535,7 @@ class ManifestParser {
535
535
  const htmlPath = path.join(this.baseDir, options.page);
536
536
  const jsPath = this.findAssociatedJS(htmlPath);
537
537
  if (jsPath) {
538
- entryPoints.options = jsPath;
538
+ entryPoints["options"] = jsPath;
539
539
  }
540
540
  }
541
541
  const devtools = this.parseDevtools();
@@ -543,7 +543,7 @@ class ManifestParser {
543
543
  const htmlPath = path.join(this.baseDir, devtools.page);
544
544
  const jsPath = this.findAssociatedJS(htmlPath);
545
545
  if (jsPath) {
546
- entryPoints.devtools = jsPath;
546
+ entryPoints["devtools"] = jsPath;
547
547
  }
548
548
  }
549
549
  return entryPoints;
@@ -705,9 +705,9 @@ class ContextAnalyzer {
705
705
  handlers: contextHandlers,
706
706
  chromeAPIs,
707
707
  externalAPIs: [],
708
- components,
708
+ ...components ? { components } : {},
709
709
  dependencies,
710
- description
710
+ ...description ? { description } : {}
711
711
  };
712
712
  }
713
713
  extractChromeAPIs(sourceFile) {
@@ -717,20 +717,23 @@ class ContextAnalyzer {
717
717
  const text = node.getText();
718
718
  if (text.startsWith("chrome.")) {
719
719
  const match = text.match(/^chrome\.([^.(]+(?:\.[^.(]+)?)/);
720
- if (match) {
721
- apis.add(match[1]);
720
+ const api = match?.[1];
721
+ if (api) {
722
+ apis.add(api);
722
723
  }
723
724
  }
724
725
  if (text.startsWith("browser.")) {
725
726
  const match = text.match(/^browser\.([^.(]+(?:\.[^.(]+)?)/);
726
- if (match) {
727
- apis.add(match[1]);
727
+ const api = match?.[1];
728
+ if (api) {
729
+ apis.add(api);
728
730
  }
729
731
  }
730
732
  if (text.includes("bus.adapters.")) {
731
733
  const match = text.match(/bus\.adapters\.([^.(]+)/);
732
- if (match) {
733
- apis.add(match[1]);
734
+ const api = match?.[1];
735
+ if (api) {
736
+ apis.add(api);
734
737
  }
735
738
  }
736
739
  }
@@ -767,13 +770,14 @@ class ContextAnalyzer {
767
770
  if (Node.isFunctionDeclaration(node)) {
768
771
  const name = node.getName();
769
772
  if (name && this.looksLikeComponent(name, node)) {
773
+ const description = this.extractJSDocDescription(node);
770
774
  components.push({
771
775
  name,
772
776
  type: "function",
773
777
  filePath: sourceFile.getFilePath(),
774
778
  line: node.getStartLineNumber(),
775
779
  props: this.extractProps(node),
776
- description: this.extractJSDocDescription(node)
780
+ ...description ? { description } : {}
777
781
  });
778
782
  }
779
783
  }
@@ -782,13 +786,14 @@ class ContextAnalyzer {
782
786
  const initializer = node.getInitializer();
783
787
  if (name && initializer && (Node.isArrowFunction(initializer) || Node.isFunctionExpression(initializer))) {
784
788
  if (this.looksLikeComponent(name, initializer)) {
789
+ const description = this.extractJSDocDescription(node);
785
790
  components.push({
786
791
  name,
787
792
  type: "function",
788
793
  filePath: sourceFile.getFilePath(),
789
794
  line: node.getStartLineNumber(),
790
795
  props: this.extractProps(initializer),
791
- description: this.extractJSDocDescription(node)
796
+ ...description ? { description } : {}
792
797
  });
793
798
  }
794
799
  }
@@ -796,13 +801,14 @@ class ContextAnalyzer {
796
801
  if (Node.isClassDeclaration(node)) {
797
802
  const name = node.getName();
798
803
  if (name && this.looksLikeClassComponent(node)) {
804
+ const description = this.extractJSDocDescription(node);
799
805
  components.push({
800
806
  name,
801
807
  type: "class",
802
808
  filePath: sourceFile.getFilePath(),
803
809
  line: node.getStartLineNumber(),
804
810
  props: this.extractPropsFromClass(node),
805
- description: this.extractJSDocDescription(node)
811
+ ...description ? { description } : {}
806
812
  });
807
813
  }
808
814
  }
@@ -905,9 +911,9 @@ class FlowAnalyzer {
905
911
  messageType,
906
912
  from: sender.context,
907
913
  to: recipients,
908
- trigger: flowMetadata.trigger,
909
- flowName: flowMetadata.flowName,
910
- description: flowMetadata.description,
914
+ ...flowMetadata.trigger ? { trigger: flowMetadata.trigger } : {},
915
+ ...flowMetadata.flowName ? { flowName: flowMetadata.flowName } : {},
916
+ ...flowMetadata.description ? { description: flowMetadata.description } : {},
911
917
  sequence
912
918
  });
913
919
  }
@@ -1218,7 +1224,7 @@ class IntegrationAnalyzer {
1218
1224
  const moduleSpecifier = importDecl.getModuleSpecifierValue();
1219
1225
  if (!moduleSpecifier.startsWith(".") && !moduleSpecifier.startsWith("/")) {
1220
1226
  const packageName = moduleSpecifier.startsWith("@") ? moduleSpecifier.split("/").slice(0, 2).join("/") : moduleSpecifier.split("/")[0];
1221
- if (!seen.has(packageName)) {
1227
+ if (packageName && !seen.has(packageName)) {
1222
1228
  seen.add(packageName);
1223
1229
  scripts.push({
1224
1230
  type: "external-script",
@@ -1284,7 +1290,7 @@ class IntegrationAnalyzer {
1284
1290
  const hostname = parsed.hostname;
1285
1291
  const cleanHost = hostname.replace(/^www\./, "");
1286
1292
  const parts = cleanHost.split(".");
1287
- if (parts.length > 0) {
1293
+ if (parts.length > 0 && parts[0]) {
1288
1294
  return parts[0].charAt(0).toUpperCase() + parts[0].slice(1) + " API";
1289
1295
  }
1290
1296
  } catch {}
@@ -1314,7 +1320,7 @@ class IntegrationAnalyzer {
1314
1320
  }
1315
1321
 
1316
1322
  // vendor/analysis/src/extract/handlers.ts
1317
- import { Project as Project4, SyntaxKind as SyntaxKind3, Node as Node5 } from "ts-morph";
1323
+ import { Project as Project4, SyntaxKind, Node as Node5 } from "ts-morph";
1318
1324
 
1319
1325
  // vendor/analysis/src/extract/relationships.ts
1320
1326
  import { Node as Node4 } from "ts-morph";
@@ -1417,7 +1423,7 @@ class RelationshipExtractor {
1417
1423
  const objectExpr = expr.getExpression();
1418
1424
  const objectName = objectExpr.getText();
1419
1425
  const methodName = expr.getName();
1420
- targetComponent = this.inferComponentFromCall(objectName, methodName);
1426
+ targetComponent = this.inferComponentFromCall(objectName);
1421
1427
  if (!targetComponent) {
1422
1428
  return null;
1423
1429
  }
@@ -1448,7 +1454,7 @@ class RelationshipExtractor {
1448
1454
  rootObject = rootObject.getExpression();
1449
1455
  }
1450
1456
  const objectName = rootObject.getText();
1451
- const targetComponent = this.inferComponentFromCall(objectName, methodName);
1457
+ const targetComponent = this.inferComponentFromCall(objectName);
1452
1458
  if (!targetComponent) {
1453
1459
  return null;
1454
1460
  }
@@ -1485,7 +1491,7 @@ class RelationshipExtractor {
1485
1491
  }
1486
1492
  extractFromFetchCall(callExpr, handlerName) {
1487
1493
  const args = callExpr.getArguments();
1488
- if (args.length === 0) {
1494
+ if (args.length === 0 || !args[0]) {
1489
1495
  return null;
1490
1496
  }
1491
1497
  const urlArg = args[0].getText();
@@ -1504,7 +1510,7 @@ class RelationshipExtractor {
1504
1510
  evidence: [`fetch() call to: ${urlArg}`]
1505
1511
  };
1506
1512
  }
1507
- inferComponentFromCall(objectName, methodName) {
1513
+ inferComponentFromCall(objectName) {
1508
1514
  const mappings = {
1509
1515
  db: "db_client",
1510
1516
  database: "database",
@@ -1581,7 +1587,7 @@ class RelationshipExtractor {
1581
1587
  }
1582
1588
  if (modulePath.includes("/service") || modulePath.includes("/services")) {
1583
1589
  const match = modulePath.match(/\/([^/]+)\.ts$/);
1584
- if (match) {
1590
+ if (match && match[1]) {
1585
1591
  return this.toComponentId(match[1]);
1586
1592
  }
1587
1593
  }
@@ -1665,8 +1671,9 @@ class HandlerExtractor {
1665
1671
  extractHandlers() {
1666
1672
  const handlers = [];
1667
1673
  const messageTypes = new Set;
1674
+ const invalidMessageTypes = new Set;
1668
1675
  const sourceFiles = this.project.getSourceFiles();
1669
- if (process.env.POLLY_DEBUG) {
1676
+ if (process.env["POLLY_DEBUG"]) {
1670
1677
  console.log(`[DEBUG] Loaded ${sourceFiles.length} source files`);
1671
1678
  if (sourceFiles.length <= 20) {
1672
1679
  for (const sf of sourceFiles) {
@@ -1678,17 +1685,30 @@ class HandlerExtractor {
1678
1685
  const fileHandlers = this.extractFromFile(sourceFile);
1679
1686
  handlers.push(...fileHandlers);
1680
1687
  for (const handler of fileHandlers) {
1681
- messageTypes.add(handler.messageType);
1688
+ if (this.isValidTLAIdentifier(handler.messageType)) {
1689
+ messageTypes.add(handler.messageType);
1690
+ } else {
1691
+ invalidMessageTypes.add(handler.messageType);
1692
+ }
1682
1693
  }
1683
1694
  }
1684
- if (process.env.POLLY_DEBUG) {
1695
+ if (process.env["POLLY_DEBUG"]) {
1685
1696
  console.log(`[DEBUG] Total handlers extracted: ${handlers.length}`);
1697
+ if (invalidMessageTypes.size > 0) {
1698
+ console.log(`[DEBUG] Filtered ${invalidMessageTypes.size} invalid message type(s) from handlers`);
1699
+ }
1686
1700
  }
1687
1701
  return {
1688
1702
  handlers,
1689
1703
  messageTypes
1690
1704
  };
1691
1705
  }
1706
+ isValidTLAIdentifier(s) {
1707
+ if (!s || s.length === 0) {
1708
+ return false;
1709
+ }
1710
+ return /^[a-zA-Z][a-zA-Z0-9_]*$/.test(s);
1711
+ }
1692
1712
  extractFromFile(sourceFile) {
1693
1713
  const handlers = [];
1694
1714
  const filePath = sourceFile.getFilePath();
@@ -1798,7 +1818,7 @@ class HandlerExtractor {
1798
1818
  extractVerificationConditions(funcNode, preconditions, postconditions) {
1799
1819
  const body = funcNode.getBody();
1800
1820
  const statements = Node5.isBlock(body) ? body.getStatements() : [body];
1801
- statements.forEach((statement, index) => {
1821
+ statements.forEach((statement) => {
1802
1822
  if (Node5.isExpressionStatement(statement)) {
1803
1823
  const expr = statement.getExpression();
1804
1824
  if (Node5.isCallExpression(expr)) {
@@ -1862,13 +1882,13 @@ class HandlerExtractor {
1862
1882
  if (Node5.isNumericLiteral(node)) {
1863
1883
  return node.getLiteralValue();
1864
1884
  }
1865
- if (node.getKind() === SyntaxKind3.TrueKeyword) {
1885
+ if (node.getKind() === SyntaxKind.TrueKeyword) {
1866
1886
  return true;
1867
1887
  }
1868
- if (node.getKind() === SyntaxKind3.FalseKeyword) {
1888
+ if (node.getKind() === SyntaxKind.FalseKeyword) {
1869
1889
  return false;
1870
1890
  }
1871
- if (node.getKind() === SyntaxKind3.NullKeyword) {
1891
+ if (node.getKind() === SyntaxKind.NullKeyword) {
1872
1892
  return null;
1873
1893
  }
1874
1894
  return;
@@ -1951,7 +1971,7 @@ class HandlerExtractor {
1951
1971
  typeGuards = this.findTypePredicateFunctions(sourceFile);
1952
1972
  this.typeGuardCache.set(sourceFile, typeGuards);
1953
1973
  }
1954
- if (process.env.POLLY_DEBUG) {
1974
+ if (process.env["POLLY_DEBUG"]) {
1955
1975
  console.log(`[DEBUG] File: ${sourceFile.getBaseName()}`);
1956
1976
  console.log(`[DEBUG] Local type guards found: ${typeGuards.size}`);
1957
1977
  if (typeGuards.size > 0) {
@@ -1965,7 +1985,7 @@ class HandlerExtractor {
1965
1985
  const handler = this.extractHandlerFromIfClause(currentIf, typeGuards, context, filePath);
1966
1986
  if (handler) {
1967
1987
  handlers.push(handler);
1968
- if (process.env.POLLY_DEBUG) {
1988
+ if (process.env["POLLY_DEBUG"]) {
1969
1989
  console.log(`[DEBUG] Found handler: ${handler.messageType} at line ${handler.location.line}`);
1970
1990
  }
1971
1991
  }
@@ -1977,7 +1997,7 @@ class HandlerExtractor {
1977
1997
  }
1978
1998
  }
1979
1999
  } catch (error) {
1980
- if (process.env.POLLY_DEBUG) {
2000
+ if (process.env["POLLY_DEBUG"]) {
1981
2001
  console.log(`[DEBUG] Error in extractTypeGuardHandlers: ${error}`);
1982
2002
  }
1983
2003
  }
@@ -1985,7 +2005,8 @@ class HandlerExtractor {
1985
2005
  }
1986
2006
  extractHandlerFromIfClause(ifNode, typeGuards, context, filePath) {
1987
2007
  try {
1988
- const condition = ifNode.getExpression();
2008
+ const ifStmt = ifNode;
2009
+ const condition = ifStmt.getExpression();
1989
2010
  if (!Node5.isCallExpression(condition)) {
1990
2011
  return null;
1991
2012
  }
@@ -1994,32 +2015,32 @@ class HandlerExtractor {
1994
2015
  if (Node5.isIdentifier(funcExpr)) {
1995
2016
  funcName = funcExpr.getText();
1996
2017
  }
1997
- if (process.env.POLLY_DEBUG && funcName) {
2018
+ if (process.env["POLLY_DEBUG"] && funcName) {
1998
2019
  console.log(`[DEBUG] Processing if condition with function: ${funcName}`);
1999
2020
  }
2000
2021
  let messageType = undefined;
2001
2022
  if (funcName && typeGuards.has(funcName)) {
2002
2023
  messageType = typeGuards.get(funcName);
2003
- if (process.env.POLLY_DEBUG) {
2024
+ if (process.env["POLLY_DEBUG"]) {
2004
2025
  console.log(`[DEBUG] Found in local type guards: ${funcName} → ${messageType}`);
2005
2026
  }
2006
2027
  } else if (Node5.isIdentifier(funcExpr)) {
2007
- if (process.env.POLLY_DEBUG) {
2028
+ if (process.env["POLLY_DEBUG"]) {
2008
2029
  console.log(`[DEBUG] Not found locally, trying import resolution for: ${funcName}`);
2009
2030
  }
2010
- messageType = this.resolveImportedTypeGuard(funcExpr);
2031
+ messageType = this.resolveImportedTypeGuard(funcExpr) ?? undefined;
2011
2032
  }
2012
2033
  if (!messageType) {
2013
- if (process.env.POLLY_DEBUG && funcName) {
2034
+ if (process.env["POLLY_DEBUG"] && funcName) {
2014
2035
  console.log(`[DEBUG] Could not resolve message type for: ${funcName}`);
2015
2036
  }
2016
2037
  return null;
2017
2038
  }
2018
- const line = ifNode.getStartLineNumber();
2019
- const sourceFile = ifNode.getSourceFile();
2039
+ const line = ifStmt.getStartLineNumber();
2040
+ const sourceFile = ifStmt.getSourceFile();
2020
2041
  const handlerName = `${messageType}_handler`;
2021
2042
  let relationships = undefined;
2022
- const thenStatement = ifNode.getThenStatement();
2043
+ const thenStatement = ifStmt.getThenStatement();
2023
2044
  if (thenStatement) {
2024
2045
  const detectedRelationships = this.relationshipExtractor.extractFromHandler(thenStatement, sourceFile, handlerName);
2025
2046
  if (detectedRelationships.length > 0) {
@@ -2072,7 +2093,7 @@ class HandlerExtractor {
2072
2093
  const bodyText = body.getText();
2073
2094
  const typeValueMatch = bodyText.match(/\.type\s*===?\s*['"](\w+)['"]/);
2074
2095
  if (typeValueMatch) {
2075
- messageType = typeValueMatch[1];
2096
+ messageType = typeValueMatch[1] ?? null;
2076
2097
  }
2077
2098
  }
2078
2099
  }
@@ -2090,7 +2111,7 @@ class HandlerExtractor {
2090
2111
  const funcName = identifier.getText();
2091
2112
  const definitions = identifier.getDefinitionNodes();
2092
2113
  if (definitions.length === 0) {
2093
- if (process.env.POLLY_DEBUG) {
2114
+ if (process.env["POLLY_DEBUG"]) {
2094
2115
  console.log(`[DEBUG] No definitions found for imported function: ${funcName}`);
2095
2116
  }
2096
2117
  return null;
@@ -2098,7 +2119,7 @@ class HandlerExtractor {
2098
2119
  for (const def of definitions) {
2099
2120
  if (Node5.isFunctionDeclaration(def) || Node5.isFunctionExpression(def) || Node5.isArrowFunction(def)) {
2100
2121
  const returnTypeNode = def.getReturnTypeNode();
2101
- if (process.env.POLLY_DEBUG) {
2122
+ if (process.env["POLLY_DEBUG"]) {
2102
2123
  const returnType = def.getReturnType().getText();
2103
2124
  console.log(`[DEBUG] Function ${funcName} return type (resolved): ${returnType}`);
2104
2125
  console.log(`[DEBUG] Has return type node: ${!!returnTypeNode}`);
@@ -2110,7 +2131,7 @@ class HandlerExtractor {
2110
2131
  const typeName = typeNode.getText();
2111
2132
  const messageType = this.extractMessageTypeFromTypeName(typeName);
2112
2133
  if (messageType) {
2113
- if (process.env.POLLY_DEBUG) {
2134
+ if (process.env["POLLY_DEBUG"]) {
2114
2135
  console.log(`[DEBUG] Resolved ${funcName} → ${messageType} (from AST type predicate)`);
2115
2136
  }
2116
2137
  return messageType;
@@ -2122,8 +2143,8 @@ class HandlerExtractor {
2122
2143
  const bodyText = body.getText();
2123
2144
  const typeValueMatch = bodyText.match(/\.type\s*===?\s*['"](\w+)['"]/);
2124
2145
  if (typeValueMatch) {
2125
- const messageType = typeValueMatch[1];
2126
- if (process.env.POLLY_DEBUG) {
2146
+ const messageType = typeValueMatch[1] ?? null;
2147
+ if (process.env["POLLY_DEBUG"]) {
2127
2148
  console.log(`[DEBUG] Resolved ${funcName} → ${messageType} (from body)`);
2128
2149
  }
2129
2150
  return messageType;
@@ -2132,7 +2153,7 @@ class HandlerExtractor {
2132
2153
  }
2133
2154
  }
2134
2155
  } catch (error) {
2135
- if (process.env.POLLY_DEBUG) {
2156
+ if (process.env["POLLY_DEBUG"]) {
2136
2157
  console.log(`[DEBUG] Error resolving imported type guard: ${error}`);
2137
2158
  }
2138
2159
  }
@@ -2228,9 +2249,9 @@ class ADRExtractor {
2228
2249
  const content = fs2.readFileSync(filePath, "utf-8");
2229
2250
  const fileName = path2.basename(filePath, ".md");
2230
2251
  const idMatch = fileName.match(/^(\d+)/);
2231
- const id = idMatch ? idMatch[1] : fileName;
2252
+ const id = idMatch?.[1] ?? fileName;
2232
2253
  const titleMatch = content.match(/^#\s+(.+)$/m);
2233
- const title = titleMatch ? titleMatch[1].trim() : fileName;
2254
+ const title = titleMatch?.[1]?.trim() ?? fileName;
2234
2255
  const status = this.extractStatus(content);
2235
2256
  const date = this.extractDate(content);
2236
2257
  const context = this.extractSection(content, "Context");
@@ -2251,8 +2272,8 @@ class ADRExtractor {
2251
2272
  context,
2252
2273
  decision,
2253
2274
  consequences,
2254
- alternatives,
2255
- links,
2275
+ ...alternatives && alternatives.length > 0 ? { alternatives } : {},
2276
+ ...links.length > 0 ? { links } : {},
2256
2277
  source: filePath
2257
2278
  };
2258
2279
  }
@@ -2260,20 +2281,20 @@ class ADRExtractor {
2260
2281
  const statusMatch = content.match(/Status:\s*(\w+)/i);
2261
2282
  if (!statusMatch)
2262
2283
  return "accepted";
2263
- const status = statusMatch[1].toLowerCase();
2264
- if (["proposed", "accepted", "deprecated", "superseded"].includes(status)) {
2284
+ const status = statusMatch[1]?.toLowerCase();
2285
+ if (status && ["proposed", "accepted", "deprecated", "superseded"].includes(status)) {
2265
2286
  return status;
2266
2287
  }
2267
2288
  return "accepted";
2268
2289
  }
2269
2290
  extractDate(content) {
2270
2291
  const dateMatch = content.match(/Date:\s*(\d{4}-\d{2}-\d{2})/i) || content.match(/(\d{4}-\d{2}-\d{2})/i);
2271
- return dateMatch ? dateMatch[1] : new Date().toISOString().split("T")[0];
2292
+ return dateMatch?.[1] ?? new Date().toISOString().split("T")[0];
2272
2293
  }
2273
2294
  extractSection(content, sectionName) {
2274
2295
  const regex = new RegExp(`##\\s+${sectionName}\\s*\\n([\\s\\S]*?)(?=\\n##|$)`, "i");
2275
2296
  const match = content.match(regex);
2276
- return match ? match[1].trim() : "";
2297
+ return match?.[1]?.trim() ?? "";
2277
2298
  }
2278
2299
  extractLinks(content) {
2279
2300
  const links = [];
@@ -2281,10 +2302,11 @@ class ADRExtractor {
2281
2302
  if (supersedesMatch) {
2282
2303
  for (const match of supersedesMatch) {
2283
2304
  const idMatch = match.match(/ADR-(\d+)/);
2284
- if (idMatch) {
2305
+ const id = idMatch?.[1];
2306
+ if (id) {
2285
2307
  links.push({
2286
2308
  type: "supersedes",
2287
- adrId: idMatch[1]
2309
+ adrId: id
2288
2310
  });
2289
2311
  }
2290
2312
  }
@@ -2293,10 +2315,11 @@ class ADRExtractor {
2293
2315
  if (supersededByMatch) {
2294
2316
  for (const match of supersededByMatch) {
2295
2317
  const idMatch = match.match(/ADR-(\d+)/);
2296
- if (idMatch) {
2318
+ const id = idMatch?.[1];
2319
+ if (id) {
2297
2320
  links.push({
2298
2321
  type: "superseded-by",
2299
- adrId: idMatch[1]
2322
+ adrId: id
2300
2323
  });
2301
2324
  }
2302
2325
  }
@@ -2327,7 +2350,7 @@ class ArchitectureAnalyzer {
2327
2350
  systemInfo = {
2328
2351
  name: manifest.name,
2329
2352
  version: manifest.version,
2330
- description: manifest.description
2353
+ ...manifest.description ? { description: manifest.description } : {}
2331
2354
  };
2332
2355
  } else {
2333
2356
  const { detectProjectConfig: detectProjectConfig2 } = await Promise.resolve().then(() => (init_project_detector(), exports_project_detector));
@@ -2336,7 +2359,7 @@ class ArchitectureAnalyzer {
2336
2359
  systemInfo = {
2337
2360
  name: projectConfig.metadata?.name || "Unknown Project",
2338
2361
  version: projectConfig.metadata?.version || "0.0.0",
2339
- description: projectConfig.metadata?.description
2362
+ ...projectConfig.metadata?.description ? { description: projectConfig.metadata.description } : {}
2340
2363
  };
2341
2364
  }
2342
2365
  const handlerExtractor = new HandlerExtractor(this.options.tsConfigPath);
@@ -2361,13 +2384,13 @@ class ArchitectureAnalyzer {
2361
2384
  return {
2362
2385
  projectRoot: this.options.projectRoot,
2363
2386
  system: systemInfo,
2364
- manifest,
2365
- projectConfig,
2387
+ ...manifest ? { manifest } : {},
2388
+ ...projectConfig ? { projectConfig } : {},
2366
2389
  contexts,
2367
2390
  messageFlows,
2368
2391
  integrations,
2369
- adrs: adrs.adrs.length > 0 ? adrs : undefined,
2370
- repository
2392
+ ...adrs.adrs.length > 0 ? { adrs } : {},
2393
+ ...repository ? { repository } : {}
2371
2394
  };
2372
2395
  }
2373
2396
  mergeExternalAPIsIntoContexts(contexts, integrations) {
@@ -2578,7 +2601,6 @@ class StructurizrDSLGenerator {
2578
2601
  const parts = [];
2579
2602
  for (const integration of this.analysis.integrations) {
2580
2603
  if (integration.type === "api" || integration.type === "websocket") {
2581
- const tech = integration.technology || (integration.type === "websocket" ? "WebSocket" : "REST API");
2582
2604
  let desc = integration.description || "";
2583
2605
  if (!desc && integration.calls && integration.calls.length > 0) {
2584
2606
  const endpoints = integration.calls.slice(0, 3).map((c) => c.endpoint).join(", ");
@@ -2691,7 +2713,7 @@ class StructurizrDSLGenerator {
2691
2713
  return parts.join(`
2692
2714
  `);
2693
2715
  }
2694
- generateComponentDescription(messageType, handler) {
2716
+ generateComponentDescription(messageType, _handler) {
2695
2717
  const type = messageType.toLowerCase();
2696
2718
  if (type.includes("login")) {
2697
2719
  return "Authenticates users and establishes sessions";
@@ -2721,7 +2743,7 @@ class StructurizrDSLGenerator {
2721
2743
  }
2722
2744
  return `Processes ${messageType} messages and coordinates business logic`;
2723
2745
  }
2724
- getComponentTags(messageType, handler) {
2746
+ getComponentTags(messageType, _handler) {
2725
2747
  const tags = ["Message Handler"];
2726
2748
  const type = messageType.toLowerCase();
2727
2749
  if (type.includes("login") || type.includes("logout") || type.includes("auth")) {
@@ -2780,7 +2802,7 @@ class StructurizrDSLGenerator {
2780
2802
  }
2781
2803
  return properties;
2782
2804
  }
2783
- generateComponentRelationships(contextType, contextInfo) {
2805
+ generateComponentRelationships(_contextType, contextInfo) {
2784
2806
  const parts = [];
2785
2807
  const handlersByType = new Map;
2786
2808
  for (const handler of contextInfo.handlers) {
@@ -2792,7 +2814,7 @@ class StructurizrDSLGenerator {
2792
2814
  if (contextInfo.chromeAPIs && contextInfo.chromeAPIs.length > 0) {
2793
2815
  for (const api of contextInfo.chromeAPIs) {
2794
2816
  const apiId = this.toId(`chrome_${api}`);
2795
- for (const [messageType, handlers] of handlersByType) {
2817
+ for (const [messageType, _handlers] of handlersByType) {
2796
2818
  const componentId = this.toId(this.toComponentName(messageType));
2797
2819
  let description = `Uses ${api}`;
2798
2820
  if (api === "storage") {
@@ -2838,7 +2860,7 @@ class StructurizrDSLGenerator {
2838
2860
  }
2839
2861
  const stateHandlers = [];
2840
2862
  const queryHandlers = [];
2841
- for (const [messageType, handlers] of handlersByType) {
2863
+ for (const [messageType, _handlers] of handlersByType) {
2842
2864
  const type = messageType.toLowerCase();
2843
2865
  const componentId = this.toId(this.toComponentName(messageType));
2844
2866
  if (type.includes("add") || type.includes("create") || type.includes("update") || type.includes("delete") || type.includes("remove") || type.includes("toggle") || type.includes("clear") || type.includes("login") || type.includes("logout")) {
@@ -2994,7 +3016,6 @@ class StructurizrDSLGenerator {
2994
3016
  }
2995
3017
  generateAutomaticDynamicDiagrams() {
2996
3018
  const diagrams = [];
2997
- const processedHandlers = new Set;
2998
3019
  const handlersWithRelationships = [];
2999
3020
  for (const [contextType, contextInfo] of Object.entries(this.analysis.contexts)) {
3000
3021
  for (const handler of contextInfo.handlers) {
@@ -3059,7 +3080,7 @@ class StructurizrDSLGenerator {
3059
3080
  const scope = handlers[0]?.contextName ? `extension.${handlers[0].contextName}` : "extension";
3060
3081
  parts.push(` dynamic ${scope} "${title}" "${description}" {`);
3061
3082
  let stepCount = 0;
3062
- for (const { handler, contextName } of handlers) {
3083
+ for (const { handler, contextName: _contextName } of handlers) {
3063
3084
  const handlerComponentId = this.toId(`${handler.messageType}_handler`);
3064
3085
  for (const rel of handler.relationships) {
3065
3086
  const toComponent = this.toId(rel.to);
@@ -3142,7 +3163,7 @@ class StructurizrDSLGenerator {
3142
3163
  state: "Application state synchronization",
3143
3164
  general: "Message flow through the system"
3144
3165
  };
3145
- return descriptions[domain] || descriptions.general;
3166
+ return descriptions[domain] || descriptions["general"];
3146
3167
  }
3147
3168
  getUserAction(domain) {
3148
3169
  const actions = {
@@ -3151,7 +3172,7 @@ class StructurizrDSLGenerator {
3151
3172
  state: "Requests state",
3152
3173
  general: "Interacts"
3153
3174
  };
3154
- return actions[domain] || actions.general;
3175
+ return actions[domain] || actions["general"];
3155
3176
  }
3156
3177
  getMessageDescription(messageType) {
3157
3178
  const type = messageType.toLowerCase();
@@ -3360,9 +3381,6 @@ class StructurizrDSLGenerator {
3360
3381
  toId(name) {
3361
3382
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
3362
3383
  }
3363
- toViewName(flowName) {
3364
- return flowName.split(/[_-]/).map((part) => this.capitalize(part)).join(" ");
3365
- }
3366
3384
  capitalize(str) {
3367
3385
  return str.charAt(0).toUpperCase() + str.slice(1);
3368
3386
  }
@@ -3429,12 +3447,12 @@ class StructurizrDSLGenerator {
3429
3447
  let entity = null;
3430
3448
  const underscoreMatch = type.match(/^([a-z]+)_(add|create|update|delete|remove|get|fetch|load|list|query)/);
3431
3449
  if (underscoreMatch) {
3432
- entity = underscoreMatch[1];
3450
+ entity = underscoreMatch[1] ?? null;
3433
3451
  }
3434
3452
  if (!entity) {
3435
3453
  const camelMatch = type.match(/(add|create|update|delete|remove|get|fetch|load|list|query)([a-z]+)/i);
3436
3454
  if (camelMatch) {
3437
- entity = camelMatch[2].toLowerCase();
3455
+ entity = camelMatch[2]?.toLowerCase() ?? null;
3438
3456
  }
3439
3457
  }
3440
3458
  if (!entity && type.match(/^[a-z]+$/)) {
@@ -3811,7 +3829,7 @@ Stack trace:`, COLORS.gray));
3811
3829
  process.exit(1);
3812
3830
  }
3813
3831
  }
3814
- async function exportCommand(args) {
3832
+ async function exportCommand(_args) {
3815
3833
  console.log(color(`
3816
3834
  \uD83D\uDCE4 Generating static site...
3817
3835
  `, COLORS.blue));
@@ -3884,7 +3902,7 @@ async function serveCommand(args) {
3884
3902
  if (!BunGlobal) {
3885
3903
  throw new Error("Bun runtime is required to run the server");
3886
3904
  }
3887
- const server = BunGlobal.serve({
3905
+ BunGlobal.serve({
3888
3906
  port,
3889
3907
  fetch(req) {
3890
3908
  const url = new URL(req.url);
@@ -3999,4 +4017,4 @@ Stack trace:`, COLORS.gray));
3999
4017
  process.exit(1);
4000
4018
  });
4001
4019
 
4002
- //# debugId=701BDB9A33D6395464756E2164756E21
4020
+ //# debugId=698279082E1D6F3764756E2164756E21