code-node-graph 0.2.0__tar.gz → 0.4.0__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.
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/CHANGELOG.md +34 -1
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/PKG-INFO +1 -1
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/__init__.py +1 -1
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/analysis/graph.py +148 -7
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/analysis/reader.py +20 -3
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/build.py +5 -3
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/graph.py +232 -52
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/style.py +42 -8
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/walk.py +42 -7
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/serve.py +2 -2
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/tests/test_code_node.py +81 -4
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/.gitignore +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/LICENSE +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/README.md +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/examples/books-api/README.md +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/examples/cart-service/README.md +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/examples/orders-service/README.md +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/pyproject.toml +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/__main__.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/analysis/__init__.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/analysis/aws_model.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/analysis/findings.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/analysis/mapping.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/analysis/model.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/analysis/syntax.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/analysis/terms.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/cli.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/deploys/__init__.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/deploys/sam.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/deploys/serverless.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/kit/__init__.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/kit/blocks.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/kit/fmt.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/kit/highlight.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/kit/render.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/kit/text.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/libraries/__init__.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/libraries/llm.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/libraries/mongo.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/libraries/redis.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/libraries/sql.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/pages.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/platforms/__init__.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/platforms/aws_lambda.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/platforms/web.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/project.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/__init__.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/brands.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/editor.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/README.md +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/anthropic-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-amplify.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-api-gateway.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-appflow.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-appsync.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-athena.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-backup.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-batch.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-certificate-manager.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-cloudformation.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-cloudfront.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-cloudtrail.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-cloudwatch.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-codebuild.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-codecommit.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-codedeploy.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-codepipeline.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-cognito.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-config.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-documentdb.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-dynamodb.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-ec2.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-ecs.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-eks.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-elasticache.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-elb.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-eventbridge.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-glacier.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-glue.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-iam.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-keyspaces.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-kinesis.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-kms.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-lake-formation.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-lambda.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-lightsail.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-mq.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-msk.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-neptune.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-open-search.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-quicksight.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-rds.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-redshift.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-route53.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-s3.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-secrets-manager.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-ses.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-shield.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-sns.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-sqs.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-step-functions.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-systems-manager.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-timestream.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-waf.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws-xray.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/aws.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/azure-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/django-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/elasticsearch.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/fastapi-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/firebase-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/flask.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/google-cloud.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/kafka-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/mariadb-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/mongodb-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/mysql-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/openai-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/postgresql.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/python.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/rabbitmq-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/redis.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/serverless.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/slack-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-api-gateway.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-appflow.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-appsync.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-athena.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-backup.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-batch.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-certificate-manager.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-cloudformation.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-cloudfront.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-cloudtrail.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-cloudwatch.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-codebuild.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-codecommit.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-codedeploy.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-codepipeline.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-cognito.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-config.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-documentdb.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-dynamodb.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-ec2.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-ecs.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-eks.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-elasticache.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-elb.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-eventbridge.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-glacier.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-glue.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-iam.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-keyspaces.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-kinesis.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-kms.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-lake-formation.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-lambda.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-mq.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-msk.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-neptune.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-open-search.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-quicksight.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-rds.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-redshift.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-route53.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-s3.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-secrets-manager.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-ses.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-shield.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-sns.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-sqs.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-step-functions.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-systems-manager.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-timestream.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-waf.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/small/aws-xray.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/snowflake-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/sqlite.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/stripe.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/supabase-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/logos/twilio-icon.svg +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/outline.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/src/code_node/report/tabs.py +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/tests/snapshots/books-api.txt +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/tests/snapshots/cart-service.txt +0 -0
- {code_node_graph-0.2.0 → code_node_graph-0.4.0}/tests/snapshots/orders-service.txt +0 -0
|
@@ -4,6 +4,37 @@ Every change a user would notice, newest first ([Keep a Changelog](https://keepa
|
|
|
4
4
|
follow [Semantic Versioning](https://semver.org/); before 1.0, a minor version may change what a command does.
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.4.0] - 2026-10-20
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Modules beside the entry point's file are read as its own code when it imports them by their own names, as a
|
|
12
|
+
Lambda function's folder does (`from helpers import x` in `src/app/handler.py`). They were taken for installed
|
|
13
|
+
libraries, so their functions, AWS calls and environment variables were missing, even when the folder is also a
|
|
14
|
+
package from the project's root.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- A box's "+12 calls not drawn" line opens them: click it and the graph starts from that function, with every call it
|
|
19
|
+
makes, a path above it back up to the whole graph, and the browser's Back button to return. **All** in the graph's
|
|
20
|
+
bar draws every function at once, for up to 150. This needs JavaScript; without it the page works as before.
|
|
21
|
+
- A box's code panel closes when you click anywhere outside it, or press Escape, as well as with its ×. Clicking
|
|
22
|
+
another box still opens that one's code.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- The graph says why functions are left out: too many to draw at once, or too deep, instead of always "too deep or
|
|
27
|
+
too many".
|
|
28
|
+
|
|
29
|
+
## [0.3.0] - 2026-10-20
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- Step by step's list folds like a tree: the arrow beside a function, loop or branch hides the steps under it and
|
|
34
|
+
shows how many it hid. The tab also moves more: a new step's card comes in a line at a time, the lit row and its
|
|
35
|
+
number ease in, a dot pings when its step is reached, the code fades in when the walk goes into another function,
|
|
36
|
+
and the lit line glows as it lands. None of it moves for readers who ask for less motion.
|
|
37
|
+
|
|
7
38
|
## [0.2.0] - 2026-10-20
|
|
8
39
|
|
|
9
40
|
### Changed
|
|
@@ -162,6 +193,8 @@ follow [Semantic Versioning](https://semver.org/); before 1.0, a minor version m
|
|
|
162
193
|
- `--platform NAME` runs a platform's checks whatever the entry point is; `--lambda` is short for
|
|
163
194
|
`--platform lambda`. `EntryPoint.is_lambda` and `PageOptions.is_lambda` are now `platform` (`"lambda"` or `""`).
|
|
164
195
|
|
|
165
|
-
[Unreleased]: https://github.com/utkarsh5026/code-node/compare/v0.
|
|
196
|
+
[Unreleased]: https://github.com/utkarsh5026/code-node/compare/v0.4.0...HEAD
|
|
197
|
+
[0.4.0]: https://github.com/utkarsh5026/code-node/compare/v0.3.0...v0.4.0
|
|
198
|
+
[0.3.0]: https://github.com/utkarsh5026/code-node/compare/v0.2.0...v0.3.0
|
|
166
199
|
[0.2.0]: https://github.com/utkarsh5026/code-node/compare/v0.1.0...v0.2.0
|
|
167
200
|
[0.1.0]: https://github.com/utkarsh5026/code-node/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: code-node-graph
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: See how Python code works without running it: a graph of who calls whom from an entry point, and what each path reaches.
|
|
5
5
|
Project-URL: Homepage, https://github.com/utkarsh5026/code-node
|
|
6
6
|
Project-URL: Documentation, https://utkarsh5026.github.io/code-node/
|
|
@@ -13,7 +13,7 @@ from .libraries import Library, register_library
|
|
|
13
13
|
from .platforms import Platform, get_platform, register_platform
|
|
14
14
|
from .project import EntryPoint, Project, entry_points, read_project
|
|
15
15
|
|
|
16
|
-
__version__ = "0.
|
|
16
|
+
__version__ = "0.4.0"
|
|
17
17
|
|
|
18
18
|
__all__ = [
|
|
19
19
|
"CodeGraph",
|
|
@@ -8,7 +8,7 @@ from __future__ import annotations
|
|
|
8
8
|
import heapq
|
|
9
9
|
import re
|
|
10
10
|
from collections import defaultdict
|
|
11
|
-
from dataclasses import dataclass, field
|
|
11
|
+
from dataclasses import dataclass, field, replace
|
|
12
12
|
|
|
13
13
|
from ..kit.fmt import _plural
|
|
14
14
|
from ..kit.text import _clip
|
|
@@ -55,6 +55,7 @@ class GraphNode:
|
|
|
55
55
|
when: str = "" # a function called only under an if, everywhere it's called: 'check(order)'
|
|
56
56
|
icon: str = "" # a service's icon, when its plugin names one: 'database', 'queue', 'spark'; or the entry
|
|
57
57
|
# point's logo, when its platform names one: 'fastapi-icon'
|
|
58
|
+
name: str = "" # a function's box: the function, as module.name ('orders.repo.save')
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
@dataclass
|
|
@@ -85,6 +86,7 @@ class CodeGraph:
|
|
|
85
86
|
nodes: list[GraphNode] = field(default_factory=list)
|
|
86
87
|
edges: list[GraphEdge] = field(default_factory=list)
|
|
87
88
|
hidden: int = 0 # functions left out to keep the graph readable (too deep, or too many)
|
|
89
|
+
too_deep: int = 0 # ...of them, the ones deeper than it draws
|
|
88
90
|
|
|
89
91
|
def node(self, node_id: str) -> GraphNode | None:
|
|
90
92
|
return next((n for n in self.nodes if n.id == node_id), None)
|
|
@@ -256,11 +258,13 @@ def _pick(
|
|
|
256
258
|
order: dict[str, int],
|
|
257
259
|
room: int,
|
|
258
260
|
max_depth: int,
|
|
261
|
+
first: bool = False,
|
|
259
262
|
) -> list[str]:
|
|
260
263
|
"""
|
|
261
264
|
The functions the graph draws besides the entry point, at most `room` of them and max_depth calls deep: grown out
|
|
262
265
|
from the entry point (and what runs at import), each time taking the one the drawn boxes call that leads to the
|
|
263
|
-
most code, so the boxes are the ones that do the work and each is joined to one above it.
|
|
266
|
+
most code, so the boxes are the ones that do the work and each is joined to one above it. first takes every
|
|
267
|
+
function the entry point calls before the rest.
|
|
264
268
|
"""
|
|
265
269
|
|
|
266
270
|
def depth(name: str) -> int:
|
|
@@ -283,6 +287,14 @@ def _pick(
|
|
|
283
287
|
if name not in level:
|
|
284
288
|
offer(name)
|
|
285
289
|
picked: list[str] = []
|
|
290
|
+
if first:
|
|
291
|
+
for child, _ in children.get(entry, []):
|
|
292
|
+
if child in by_name and child != entry and child not in picked and depth(child) <= max_depth:
|
|
293
|
+
picked.append(child)
|
|
294
|
+
for name in list(picked):
|
|
295
|
+
for child, _ in children.get(name, []):
|
|
296
|
+
if child not in picked:
|
|
297
|
+
offer(child)
|
|
286
298
|
while queue and len(picked) < room:
|
|
287
299
|
name = heapq.heappop(queue)[3]
|
|
288
300
|
if name in picked:
|
|
@@ -294,7 +306,46 @@ def _pick(
|
|
|
294
306
|
return picked
|
|
295
307
|
|
|
296
308
|
|
|
297
|
-
def
|
|
309
|
+
def _focused(cmap: CodeMap, focus: str) -> CodeMap:
|
|
310
|
+
"""
|
|
311
|
+
The map cut down to one of its functions and what it calls, however deep, as if the code started there: its
|
|
312
|
+
calls in from elsewhere, what runs at import and the entry point's input left out.
|
|
313
|
+
"""
|
|
314
|
+
children: dict[str, list[str]] = defaultdict(list)
|
|
315
|
+
for f in cmap.functions:
|
|
316
|
+
for where in f.called_from:
|
|
317
|
+
children[where.rpartition(" line ")[0]].append(f.name)
|
|
318
|
+
scope, todo = {focus}, [focus]
|
|
319
|
+
while todo:
|
|
320
|
+
for child in children.get(todo.pop(), []):
|
|
321
|
+
if child not in scope:
|
|
322
|
+
scope.add(child)
|
|
323
|
+
todo.append(child)
|
|
324
|
+
root = next(f for f in cmap.functions if f.name == focus)
|
|
325
|
+
functions = []
|
|
326
|
+
for f in cmap.functions:
|
|
327
|
+
if f.name in scope:
|
|
328
|
+
kept = [] if f.name == focus else [w for w in f.called_from if w.rpartition(" line ")[0] in scope]
|
|
329
|
+
functions.append(replace(f, called_from=kept, when={w: t for w, t in f.when.items() if w in kept}))
|
|
330
|
+
return replace(
|
|
331
|
+
cmap,
|
|
332
|
+
entry=focus,
|
|
333
|
+
handler_file=root.file,
|
|
334
|
+
entry_line=root.line,
|
|
335
|
+
startup=[],
|
|
336
|
+
event=[],
|
|
337
|
+
functions=functions,
|
|
338
|
+
aws=[c for c in cmap.aws if c.function in scope],
|
|
339
|
+
library_calls=[c for c in cmap.library_calls if c.function in scope],
|
|
340
|
+
env=[e for e in cmap.env if e.function in scope],
|
|
341
|
+
spots=[s for s in cmap.spots if s.function in scope and not s.startup],
|
|
342
|
+
routes=[r for r in cmap.routes if r[2] in scope],
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def code_graph(
|
|
347
|
+
cmap: CodeMap, *, max_functions: int = _GRAPH_FUNCTIONS, max_depth: int = _GRAPH_DEPTH, focus: str = ""
|
|
348
|
+
) -> CodeGraph:
|
|
298
349
|
"""
|
|
299
350
|
How the code fits together, as boxes and arrows, from a CodeMap: on the left what comes in (the event, with the
|
|
300
351
|
fields it reads, and the environment variables), then the handler, what runs at start-up, and its own functions
|
|
@@ -304,10 +355,16 @@ def code_graph(cmap: CodeMap, *, max_functions: int = _GRAPH_FUNCTIONS, max_dept
|
|
|
304
355
|
they do is drawn from the nearest box above them that is.
|
|
305
356
|
The functions drawn are grown out from the entry point, each time the one that leads to the most code (and to AWS,
|
|
306
357
|
the web and the environment), so the boxes are the ones that do the work, not the nearest helpers.
|
|
358
|
+
focus draws the graph from one of its functions instead (module.name): it and what it calls, every call it makes
|
|
359
|
+
drawn even past max_functions, and nothing from what comes in or what runs at import.
|
|
307
360
|
"""
|
|
308
361
|
graph = CodeGraph()
|
|
309
362
|
if cmap.note or cmap.entry is None:
|
|
310
363
|
return graph
|
|
364
|
+
if focus:
|
|
365
|
+
if not any(f.name == focus for f in cmap.functions):
|
|
366
|
+
return graph
|
|
367
|
+
cmap = _focused(cmap, focus)
|
|
311
368
|
platform = get_platform(cmap.platform)
|
|
312
369
|
entry = cmap.entry
|
|
313
370
|
by_name = {f.name: f for f in cmap.functions}
|
|
@@ -342,11 +399,15 @@ def code_graph(cmap: CodeMap, *, max_functions: int = _GRAPH_FUNCTIONS, max_dept
|
|
|
342
399
|
todo.append(child)
|
|
343
400
|
order = {f.name: i for i, f in enumerate(cmap.functions)}
|
|
344
401
|
weight = _weights(cmap, children)
|
|
345
|
-
|
|
402
|
+
room = max_functions - entry_is_function
|
|
403
|
+
if focus: # a function opened on its own shows every call it makes
|
|
404
|
+
room = max(room, len({child for child, _ in children.get(entry, [])}))
|
|
405
|
+
picked = _pick(cmap, entry, start_key, children, level, weight, order, room, max_depth, first=bool(focus))
|
|
346
406
|
shown = ([by_name[entry]] if entry_is_function else []) + sorted(
|
|
347
407
|
(by_name[name] for name in picked), key=lambda f: (level.get(f.name, f.depth + 1), order[f.name])
|
|
348
408
|
)
|
|
349
409
|
graph.hidden = len(cmap.functions) - len(shown)
|
|
410
|
+
graph.too_deep = sum(1 for f in cmap.functions if level.get(f.name, f.depth + 1) > max_depth)
|
|
350
411
|
ids = {f.name: f"f{i}" for i, f in enumerate(shown)}
|
|
351
412
|
drawn_level = {entry: 0, start_key: 0} # calls from the entry point through the boxes drawn: each box's column
|
|
352
413
|
todo = [entry, start_key]
|
|
@@ -408,7 +469,9 @@ def code_graph(cmap: CodeMap, *, max_functions: int = _GRAPH_FUNCTIONS, max_dept
|
|
|
408
469
|
reasons[call.function].append(f"{call.label} isn't allowed: {denied[call.permission]} (line {call.line})")
|
|
409
470
|
for function, why in platform.marks(cmap).items():
|
|
410
471
|
reasons[function] += why
|
|
411
|
-
handler_reasons =
|
|
472
|
+
handler_reasons = (
|
|
473
|
+
[] if focus else [m.split(":")[0] for level_, m in platform.entry_findings(cmap) if level_ == "warn"]
|
|
474
|
+
)
|
|
412
475
|
unset = platform.unset_env(cmap)
|
|
413
476
|
|
|
414
477
|
# the lines in each function worth pointing at: where it calls, reads or sends what the graph draws
|
|
@@ -455,7 +518,7 @@ def code_graph(cmap: CodeMap, *, max_functions: int = _GRAPH_FUNCTIONS, max_dept
|
|
|
455
518
|
when=_when(f, [where for where in f.called_from if where.rpartition(" line ")[0] == caller]),
|
|
456
519
|
)
|
|
457
520
|
)
|
|
458
|
-
has_input = bool(cmap.event) or platform.draws_input
|
|
521
|
+
has_input = not focus and (bool(cmap.event) or platform.draws_input)
|
|
459
522
|
if has_input:
|
|
460
523
|
edges.insert(0, GraphEdge("event", entry_id, "event", "its input"))
|
|
461
524
|
|
|
@@ -769,7 +832,8 @@ def code_graph(cmap: CodeMap, *, max_functions: int = _GRAPH_FUNCTIONS, max_dept
|
|
|
769
832
|
],
|
|
770
833
|
more=more,
|
|
771
834
|
when=_when(f, f.called_from),
|
|
772
|
-
icon=platform.entry_icon(cmap) if is_entry else "",
|
|
835
|
+
icon=platform.entry_icon(cmap) if is_entry and not focus else "",
|
|
836
|
+
name=f.name,
|
|
773
837
|
)
|
|
774
838
|
)
|
|
775
839
|
out_column = 1 + max([n.column for n in graph.nodes if n.kind in ("handler", "function", "start")] + [1])
|
|
@@ -778,3 +842,80 @@ def code_graph(cmap: CodeMap, *, max_functions: int = _GRAPH_FUNCTIONS, max_dept
|
|
|
778
842
|
graph.nodes += reach
|
|
779
843
|
graph.edges = edges
|
|
780
844
|
return graph
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
_GRAPH_VIEWS = 120 # functions opened on their own, at most: past that a box's calls left out stay in the steps
|
|
848
|
+
|
|
849
|
+
_GRAPH_EVERYTHING = 150 # functions, at most, for a graph of all of them besides the one drawn
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
@dataclass
|
|
853
|
+
class GraphView:
|
|
854
|
+
"""
|
|
855
|
+
The graph from one of the functions a graph leaves calls out of: the function and what it calls (`graph`), and the
|
|
856
|
+
calls from the entry point down to it (`path`, module.name each, the entry point first and the function last).
|
|
857
|
+
"""
|
|
858
|
+
|
|
859
|
+
name: str
|
|
860
|
+
graph: CodeGraph
|
|
861
|
+
path: list[str] = field(default_factory=list)
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
def _call_path(cmap: CodeMap, name: str) -> list[str]:
|
|
865
|
+
"""
|
|
866
|
+
The fewest calls from the entry point to a function: [entry, ..., name]. [name] when the entry point isn't above it.
|
|
867
|
+
"""
|
|
868
|
+
known = {f.name for f in cmap.functions} | {cmap.entry or ""}
|
|
869
|
+
parents: dict[str, list[str]] = defaultdict(list)
|
|
870
|
+
for f in cmap.functions:
|
|
871
|
+
for where in f.called_from:
|
|
872
|
+
caller = where.rpartition(" line ")[0]
|
|
873
|
+
if caller in known:
|
|
874
|
+
parents[f.name].append(caller)
|
|
875
|
+
came: dict[str, str] = {name: ""}
|
|
876
|
+
todo = [name]
|
|
877
|
+
while todo:
|
|
878
|
+
key = todo.pop(0)
|
|
879
|
+
if key == cmap.entry:
|
|
880
|
+
path = [key]
|
|
881
|
+
while came[path[-1]]:
|
|
882
|
+
path.append(came[path[-1]])
|
|
883
|
+
return path
|
|
884
|
+
for parent in parents.get(key, []):
|
|
885
|
+
if parent not in came:
|
|
886
|
+
came[parent] = key
|
|
887
|
+
todo.append(parent)
|
|
888
|
+
return [name]
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
def graph_views(
|
|
892
|
+
cmap: CodeMap,
|
|
893
|
+
graph: CodeGraph,
|
|
894
|
+
*,
|
|
895
|
+
max_functions: int = _GRAPH_FUNCTIONS,
|
|
896
|
+
max_depth: int = _GRAPH_DEPTH,
|
|
897
|
+
most: int = _GRAPH_VIEWS,
|
|
898
|
+
) -> list[GraphView]:
|
|
899
|
+
"""
|
|
900
|
+
A graph from each function whose calls `graph` leaves out, and from each such function in those, so that from any
|
|
901
|
+
box with calls not drawn, its own graph has them: at most `most`, nearest the entry point first.
|
|
902
|
+
"""
|
|
903
|
+
views: dict[str, GraphView] = {}
|
|
904
|
+
todo = [graph]
|
|
905
|
+
while todo and len(views) < most:
|
|
906
|
+
for n in todo.pop(0).nodes:
|
|
907
|
+
if not (n.more and n.name) or n.name in views or len(views) >= most:
|
|
908
|
+
continue
|
|
909
|
+
opened = code_graph(cmap, max_functions=max_functions, max_depth=max_depth, focus=n.name)
|
|
910
|
+
views[n.name] = GraphView(n.name, opened, _call_path(cmap, n.name))
|
|
911
|
+
todo.append(opened)
|
|
912
|
+
return list(views.values())
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
def everything_graph(cmap: CodeMap, graph: CodeGraph, most: int = _GRAPH_EVERYTHING) -> CodeGraph | None:
|
|
916
|
+
"""
|
|
917
|
+
Every function in one graph, when `graph` leaves some out and there are at most `most` of them; else None.
|
|
918
|
+
"""
|
|
919
|
+
if not graph.hidden or len(cmap.functions) > most:
|
|
920
|
+
return None
|
|
921
|
+
return code_graph(cmap, max_functions=len(cmap.functions) + 1, max_depth=len(cmap.functions) + 1)
|
|
@@ -203,6 +203,22 @@ class _CodeReader:
|
|
|
203
203
|
module = module[: -len(".__init__")] if module.endswith(".__init__") else module
|
|
204
204
|
if module and module != "__init__":
|
|
205
205
|
self.sources[module] = path
|
|
206
|
+
here = (cmap.handler_file or "").rpartition("/")[0]
|
|
207
|
+
here = here + "/" if here else ""
|
|
208
|
+
if here.startswith(root) and here != root:
|
|
209
|
+
# the entry point's own folder is on sys.path too, as a script's is and a Lambda function's code is:
|
|
210
|
+
# `from helpers import x` beside it is its own code, though the folder is also a package from the root
|
|
211
|
+
beside: set[str] = set()
|
|
212
|
+
for path in texts:
|
|
213
|
+
if not path.startswith(here) or not path.endswith(".py") or "site-packages" in path:
|
|
214
|
+
continue
|
|
215
|
+
module = path[len(here) : -3].replace("/", ".")
|
|
216
|
+
module = module[: -len(".__init__")] if module.endswith(".__init__") else module
|
|
217
|
+
if module and module != "__init__" and "-" not in module and module not in self.sources:
|
|
218
|
+
self.sources[module] = path
|
|
219
|
+
beside.add(module.split(".")[0])
|
|
220
|
+
self.tops |= beside
|
|
221
|
+
self.libraries = libraries - beside
|
|
206
222
|
self.modules: dict[str, _Module] = {}
|
|
207
223
|
self.defs: dict[str, tuple[str, Any, str | None]] = {} # key -> (module, node, class key)
|
|
208
224
|
self.classes: dict[str, dict[str, Any]] = {}
|
|
@@ -311,8 +327,9 @@ class _CodeReader:
|
|
|
311
327
|
cmap.event = list(self.event_reads.values())
|
|
312
328
|
cmap.routes = [(method, path, key) for method, path, key, _ in self.routes]
|
|
313
329
|
loaded = [m for m in self.modules.values() if m.tree is not None]
|
|
314
|
-
|
|
315
|
-
|
|
330
|
+
read = {path for name, path in self.sources.items() if name in self.modules} # a file has two names, at most
|
|
331
|
+
cmap.other_files = len(
|
|
332
|
+
{path for path in self.sources.values() if path not in read and not _JUNK_RE.search(path)}
|
|
316
333
|
)
|
|
317
334
|
for m in loaded:
|
|
318
335
|
for node in m.tree.body:
|
|
@@ -324,7 +341,7 @@ class _CodeReader:
|
|
|
324
341
|
if isinstance(f, (ast.FunctionDef, ast.AsyncFunctionDef)) and prefix + f.name not in self.reached
|
|
325
342
|
)
|
|
326
343
|
self._secrets(m)
|
|
327
|
-
for name, path in self.sources.items():
|
|
344
|
+
for path, name in {path: name for name, path in reversed(self.sources.items())}.items():
|
|
328
345
|
for match in _AWS_KEY_RE.finditer(self.texts[path]):
|
|
329
346
|
line = self.texts[path].count("\n", 0, match.start()) + 1
|
|
330
347
|
self._spot("secret", path, line, name, "", "an AWS access key", ok=False)
|
|
@@ -10,7 +10,7 @@ from ..kit.blocks import _Note, _Text
|
|
|
10
10
|
from ..kit.fmt import _plural
|
|
11
11
|
|
|
12
12
|
from ..analysis.findings import _listed, _short_name
|
|
13
|
-
from ..analysis.graph import CodeGraph, code_graph
|
|
13
|
+
from ..analysis.graph import CodeGraph, code_graph, everything_graph, graph_views
|
|
14
14
|
from ..analysis.model import CodeMap
|
|
15
15
|
from .graph import _Graph
|
|
16
16
|
from .tabs import _Tabs
|
|
@@ -34,8 +34,8 @@ def _closing_note(cmap: CodeMap, graph: CodeGraph) -> str:
|
|
|
34
34
|
said.append(f"Calls to {_listed(services, 4)} are read by code-node's library plugins.")
|
|
35
35
|
if graph.hidden:
|
|
36
36
|
said.append(
|
|
37
|
-
f"The graph leaves out {_plural(graph.hidden, 'function')} to stay readable:
|
|
38
|
-
"
|
|
37
|
+
f"The graph leaves out {_plural(graph.hidden, 'function')} to stay readable: a box's line of calls not "
|
|
38
|
+
"drawn opens them, and --all draws every one."
|
|
39
39
|
)
|
|
40
40
|
if cmap.other_files:
|
|
41
41
|
said.append(
|
|
@@ -99,6 +99,8 @@ def page_blocks(
|
|
|
99
99
|
"How it fits together: what comes in, who calls whom, and what it reaches",
|
|
100
100
|
sources=sources or {},
|
|
101
101
|
editor=editor,
|
|
102
|
+
views=graph_views(cmap, graph, max_functions=max_functions, max_depth=max_depth),
|
|
103
|
+
everything=everything_graph(cmap, graph),
|
|
102
104
|
),
|
|
103
105
|
_Text(
|
|
104
106
|
graph.mermaid(),
|