cg-code-graph 0.23.9__tar.gz → 0.23.11__tar.gz

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 (420) hide show
  1. {cg_code_graph-0.23.9/cg_code_graph.egg-info → cg_code_graph-0.23.11}/PKG-INFO +1 -1
  2. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/__init__.py +1 -1
  3. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/cli.py +10 -3
  4. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/lint_async.py +47 -10
  5. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/mcp_server.py +5 -2
  6. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/parity_structure.py +136 -15
  7. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/kotlin/plugin.py +39 -0
  8. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/extractor/extract.mjs +17 -0
  9. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/roundtrip.py +71 -7
  10. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11/cg_code_graph.egg-info}/PKG-INFO +1 -1
  11. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph.egg-info/SOURCES.txt +2 -0
  12. cg_code_graph-0.23.11/tests/test_kotlin_local_state.py +77 -0
  13. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_lint_async.py +21 -4
  14. cg_code_graph-0.23.11/tests/test_parity_members.py +67 -0
  15. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_roundtrip.py +12 -0
  16. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_fields_js_vue.py +15 -4
  17. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/LICENSE +0 -0
  18. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/MANIFEST.in +0 -0
  19. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/README.md +0 -0
  20. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/__main__.py +0 -0
  21. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/affected.py +0 -0
  22. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/agent_rules.py +0 -0
  23. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/agents.py +0 -0
  24. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/aitools.py +0 -0
  25. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/android_ipc.py +0 -0
  26. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/apple_ipc.py +0 -0
  27. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/apps.py +0 -0
  28. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/blindspots.py +0 -0
  29. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/bridges.py +0 -0
  30. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/brokers.py +0 -0
  31. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/cmake_sources.py +0 -0
  32. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/concepts.py +0 -0
  33. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/config.py +0 -0
  34. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/__init__.py +0 -0
  35. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/cache.py +0 -0
  36. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/detect.py +0 -0
  37. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/env.py +0 -0
  38. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/extractors.py +0 -0
  39. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/fsutil.py +0 -0
  40. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/generated.py +0 -0
  41. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/model.py +0 -0
  42. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/paths.py +0 -0
  43. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/plugin.py +0 -0
  44. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/redact.py +0 -0
  45. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/store.py +0 -0
  46. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/core/syntax_errors.py +0 -0
  47. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/coverage.py +0 -0
  48. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/dart_isolates.py +0 -0
  49. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/doctor.py +0 -0
  50. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/explore.py +0 -0
  51. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/external.py +0 -0
  52. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/graphql.py +0 -0
  53. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/guard_facts.py +0 -0
  54. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/hooks.py +0 -0
  55. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/indexer.py +0 -0
  56. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/insecure_transport.py +0 -0
  57. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/install_rules.py +0 -0
  58. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/jobs.py +0 -0
  59. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/link.py +0 -0
  60. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/local_ipc.py +0 -0
  61. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/local_sockets.py +0 -0
  62. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/mcp_graphs.py +0 -0
  63. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/mcp_install.py +0 -0
  64. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/parity.py +0 -0
  65. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/payload.py +0 -0
  66. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plans.py +0 -0
  67. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/platform_scan.py +0 -0
  68. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/platforms.py +0 -0
  69. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/__init__.py +0 -0
  70. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/astro/__init__.py +0 -0
  71. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/astro/content.py +0 -0
  72. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/astro/plugin.py +0 -0
  73. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/cfamily/__init__.py +0 -0
  74. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/cfamily/plugin.py +0 -0
  75. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/cfamily/syntax.py +0 -0
  76. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/__init__.py +0 -0
  77. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/bridges.py +0 -0
  78. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/extractor/bin/extract.dart +0 -0
  79. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/extractor/pubspec.lock +0 -0
  80. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/extractor/pubspec.yaml +0 -0
  81. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/http.py +0 -0
  82. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/models.py +0 -0
  83. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/plugin.py +0 -0
  84. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/dart/program.py +0 -0
  85. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/__init__.py +0 -0
  86. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/extras.py +0 -0
  87. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/models.py +0 -0
  88. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/plugin.py +0 -0
  89. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/schemas.py +0 -0
  90. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/shapes.py +0 -0
  91. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/django/urls.py +0 -0
  92. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/express/__init__.py +0 -0
  93. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/express/plugin.py +0 -0
  94. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/flutter/__init__.py +0 -0
  95. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/flutter/plugin.py +0 -0
  96. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/java/__init__.py +0 -0
  97. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/java/exact.py +0 -0
  98. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/java/plugin.py +0 -0
  99. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/jvm/__init__.py +0 -0
  100. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/jvm/interop.py +0 -0
  101. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/jvm/spring.py +0 -0
  102. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/kotlin/__init__.py +0 -0
  103. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/kotlin/exact.py +0 -0
  104. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/kotlin/reparse.py +0 -0
  105. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/__init__.py +0 -0
  106. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/broadcast.py +0 -0
  107. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/massassign.py +0 -0
  108. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/plugin.py +0 -0
  109. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/tests.py +0 -0
  110. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/laravel/values.py +0 -0
  111. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/native/__init__.py +0 -0
  112. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/native/gates.py +0 -0
  113. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/native/runner.py +0 -0
  114. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/native/scipread.py +0 -0
  115. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/native/ts.py +0 -0
  116. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nest/__init__.py +0 -0
  117. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nest/plugin.py +0 -0
  118. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nextjs/__init__.py +0 -0
  119. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nextjs/plugin.py +0 -0
  120. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nuxt/__init__.py +0 -0
  121. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/nuxt/plugin.py +0 -0
  122. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/__init__.py +0 -0
  123. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/external.py +0 -0
  124. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/extractor/composer.json +0 -0
  125. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/extractor/composer.lock +0 -0
  126. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/extractor/extract.php +0 -0
  127. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/gating.py +0 -0
  128. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/http_client.py +0 -0
  129. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/inline_guards.py +0 -0
  130. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/plugin.py +0 -0
  131. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/php/strings.py +0 -0
  132. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/__init__.py +0 -0
  133. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/aitools.py +0 -0
  134. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/external.py +0 -0
  135. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/fields.py +0 -0
  136. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/plugin.py +0 -0
  137. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/refs.py +0 -0
  138. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/roots.py +0 -0
  139. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/socketio.py +0 -0
  140. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/subproc.py +0 -0
  141. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/tests.py +0 -0
  142. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/python/values.py +0 -0
  143. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/pyweb/__init__.py +0 -0
  144. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/pyweb/plugin.py +0 -0
  145. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/pyweb/values.py +0 -0
  146. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/rust/__init__.py +0 -0
  147. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/rust/cargo.py +0 -0
  148. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/rust/plugin.py +0 -0
  149. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/rust/syntax.py +0 -0
  150. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/scip/__init__.py +0 -0
  151. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/scip/importer.py +0 -0
  152. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/scip/scip.proto +0 -0
  153. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/scip/scip_pb2.py +0 -0
  154. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/stubs/__init__.py +0 -0
  155. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/stubs/plugins.py +0 -0
  156. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/__init__.py +0 -0
  157. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/baseurl.py +0 -0
  158. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/exact.py +0 -0
  159. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/indexstore.py +0 -0
  160. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/packages.py +0 -0
  161. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/swift/plugin.py +0 -0
  162. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/__init__.py +0 -0
  163. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/baseurl.py +0 -0
  164. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/extractor/fw.mjs +0 -0
  165. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/extractor/package-lock.json +0 -0
  166. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/extractor/package.json +0 -0
  167. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/extractor/rr.mjs +0 -0
  168. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/nav.py +0 -0
  169. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/plugin.py +0 -0
  170. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/ts/react_router.py +0 -0
  171. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/tsweb/__init__.py +0 -0
  172. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/tsweb/common.py +0 -0
  173. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/plugins/tsweb/data.py +0 -0
  174. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/__init__.py +0 -0
  175. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/c_cpp.yaml +0 -0
  176. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/common.yaml +0 -0
  177. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/dart.yaml +0 -0
  178. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/django-ninja.yaml +0 -0
  179. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/django.yaml +0 -0
  180. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/djangorestframework.yaml +0 -0
  181. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/express.yaml +0 -0
  182. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/java.yaml +0 -0
  183. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/kotlin.yaml +0 -0
  184. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/laravel.yaml +0 -0
  185. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/nest.yaml +0 -0
  186. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/nextjs.yaml +0 -0
  187. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/nuxt.yaml +0 -0
  188. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/php.yaml +0 -0
  189. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/python.yaml +0 -0
  190. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/react-router.yaml +0 -0
  191. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/rust.yaml +0 -0
  192. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/spring.yaml +0 -0
  193. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/swift.yaml +0 -0
  194. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/presets/typescript.yaml +0 -0
  195. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/process_runs.py +0 -0
  196. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/protocols/__init__.py +0 -0
  197. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/protocols/builtin.py +0 -0
  198. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/protocols/matchers.py +0 -0
  199. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/protocols/view.py +0 -0
  200. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/query.py +0 -0
  201. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/realtime.py +0 -0
  202. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/realtime_events.py +0 -0
  203. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/realtime_native.py +0 -0
  204. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/realtime_ws.py +0 -0
  205. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/routes.py +0 -0
  206. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/rpc.py +0 -0
  207. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/sdk_systems.py +0 -0
  208. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/sockets.py +0 -0
  209. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/ssrf_input.py +0 -0
  210. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/starters.py +0 -0
  211. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/surface.py +0 -0
  212. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/tests_index.py +0 -0
  213. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/udp_apps.py +0 -0
  214. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/update_check.py +0 -0
  215. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/__init__.py +0 -0
  216. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/graph.py +0 -0
  217. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/server.py +0 -0
  218. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/app.css +0 -0
  219. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/app.js +0 -0
  220. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/index.html +0 -0
  221. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/layered.js +0 -0
  222. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/vendor/VERSIONS.txt +0 -0
  223. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/vendor/cose-base.js +0 -0
  224. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/vendor/cytoscape-fcose.js +0 -0
  225. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/vendor/cytoscape.min.js +0 -0
  226. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/static/vendor/layout-base.js +0 -0
  227. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/tools/package-lock.json +0 -0
  228. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/tools/package.json +0 -0
  229. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/viz/tools/shoot.mjs +0 -0
  230. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/watch.py +0 -0
  231. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/webhook_subs.py +0 -0
  232. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/webhooks.py +0 -0
  233. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph/xcode.py +0 -0
  234. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph.egg-info/dependency_links.txt +0 -0
  235. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph.egg-info/entry_points.txt +0 -0
  236. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph.egg-info/requires.txt +0 -0
  237. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/cg_code_graph.egg-info/top_level.txt +0 -0
  238. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/pyproject.toml +0 -0
  239. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/setup.cfg +0 -0
  240. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_affected.py +0 -0
  241. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_agents_install.py +0 -0
  242. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_agents_md.py +0 -0
  243. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ai_tools.py +0 -0
  244. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_android_ipc.py +0 -0
  245. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_apple_ipc.py +0 -0
  246. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_astro.py +0 -0
  247. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_astro_content.py +0 -0
  248. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_astro_server.py +0 -0
  249. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_bridges.py +0 -0
  250. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_bridges_pigeon_monorepo.py +0 -0
  251. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_brokers.py +0 -0
  252. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_cache_clean.py +0 -0
  253. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_cache_invalidation.py +0 -0
  254. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_cache_migration.py +0 -0
  255. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_channels_tests.py +0 -0
  256. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_cli_output.py +0 -0
  257. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_cmake_sources.py +0 -0
  258. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_completeness.py +0 -0
  259. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_config_extras.py +0 -0
  260. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_coverage.py +0 -0
  261. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_coverage_ts_hint.py +0 -0
  262. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_dart_isolates.py +0 -0
  263. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_db_connections.py +0 -0
  264. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_dead_wrappers.py +0 -0
  265. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_dispatch_interfaces.py +0 -0
  266. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_dispatch_part3.py +0 -0
  267. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_django_flutter.py +0 -0
  268. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_docs_languages.py +0 -0
  269. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_docs_links.py +0 -0
  270. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_echo_ref.py +0 -0
  271. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_echo_wrapper.py +0 -0
  272. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_elysia.py +0 -0
  273. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_enum_constants.py +0 -0
  274. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_env.py +0 -0
  275. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_explore.py +0 -0
  276. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_external.py +0 -0
  277. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_fp_batch.py +0 -0
  278. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_gating.py +0 -0
  279. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_generated.py +0 -0
  280. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_graphql.py +0 -0
  281. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_hooks.py +0 -0
  282. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_impact_overrides.py +0 -0
  283. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_index_isolation.py +0 -0
  284. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_inherited_overrides.py +0 -0
  285. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_inline_guards.py +0 -0
  286. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_install_rules.py +0 -0
  287. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_192.py +0 -0
  288. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_201.py +0 -0
  289. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_202.py +0 -0
  290. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_203.py +0 -0
  291. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_204.py +0 -0
  292. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_205.py +0 -0
  293. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_206.py +0 -0
  294. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_207.py +0 -0
  295. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_208.py +0 -0
  296. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_209.py +0 -0
  297. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_210.py +0 -0
  298. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_211.py +0 -0
  299. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_211_used_at.py +0 -0
  300. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_212.py +0 -0
  301. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_212_langs.py +0 -0
  302. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_218.py +0 -0
  303. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_219.py +0 -0
  304. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_issue_222.py +0 -0
  305. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_java.py +0 -0
  306. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_java_exact.py +0 -0
  307. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_jobs.py +0 -0
  308. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_js_refs.py +0 -0
  309. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_js_runtime.py +0 -0
  310. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin.py +0 -0
  311. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_binding.py +0 -0
  312. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_exact.py +0 -0
  313. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_fields.py +0 -0
  314. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_mixed.py +0 -0
  315. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_nav_consts.py +0 -0
  316. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_kotlin_props.py +0 -0
  317. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_laravel_determinism.py +0 -0
  318. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_laravel_receiver_narrowing.py +0 -0
  319. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_laravel_resources.py +0 -0
  320. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_laravel_route_paths.py +0 -0
  321. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_link_origin.py +0 -0
  322. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_listing_files.py +0 -0
  323. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_local_ipc.py +0 -0
  324. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_local_sockets.py +0 -0
  325. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mass_assignment.py +0 -0
  326. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mcp.py +0 -0
  327. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mcp_index.py +0 -0
  328. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mcp_install.py +0 -0
  329. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mcp_instructions.py +0 -0
  330. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_mcp_multi_graph.py +0 -0
  331. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_native_cfamily.py +0 -0
  332. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_native_concurrency.py +0 -0
  333. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_native_rust.py +0 -0
  334. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_nuxt_i18n.py +0 -0
  335. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_nuxt_import_dirs.py +0 -0
  336. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_nuxt_nitro.py +0 -0
  337. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_nuxt_rootsrc.py +0 -0
  338. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_nuxt_side_dirs.py +0 -0
  339. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_packaging.py +0 -0
  340. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_parity.py +0 -0
  341. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_parity_fields.py +0 -0
  342. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_parity_structure.py +0 -0
  343. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_php_http.py +0 -0
  344. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_php_props.py +0 -0
  345. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_plain_js.py +0 -0
  346. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_plans.py +0 -0
  347. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_platform_gaps.py +0 -0
  348. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_platform_type_variants.py +0 -0
  349. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_platforms.py +0 -0
  350. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_platforms_apple.py +0 -0
  351. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_presets_config.py +0 -0
  352. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_process_ipc.py +0 -0
  353. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_process_runs.py +0 -0
  354. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_protocols.py +0 -0
  355. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_app_tests_module.py +0 -0
  356. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_collection_chains.py +0 -0
  357. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_compat.py +0 -0
  358. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_entry_refs.py +0 -0
  359. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_fields.py +0 -0
  360. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_infer_cycles.py +0 -0
  361. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_roots.py +0 -0
  362. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_subprocess_tests.py +0 -0
  363. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_tests.py +0 -0
  364. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_web_hosts.py +0 -0
  365. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_web_routes.py +0 -0
  366. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_python_web_routes_more.py +0 -0
  367. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_react_router.py +0 -0
  368. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_readers_writers_specs.py +0 -0
  369. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_realtime.py +0 -0
  370. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_realtime_native.py +0 -0
  371. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_redact_salt.py +0 -0
  372. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_request_keys.py +0 -0
  373. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_route_specs.py +0 -0
  374. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_route_tables.py +0 -0
  375. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_routes_search_explain.py +0 -0
  376. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_rpc.py +0 -0
  377. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_scip_health.py +0 -0
  378. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_sdk_external.py +0 -0
  379. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_sdk_external_infra.py +0 -0
  380. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_sdk_external_mail.py +0 -0
  381. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_snippet.py +0 -0
  382. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_sockets.py +0 -0
  383. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_spring.py +0 -0
  384. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ssrf.py +0 -0
  385. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_starters_budget.py +0 -0
  386. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_surface.py +0 -0
  387. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_surface_transport.py +0 -0
  388. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift.py +0 -0
  389. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_binding.py +0 -0
  390. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_exact.py +0 -0
  391. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_facts.py +0 -0
  392. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_fields.py +0 -0
  393. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_modules.py +0 -0
  394. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_more.py +0 -0
  395. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_nav_values.py +0 -0
  396. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_parse_errors.py +0 -0
  397. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_platform_files.py +0 -0
  398. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_props.py +0 -0
  399. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_sdk.py +0 -0
  400. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_testing.py +0 -0
  401. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_swift_values.py +0 -0
  402. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_tauri_events.py +0 -0
  403. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_tests_scope.py +0 -0
  404. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_fields.py +0 -0
  405. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_file_specs.py +0 -0
  406. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_frameworks.py +0 -0
  407. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_node_http.py +0 -0
  408. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_nuxt.py +0 -0
  409. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_ts_src_tsconfig.py +0 -0
  410. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_unguarded.py +0 -0
  411. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_update_check.py +0 -0
  412. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_values.py +0 -0
  413. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_viz.py +0 -0
  414. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_vue_nav.py +0 -0
  415. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_watch.py +0 -0
  416. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_webhooks.py +0 -0
  417. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_webhooks_152.py +0 -0
  418. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_webhooks_152b.py +0 -0
  419. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_websockets.py +0 -0
  420. {cg_code_graph-0.23.9 → cg_code_graph-0.23.11}/tests/test_workspace_link.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cg-code-graph
3
- Version: 0.23.9
3
+ Version: 0.23.11
4
4
  Summary: cg: a code graph of routes, calls, data access and platform boundaries for agents and humans (CLI + MCP server)
5
5
  Author: cyberchronos00
6
6
  License-Expression: MIT
@@ -1,2 +1,2 @@
1
1
  """code-graph: deterministic code knowledge graph (beta)."""
2
- __version__ = "0.23.9"
2
+ __version__ = "0.23.11"
@@ -317,7 +317,8 @@ def main(argv=None):
317
317
  p.add_argument("--json", action="store_true"); p.add_argument("--tests", action="store_true", help="include test code")
318
318
  p = sub.add_parser("roundtrip", help="heuristic: writes of a stored property `Type.prop` through a lossy transform, "
319
319
  "read back to seed UI state (#88)")
320
- p.add_argument("spec"); p.add_argument("--db", required=True); p.add_argument("--json", action="store_true")
320
+ p.add_argument("spec", nargs="?"); p.add_argument("--db", required=True); p.add_argument("--json", action="store_true")
321
+ p.add_argument("--all", action="store_true", help="every stored property in one pass: only the round trips")
321
322
  p.add_argument("--tests", action="store_true", help="include test code's reads and writes")
322
323
  p = sub.add_parser("viz-plan", help="self-contained HTML overlay of a plan on the real graph")
323
324
  p.add_argument("name"); p.add_argument("--db", required=True); p.add_argument("-o", "--out", required=True); p.add_argument("--plans-dir")
@@ -769,8 +770,14 @@ def main(argv=None):
769
770
  print(json.dumps(res, indent=1) if a.json else LA.render(res))
770
771
  elif a.cmd == "roundtrip":
771
772
  from . import roundtrip as RT
772
- res = RT.roundtrip(st, a.spec, include_tests=a.tests)
773
- print(RT.to_json(res) if a.json else RT.render(res))
773
+ if a.all or not a.spec:
774
+ if not a.all:
775
+ sys.exit("cg roundtrip: give a property `Type.prop`, or --all")
776
+ res = RT.roundtrip_all(st, include_tests=a.tests)
777
+ print(RT.to_json(res) if a.json else RT.render_all(res))
778
+ else:
779
+ res = RT.roundtrip(st, a.spec, include_tests=a.tests)
780
+ print(RT.to_json(res) if a.json else RT.render(res))
774
781
  elif a.cmd in ("writers", "readers"):
775
782
  groups = Q.access(st, a.spec, a.cmd, a.connection)
776
783
  rows = [r for g in groups for r in g["rows"]]
@@ -61,6 +61,24 @@ def _input_dependent(text: str) -> bool:
61
61
  t = re.sub(r"(?<![\w$])[A-Za-z_$][\w$]*\s*(?:<[^<>]*>)?\s*\(", " (", t) # call names
62
62
  ids = re.findall(r"(?<![\w$])[A-Za-z_$][\w$]*", t)
63
63
  return any(i.startswith("SELFREF_") or (i not in KEYWORDS and not i[0].isupper()) for i in ids)
64
+ _SUSP: dict = {} # file -> lines with a call of a Kotlin `suspend` function (an await point with no marker)
65
+
66
+
67
+ def _load_suspend(st: GraphStore) -> None:
68
+ """A plain call of a project `suspend` function inside `launch { }` (`val x = repo.load()`) suspends like
69
+ `.await()`: the indexed CALLS edges to nodes marked `suspend` give those lines."""
70
+ _SUSP.clear()
71
+ for r in st.q("""SELECT e.file, e.line FROM edges e JOIN nodes n ON n.id = e.dst
72
+ WHERE e.kind = 'CALLS' AND n.attrs LIKE '%"suspend": true%' AND e.file IS NOT NULL"""):
73
+ _SUSP.setdefault(r["file"], set()).add(r["line"])
74
+
75
+
76
+ def _aw(t: str, file: str | None = None, line: int | None = None) -> bool:
77
+ """Is there an await point in this line of source: a marker (`await`, `.await()`, `withContext`, `delay`) or,
78
+ in Kotlin, a call of a `suspend` function?"""
79
+ return bool(AWAIT_RE.search(t)) or (line is not None and line in _SUSP.get(file, ()))
80
+
81
+
64
82
  TEST_PATH_RE = re.compile(r"(?:^|/)(?:tests?|__tests__|spec|testing|fixtures?|factories|Tests|UITests)/|"
65
83
  r"[._-](?:test|spec)s?\.\w+:|Tests?\.swift:|Test\.kt:|(?:^|/)test_\w+\.py:")
66
84
 
@@ -104,6 +122,7 @@ def _opener(ls: list[str], line: int, lo: int):
104
122
 
105
123
 
106
124
  def stale_async(st: GraphStore, include_tests: bool = False, limit: int = 500) -> list[dict]:
125
+ _load_suspend(st)
107
126
  root = st.meta().get("root")
108
127
  cache: dict = {}
109
128
  fields = {r["id"]: json.loads(r["attrs"] or "{}") for r in st.q("SELECT id, attrs FROM nodes WHERE kind IN ('field','property')")}
@@ -122,7 +141,8 @@ def stale_async(st: GraphStore, include_tests: bool = False, limit: int = 500) -
122
141
  continue
123
142
  kind, o = op
124
143
  body = ls[o - 1:r["line"] - 1]
125
- aw = next((o + i for i, t in enumerate(body) if AWAIT_RE.search(t if i else t[t.find("{") + 1:] if "{" in t else t)), None)
144
+ aw = next((o + i for i, t in enumerate(body) if _aw(t if i else t[t.find("{") + 1:] if "{" in t else t,
145
+ r["file"], o + i if i else None)), None)
126
146
  if aw is None:
127
147
  continue
128
148
  before = "\n".join(ls[o - 1:aw - 1]) + "\n" + ls[aw - 1].split("await")[0]
@@ -131,7 +151,10 @@ def stale_async(st: GraphStore, include_tests: bool = False, limit: int = 500) -
131
151
  if GUARD_RE.search(after) or SINGLE_FLIGHT_RE.search(ls[o - 1]):
132
152
  continue
133
153
  aw_line = ls[aw - 1]
134
- k = max(aw_line.find("await"), 0)
154
+ k = aw_line.find("await")
155
+ if k < 0: # a Kotlin suspend call: the request is the right-hand side
156
+ m = re.search(r"(?<![=!<>])=(?!=)", aw_line)
157
+ k = m.end() if m else 0
135
158
  stmt, depth = [], 0
136
159
  for i in range(aw - 1, min(len(ls), aw + 5)):
137
160
  x = ls[i][k:] if i == aw - 1 else ls[i]
@@ -144,10 +167,10 @@ def stale_async(st: GraphStore, include_tests: bool = False, limit: int = 500) -
144
167
  # the written value must come from the awaited result: a name bound on the await line (or derived from one
145
168
  # after it), or the await itself on the write line; a loading flag reset after the await is not a stale result
146
169
  res_vars = set()
147
- for t in ls[aw - 1:r["line"] - 1]:
170
+ for j, t in enumerate(ls[aw - 1:r["line"] - 1]):
148
171
  m = DECL_RE.search(t) or re.search(r"^\s*(?:self\.)?([A-Za-z_$][\w$]*)\s*=(?!=)", t)
149
172
  nm = m and (m.group(1) or (m.group(2) if m.lastindex and m.lastindex >= 2 else None))
150
- if nm and (AWAIT_RE.search(t) or any(re.search(rf"(?<![\w$.]){re.escape(v)}\b", t.split("=", 1)[-1]) for v in res_vars)):
173
+ if nm and (_aw(t, r["file"], aw + j) or any(re.search(rf"(?<![\w$.]){re.escape(v)}\b", t.split("=", 1)[-1]) for v in res_vars)):
151
174
  res_vars.add(nm)
152
175
  wl = ls[r["line"] - 1]
153
176
  if r["line"] - 1 == aw - 1:
@@ -182,7 +205,8 @@ LIFECYCLE_RE = re.compile(r"\.onAppear\b|\bonAppear\s*\(|\.task\s*[({]|\bonMount
182
205
  CALLBACK_RE = re.compile(r"\.sink\s*\{|\.onReceive\s*\(|\.subscribe\s*[({]|\.collect(?:Latest)?\s*\{|\.observe\w*\s*\(|"
183
206
  r"\bcompletion(?:Handler)?\s*:\s*\{|\{\s*\[?\s*(?:weak|unowned)\s+self\s*\]?|\.then\s*\(|\.on\s*\(\s*['\"]")
184
207
  LITERAL_RHS_RE = re.compile(r"(?<![=!<>])=\s*(?:\[\s*\]|\[:\]|\{\s*\}|nil|null|None|undefined|0(?:\.0+)?[fFdDL]?|false|False|true|True|"
185
- r"''|\"\"|emptyList\(\)|emptyMap\(\)|mutableListOf\(\)|\.init\(\)|\w+\(\)|\.\w+)\s*;?\s*$")
208
+ r"''|\"\"|emptyList\(\)|emptyMap\(\)|mutableListOf\(\)|\.init\(\)|\w+\(\)|\.\w+|"
209
+ r"[A-Z]\w*(?:\.[A-Z]\w*)+(?:\(\))?)\s*;?\s*$") # a sentinel: `AsyncData.Loading()`, `UiState.Idle`
186
210
  LITERAL_ARG_RE = re.compile(r"\(\s*(?:nil|null|None|undefined|true|false|True|False|-?\d+(?:\.\d+)?|'[^']*'|\"[^\"]*\"|\[\s*\]|\{\s*\})\s*\)\s*;?\s*$")
187
211
  INIT_NAMES = {"init", "__init__", "constructor", "<init>", "viewDidLoad", "onCreate", "initState", "mounted", "created", "setup"}
188
212
 
@@ -198,7 +222,7 @@ def _lifecycle(st, ls, r, node) -> str | None:
198
222
  """Is this write lifecycle code: an initializer with a real (non-default) value, or code directly inside
199
223
  onAppear / .task (before any await) / onMounted / LaunchedEffect / useEffect / viewDidLoad?"""
200
224
  t = ls[r["line"] - 1]
201
- if AWAIT_RE.search(t):
225
+ if _aw(t, r["file"], r["line"]):
202
226
  return None
203
227
  nm = ((node["name"] if node is not None else "") or "").split(".")[-1]
204
228
  if nm in INIT_NAMES or (node is not None and node["kind"] == "class"):
@@ -212,7 +236,7 @@ def _lifecycle(st, ls, r, node) -> str | None:
212
236
  lo = max(1, r["line"] - 6)
213
237
  for i in range(r["line"], lo - 1, -1):
214
238
  x = ls[i - 1]
215
- if i < r["line"] and AWAIT_RE.search(x):
239
+ if i < r["line"] and _aw(x, r["file"], i):
216
240
  return None
217
241
  m = LIFECYCLE_RE.search(x)
218
242
  if m:
@@ -225,7 +249,8 @@ def _async_callback(ls, r, lo) -> str | None:
225
249
  op = _opener(ls, r["line"], lo)
226
250
  if op is not None:
227
251
  body = ls[op[1] - 1:r["line"] - 1]
228
- if any(AWAIT_RE.search(x) for x in body[1:]) or AWAIT_RE.search(body[0][body[0].find("{") + 1:] if body else ""):
252
+ if any(_aw(x, r["file"], op[1] + j) for j, x in enumerate(body) if j) or \
253
+ (body and AWAIT_RE.search(body[0][body[0].find("{") + 1:])):
229
254
  return f"after await in {op[0]}"
230
255
  for i in range(r["line"] - 1, max(lo, r["line"] - 8) - 1, -1):
231
256
  m = CALLBACK_RE.search(ls[i - 1]) if i >= 1 else None
@@ -235,6 +260,7 @@ def _async_callback(ls, r, lo) -> str | None:
235
260
 
236
261
 
237
262
  def two_writers(st: GraphStore, include_tests: bool = False) -> list[dict]:
263
+ _load_suspend(st)
238
264
  root = st.meta().get("root")
239
265
  cache: dict = {}
240
266
  rows = st.q("""SELECT e.src, e.dst, e.file, e.line, e.attrs, n.line AS fl FROM edges e JOIN nodes n ON n.id = e.src
@@ -361,6 +387,14 @@ def incomplete_cache_key(st: GraphStore, include_tests: bool = False) -> list[di
361
387
  sig = "\n".join(body[:3])
362
388
  sig = sig[sig.find("("):] if "(" in sig else ""
363
389
  params = _idents(_split_args(sig[1:]) and ",".join(re.sub(r"[:=].*", "", a, flags=re.S) for a in _split_args(sig[1:])) or "")
390
+ # instance fields used without `self.` / `this.` (Swift, Kotlin): the READS_PROP edges bind them
391
+ own = {e["dst"].rsplit(".", 1)[-1] for e in st.q("SELECT dst, attrs FROM edges WHERE kind = 'READS_PROP' AND src = ?",
392
+ (fn["id"],))
393
+ if json.loads(e["attrs"] or "{}").get("receiver") in (None, "self", "this")}
394
+ text = "\n".join(body)
395
+ # ... unless the field is an object the function calls (`client.get(…)`, `mediaLoader.load(…)`): a dependency,
396
+ # not an input of the cached value
397
+ own = {f for f in own if not re.search(rf"(?<![\w$.]){re.escape(f)}\s*[?!]?\.\s*\w+\s*[({{]", text)}
364
398
  assigns: dict = {}
365
399
  for off, t in enumerate(body):
366
400
  m = re.search(r"\b(?:let|var|val|const)\s+([A-Za-z_$][\w$]*)\s*(?::[^=]+)?=(?!=)(.*)|^\s*([a-z_$][\w$]*)\s*=(?!=)(.*)", t)
@@ -384,8 +418,10 @@ def incomplete_cache_key(st: GraphStore, include_tests: bool = False) -> list[di
384
418
  vb = val.strip().rstrip(";")
385
419
  if re.fullmatch(r"[A-Za-z_$][\w$]*", vb) and vb not in assigns:
386
420
  continue # `put(key, value)` passes a value through; nothing is computed here
387
- kids = _expand(_idents(key), assigns)
388
- vids = _expand(_idents(val), assigns) - {vb}
421
+ bare = lambda ids: {f"self_{i}" if i in own and i not in params and i not in assigns else i # noqa: E731
422
+ for i in ids}
423
+ kids = bare(_expand(_idents(key), assigns))
424
+ vids = bare(_expand(_idents(val), assigns)) - {vb}
389
425
  setter = re.match(r"(?:set|put|insert|store|save|cache|add|update|write|remember|record)(?![a-z])",
390
426
  (fn["fqn"] or "").split(".")[-1].split("(")[0])
391
427
  inputs = {i for i in vids if (i in params and not setter and not PAYLOAD_RE.fullmatch(i.lstrip("$")))
@@ -421,6 +457,7 @@ def echo_suppression(st: GraphStore, include_tests: bool = False, patterns: list
421
457
  programmatic writes of some state so the change observer, which checks the guard and returns early, does not
422
458
  echo them back. Reports state of that pattern that is also written from an async path / callback *without*
423
459
  setting the guard: that write reaches the observer and is sent back (a loop or a clobbered edit)."""
460
+ _load_suspend(st)
424
461
  root = st.meta().get("root")
425
462
  cache: dict = {}
426
463
  guards = [r["id"] for r in st.q("SELECT id, name FROM nodes WHERE kind = 'field'") if GUARD_FIELD_RE.fullmatch((r["name"] or "").rsplit(".", 1)[-1].replace("_", ""))]
@@ -877,13 +877,16 @@ def _access_text(st, spec: str, what: str, limit: int, connection: str | None =
877
877
 
878
878
 
879
879
  @tool
880
- def roundtrip(prop: str, include_tests: bool = False) -> str:
880
+ def roundtrip(prop: str = "", include_tests: bool = False) -> str:
881
881
  """Heuristic (#88): does a stored property `Type.prop` round-trip through a lossy transform? Each write site with
882
882
  the lossy call it passes through (clamp, min/max, round, truncating casts, `fit*`, `.cg.yaml` `lossy:` names,
883
883
  `@cg-lossy` functions; within the function plus one hop through direct callers), each read site that seeds UI
884
884
  state (init, onAppear, remember, useState(initial), mounted), and un-narrowed ranges drawn next to such a read.
885
- Findings are heuristic evidence with file:line, not proof."""
885
+ Findings are heuristic evidence with file:line, not proof. An empty `prop` (or `*`) checks every stored property
886
+ in one pass and lists only the round trips."""
886
887
  from . import roundtrip as RT
888
+ if prop.strip() in ("", "*"):
889
+ return RT.render_all(RT.roundtrip_all(_st(), include_tests=include_tests))
887
890
  return RT.render(RT.roundtrip(_st(), prop, include_tests=include_tests))
888
891
 
889
892
 
@@ -56,6 +56,11 @@ RES = re.compile(r"""\b(?:UI|NS)?Image\(\s*(?:named:\s*)?"([^"\n]+)"|\bAsset\.(?
56
56
  r"""\bImage\(\s*\.(\w+)\s*\)|\b(?:R|Res)\.(?:drawable|mipmap|raw)\.(\w+)""")
57
57
  WIRE = re.compile(r'@(?:SerialName|SerializedName|JsonProperty|Json\(\s*name\s*=)\s*\(?\s*(?:value\s*=\s*)?"([^"\n]+)"')
58
58
  RAW_CASE = re.compile(r'\bcase\s+`?(\w+)`?\s*=\s*"([^"\n]+)"')
59
+ ARM = re.compile(r"^(\s*)case\s+(?:let\s+|var\s+)?((?:\.\w+(?:\([^)]*\))?\s*,?\s*)+)(?:where\b[^:]*)?:")
60
+ DISPATCH = {"receive", "perform", "handle", "handleaction", "onaction", "reduce", "send", "dispatch", "process"}
61
+ MIN_MEMBER_SCORE = 0.35
62
+ MIN_NAME_SCORE = 0.45
63
+ MIN_BOTH_SCORE = 0.25
59
64
  EDGE_KINDS = ("CALLS", "INSTANTIATES", "NAVIGATES_TO", "HTTP_CALLS", "USES_VALUE", "REFERENCES_FN", "USES_TYPE")
60
65
 
61
66
 
@@ -140,9 +145,11 @@ class Side:
140
145
  self.by_file[r["file"]].append((r["line"], r["end_line"] or r["line"], r["id"]))
141
146
  self.span[r["id"]] = (r["file"], r["line"], r["end_line"] or r["line"])
142
147
  self.out = defaultdict(list)
143
- q = f"SELECT src, dst, kind FROM edges WHERE kind IN ({','.join('?' * len(EDGE_KINDS))})"
148
+ self.out_at = defaultdict(list) # src -> [(line, dst, kind)]
149
+ q = f"SELECT src, dst, kind, line FROM edges WHERE kind IN ({','.join('?' * len(EDGE_KINDS))})"
144
150
  for r in c.execute(q, EDGE_KINDS):
145
151
  self.out[r["src"]].append((r["dst"], r["kind"]))
152
+ self.out_at[r["src"]].append((r["line"] or 0, r["dst"], r["kind"]))
146
153
  self.http = {r["id"]: (r["name"] or r["id"]) for r in c.execute("SELECT id, name FROM nodes WHERE kind='http'")}
147
154
  self.decl = defaultdict(list) # owner fqn -> [(name, kind, file, line)] of stored fields / enum cases
148
155
  for r in c.execute("SELECT kind, fqn, file, line, attrs FROM nodes WHERE kind IN ('field', 'enum_case') "
@@ -202,21 +209,50 @@ class Side:
202
209
  for la, lb, nid in self.by_file.get(file, ()):
203
210
  if a <= la <= b:
204
211
  for dst, kind in self.out.get(nid, ()):
205
- if kind == "HTTP_CALLS" or dst in self.http:
206
- p = re.sub(r"\{[^}]*\}|(?<=/):\w+", "{}", self.http.get(dst, dst).split(" ")[-1])
207
- f.add("http:" + p.lstrip("/").lower())
208
- continue
209
- dq = self.fqn_of.get(dst)
210
- t = rename(dq)
211
- if t:
212
- f.add("sym:" + t)
213
- # the called member's own name (two words or more): `getDevices` on an AuthService and on
214
- # an AuthRepository is the same call even when the owners are not paired
215
- nm = _short(dq or "").split("(")[0]
216
- if kind == "CALLS" and len(_words(nm)) >= 2:
217
- f.add("call:" + re.sub(r"^(get|fetch|load)(?=[a-z])", "", _fold(nm)))
212
+ self._edge(f, dst, kind, rename)
218
213
  return f
219
214
 
215
+ def _edge(self, f: set, dst: str, kind: str, rename) -> None:
216
+ if kind == "HTTP_CALLS" or dst in self.http:
217
+ p = re.sub(r"\{[^}]*\}|(?<=/):\w+", "{}", self.http.get(dst, dst).split(" ")[-1])
218
+ f.add("http:" + p.lstrip("/").lower())
219
+ return
220
+ dq = self.fqn_of.get(dst)
221
+ t = rename(dq)
222
+ if t:
223
+ f.add("sym:" + t)
224
+ # the called member's own name (two words or more): `getDevices` on an AuthService and on
225
+ # an AuthRepository is the same call even when the owners are not paired
226
+ nm = _short(dq or "").split("(")[0]
227
+ if kind == "CALLS" and len(_words(nm)) >= 2:
228
+ f.add("call:" + re.sub(r"^(get|fetch|load)(?=[a-z])", "", _fold(nm)))
229
+
230
+ def arms(self, sym: dict) -> list:
231
+ """The top-level `case .x:` arms of a Swift `receive(_:)` / `perform(_:)` style switch: [(case names,
232
+ features of what the arm calls)]."""
233
+ file, a, b = self.span.get(sym["id"], (None, 0, 0))
234
+ if not file or b <= a:
235
+ return []
236
+ rows = self.text(file, a, b).split("\n")
237
+ hits = [(i, len(m.group(1)), m.group(2)) for i, ln in enumerate(rows) for m in [ARM.match(ln)] if m]
238
+ if not hits:
239
+ return []
240
+ top = min(h[1] for h in hits)
241
+ hits = [h for h in hits if h[1] == top]
242
+ out = []
243
+ for k, (i, _, names) in enumerate(hits):
244
+ lo, hi = a + i, (a + hits[k + 1][0] - 1 if k + 1 < len(hits) else b)
245
+ f = set()
246
+ for ln, dst, kind in self.out_at.get(sym["id"], ()):
247
+ if lo <= ln <= hi:
248
+ self._edge(f, dst, kind, lambda x: x)
249
+ for la, lb, nid in self.by_file.get(file, ()):
250
+ if lo <= la <= hi and nid != sym["id"]:
251
+ for dst, kind in self.out.get(nid, ()):
252
+ self._edge(f, dst, kind, lambda x: x)
253
+ out.append((re.findall(r"\.(\w+)", names), f))
254
+ return out
255
+
220
256
 
221
257
  def match(S: dict, T: dict, out: dict, sym_of: dict, cat_of: dict, t_syms: dict, t_cat: dict, learn: bool = True) -> dict:
222
258
  ui_s = {fq: is_ui(fq, sym, S["members"]) for fq, sym in sym_of.items()}
@@ -369,7 +405,8 @@ def _structure(S, T, out, sym_of, cat_of, t_syms, t_cat, used):
369
405
  if sym is not None:
370
406
  src_f[row["symbol"]] = sS.features(sym, lambda x: pairs.get(x)) | name_words(row["symbol"])
371
407
  tgt_f = {fq: sT.features(sym, lambda x: x) | name_words(fq) for fq, sym in t_syms.items()
372
- if fq not in used and not PREVIEW.search(_short(fq))}
408
+ if fq not in used and not PREVIEW.search(_short(fq))
409
+ and not (_fold(_short(fq).split("(")[0]) in DISPATCH and sT.arms(sym))} # its arms are paired below
373
410
  src_f = {fq: f for fq, f in src_f.items() if not PREVIEW.search(_short(fq))}
374
411
  df = Counter()
375
412
  for fs in list(src_f.values()) + list(tgt_f.values()):
@@ -423,4 +460,88 @@ def _structure(S, T, out, sym_of, cat_of, t_syms, t_cat, used):
423
460
  else:
424
461
  keep.append(row)
425
462
  out["missing"] = keep
463
+ _members(sS, sT, out, sym_of, cat_of, t_syms, t_cat, used, S, T)
426
464
  _by_fields(sS, sT, out, sym_of, cat_of, t_syms, t_cat, used, S, T)
465
+
466
+
467
+ def _members(sS, sT, out, sym_of, cat_of, t_syms, t_cat, used, S, T):
468
+ """Members of matched types still missing, paired inside the counterpart type (#227): with an unmatched member,
469
+ or a top-level function, that makes the same paired calls, or with a `case .x:` arm of its `receive(_:)` /
470
+ `perform(_:)` switch (`handleCopyClick` on a view model, `case .copyGeneratedValue:` in the processor). Uses are
471
+ weighed within the pair of types, so a call that is common across the project still tells two members apart."""
472
+ pairs = {r["symbol"]: r["target"] for r in out["matched"]}
473
+ by_owner = defaultdict(list)
474
+ for r in out["missing"]:
475
+ if r.get("owner_matched") and r["symbol"] in sym_of and not PREVIEW.search(_short(r["symbol"])):
476
+ by_owner[r["owner_matched"]].append(r)
477
+ if not by_owner:
478
+ return
479
+ ident = lambda x: x # noqa: E731
480
+ top, top_inv = None, defaultdict(list)
481
+ taken_arms, best, cand_of = set(), {}, {}
482
+ for o, rows in by_owner.items():
483
+ units = [] # (key, target fqn, case name or None, category, features)
484
+ for nm, sym in T["members"].get(o, {}).items():
485
+ fq = f"{o}.{nm}"
486
+ arms = sT.arms(sym) if _fold(nm.split("(")[0]) in DISPATCH else []
487
+ for names, f in arms: # a dispatching switch stands for its arms, not as a whole
488
+ ws = set().union(*(name_words(n) for n in names))
489
+ units.append(((fq, names[0]), fq, names[0], "func", f | ws))
490
+ if not arms and fq not in used and not PREVIEW.search(nm):
491
+ units.append((fq, fq, None, t_cat.get(fq), sT.features(sym, ident) | name_words(fq)))
492
+ src = {r["symbol"]: sS.features(sym_of[r["symbol"]], pairs.get) | name_words(r["symbol"]) for r in rows}
493
+ if top is None: # top-level functions, met only through two paired uses
494
+ top = {}
495
+ for fq, sym in t_syms.items():
496
+ if t_cat.get(fq) == "func" and fq not in used and "." not in fq.replace(o, "") and fq in T["top"]:
497
+ top[fq] = {f for f in sT.features(sym, ident) if f.startswith("sym:")} | name_words(fq)
498
+ for f in top[fq]:
499
+ if f.startswith("sym:"):
500
+ top_inv[f].append(fq)
501
+ df = Counter()
502
+ for fs in list(src.values()) + [u[4] for u in units]:
503
+ df.update(fs)
504
+ n = len(src) + len(units)
505
+ for fq, fs in src.items():
506
+ extra = {t for f in fs if f.startswith("sym:") for t in top_inv.get(f, ())}
507
+ cands = [u for u in units if u[3] == cat_of.get(fq)] + [(t, t, None, "func", top[t]) for t in extra
508
+ if cat_of.get(fq) == "func"]
509
+ scored = []
510
+ for key, t, case, cat, tf in cands:
511
+ sh = fs & tf
512
+ uses = sum(1 for f in sh if not f.startswith("w:"))
513
+ words = len(sh) - uses
514
+ if case is None and t in top and uses < 2:
515
+ continue
516
+ if not (uses >= 2 or (uses and words) or words >= 2):
517
+ continue
518
+ wt = lambda f: math.log(1 + n / df[f]) if df[f] else math.log(1 + n) # noqa: E731
519
+ sc = sum(wt(f) for f in sh) / math.sqrt(sum(wt(f) for f in fs) * sum(wt(f) for f in tf))
520
+ if sc < (MIN_NAME_SCORE if not uses else MIN_BOTH_SCORE if words else MIN_MEMBER_SCORE):
521
+ continue # names alone must agree more; a use and a name word less
522
+ scored.append((sc, key, t, case, sh))
523
+ scored.sort(key=lambda x: -x[0])
524
+ if scored and (len(scored) == 1 or scored[1][0] < MARGIN * scored[0][0]):
525
+ best[fq] = scored[0]
526
+ by_key = defaultdict(list)
527
+ for fq, (sc, key, *_rest) in best.items():
528
+ by_key[key].append((sc, fq))
529
+ keep = []
530
+ for row in out["missing"]:
531
+ got = best.get(row["symbol"])
532
+ if not got:
533
+ keep.append(row)
534
+ continue
535
+ sc, key, t, case, sh = got
536
+ if max(by_key[key])[1] != row["symbol"] or (case is None and t in used) or key in taken_arms:
537
+ keep.append(row)
538
+ continue
539
+ if case is None:
540
+ used.add(t)
541
+ taken_arms.add(key)
542
+ ev = sorted(sh, key=lambda f: (f.startswith("w:"), f))[:6]
543
+ r = dict(row, target=t, confidence="structure", score=round(sc, 2), evidence=([f"case .{case}"] if case else []) + ev)
544
+ if case:
545
+ r["case"] = case
546
+ out["matched"].append(r)
547
+ out["missing"] = keep
@@ -207,6 +207,12 @@ def _suspend_lambdas(src: bytes) -> tuple[bytes, int]:
207
207
  # `get<T>` the whitespace character in front of `get` / `set` becomes `;`. That ends the declaration and keeps every
208
208
  # byte offset.
209
209
  _ROUTE_ACCESSOR = re.compile(rb"([ \t]*)[ \t]((?:get|set)\s*(?:\(\s*(?:\"|\"\"\")|\{|<))")
210
+ # `var x by remember { mutableStateOf(..) }`, `by rememberSaveable { .. }`, `by mutableStateOf(..)`,
211
+ # `by vm.state.collectAsState()` / `collectAsStateWithLifecycle()`, `by produceState(..)`, `by derivedStateOf { }`
212
+ LOCAL_STATE = re.compile(r"\b(var|val)\s+(\w+)(?:\s*:\s*[^=\n]+?)?\s+by\s+(?:remember(?:Saveable)?\b[^\n{]*\{\s*"
213
+ r"(mutable\w*StateOf|derivedStateOf|mutableStateListOf|mutableStateMapOf)"
214
+ r"|(mutable\w*StateOf|derivedStateOf)\s*[({]|[\w.]*?\b(collectAs\w*State\w*)\s*\("
215
+ r"|(produceState|observeAsState|subscribeAsState)\b|[\w.]*?\.(observeAsState|subscribeAsState)\s*\()")
210
216
  _DECL_LINE = re.compile(rb"^\s*(?:(?:private|internal|public|protected|const|lateinit|override|open)\s+)*(?:val|var)\s")
211
217
 
212
218
 
@@ -321,6 +327,7 @@ class KotlinPlugin(LanguagePlugin):
321
327
  # stored properties of a class / enum are `field:<Type>.<name>` nodes with READS_PROP / WRITES_PROP (#88)
322
328
  self.fields: dict[str, dict[str, str]] = defaultdict(dict) # class fqn -> name -> field id
323
329
  self.field_names: set[str] = set()
330
+ self.local_state: dict[str, dict[str, str]] = defaultdict(dict) # function id -> local state name -> field id
324
331
  self.copies: list[tuple] = [] # (owner, receiver (type, text), [(arg name, line)], kf, decl): `x.copy(a = 1)`
325
332
  self.frefs: list[tuple] = [] # (owner_id, name, receiver (type, text) | None, line, file, decl, write)
326
333
  self.reads: list[tuple] = [] # (owner_id, name, receiver node, line, file obj, decl, read | write)
@@ -617,6 +624,7 @@ class KotlinPlugin(LanguagePlugin):
617
624
  if any(dflt for _n, dflt, _f in fparams):
618
625
  self.fn_params[dc.id] = fparams
619
626
  self._add_decl(dc, kind)
627
+ self._local_state(dc, kf, c)
620
628
  self._local_fns(c, kf, dc)
621
629
  elif ty == "enum_entry" and cls is not None:
622
630
  nm = self._name(c)
@@ -655,6 +663,30 @@ class KotlinPlugin(LanguagePlugin):
655
663
  self.field_names.add(nm)
656
664
  self.st["stored_property_nodes"] += 1
657
665
 
666
+ def _local_state(self, dc: Decl, kf: KFile, c):
667
+ """Compose state held in a local delegate (`var count by remember { mutableIntStateOf(0) }`, `val ui by
668
+ vm.state.collectAsStateWithLifecycle()`): a `field:<function>.<name>` node (`property: state`, `hook`) whose
669
+ reads and writes inside the function are READS_PROP / WRITES_PROP, like React `useState` (#105, #88)."""
670
+ txt = self.t(c)
671
+ if " by " not in txt:
672
+ return
673
+ for m in LOCAL_STATE.finditer(txt):
674
+ nm = m.group(2)
675
+ if nm in self.local_state[dc.id]:
676
+ continue
677
+ hook = next(g for g in m.groups()[2:] if g).split(".")[-1]
678
+ line = c.start_point[0] + 1 + txt.count("\n", 0, m.start(2))
679
+ a = {"property": "state", "hook": hook, "binding": m.group(1), "local": True}
680
+ if kf.test:
681
+ a["test"] = True
682
+ fid = self.b.add_node("field", f"{dc.fqn}.{nm}", name=nm, fqn=f"{dc.fqn}.{nm}", file=kf.rel, line=line,
683
+ end_line=line, module=getattr(self.b.nodes.get(dc.id), "module", None), lang="kotlin",
684
+ attrs=a)
685
+ self.b.add_edge(dc.id, fid, "CONTAINS", kf.rel, line, EXACT)
686
+ self.local_state[dc.id][nm] = fid
687
+ self.field_names.add(nm)
688
+ self.st["local_state_nodes"] += 1
689
+
658
690
  def _prop_kind(self, c) -> tuple | None:
659
691
  """(property kind, accessors) of a property that runs code when read or written (#89): a custom `get()` /
660
692
  `set(value)` (custom), `by lazy { }` (lazy) or another delegate (delegated); None for a stored property."""
@@ -1954,6 +1986,13 @@ class KotlinPlugin(LanguagePlugin):
1954
1986
  decl if decl is not None and decl.kind == "class" else None)
1955
1987
  fid = None
1956
1988
  rt = recv[1] if recv is not None else None
1989
+ loc = self.local_state.get(decl.id, {}).get(name) if recv is None and decl is not None else None
1990
+ if loc:
1991
+ if loc != owner:
1992
+ self.b.add_edge(owner, loc, "WRITES_PROP" if write else "READS_PROP", kf.rel, line, RESOLVED,
1993
+ receiver="local", **({"via": write} if isinstance(write, str) else {}))
1994
+ self.st["local_state_writes" if write else "local_state_reads"] += 1
1995
+ continue
1957
1996
  if recv is None or rt == "this":
1958
1997
  if recv is None and self._shadowed_local(decl, name, kf):
1959
1998
  continue
@@ -983,6 +983,7 @@ const STATE_HOOKS = new Set(['useState', 'useReducer', 'ref', 'shallowRef', 'rea
983
983
  const optFields = new Map() // options ObjectLiteralExpression -> Map(name -> field id)
984
984
  const storeFields = new Map() // store VariableDeclaration -> Map(name -> field id)
985
985
  const optNames = new Set()
986
+ const fileOptData = new Map() // rel .vue path -> Map(data() key -> field id): bare names in its template
986
987
  function returnedObject(f) {
987
988
  if (!f) return null
988
989
  f = unwrap(f)
@@ -1013,6 +1014,7 @@ function optionsFields(sf, r, fid) {
1013
1014
  }
1014
1015
  optFields.set(obj, m)
1015
1016
  if (storeDecl) storeFields.set(storeDecl, m)
1017
+ if (hook === 'data') { const f = fileOptData.get(r) || new Map(); for (const [k, v] of m) f.set(k, v); fileOptData.set(r, f) }
1016
1018
  }
1017
1019
  const scan = (n) => {
1018
1020
  if (ts.isCallExpression(n) && ts.isIdentifier(n.expression) && n.expression.text === 'defineStore') {
@@ -1065,6 +1067,20 @@ function optionsRef(node, cur, sf, r) {
1065
1067
  addEdge(cur, fid, write ? 'WRITES_PROP' : 'READS_PROP', r, lineOf(node, sf), m === null ? 'resolved' : (recv === 'this' ? 'exact' : 'resolved'), { receiver: recv, ...(via ? { via } : {}) })
1066
1068
  stats[write ? 'state_field_writes' : 'state_field_reads'] = (stats[write ? 'state_field_writes' : 'state_field_reads'] || 0) + 1
1067
1069
  }
1070
+ // `{{ count }}` / `@click="count++"` in the template of an Options API component: a bare data() key (#88)
1071
+ function optionsTplRef(node, cur, sf, r) {
1072
+ const m = fileOptData.get(r), fid = m && m.get(node.text)
1073
+ if (!fid || fid === cur || !isTemplateLine(node, sf)) return
1074
+ const p = node.parent
1075
+ if (p && ((ts.isPropertyAccessExpression(p) && p.name === node) || (ts.isPropertyAssignment(p) && p.name === node)
1076
+ || ts.isParameter(p) || ts.isFunctionDeclaration(p))) return
1077
+ let s = null
1078
+ try { s = checker.getSymbolAtLocation(node) } catch { }
1079
+ if (s && (s.declarations || []).length) return // a v-for variable / slot prop / import of that name
1080
+ const [write, via] = writeOf(node)
1081
+ addEdge(cur, fid, write ? 'WRITES_PROP' : 'READS_PROP', r, lineOf(node, sf), 'resolved', { template: true, ...(via ? { via } : {}) })
1082
+ stats[write ? 'state_field_writes' : 'state_field_reads'] = (stats[write ? 'state_field_writes' : 'state_field_reads'] || 0) + 1
1083
+ }
1068
1084
  function stateDecl(d, cur, sf, r) {
1069
1085
  const init = unwrap(d.initializer)
1070
1086
  if (!init || !ts.isCallExpression(init)) return
@@ -2865,6 +2881,7 @@ for (const sf of sourceFiles) {
2865
2881
  if (ts.isVariableDeclaration(node) && node.initializer && cur) stateDecl(node, cur, sf, r)
2866
2882
  if (ts.isIdentifier(node) && stateNames.has(node.text) && cur) stateRef(node, cur, sf, r)
2867
2883
  if (ts.isPropertyAccessExpression(node) && optNames.has(node.name.text) && cur) optionsRef(node, cur, sf, r)
2884
+ if (isVue && ts.isIdentifier(node) && optNames.has(node.text) && cur && fileOptData.has(r)) optionsTplRef(node, cur, sf, r)
2868
2885
  // .astro: `<a href>` inside a template expression (`{posts.map(p => <a href={`/blog/${p.slug}`}>)}`) is navigation,
2869
2886
  // like a top-level one (the scanner handles those)
2870
2887
  if (isAstro && ts.isJsxAttribute(node) && node.initializer && ts.isIdentifier(node.name) && node.name.text === 'href'
@@ -119,6 +119,13 @@ def _bounds(text: str) -> tuple[float, float] | None:
119
119
 
120
120
  def _fn_bounds(st: GraphStore, src: _Src, name: str) -> tuple[float, float] | None:
121
121
  """Bounds inside a project function `name` (`func clamp(_ v: Double) -> Double { min(max(v, 0), 10) }`)."""
122
+ memo = src.__dict__.setdefault("bounds", {})
123
+ if name not in memo:
124
+ memo[name] = _fn_bounds1(st, src, name)
125
+ return memo[name]
126
+
127
+
128
+ def _fn_bounds1(st: GraphStore, src: _Src, name: str) -> tuple[float, float] | None:
122
129
  for r in st.q("SELECT file, line, end_line FROM nodes WHERE kind IN ('function','method') AND name=? LIMIT 3", (name,)):
123
130
  ls = src.lines(r["file"])
124
131
  body = "\n".join(ls[(r["line"] or 1) - 1:(r["end_line"] or r["line"] or 1)])
@@ -273,8 +280,19 @@ def roundtrip(st: GraphStore, spec: str, include_tests: bool = False) -> dict:
273
280
  for r in Q.prop_access(st, spec, "READS_PROP"):
274
281
  if include_tests or not r.get("test"):
275
282
  res["reads"].append(_read_site(st, src, r))
276
- lossy = [w for w in res["writes"] if w["lossy"]]
277
- seeds = [x for x in res["reads"] if x["seeds"]]
283
+ res["findings"] = _pair(src, res["writes"], res["reads"], prop)
284
+ for x in res["writes"]:
285
+ x.pop("_span", None)
286
+ for x in res["reads"]:
287
+ x.pop("_src", None)
288
+ return res
289
+
290
+
291
+ def _pair(src, writes, reads, prop) -> list[dict]:
292
+ """Findings: a lossy write and a read of the same property that seeds UI state."""
293
+ out = []
294
+ lossy = [w for w in writes if w["lossy"]]
295
+ seeds = [x for x in reads if x["seeds"]]
278
296
  for w in lossy:
279
297
  b = w["lossy"].get("bounds")
280
298
  for x in seeds:
@@ -287,14 +305,60 @@ def roundtrip(st: GraphStore, spec: str, include_tests: bool = False) -> dict:
287
305
  "read": x["at"], "seeds": x["seeds"], "wider_ranges": []}
288
306
  if b:
289
307
  f["wider_ranges"] = _ranges(src, x["at"].rsplit(":", 1)[0], tuple(b))[:5]
290
- res["findings"].append(f)
291
- for x in res["writes"]:
292
- x.pop("_span", None)
293
- for x in res["reads"]:
294
- x.pop("_src", None)
308
+ out.append(f)
309
+ return out
310
+
311
+
312
+ def roundtrip_all(st: GraphStore, include_tests: bool = False) -> dict:
313
+ """Every stored property in one pass (`cg roundtrip --all`): write sites are read once, file lines and callee
314
+ bounds are shared, and reads are looked at only for properties with a lossy write."""
315
+ root = st.meta().get("root")
316
+ globs, lsrc = lossy_names(st, root)
317
+ src = _Src(root)
318
+ stored = {r["id"]: r["fqn"] or r["name"] or r["id"] for r in st.q(
319
+ """SELECT id, fqn, name FROM nodes WHERE kind IN ('field', 'property') AND attrs LIKE '%"property": "stored"%'""")}
320
+
321
+ def rows(kind):
322
+ by: dict = {}
323
+ for r in st.q("""SELECT e.src, e.dst, e.kind, e.file, e.line, n.fqn FROM edges e JOIN nodes n ON n.id = e.src
324
+ WHERE e.kind = ? OR (e.kind = 'TEST_USES' AND e.attrs LIKE ?) ORDER BY e.dst, e.file, e.line""",
325
+ (kind, f'%"orig": "{kind}"%')):
326
+ if r["dst"] in stored and r["file"] and (include_tests or r["kind"] != "TEST_USES"):
327
+ d = dict(r)
328
+ d["test"] = r["kind"] == "TEST_USES"
329
+ by.setdefault(r["dst"], []).append(d)
330
+ return by
331
+
332
+ res = {"confidence": "heuristic", "lossy_sources": lsrc, "fields_with_writes": 0, "lossy_writes": 0, "findings": []}
333
+ writes = rows("WRITES_PROP")
334
+ res["fields_with_writes"] = len(writes)
335
+ lossy: dict = {}
336
+ for fid, ws in writes.items():
337
+ prop = re.split(r"[.$:#]", stored[fid])[-1]
338
+ sites = [_write_site(st, src, r, prop, globs) for r in ws]
339
+ if any(w["lossy"] for w in sites):
340
+ lossy[fid] = (prop, sites)
341
+ res["lossy_writes"] += sum(1 for w in sites if w["lossy"])
342
+ if lossy:
343
+ reads = rows("READS_PROP")
344
+ for fid, (prop, sites) in lossy.items():
345
+ for f in _pair(src, sites, [_read_site(st, src, r) for r in reads.get(fid, [])], prop):
346
+ res["findings"].append(dict(f, state=fid))
295
347
  return res
296
348
 
297
349
 
350
+ def render_all(res: dict) -> str:
351
+ out = [f"roundtrip --all — heuristic; lossy names: {', '.join(res['lossy_sources'])}",
352
+ f"{res['fields_with_writes']} stored properties with writes, {res['lossy_writes']} lossy writes, "
353
+ f"{len(res['findings'])} round trips"]
354
+ for f in res["findings"]:
355
+ out.append(f" {f['state']}: write @{f['write']} -> {'/'.join(f['lossy']['calls'])} @{f['lossy']['at']} "
356
+ f"-> read @{f['read']} -> seeds {f['seeds']}")
357
+ for g in f["wider_ranges"]:
358
+ out.append(f" un-narrowed range {_fmt(g['range'])} @{g['at']}: {g['code']}")
359
+ return "\n".join(out)
360
+
361
+
298
362
  def _read_back(src, w, x, prop) -> bool:
299
363
  """Is this read a read-back of the stored value rather than the same setup using what it just built? Not when
300
364
  the writer reads it a few lines later (`scrollBar = ScrollBarView(...)` then `addView(scrollBar)` in one init),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cg-code-graph
3
- Version: 0.23.9
3
+ Version: 0.23.11
4
4
  Summary: cg: a code graph of routes, calls, data access and platform boundaries for agents and humans (CLI + MCP server)
5
5
  Author: cyberchronos00
6
6
  License-Expression: MIT