entrygraph 0.1.128__tar.gz → 0.1.129__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 (341) hide show
  1. {entrygraph-0.1.128 → entrygraph-0.1.129}/PKG-INFO +1 -1
  2. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/_version.py +2 -2
  3. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/golang.py +46 -0
  4. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/javascript.py +139 -0
  5. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/ruby.py +131 -1
  6. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/frameworks.py +52 -0
  7. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/javascript.py +115 -0
  8. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_go.py +68 -0
  9. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_javascript.py +180 -0
  10. entrygraph-0.1.129/tests/test_entrypoints_ruby.py +167 -0
  11. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_frameworks.py +53 -0
  12. entrygraph-0.1.128/tests/test_entrypoints_ruby.py +0 -67
  13. {entrygraph-0.1.128 → entrygraph-0.1.129}/.claude/launch.json +0 -0
  14. {entrygraph-0.1.128 → entrygraph-0.1.129}/.dockerignore +0 -0
  15. {entrygraph-0.1.128 → entrygraph-0.1.129}/.github/workflows/ci.yml +0 -0
  16. {entrygraph-0.1.128 → entrygraph-0.1.129}/.github/workflows/release.yml +0 -0
  17. {entrygraph-0.1.128 → entrygraph-0.1.129}/.gitignore +0 -0
  18. {entrygraph-0.1.128 → entrygraph-0.1.129}/Dockerfile +0 -0
  19. {entrygraph-0.1.128 → entrygraph-0.1.129}/LICENSE +0 -0
  20. {entrygraph-0.1.128 → entrygraph-0.1.129}/README.md +0 -0
  21. {entrygraph-0.1.128 → entrygraph-0.1.129}/RELEASING.md +0 -0
  22. {entrygraph-0.1.128 → entrygraph-0.1.129}/pyproject.toml +0 -0
  23. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/__init__.py +0 -0
  24. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/__main__.py +0 -0
  25. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/analysis/__init__.py +0 -0
  26. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/analysis/facts.py +0 -0
  27. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/analysis/reaching.py +0 -0
  28. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/analysis/summaries.py +0 -0
  29. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/analysis/verify.py +0 -0
  30. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/api.py +0 -0
  31. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/cli/__init__.py +0 -0
  32. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/cli/main.py +0 -0
  33. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/cli/render.py +0 -0
  34. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/csharp.toml +0 -0
  35. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/go.toml +0 -0
  36. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/java.toml +0 -0
  37. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/javascript.toml +0 -0
  38. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/lib_javascript.toml +0 -0
  39. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/lib_python.toml +0 -0
  40. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/php.toml +0 -0
  41. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/python.toml +0 -0
  42. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/ruby.toml +0 -0
  43. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/rust.toml +0 -0
  44. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/db/__init__.py +0 -0
  45. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/db/engine.py +0 -0
  46. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/db/meta.py +0 -0
  47. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/db/models.py +0 -0
  48. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/db/queries.py +0 -0
  49. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/__init__.py +0 -0
  50. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/__init__.py +0 -0
  51. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/base.py +0 -0
  52. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/configs.py +0 -0
  53. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/csharp.py +0 -0
  54. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/graphql.py +0 -0
  55. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/java.py +0 -0
  56. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/php.py +0 -0
  57. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/python.py +0 -0
  58. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/rust.py +0 -0
  59. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/express_mounts.py +0 -0
  60. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/grpc_expand.py +0 -0
  61. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/manifests.py +0 -0
  62. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/taint.py +0 -0
  63. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/errors.py +0 -0
  64. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/__init__.py +0 -0
  65. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/base.py +0 -0
  66. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/csharp.py +0 -0
  67. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/golang.py +0 -0
  68. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/graphql.py +0 -0
  69. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/ir.py +0 -0
  70. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/java.py +0 -0
  71. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/php.py +0 -0
  72. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/python.py +0 -0
  73. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/registry.py +0 -0
  74. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/ruby.py +0 -0
  75. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/rust.py +0 -0
  76. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/__init__.py +0 -0
  77. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/hashing.py +0 -0
  78. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/lang.py +0 -0
  79. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/remote.py +0 -0
  80. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/testfiles.py +0 -0
  81. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/walker.py +0 -0
  82. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/graph/__init__.py +0 -0
  83. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/graph/adjacency.py +0 -0
  84. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/graph/cte.py +0 -0
  85. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/kinds.py +0 -0
  86. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/parsing/__init__.py +0 -0
  87. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/parsing/parsers.py +0 -0
  88. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/parsing/queries.py +0 -0
  89. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/pipeline/__init__.py +0 -0
  90. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/pipeline/scanner.py +0 -0
  91. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/pipeline/worker.py +0 -0
  92. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/pipeline/writer.py +0 -0
  93. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/py.typed +0 -0
  94. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/csharp/calls.scm +0 -0
  95. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/csharp/definitions.scm +0 -0
  96. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/csharp/imports.scm +0 -0
  97. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/go/calls.scm +0 -0
  98. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/go/definitions.scm +0 -0
  99. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/go/imports.scm +0 -0
  100. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/graphql/definitions.scm +0 -0
  101. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/java/calls.scm +0 -0
  102. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/java/definitions.scm +0 -0
  103. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/java/imports.scm +0 -0
  104. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/javascript/calls.scm +0 -0
  105. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/javascript/definitions.scm +0 -0
  106. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/javascript/imports.scm +0 -0
  107. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/php/calls.scm +0 -0
  108. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/php/definitions.scm +0 -0
  109. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/php/imports.scm +0 -0
  110. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/python/calls.scm +0 -0
  111. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/python/definitions.scm +0 -0
  112. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/python/imports.scm +0 -0
  113. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/ruby/calls.scm +0 -0
  114. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/ruby/definitions.scm +0 -0
  115. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/ruby/imports.scm +0 -0
  116. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/rust/calls.scm +0 -0
  117. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/rust/definitions.scm +0 -0
  118. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/rust/imports.scm +0 -0
  119. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/__init__.py +0 -0
  120. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/bindings.py +0 -0
  121. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/externals.py +0 -0
  122. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/hierarchy.py +0 -0
  123. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/resolver.py +0 -0
  124. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/symbol_table.py +0 -0
  125. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/results.py +0 -0
  126. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/__init__.py +0 -0
  127. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/app.py +0 -0
  128. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/appdb.py +0 -0
  129. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/auth/__init__.py +0 -0
  130. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/auth/csrf.py +0 -0
  131. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/auth/deps.py +0 -0
  132. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/auth/oidc.py +0 -0
  133. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/auth/sessions.py +0 -0
  134. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/cli.py +0 -0
  135. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/config.py +0 -0
  136. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/jobs/__init__.py +0 -0
  137. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/jobs/handlers.py +0 -0
  138. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/jobs/progress.py +0 -0
  139. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/jobs/runner.py +0 -0
  140. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/models.py +0 -0
  141. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/__init__.py +0 -0
  142. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/graph.py +0 -0
  143. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/jobs.py +0 -0
  144. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/keys.py +0 -0
  145. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/meta.py +0 -0
  146. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/repos.py +0 -0
  147. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/serializers.py +0 -0
  148. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/static/assets/index-DGvRIX6y.css +0 -0
  149. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/static/assets/index-VD0Fnuj0.js +0 -0
  150. {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/static/index.html +0 -0
  151. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/conftest.py +0 -0
  152. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/aspnet_app/Controllers/ReportsController.cs +0 -0
  153. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/aspnet_app/Program.cs +0 -0
  154. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/aspnet_app/Services/ReportService.cs +0 -0
  155. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/aspnet_app/app.csproj +0 -0
  156. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/minimalapi_app/Program.cs +0 -0
  157. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/minimalapi_app/app.csproj +0 -0
  158. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/catalog_full_app/go.mod +0 -0
  159. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/catalog_full_app/main.go +0 -0
  160. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/gin_app/go.mod +0 -0
  161. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/gin_app/main.go +0 -0
  162. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/gin_app/service.go +0 -0
  163. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/grpc_app/server.go +0 -0
  164. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/nethttp_app/go.mod +0 -0
  165. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/nethttp_app/main.go +0 -0
  166. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/reaching/app.go +0 -0
  167. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/graphql/schema_app/schema/custom_roots.gql +0 -0
  168. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/graphql/schema_app/schema/schema.graphql +0 -0
  169. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/methodref_app/pom.xml +0 -0
  170. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/methodref_app/src/main/java/com/example/App.java +0 -0
  171. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/reaching/App.java +0 -0
  172. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/spring_app/pom.xml +0 -0
  173. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/spring_app/src/main/java/com/example/Application.java +0 -0
  174. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportRunner.java +0 -0
  175. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportService.java +0 -0
  176. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/spring_app/src/main/java/com/example/UserController.java +0 -0
  177. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/commonjs_app/server.js +0 -0
  178. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/express_app/package.json +0 -0
  179. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/express_app/src/routes.js +0 -0
  180. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/express_app/src/services.js +0 -0
  181. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/express_channels_app/package.json +0 -0
  182. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/express_channels_app/src/routes.js +0 -0
  183. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/php/laravel_app/app/Http/Controllers/ReportController.php +0 -0
  184. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/php/laravel_app/artisan +0 -0
  185. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/php/laravel_app/composer.json +0 -0
  186. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/php/laravel_app/routes/web.php +0 -0
  187. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/php/reaching/app.php +0 -0
  188. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/precision/java/App.java +0 -0
  189. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/precision/javascript/app.js +0 -0
  190. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/precision/php/app.php +0 -0
  191. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/precision/python/app.py +0 -0
  192. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/precision/ruby/app.rb +0 -0
  193. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/chained_sinks/app.py +0 -0
  194. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/channels_app/app.py +0 -0
  195. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/cli_app/cli.py +0 -0
  196. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/cli_app/tool.py +0 -0
  197. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/fastapi_app/main.py +0 -0
  198. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/app/__init__.py +0 -0
  199. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/app/db.py +0 -0
  200. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/app/routes.py +0 -0
  201. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/app/services.py +0 -0
  202. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/cli.py +0 -0
  203. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/requirements.txt +0 -0
  204. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/fuzzy_sink/app.py +0 -0
  205. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/heal_fidelity/caller.py +0 -0
  206. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/heal_fidelity/worker.py +0 -0
  207. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/may_continue/app.py +0 -0
  208. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/reaching_defs/app.py +0 -0
  209. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/sanitizer/app.py +0 -0
  210. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/source_split/app.py +0 -0
  211. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/speculative_frontier/app/__init__.py +0 -0
  212. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/speculative_frontier/app/dynamo.py +0 -0
  213. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/speculative_frontier/app/routes.py +0 -0
  214. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/speculative_frontier/app/scope.py +0 -0
  215. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/speculative_frontier/requirements.txt +0 -0
  216. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/taint_source/handler.py +0 -0
  217. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/ruby/rails_sql/app.rb +0 -0
  218. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/ruby/reaching/app.rb +0 -0
  219. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/ruby/sinatra_app/Gemfile +0 -0
  220. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/ruby/sinatra_app/app.rb +0 -0
  221. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/ruby/sinatra_app/services/runner.rb +0 -0
  222. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/axum_app/Cargo.toml +0 -0
  223. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/axum_app/src/handlers.rs +0 -0
  224. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/axum_app/src/main.rs +0 -0
  225. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/axum_callback_app/Cargo.toml +0 -0
  226. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/axum_callback_app/src/main.rs +0 -0
  227. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/catalog_full_app/Cargo.toml +0 -0
  228. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/catalog_full_app/src/main.rs +0 -0
  229. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/scoped_sink_app/Cargo.toml +0 -0
  230. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/scoped_sink_app/src/main.rs +0 -0
  231. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_api.py +0 -0
  232. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_appdb.py +0 -0
  233. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_bindings.py +0 -0
  234. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_catalog_full.py +0 -0
  235. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_cli.py +0 -0
  236. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_commonjs.py +0 -0
  237. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_csharp_callbacks.py +0 -0
  238. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_cte_bounds.py +0 -0
  239. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_engine_pragmas.py +0 -0
  240. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoint_expansion.py +0 -0
  241. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints.py +0 -0
  242. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_csharp.py +0 -0
  243. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_php.py +0 -0
  244. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_python.py +0 -0
  245. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_rust.py +0 -0
  246. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_csharp.py +0 -0
  247. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_go.py +0 -0
  248. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_graphql.py +0 -0
  249. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_java.py +0 -0
  250. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_javascript.py +0 -0
  251. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_php.py +0 -0
  252. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_python.py +0 -0
  253. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_ruby.py +0 -0
  254. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_rust.py +0 -0
  255. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_framework_sources.py +0 -0
  256. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_fuzzy_sink.py +0 -0
  257. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_go_callbacks.py +0 -0
  258. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_grpc_expand.py +0 -0
  259. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_hardening.py +0 -0
  260. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_heal_fidelity.py +0 -0
  261. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_incremental.py +0 -0
  262. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_indexer.py +0 -0
  263. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_interprocedural.py +0 -0
  264. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_java_callbacks.py +0 -0
  265. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_lang.py +0 -0
  266. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_manifests.py +0 -0
  267. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_may_continue.py +0 -0
  268. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_models.py +0 -0
  269. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_multirepo.py +0 -0
  270. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_pool.py +0 -0
  271. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_precision.py +0 -0
  272. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_reachability.py +0 -0
  273. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_reaching.py +0 -0
  274. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_reaching_e2e.py +0 -0
  275. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_reaching_languages.py +0 -0
  276. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_registry_cache.py +0 -0
  277. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_remote.py +0 -0
  278. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_render.py +0 -0
  279. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_resolver.py +0 -0
  280. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_rust_callbacks.py +0 -0
  281. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_rust_scoped_sinks.py +0 -0
  282. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_scanner_progress.py +0 -0
  283. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_server_api.py +0 -0
  284. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_server_auth.py +0 -0
  285. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_server_config.py +0 -0
  286. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_server_jobs.py +0 -0
  287. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_server_keys.py +0 -0
  288. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_sink_catalog.py +0 -0
  289. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_source_keys.py +0 -0
  290. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_source_split.py +0 -0
  291. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_speculative_frontier.py +0 -0
  292. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_taint.py +0 -0
  293. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_taint_sources.py +0 -0
  294. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_testfiles.py +0 -0
  295. {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_walker.py +0 -0
  296. {entrygraph-0.1.128 → entrygraph-0.1.129}/uv.lock +0 -0
  297. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/index.html +0 -0
  298. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/package-lock.json +0 -0
  299. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/package.json +0 -0
  300. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/App.tsx +0 -0
  301. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/api/client.test.ts +0 -0
  302. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/api/client.ts +0 -0
  303. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/api/queries.ts +0 -0
  304. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/api/types.ts +0 -0
  305. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/auth/AuthProvider.tsx +0 -0
  306. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/auth/LoginPage.tsx +0 -0
  307. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/components/EmptyState.tsx +0 -0
  308. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/components/InfoPopover.tsx +0 -0
  309. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/components/ui.tsx +0 -0
  310. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/dashboard/DashboardPage.tsx +0 -0
  311. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/EntrypointsTab.tsx +0 -0
  312. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/OverviewTab.tsx +0 -0
  313. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/RepoLayout.tsx +0 -0
  314. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/SymbolDetailPane.tsx +0 -0
  315. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/SymbolsTab.tsx +0 -0
  316. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/graph/CallGraphCanvas.tsx +0 -0
  317. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/graph/CallGraphTab.tsx +0 -0
  318. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/graph/graphLayout.test.ts +0 -0
  319. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/graph/graphLayout.ts +0 -0
  320. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/graph/graphStyle.ts +0 -0
  321. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/AdvancedOptions.tsx +0 -0
  322. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/PathFlow.tsx +0 -0
  323. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/ReachabilityTab.tsx +0 -0
  324. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/categories.ts +0 -0
  325. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/queryEcho.test.ts +0 -0
  326. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/queryEcho.ts +0 -0
  327. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/jobs/JobBadges.tsx +0 -0
  328. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/jobs/JobToaster.tsx +0 -0
  329. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/jobs/JobsPage.tsx +0 -0
  330. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/jobs/jobName.ts +0 -0
  331. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/jobs/useJob.ts +0 -0
  332. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/repos/AddRepoWizard.tsx +0 -0
  333. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/repos/RepoListPage.tsx +0 -0
  334. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/repos/wizard.test.ts +0 -0
  335. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/repos/wizardLogic.ts +0 -0
  336. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/settings/SettingsPage.tsx +0 -0
  337. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/lib/glossary.ts +0 -0
  338. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/main.tsx +0 -0
  339. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/styles.css +0 -0
  340. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/tsconfig.json +0 -0
  341. {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/vite.config.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: entrygraph
3
- Version: 0.1.128
3
+ Version: 0.1.129
4
4
  Summary: Language-agnostic code graph: query symbols, entrypoints, and source-to-sink call paths from a SQLite index
5
5
  Project-URL: Repository, https://github.com/brettbergin/entrygraph
6
6
  Author-email: Brett Bergin <brettberginbc@yahoo.com>
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.1.128'
22
- __version_tuple__ = version_tuple = (0, 1, 128)
21
+ __version__ = version = '0.1.129'
22
+ __version_tuple__ = version_tuple = (0, 1, 129)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -323,6 +323,47 @@ def _grpc_services(x: FileExtraction) -> list[EntrypointHint]:
323
323
  return hints
324
324
 
325
325
 
326
+ # gqlgen's generated wiring instantiates unexported per-type resolver structs
327
+ # (`type queryResolver struct{ *Resolver }`); user code implements the schema as
328
+ # exported methods on them in resolver.go / *.resolvers.go. The lowercase-first
329
+ # anchor excludes both the exported root `Resolver` (whose Query()/Mutation()
330
+ # methods are wiring, not fields) and generated executionContext methods.
331
+ _GQLGEN_RECEIVER = re.compile(r"^([a-z]\w*)Resolver$")
332
+ _GQL_OPERATIONS = {"Query": "query", "Mutation": "mutation", "Subscription": "subscription"}
333
+
334
+
335
+ def _gqlgen_resolvers(x: FileExtraction) -> list[EntrypointHint]:
336
+ hints = []
337
+ for symbol in x.symbols:
338
+ if symbol.kind is not SymbolKind.METHOD or not symbol.is_exported:
339
+ continue
340
+ parent = (symbol.parent_qualified_name or "").rsplit(".", 1)[-1]
341
+ m = _GQLGEN_RECEIVER.match(parent)
342
+ if m is None:
343
+ continue
344
+ # every gqlgen resolver method takes ctx first — cheap precision guard
345
+ if "ctx context.Context" not in (symbol.signature or ""):
346
+ continue
347
+ parent_type = m.group(1)[:1].upper() + m.group(1)[1:]
348
+ field = symbol.name[:1].lower() + symbol.name[1:] # gqlgen lowerCamels fields
349
+ hints.append(
350
+ EntrypointHint(
351
+ rule_id="go.gqlgen.resolver",
352
+ kind=EntrypointKind.GRAPHQL_RESOLVER,
353
+ handler_qualified_name=symbol.qualified_name,
354
+ route=f"{parent_type}.{field}",
355
+ name=field,
356
+ framework="gqlgen",
357
+ span=symbol.span,
358
+ metadata={
359
+ "operation": _GQL_OPERATIONS.get(parent_type, "field"),
360
+ "parent_type": parent_type,
361
+ },
362
+ )
363
+ )
364
+ return hints
365
+
366
+
326
367
  register(EntrypointRule("go.core.main", "go", None, EntrypointKind.MAIN, _go_main))
327
368
  register(
328
369
  EntrypointRule("go.nethttp.route", "go", "net/http", EntrypointKind.HTTP_ROUTE, _nethttp_routes)
@@ -343,3 +384,8 @@ register(
343
384
  register(
344
385
  EntrypointRule("go.grpc.service", "go", "grpc-go", EntrypointKind.RPC_HANDLER, _grpc_services)
345
386
  )
387
+ register(
388
+ EntrypointRule(
389
+ "go.gqlgen.resolver", "go", "gqlgen", EntrypointKind.GRAPHQL_RESOLVER, _gqlgen_resolvers
390
+ )
391
+ )
@@ -207,6 +207,118 @@ def _nest_routes(x: FileExtraction) -> list[EntrypointHint]:
207
207
  return hints
208
208
 
209
209
 
210
+ _GQL_RESOLVER_CLASS = re.compile(r"^@Resolver\b")
211
+ _GQL_FIELD_DECORATOR = re.compile(r"^@(Query|Mutation|Subscription|ResolveField|FieldResolver)\b")
212
+ _GQL_NAME_OPT = re.compile(r"""name\s*:\s*['"]([\w.]+)['"]""")
213
+ _GQL_TYPE_THUNK = re.compile(r"=>\s*\[?\s*([A-Za-z_$][\w$]*)")
214
+ _GQL_OPERATIONS = {"Query": "query", "Mutation": "mutation", "Subscription": "subscription"}
215
+
216
+
217
+ def _gql_decorator_resolvers(framework: str):
218
+ """@Resolver classes with @Query/@Mutation/@Subscription/@ResolveField methods.
219
+
220
+ NestJS-GraphQL and TypeGraphQL share this shape (TypeGraphQL says
221
+ @FieldResolver where Nest says @ResolveField). Requiring the enclosing class
222
+ to carry @Resolver keeps other libraries' @Query decorators out; when both
223
+ frameworks are detected the scanner's hint dedup keeps one row.
224
+ """
225
+
226
+ def matcher(x: FileExtraction) -> list[EntrypointHint]:
227
+ # resolver class qname -> the parent type it resolves for:
228
+ # @Resolver('User') / @Resolver(() => User) / bare -> class name - "Resolver"
229
+ targets: dict[str, str] = {}
230
+ for symbol in x.symbols:
231
+ if symbol.kind is not SymbolKind.CLASS:
232
+ continue
233
+ for decorator in symbol.decorators:
234
+ if _GQL_RESOLVER_CLASS.match(decorator):
235
+ thunk = _GQL_TYPE_THUNK.search(decorator)
236
+ targets[symbol.qualified_name] = (
237
+ first_string_arg(decorator)
238
+ or (thunk.group(1) if thunk else None)
239
+ or symbol.name.removesuffix("Resolver")
240
+ or symbol.name
241
+ )
242
+ if not targets:
243
+ return []
244
+ hints = []
245
+ for symbol in x.symbols:
246
+ if symbol.kind is not SymbolKind.METHOD or symbol.parent_qualified_name not in targets:
247
+ continue
248
+ for decorator in symbol.decorators:
249
+ m = _GQL_FIELD_DECORATOR.match(decorator)
250
+ if not m:
251
+ continue
252
+ kind = m.group(1)
253
+ if kind in ("ResolveField", "FieldResolver"):
254
+ parent = targets[symbol.parent_qualified_name]
255
+ field = first_string_arg(decorator) or symbol.name
256
+ operation = "field"
257
+ else:
258
+ parent, operation = kind, _GQL_OPERATIONS[kind]
259
+ named = _GQL_NAME_OPT.search(decorator)
260
+ field = named.group(1) if named else symbol.name
261
+ hints.append(
262
+ EntrypointHint(
263
+ rule_id=f"javascript.{framework}.resolver",
264
+ kind=EntrypointKind.GRAPHQL_RESOLVER,
265
+ handler_qualified_name=symbol.qualified_name,
266
+ route=f"{parent}.{field}",
267
+ name=field,
268
+ framework=framework,
269
+ span=symbol.span,
270
+ metadata={"operation": operation, "parent_type": parent},
271
+ )
272
+ )
273
+ return hints
274
+
275
+ return matcher
276
+
277
+
278
+ _RESOLVERS_VAR = re.compile(r"resolvers?$", re.IGNORECASE)
279
+
280
+
281
+ def _apollo_resolvers(x: FileExtraction) -> list[EntrypointHint]:
282
+ """Apollo resolver-map objects: `const resolvers = { Query: { user() {} } }`.
283
+
284
+ Keys off the object-literal method symbols the JS extractor emits with
285
+ key-path qnames. Query/Mutation/Subscription parents match anywhere; other
286
+ TitleCase parents (type-field resolvers like `User: { posts }`) only inside
287
+ a *resolvers*-named or `Resolvers`-typed container. Shorthand *references*
288
+ (`Query: { user }` where `user` is imported) emit no symbol and are a known
289
+ gap.
290
+ """
291
+ typed_vars = {
292
+ b.name for b in x.bindings if b.kind == "declared" and b.type_text.endswith("Resolvers")
293
+ }
294
+ hints = []
295
+ for symbol in x.symbols:
296
+ if symbol.kind is not SymbolKind.METHOD or not symbol.parent_qualified_name:
297
+ continue
298
+ parts = symbol.parent_qualified_name.split(".")
299
+ parent_type = parts[-1]
300
+ container = parts[-2] if len(parts) >= 2 else ""
301
+ operation = _GQL_OPERATIONS.get(parent_type)
302
+ if operation is None:
303
+ in_map = bool(_RESOLVERS_VAR.search(container)) or container in typed_vars
304
+ if not in_map or not parent_type[:1].isupper():
305
+ continue
306
+ operation = "field"
307
+ hints.append(
308
+ EntrypointHint(
309
+ rule_id="javascript.apollo.resolver",
310
+ kind=EntrypointKind.GRAPHQL_RESOLVER,
311
+ handler_qualified_name=symbol.qualified_name,
312
+ route=f"{parent_type}.{symbol.name}",
313
+ name=symbol.name,
314
+ framework="apollo",
315
+ span=symbol.span,
316
+ metadata={"operation": operation, "parent_type": parent_type},
317
+ )
318
+ )
319
+ return hints
320
+
321
+
210
322
  def _next_handlers(x: FileExtraction) -> list[EntrypointHint]:
211
323
  """Next.js App Router route handlers in app/**/route.{ts,js}.
212
324
 
@@ -382,3 +494,30 @@ register(
382
494
  _next_pages_handlers,
383
495
  )
384
496
  )
497
+ register(
498
+ EntrypointRule(
499
+ "javascript.apollo.resolver",
500
+ "javascript",
501
+ "apollo",
502
+ EntrypointKind.GRAPHQL_RESOLVER,
503
+ _apollo_resolvers,
504
+ )
505
+ )
506
+ register(
507
+ EntrypointRule(
508
+ "javascript.nestjs-graphql.resolver",
509
+ "javascript",
510
+ "nestjs-graphql",
511
+ EntrypointKind.GRAPHQL_RESOLVER,
512
+ _gql_decorator_resolvers("nestjs-graphql"),
513
+ )
514
+ )
515
+ register(
516
+ EntrypointRule(
517
+ "javascript.type-graphql.resolver",
518
+ "javascript",
519
+ "type-graphql",
520
+ EntrypointKind.GRAPHQL_RESOLVER,
521
+ _gql_decorator_resolvers("type-graphql"),
522
+ )
523
+ )
@@ -9,13 +9,15 @@ level) — the block body's calls still resolve as ordinary edges.
9
9
 
10
10
  from __future__ import annotations
11
11
 
12
+ import re
13
+
12
14
  from entrygraph.detect.entrypoints.base import (
13
15
  EntrypointRule,
14
16
  first_string_arg,
15
17
  register,
16
18
  )
17
19
  from entrygraph.extract.ir import EntrypointHint, FileExtraction
18
- from entrygraph.kinds import EntrypointKind
20
+ from entrygraph.kinds import EntrypointKind, SymbolKind
19
21
 
20
22
  _SINATRA_VERBS = frozenset({"get", "post", "put", "delete", "patch"})
21
23
  _RAILS_VERBS = frozenset({"get", "post", "put", "patch", "delete", "resources", "resource", "root"})
@@ -155,6 +157,116 @@ def _sidekiq_workers(x: FileExtraction) -> list[EntrypointHint]:
155
157
  return hints
156
158
 
157
159
 
160
+ # ---------------- graphql-ruby ----------------
161
+
162
+ # base-class shapes marking a graphql-ruby type / mutation / resolver class
163
+ _GQL_TYPE_BASE = re.compile(r"(::BaseObject$|^GraphQL::Schema::Object$|^Types::Base)")
164
+ _GQL_MUTATION_BASE = re.compile(r"(BaseMutation$|^GraphQL::Schema::(RelayClassic)?Mutation$)")
165
+ _GQL_RESOLVER_BASE = re.compile(r"(BaseResolver$|^GraphQL::Schema::Resolver$)")
166
+ _GQL_RESOLVER_OPT = re.compile(r"(?:resolver|mutation)\s*(?:=>|:)\s*([A-Za-z_][\w:]*)")
167
+ _GQL_ROOT_OPERATIONS = {
168
+ "QueryType": "query",
169
+ "MutationType": "mutation",
170
+ "SubscriptionType": "subscription",
171
+ }
172
+
173
+
174
+ def _camelize(name: str) -> str:
175
+ """graphql-ruby camelizes snake_case field names by default."""
176
+ head, *rest = name.split("_")
177
+ return head + "".join(s.title() for s in rest)
178
+
179
+
180
+ def _graphql_field_hints(x: FileExtraction) -> list[EntrypointHint]:
181
+ """`field :posts, ...` declarations in graphql-ruby type classes.
182
+
183
+ The convention handler is an instance method named after the field; a
184
+ ``resolver:``/``mutation:`` option delegates to a class instead (recorded in
185
+ metadata, handler left unbound for the linking pass / span fallback).
186
+ """
187
+ type_classes = [
188
+ s
189
+ for s in x.symbols
190
+ if s.kind is SymbolKind.CLASS and any(_GQL_TYPE_BASE.search(b) for b in s.bases)
191
+ ]
192
+ if not type_classes:
193
+ return []
194
+ # innermost-first so a nested class wins span containment
195
+ type_classes.sort(key=lambda s: s.span.start_line, reverse=True)
196
+ hints = []
197
+ for ref in x.references:
198
+ if ref.kind != "call" or ref.receiver_text is not None or ref.callee_name != "field":
199
+ continue
200
+ if not ref.arg_preview:
201
+ continue
202
+ cls = next(
203
+ (
204
+ s
205
+ for s in type_classes
206
+ if s.span.start_line <= ref.span.start_line <= s.span.end_line
207
+ ),
208
+ None,
209
+ )
210
+ if cls is None:
211
+ continue
212
+ name = first_string_arg("(" + ref.arg_preview.lstrip("("))
213
+ if name is None:
214
+ # symbol first arg like `:posts` — same fallback the rake rule uses
215
+ stripped = ref.arg_preview.strip("()").lstrip(":").split(",", 1)[0]
216
+ name = stripped.split(" ", 1)[0].strip() or None
217
+ if not name:
218
+ continue
219
+ operation = _GQL_ROOT_OPERATIONS.get(cls.name, "field")
220
+ parent = cls.name.removesuffix("Type") or cls.name
221
+ resolver_opt = _GQL_RESOLVER_OPT.search(ref.arg_preview)
222
+ metadata: dict = {"operation": operation, "parent_type": parent}
223
+ if resolver_opt:
224
+ metadata["resolver_class"] = resolver_opt.group(1)
225
+ hints.append(
226
+ EntrypointHint(
227
+ rule_id="ruby.graphql-ruby.field",
228
+ kind=EntrypointKind.GRAPHQL_RESOLVER,
229
+ handler_qualified_name=None if resolver_opt else f"{cls.qualified_name}.{name}",
230
+ route=f"{parent}.{_camelize(name)}",
231
+ name=name,
232
+ framework="graphql-ruby",
233
+ span=ref.span,
234
+ metadata=metadata,
235
+ )
236
+ )
237
+ return hints
238
+
239
+
240
+ def _graphql_resolver_classes(x: FileExtraction) -> list[EntrypointHint]:
241
+ """Mutation/Resolver classes: `class CreateOrder < BaseMutation` with a
242
+ `resolve` method. Mutations get a best-effort `Mutation.<camelized name>`
243
+ route (the default when mounted as `field :create_order, mutation: ...`);
244
+ plain resolvers leave the route to the mounting field's hint."""
245
+ hints = []
246
+ for cls in x.symbols:
247
+ if cls.kind is not SymbolKind.CLASS:
248
+ continue
249
+ if any(_GQL_MUTATION_BASE.search(b) for b in cls.bases):
250
+ operation, route = "mutation", f"Mutation.{cls.name[:1].lower()}{cls.name[1:]}"
251
+ elif any(_GQL_RESOLVER_BASE.search(b) for b in cls.bases):
252
+ operation, route = "field", None
253
+ else:
254
+ continue
255
+ hints.append(
256
+ EntrypointHint(
257
+ rule_id="ruby.graphql-ruby.resolver",
258
+ kind=EntrypointKind.GRAPHQL_RESOLVER,
259
+ handler_qualified_name=f"{cls.qualified_name}.resolve",
260
+ route=route,
261
+ name=cls.name,
262
+ framework="graphql-ruby",
263
+ span=cls.span,
264
+ metadata={"operation": operation, "parent_type": "Mutation" if route else None},
265
+ )
266
+ )
267
+ return hints
268
+
269
+
158
270
  register(
159
271
  EntrypointRule(
160
272
  "ruby.sinatra.route", "ruby", "sinatra", EntrypointKind.HTTP_ROUTE, _sinatra_routes
@@ -170,3 +282,21 @@ register(
170
282
  EntrypointRule("ruby.rails.routes", "ruby", "rails", EntrypointKind.HTTP_ROUTE, _rails_routes)
171
283
  )
172
284
  register(EntrypointRule("ruby.rake.task", "ruby", "rake", EntrypointKind.CLI_COMMAND, _rake_tasks))
285
+ register(
286
+ EntrypointRule(
287
+ "ruby.graphql-ruby.field",
288
+ "ruby",
289
+ "graphql-ruby",
290
+ EntrypointKind.GRAPHQL_RESOLVER,
291
+ _graphql_field_hints,
292
+ )
293
+ )
294
+ register(
295
+ EntrypointRule(
296
+ "ruby.graphql-ruby.resolver",
297
+ "ruby",
298
+ "graphql-ruby",
299
+ EntrypointKind.GRAPHQL_RESOLVER,
300
+ _graphql_resolver_classes,
301
+ )
302
+ )
@@ -546,6 +546,58 @@ FRAMEWORKS: list[FrameworkSpec] = [
546
546
  FrameworkSignal("import", "tokio", 0.7),
547
547
  ),
548
548
  ),
549
+ # --- GraphQL server frameworks. Deliberately no signal for the bare npm
550
+ # `graphql` package: it is a peer dep of every GraphQL *client* (apollo-client,
551
+ # urql, relay) and would fire on repos that only consume GraphQL.
552
+ FrameworkSpec(
553
+ "apollo",
554
+ "javascript",
555
+ (
556
+ FrameworkSignal("manifest_dep", "@apollo/server", 0.8), # v4
557
+ FrameworkSignal("manifest_dep", "apollo-server*", 0.8), # v2/3 + -express/-koa/...
558
+ FrameworkSignal("manifest_dep", "@apollo/subgraph", 0.6),
559
+ FrameworkSignal("import", "@apollo/server*", 0.7),
560
+ FrameworkSignal("import", "apollo-server*", 0.7),
561
+ ),
562
+ ),
563
+ FrameworkSpec(
564
+ "nestjs-graphql",
565
+ "javascript",
566
+ (
567
+ FrameworkSignal("manifest_dep", "@nestjs/graphql", 0.8),
568
+ FrameworkSignal("import", "@nestjs/graphql", 0.7),
569
+ ),
570
+ ),
571
+ FrameworkSpec(
572
+ "type-graphql",
573
+ "javascript",
574
+ (
575
+ FrameworkSignal("manifest_dep", "type-graphql", 0.8),
576
+ FrameworkSignal("import", "type-graphql", 0.7),
577
+ ),
578
+ ),
579
+ FrameworkSpec(
580
+ "graphql-ruby",
581
+ "ruby",
582
+ (
583
+ # the `graphql` gem IS graphql-ruby (the server library); the client
584
+ # ships as `graphql-client`, so the exact-name match stays quiet for
585
+ # consumer-only repos. `require 'graphql/client'` also surfaces top
586
+ # segment "graphql", so the import signal is corroborative-only.
587
+ FrameworkSignal("manifest_dep", "graphql", 0.8),
588
+ FrameworkSignal("import", "graphql", 0.4),
589
+ FrameworkSignal("file_presence", "*app/graphql/*", 0.4),
590
+ ),
591
+ ),
592
+ FrameworkSpec(
593
+ "gqlgen",
594
+ "go",
595
+ (
596
+ FrameworkSignal("manifest_dep", "github.com/99designs/gqlgen", 0.8),
597
+ FrameworkSignal("import", "github.com/99designs/gqlgen*", 0.7),
598
+ FrameworkSignal("file_presence", "*gqlgen.y*ml", 0.6),
599
+ ),
600
+ ),
549
601
  ]
550
602
 
551
603
 
@@ -108,6 +108,7 @@ class JavaScriptExtractor:
108
108
  error_count=1 if root.has_error else 0,
109
109
  )
110
110
  self._definitions(root, ctx, out)
111
+ self._object_literal_callables(root, ctx, out)
111
112
  self._imports(root, ctx, out)
112
113
  self._calls(root, ctx, out)
113
114
  self._bindings(root, ctx, out)
@@ -239,6 +240,8 @@ class JavaScriptExtractor:
239
240
  for node in caps.get("def.function", []):
240
241
  self._add_callable(node, ctx, out, SymbolKind.FUNCTION)
241
242
  for node in caps.get("def.method", []):
243
+ if node.parent is not None and node.parent.type == "object":
244
+ continue # object-literal shorthand: emitted with a key-path qname below
242
245
  self._add_callable(node, ctx, out, SymbolKind.METHOD)
243
246
 
244
247
  for node in caps.get("def.class", []):
@@ -340,6 +343,104 @@ class JavaScriptExtractor:
340
343
  )
341
344
  )
342
345
 
346
+ # ---------------- object-literal callables ----------------
347
+
348
+ def _object_literal_callables(self, root, ctx, out) -> None:
349
+ """Function-valued object-literal properties become METHOD symbols with
350
+ key-path qnames: `const resolvers = { Query: { user: () => {} } }` ->
351
+ ``mod.resolvers.Query.user``. Covers shorthand methods (`user() {}`) and
352
+ arrow/function-expression pairs; makes Apollo-style resolver maps (and any
353
+ handler-map idiom) addressable/bindable. Members with no stable path
354
+ anchor (`module.exports = { run() {} }`) keep their previous flat qname.
355
+ """
356
+ stack = [root]
357
+ while stack:
358
+ n = stack.pop()
359
+ stack.extend(n.named_children)
360
+ if n.type == "method_definition" and n.parent is not None and n.parent.type == "object":
361
+ name = self._key_name(n.child_by_field_name("name"))
362
+ if name is not None:
363
+ self._emit_object_member(n, name, ctx, out)
364
+ elif n.type == "pair":
365
+ value = n.child_by_field_name("value")
366
+ if value is None or value.type not in ("arrow_function", "function_expression"):
367
+ continue
368
+ name = self._key_name(n.child_by_field_name("key"))
369
+ if name is not None:
370
+ self._emit_object_member(n, name, ctx, out)
371
+
372
+ def _emit_object_member(self, node, name, ctx, out) -> None:
373
+ parent_path = self._object_parent_path(node, ctx)
374
+ if parent_path is None:
375
+ qname, parent_q = self._qualify(node, name, ctx)
376
+ else:
377
+ qname, parent_q = f"{parent_path}.{name}", parent_path
378
+ out.symbols.append(
379
+ RawSymbol(
380
+ kind=SymbolKind.METHOD,
381
+ name=name,
382
+ qualified_name=qname,
383
+ span=span_of(node),
384
+ parent_qualified_name=parent_q,
385
+ signature=self._signature(node),
386
+ is_exported=self._exported(node),
387
+ return_type_text=self._return_type_text(node),
388
+ )
389
+ )
390
+
391
+ def _key_name(self, key_node) -> str | None:
392
+ if key_node is None:
393
+ return None
394
+ if key_node.type == "property_identifier":
395
+ return node_text(key_node)
396
+ if key_node.type == "string":
397
+ text = node_text(key_node).strip("'\"`")
398
+ return text or None
399
+ return None # computed / numeric keys carry no stable name
400
+
401
+ # An object member's path stops at 4 key segments — deeper nesting is config
402
+ # data, not a handler map.
403
+ _OBJECT_PATH_MAX_KEYS = 4
404
+
405
+ def _object_parent_path(self, node, ctx) -> str | None:
406
+ """Dotted parent path of an object-literal member: enclosing pair keys up
407
+ to a variable/assignment anchor (`const resolvers = ...` -> resolvers).
408
+ An object passed inline as a call argument uses its keys alone
409
+ (`new ApolloServer({resolvers: {Query: ...}})` -> mod.resolvers.Query).
410
+ None when there is neither anchor nor keys (flat-qname fallback).
411
+ """
412
+ keys: list[str] = []
413
+ anchor: str | None = None
414
+ current = node.parent
415
+ while current is not None:
416
+ t = current.type
417
+ if t in ("object", "parenthesized_expression"):
418
+ current = current.parent
419
+ continue
420
+ if t == "pair":
421
+ key = self._key_name(current.child_by_field_name("key"))
422
+ if key is None or len(keys) >= self._OBJECT_PATH_MAX_KEYS:
423
+ return None
424
+ keys.append(key)
425
+ current = current.parent
426
+ continue
427
+ if t == "variable_declarator":
428
+ name = current.child_by_field_name("name")
429
+ if name is not None and name.type == "identifier":
430
+ anchor = node_text(name)
431
+ break
432
+ if t == "assignment_expression":
433
+ left = current.child_by_field_name("left")
434
+ if left is not None and left.type == "identifier":
435
+ anchor = node_text(left)
436
+ break
437
+ break # arguments / export_statement / return_statement / program ...
438
+ if not keys and anchor is None:
439
+ return None
440
+ segments = ([anchor] if anchor else []) + list(reversed(keys))
441
+ chain = self._scope_chain(node)
442
+ return ".".join([ctx.module_path, *chain, *segments])
443
+
343
444
  # value-preserving generic wrappers: `Promise<T>` (awaited) and `Awaited<T>`
344
445
  # still denote a T; `Array<T>`/`Readonly<T>` do not, so they don't unwrap.
345
446
  _RETURN_TYPE_UNWRAP = frozenset({"Promise", "Awaited"})
@@ -740,7 +841,21 @@ class JavaScriptExtractor:
740
841
  if current.type in ("function_declaration", "method_definition"):
741
842
  name = current.child_by_field_name("name")
742
843
  if name is not None:
844
+ # object-literal shorthand: same key-path qname its symbol got,
845
+ # so body call edges attach to the resolver, not a flat alias.
846
+ if current.parent is not None and current.parent.type == "object":
847
+ key = self._key_name(name)
848
+ path = self._object_parent_path(current, ctx)
849
+ if key is not None and path is not None:
850
+ return f"{path}.{key}"
743
851
  return self._qualify(current, node_text(name), ctx)[0]
852
+ if current.type == "pair":
853
+ value = current.child_by_field_name("value")
854
+ if value is not None and value.type in ("arrow_function", "function_expression"):
855
+ key = self._key_name(current.child_by_field_name("key"))
856
+ path = self._object_parent_path(current, ctx)
857
+ if key is not None and path is not None:
858
+ return f"{path}.{key}"
744
859
  if current.type == "variable_declarator":
745
860
  value = current.child_by_field_name("value")
746
861
  if value is not None and value.type in ("arrow_function", "function_expression"):
@@ -223,3 +223,71 @@ def test_go_test_route_paths_classified_centrally():
223
223
  refs = [_call("Get", "r", '("/users", h)')]
224
224
  assert _chi_rule().match(_go_ext(refs, path="middleware/compress_test.go"))
225
225
  assert _chi_rule().match(_go_ext(refs, path="middleware/compress.go"))
226
+
227
+
228
+ # ---------------- gqlgen ----------------
229
+
230
+ from entrygraph.extract.ir import RawSymbol
231
+ from entrygraph.kinds import EntrypointKind, SymbolKind
232
+
233
+
234
+ def _gql_method(name, receiver, signature, exported=True):
235
+ parent = f"internal.graph.{receiver}"
236
+ return RawSymbol(
237
+ kind=SymbolKind.METHOD,
238
+ name=name,
239
+ qualified_name=f"{parent}.{name}",
240
+ span=Span(10, 0, 20, 1),
241
+ parent_qualified_name=parent,
242
+ signature=signature,
243
+ is_exported=exported,
244
+ )
245
+
246
+
247
+ def _gqlgen_ext(symbols):
248
+ return FileExtraction(
249
+ path="internal/graph/schema.resolvers.go",
250
+ language="go",
251
+ module_path="internal.graph",
252
+ parse_ok=True,
253
+ error_count=0,
254
+ symbols=list(symbols),
255
+ )
256
+
257
+
258
+ def _gqlgen_rule():
259
+ return {r.id: r for r in rules_for("go", {"gqlgen"})}["go.gqlgen.resolver"]
260
+
261
+
262
+ def test_gqlgen_query_resolver_method():
263
+ sym = _gql_method(
264
+ "Todos",
265
+ "queryResolver",
266
+ "func (r *queryResolver) Todos(ctx context.Context) ([]*Todo, error)",
267
+ )
268
+ (hint,) = _gqlgen_rule().match(_gqlgen_ext([sym]))
269
+ assert hint.kind is EntrypointKind.GRAPHQL_RESOLVER
270
+ assert hint.route == "Query.todos"
271
+ assert hint.handler_qualified_name == "internal.graph.queryResolver.Todos"
272
+ assert hint.metadata["operation"] == "query"
273
+ assert hint.http_methods == []
274
+
275
+
276
+ def test_gqlgen_type_field_resolver():
277
+ sym = _gql_method(
278
+ "Owner",
279
+ "todoResolver",
280
+ "func (r *todoResolver) Owner(ctx context.Context, obj *Todo) (*User, error)",
281
+ )
282
+ (hint,) = _gqlgen_rule().match(_gqlgen_ext([sym]))
283
+ assert hint.route == "Todo.owner"
284
+ assert hint.metadata["operation"] == "field"
285
+
286
+
287
+ def test_gqlgen_root_resolver_wiring_and_helpers_excluded():
288
+ wiring = _gql_method("Query", "Resolver", "func (r *Resolver) Query() generated.QueryResolver")
289
+ no_ctx = _gql_method(
290
+ "helper", "queryResolver", "func (r *queryResolver) helper() int", exported=False
291
+ )
292
+ no_ctx_exported = _gql_method("Helper", "queryResolver", "func (r *queryResolver) Helper() int")
293
+ assert _gqlgen_rule().match(_gqlgen_ext([wiring, no_ctx, no_ctx_exported])) == []