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.
- {entrygraph-0.1.128 → entrygraph-0.1.129}/PKG-INFO +1 -1
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/_version.py +2 -2
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/golang.py +46 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/javascript.py +139 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/ruby.py +131 -1
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/frameworks.py +52 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/javascript.py +115 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_go.py +68 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_javascript.py +180 -0
- entrygraph-0.1.129/tests/test_entrypoints_ruby.py +167 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_frameworks.py +53 -0
- entrygraph-0.1.128/tests/test_entrypoints_ruby.py +0 -67
- {entrygraph-0.1.128 → entrygraph-0.1.129}/.claude/launch.json +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/.dockerignore +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/.github/workflows/ci.yml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/.github/workflows/release.yml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/.gitignore +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/Dockerfile +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/LICENSE +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/README.md +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/RELEASING.md +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/pyproject.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/__main__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/analysis/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/analysis/facts.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/analysis/reaching.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/analysis/summaries.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/analysis/verify.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/api.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/cli/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/cli/main.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/cli/render.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/csharp.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/go.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/java.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/javascript.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/lib_javascript.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/lib_python.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/php.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/python.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/ruby.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/data/sinks/rust.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/db/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/db/engine.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/db/meta.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/db/models.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/db/queries.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/base.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/configs.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/csharp.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/graphql.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/java.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/php.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/python.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/entrypoints/rust.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/express_mounts.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/grpc_expand.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/manifests.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/detect/taint.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/errors.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/base.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/csharp.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/golang.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/graphql.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/ir.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/java.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/php.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/python.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/registry.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/ruby.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/extract/rust.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/hashing.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/lang.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/remote.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/testfiles.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/fs/walker.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/graph/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/graph/adjacency.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/graph/cte.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/kinds.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/parsing/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/parsing/parsers.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/parsing/queries.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/pipeline/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/pipeline/scanner.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/pipeline/worker.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/pipeline/writer.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/py.typed +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/csharp/calls.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/csharp/definitions.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/csharp/imports.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/go/calls.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/go/definitions.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/go/imports.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/graphql/definitions.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/java/calls.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/java/definitions.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/java/imports.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/javascript/calls.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/javascript/definitions.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/javascript/imports.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/php/calls.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/php/definitions.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/php/imports.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/python/calls.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/python/definitions.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/python/imports.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/ruby/calls.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/ruby/definitions.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/ruby/imports.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/rust/calls.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/rust/definitions.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/queries/rust/imports.scm +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/bindings.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/externals.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/hierarchy.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/resolver.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/resolve/symbol_table.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/results.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/app.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/appdb.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/auth/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/auth/csrf.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/auth/deps.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/auth/oidc.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/auth/sessions.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/cli.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/config.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/jobs/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/jobs/handlers.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/jobs/progress.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/jobs/runner.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/models.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/graph.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/jobs.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/keys.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/meta.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/repos.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/routes/serializers.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/static/assets/index-DGvRIX6y.css +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/static/assets/index-VD0Fnuj0.js +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/src/entrygraph/server/static/index.html +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/conftest.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/aspnet_app/Controllers/ReportsController.cs +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/aspnet_app/Program.cs +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/aspnet_app/Services/ReportService.cs +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/aspnet_app/app.csproj +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/minimalapi_app/Program.cs +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/csharp/minimalapi_app/app.csproj +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/catalog_full_app/go.mod +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/catalog_full_app/main.go +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/gin_app/go.mod +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/gin_app/main.go +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/gin_app/service.go +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/grpc_app/server.go +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/nethttp_app/go.mod +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/nethttp_app/main.go +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/go/reaching/app.go +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/graphql/schema_app/schema/custom_roots.gql +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/graphql/schema_app/schema/schema.graphql +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/methodref_app/pom.xml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/methodref_app/src/main/java/com/example/App.java +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/reaching/App.java +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/spring_app/pom.xml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/spring_app/src/main/java/com/example/Application.java +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportRunner.java +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/spring_app/src/main/java/com/example/ReportService.java +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/java/spring_app/src/main/java/com/example/UserController.java +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/commonjs_app/server.js +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/express_app/package.json +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/express_app/src/routes.js +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/express_app/src/services.js +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/express_channels_app/package.json +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/javascript/express_channels_app/src/routes.js +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/php/laravel_app/app/Http/Controllers/ReportController.php +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/php/laravel_app/artisan +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/php/laravel_app/composer.json +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/php/laravel_app/routes/web.php +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/php/reaching/app.php +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/precision/java/App.java +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/precision/javascript/app.js +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/precision/php/app.php +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/precision/python/app.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/precision/ruby/app.rb +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/chained_sinks/app.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/channels_app/app.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/cli_app/cli.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/cli_app/tool.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/fastapi_app/main.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/app/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/app/db.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/app/routes.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/app/services.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/cli.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/flask_app/requirements.txt +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/fuzzy_sink/app.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/heal_fidelity/caller.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/heal_fidelity/worker.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/may_continue/app.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/reaching_defs/app.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/sanitizer/app.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/source_split/app.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/speculative_frontier/app/__init__.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/speculative_frontier/app/dynamo.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/speculative_frontier/app/routes.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/speculative_frontier/app/scope.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/speculative_frontier/requirements.txt +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/python/taint_source/handler.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/ruby/rails_sql/app.rb +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/ruby/reaching/app.rb +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/ruby/sinatra_app/Gemfile +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/ruby/sinatra_app/app.rb +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/ruby/sinatra_app/services/runner.rb +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/axum_app/Cargo.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/axum_app/src/handlers.rs +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/axum_app/src/main.rs +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/axum_callback_app/Cargo.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/axum_callback_app/src/main.rs +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/catalog_full_app/Cargo.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/catalog_full_app/src/main.rs +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/scoped_sink_app/Cargo.toml +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/fixtures/rust/scoped_sink_app/src/main.rs +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_api.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_appdb.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_bindings.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_catalog_full.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_cli.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_commonjs.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_csharp_callbacks.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_cte_bounds.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_engine_pragmas.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoint_expansion.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_csharp.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_php.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_python.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_entrypoints_rust.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_csharp.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_go.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_graphql.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_java.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_javascript.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_php.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_python.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_ruby.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_extract_rust.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_framework_sources.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_fuzzy_sink.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_go_callbacks.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_grpc_expand.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_hardening.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_heal_fidelity.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_incremental.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_indexer.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_interprocedural.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_java_callbacks.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_lang.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_manifests.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_may_continue.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_models.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_multirepo.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_pool.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_precision.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_reachability.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_reaching.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_reaching_e2e.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_reaching_languages.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_registry_cache.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_remote.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_render.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_resolver.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_rust_callbacks.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_rust_scoped_sinks.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_scanner_progress.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_server_api.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_server_auth.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_server_config.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_server_jobs.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_server_keys.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_sink_catalog.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_source_keys.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_source_split.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_speculative_frontier.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_taint.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_taint_sources.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_testfiles.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/tests/test_walker.py +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/uv.lock +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/index.html +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/package-lock.json +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/package.json +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/App.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/api/client.test.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/api/client.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/api/queries.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/api/types.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/auth/AuthProvider.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/auth/LoginPage.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/components/EmptyState.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/components/InfoPopover.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/components/ui.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/dashboard/DashboardPage.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/EntrypointsTab.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/OverviewTab.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/RepoLayout.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/SymbolDetailPane.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/SymbolsTab.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/graph/CallGraphCanvas.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/graph/CallGraphTab.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/graph/graphLayout.test.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/graph/graphLayout.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/graph/graphStyle.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/AdvancedOptions.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/PathFlow.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/ReachabilityTab.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/categories.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/queryEcho.test.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/explore/reachability/queryEcho.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/jobs/JobBadges.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/jobs/JobToaster.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/jobs/JobsPage.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/jobs/jobName.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/jobs/useJob.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/repos/AddRepoWizard.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/repos/RepoListPage.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/repos/wizard.test.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/repos/wizardLogic.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/features/settings/SettingsPage.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/lib/glossary.ts +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/main.tsx +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/src/styles.css +0 -0
- {entrygraph-0.1.128 → entrygraph-0.1.129}/webapp/tsconfig.json +0 -0
- {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.
|
|
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.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
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])) == []
|