code-node-graph 0.1.0__py3-none-any.whl
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/__init__.py +40 -0
- code_node/__main__.py +7 -0
- code_node/analysis/__init__.py +3 -0
- code_node/analysis/aws_model.py +197 -0
- code_node/analysis/findings.py +462 -0
- code_node/analysis/graph.py +780 -0
- code_node/analysis/mapping.py +114 -0
- code_node/analysis/model.py +289 -0
- code_node/analysis/reader.py +1926 -0
- code_node/analysis/syntax.py +220 -0
- code_node/analysis/terms.py +505 -0
- code_node/cli.py +316 -0
- code_node/deploys/__init__.py +321 -0
- code_node/deploys/sam.py +371 -0
- code_node/deploys/serverless.py +117 -0
- code_node/kit/__init__.py +4 -0
- code_node/kit/blocks.py +184 -0
- code_node/kit/fmt.py +389 -0
- code_node/kit/highlight.py +131 -0
- code_node/kit/render.py +1066 -0
- code_node/kit/text.py +81 -0
- code_node/libraries/__init__.py +333 -0
- code_node/libraries/llm.py +124 -0
- code_node/libraries/mongo.py +169 -0
- code_node/libraries/redis.py +253 -0
- code_node/libraries/sql.py +232 -0
- code_node/pages.py +92 -0
- code_node/platforms/__init__.py +203 -0
- code_node/platforms/aws_lambda.py +290 -0
- code_node/platforms/web.py +301 -0
- code_node/project.py +402 -0
- code_node/report/__init__.py +3 -0
- code_node/report/brands.py +244 -0
- code_node/report/build.py +119 -0
- code_node/report/editor.py +40 -0
- code_node/report/graph.py +1411 -0
- code_node/report/logos/README.md +8 -0
- code_node/report/logos/anthropic-icon.svg +1 -0
- code_node/report/logos/aws-amplify.svg +1 -0
- code_node/report/logos/aws-api-gateway.svg +1 -0
- code_node/report/logos/aws-appflow.svg +1 -0
- code_node/report/logos/aws-appsync.svg +1 -0
- code_node/report/logos/aws-athena.svg +1 -0
- code_node/report/logos/aws-backup.svg +1 -0
- code_node/report/logos/aws-batch.svg +1 -0
- code_node/report/logos/aws-certificate-manager.svg +1 -0
- code_node/report/logos/aws-cloudformation.svg +1 -0
- code_node/report/logos/aws-cloudfront.svg +1 -0
- code_node/report/logos/aws-cloudtrail.svg +1 -0
- code_node/report/logos/aws-cloudwatch.svg +1 -0
- code_node/report/logos/aws-codebuild.svg +1 -0
- code_node/report/logos/aws-codecommit.svg +1 -0
- code_node/report/logos/aws-codedeploy.svg +1 -0
- code_node/report/logos/aws-codepipeline.svg +1 -0
- code_node/report/logos/aws-cognito.svg +1 -0
- code_node/report/logos/aws-config.svg +1 -0
- code_node/report/logos/aws-documentdb.svg +1 -0
- code_node/report/logos/aws-dynamodb.svg +1 -0
- code_node/report/logos/aws-ec2.svg +1 -0
- code_node/report/logos/aws-ecs.svg +1 -0
- code_node/report/logos/aws-eks.svg +1 -0
- code_node/report/logos/aws-elasticache.svg +1 -0
- code_node/report/logos/aws-elb.svg +1 -0
- code_node/report/logos/aws-eventbridge.svg +1 -0
- code_node/report/logos/aws-glacier.svg +1 -0
- code_node/report/logos/aws-glue.svg +1 -0
- code_node/report/logos/aws-iam.svg +1 -0
- code_node/report/logos/aws-keyspaces.svg +1 -0
- code_node/report/logos/aws-kinesis.svg +1 -0
- code_node/report/logos/aws-kms.svg +1 -0
- code_node/report/logos/aws-lake-formation.svg +1 -0
- code_node/report/logos/aws-lambda.svg +1 -0
- code_node/report/logos/aws-lightsail.svg +1 -0
- code_node/report/logos/aws-mq.svg +1 -0
- code_node/report/logos/aws-msk.svg +1 -0
- code_node/report/logos/aws-neptune.svg +1 -0
- code_node/report/logos/aws-open-search.svg +1 -0
- code_node/report/logos/aws-quicksight.svg +1 -0
- code_node/report/logos/aws-rds.svg +1 -0
- code_node/report/logos/aws-redshift.svg +1 -0
- code_node/report/logos/aws-route53.svg +1 -0
- code_node/report/logos/aws-s3.svg +1 -0
- code_node/report/logos/aws-secrets-manager.svg +1 -0
- code_node/report/logos/aws-ses.svg +1 -0
- code_node/report/logos/aws-shield.svg +1 -0
- code_node/report/logos/aws-sns.svg +1 -0
- code_node/report/logos/aws-sqs.svg +1 -0
- code_node/report/logos/aws-step-functions.svg +1 -0
- code_node/report/logos/aws-systems-manager.svg +1 -0
- code_node/report/logos/aws-timestream.svg +1 -0
- code_node/report/logos/aws-waf.svg +1 -0
- code_node/report/logos/aws-xray.svg +1 -0
- code_node/report/logos/aws.svg +1 -0
- code_node/report/logos/azure-icon.svg +1 -0
- code_node/report/logos/django-icon.svg +1 -0
- code_node/report/logos/elasticsearch.svg +1 -0
- code_node/report/logos/fastapi-icon.svg +1 -0
- code_node/report/logos/firebase-icon.svg +1 -0
- code_node/report/logos/flask.svg +1 -0
- code_node/report/logos/google-cloud.svg +1 -0
- code_node/report/logos/kafka-icon.svg +1 -0
- code_node/report/logos/mariadb-icon.svg +1 -0
- code_node/report/logos/mongodb-icon.svg +1 -0
- code_node/report/logos/mysql-icon.svg +1 -0
- code_node/report/logos/openai-icon.svg +1 -0
- code_node/report/logos/postgresql.svg +1 -0
- code_node/report/logos/python.svg +1 -0
- code_node/report/logos/rabbitmq-icon.svg +1 -0
- code_node/report/logos/redis.svg +1 -0
- code_node/report/logos/serverless.svg +1 -0
- code_node/report/logos/slack-icon.svg +1 -0
- code_node/report/logos/small/aws-api-gateway.svg +1 -0
- code_node/report/logos/small/aws-appflow.svg +1 -0
- code_node/report/logos/small/aws-appsync.svg +1 -0
- code_node/report/logos/small/aws-athena.svg +1 -0
- code_node/report/logos/small/aws-backup.svg +1 -0
- code_node/report/logos/small/aws-batch.svg +1 -0
- code_node/report/logos/small/aws-certificate-manager.svg +1 -0
- code_node/report/logos/small/aws-cloudformation.svg +1 -0
- code_node/report/logos/small/aws-cloudfront.svg +1 -0
- code_node/report/logos/small/aws-cloudtrail.svg +1 -0
- code_node/report/logos/small/aws-cloudwatch.svg +1 -0
- code_node/report/logos/small/aws-codebuild.svg +1 -0
- code_node/report/logos/small/aws-codecommit.svg +1 -0
- code_node/report/logos/small/aws-codedeploy.svg +1 -0
- code_node/report/logos/small/aws-codepipeline.svg +1 -0
- code_node/report/logos/small/aws-cognito.svg +1 -0
- code_node/report/logos/small/aws-config.svg +1 -0
- code_node/report/logos/small/aws-documentdb.svg +1 -0
- code_node/report/logos/small/aws-dynamodb.svg +1 -0
- code_node/report/logos/small/aws-ec2.svg +1 -0
- code_node/report/logos/small/aws-ecs.svg +1 -0
- code_node/report/logos/small/aws-eks.svg +1 -0
- code_node/report/logos/small/aws-elasticache.svg +1 -0
- code_node/report/logos/small/aws-elb.svg +1 -0
- code_node/report/logos/small/aws-eventbridge.svg +1 -0
- code_node/report/logos/small/aws-glacier.svg +1 -0
- code_node/report/logos/small/aws-glue.svg +1 -0
- code_node/report/logos/small/aws-iam.svg +1 -0
- code_node/report/logos/small/aws-keyspaces.svg +1 -0
- code_node/report/logos/small/aws-kinesis.svg +1 -0
- code_node/report/logos/small/aws-kms.svg +1 -0
- code_node/report/logos/small/aws-lake-formation.svg +1 -0
- code_node/report/logos/small/aws-lambda.svg +1 -0
- code_node/report/logos/small/aws-mq.svg +1 -0
- code_node/report/logos/small/aws-msk.svg +1 -0
- code_node/report/logos/small/aws-neptune.svg +1 -0
- code_node/report/logos/small/aws-open-search.svg +1 -0
- code_node/report/logos/small/aws-quicksight.svg +1 -0
- code_node/report/logos/small/aws-rds.svg +1 -0
- code_node/report/logos/small/aws-redshift.svg +1 -0
- code_node/report/logos/small/aws-route53.svg +1 -0
- code_node/report/logos/small/aws-s3.svg +1 -0
- code_node/report/logos/small/aws-secrets-manager.svg +1 -0
- code_node/report/logos/small/aws-ses.svg +1 -0
- code_node/report/logos/small/aws-shield.svg +1 -0
- code_node/report/logos/small/aws-sns.svg +1 -0
- code_node/report/logos/small/aws-sqs.svg +1 -0
- code_node/report/logos/small/aws-step-functions.svg +1 -0
- code_node/report/logos/small/aws-systems-manager.svg +1 -0
- code_node/report/logos/small/aws-timestream.svg +1 -0
- code_node/report/logos/small/aws-waf.svg +1 -0
- code_node/report/logos/small/aws-xray.svg +1 -0
- code_node/report/logos/snowflake-icon.svg +1 -0
- code_node/report/logos/sqlite.svg +1 -0
- code_node/report/logos/stripe.svg +1 -0
- code_node/report/logos/supabase-icon.svg +1 -0
- code_node/report/logos/twilio-icon.svg +1 -0
- code_node/report/outline.py +219 -0
- code_node/report/style.py +358 -0
- code_node/report/tabs.py +73 -0
- code_node/report/walk.py +624 -0
- code_node/serve.py +277 -0
- code_node_graph-0.1.0.dist-info/METADATA +165 -0
- code_node_graph-0.1.0.dist-info/RECORD +178 -0
- code_node_graph-0.1.0.dist-info/WHEEL +4 -0
- code_node_graph-0.1.0.dist-info/entry_points.txt +2 -0
- code_node_graph-0.1.0.dist-info/licenses/LICENSE +201 -0
code_node/serve.py
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""
|
|
2
|
+
code-node --serve: the page on a local server, made again whenever a .py file (or a deploy file, like template.yaml)
|
|
3
|
+
under the folder changes, and the browser told to reload (keeping its scroll position, and the tab and step that were
|
|
4
|
+
open). Standard library only: a polling watcher thread, a threaded HTTP server, and Server-Sent Events for the reload.
|
|
5
|
+
|
|
6
|
+
The saved pages have no JavaScript; the served one gets a few lines (_LIVE_SCRIPT) to reload and to say when the
|
|
7
|
+
server has stopped.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import html
|
|
13
|
+
import os
|
|
14
|
+
import threading
|
|
15
|
+
import time
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Callable
|
|
20
|
+
|
|
21
|
+
from .deploys import deploy_file_names
|
|
22
|
+
from .pages import PageOptions, make_page
|
|
23
|
+
from .project import _SKIPPED_FOLDERS, EntryPoint
|
|
24
|
+
from .report.style import FAVICON_LINK, FAVICON_SVG
|
|
25
|
+
|
|
26
|
+
DEFAULT_PORT = 8765 # without --port, the first free port from here on is used, so several folders can be served
|
|
27
|
+
|
|
28
|
+
PORTS_TRIED = 20 # how many ports from DEFAULT_PORT are tried before letting the system pick any free one
|
|
29
|
+
|
|
30
|
+
POLL_SECONDS = 0.5 # how often the folder is checked for changes
|
|
31
|
+
|
|
32
|
+
SETTLE_SECONDS = 0.3 # a change waits this long for the rest of a save (editors write files in steps)
|
|
33
|
+
|
|
34
|
+
HEARTBEAT_SECONDS = 15 # an idle event stream sends a comment this often, so proxies don't close it
|
|
35
|
+
|
|
36
|
+
_LIVE_SCRIPT = """
|
|
37
|
+
<div id="cn-live" style="position:fixed;right:14px;bottom:14px;z-index:9;font:12px/1.4 system-ui,sans-serif;
|
|
38
|
+
padding:6px 10px;border-radius:999px;background:rgba(127,127,127,.14);backdrop-filter:blur(6px)">● live</div>
|
|
39
|
+
<script>
|
|
40
|
+
(() => {
|
|
41
|
+
const key = "code-node-scroll:" + location.pathname;
|
|
42
|
+
const saved = JSON.parse(sessionStorage.getItem(key) || "null");
|
|
43
|
+
sessionStorage.removeItem(key);
|
|
44
|
+
if (saved) { // the tab and the step that were open, where the list of steps was, then the page
|
|
45
|
+
for (const id of saved.picked || []) { const radio = document.getElementById(id); if (radio) radio.checked = true; }
|
|
46
|
+
document.querySelectorAll(".walk .wl").forEach((list, i) => { list.scrollTop = (saved.lists || [])[i] || 0; });
|
|
47
|
+
window.scrollTo(0, saved.y || 0);
|
|
48
|
+
}
|
|
49
|
+
const badge = document.getElementById("cn-live");
|
|
50
|
+
const events = new EventSource("/events");
|
|
51
|
+
let version = null;
|
|
52
|
+
events.onmessage = (e) => {
|
|
53
|
+
if (version !== null && e.data !== version) {
|
|
54
|
+
sessionStorage.setItem(key, JSON.stringify({
|
|
55
|
+
y: window.scrollY,
|
|
56
|
+
picked: [...document.querySelectorAll("input[type=radio]:checked")].map((radio) => radio.id),
|
|
57
|
+
lists: [...document.querySelectorAll(".walk .wl")].map((list) => list.scrollTop),
|
|
58
|
+
}));
|
|
59
|
+
location.reload();
|
|
60
|
+
}
|
|
61
|
+
version = e.data;
|
|
62
|
+
badge.textContent = "● live";
|
|
63
|
+
badge.style.color = "";
|
|
64
|
+
};
|
|
65
|
+
events.onerror = () => { badge.textContent = "○ server stopped"; badge.style.color = "#d97706"; };
|
|
66
|
+
})();
|
|
67
|
+
</script>
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def snapshot(root: Path) -> dict[str, tuple[int, int]]:
|
|
72
|
+
"""
|
|
73
|
+
Every .py file under root (skipping what read_project skips), and every deploy file (template.yaml,
|
|
74
|
+
serverless.yml), with its modified time and size.
|
|
75
|
+
"""
|
|
76
|
+
found: dict[str, tuple[int, int]] = {}
|
|
77
|
+
deploy_names = deploy_file_names()
|
|
78
|
+
for folder, subfolders, names in os.walk(root):
|
|
79
|
+
subfolders[:] = [d for d in subfolders if d not in _SKIPPED_FOLDERS and not d.endswith(".egg-info")]
|
|
80
|
+
for name in names:
|
|
81
|
+
if not name.endswith(".py") and name not in deploy_names:
|
|
82
|
+
continue
|
|
83
|
+
path = os.path.join(folder, name)
|
|
84
|
+
try:
|
|
85
|
+
stat = os.stat(path)
|
|
86
|
+
except OSError:
|
|
87
|
+
continue
|
|
88
|
+
found[path] = (stat.st_mtime_ns, stat.st_size)
|
|
89
|
+
return found
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def changed_files(before: dict[str, tuple[int, int]], after: dict[str, tuple[int, int]]) -> list[str]:
|
|
93
|
+
"""
|
|
94
|
+
The files added, removed or changed between two snapshots.
|
|
95
|
+
"""
|
|
96
|
+
return sorted(path for path in before.keys() | after.keys() if before.get(path) != after.get(path))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _error_page(message: str, entry: EntryPoint) -> str:
|
|
100
|
+
return (
|
|
101
|
+
f"<!doctype html><meta charset='utf-8'>{FAVICON_LINK}<title>code-node</title><body style='font:15px system-ui;"
|
|
102
|
+
f"margin:40px;color-scheme:light dark'><h2>Couldn't draw {html.escape(entry.name)}</h2>"
|
|
103
|
+
f"<p>{html.escape(message)}</p><p>Fix it and save: the page comes back by itself.</p></body>"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class LivePage:
|
|
109
|
+
"""
|
|
110
|
+
The latest page, its version, and a condition the event streams wait on for the next one.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
root: Path
|
|
114
|
+
entry: EntryPoint
|
|
115
|
+
options: PageOptions
|
|
116
|
+
log: Callable[[str], None] = field(default=lambda line: print(line, flush=True))
|
|
117
|
+
page: str = ""
|
|
118
|
+
version: int = 0
|
|
119
|
+
changed: threading.Condition = field(default_factory=threading.Condition)
|
|
120
|
+
stopping: threading.Event = field(default_factory=threading.Event)
|
|
121
|
+
|
|
122
|
+
def build(self, reason: str = "") -> None:
|
|
123
|
+
"""
|
|
124
|
+
Makes the page again. When that fails, the error is shown in the page instead, and the server keeps going.
|
|
125
|
+
"""
|
|
126
|
+
started = time.monotonic()
|
|
127
|
+
try:
|
|
128
|
+
page, cmap = make_page(self.root, self.entry, self.options)
|
|
129
|
+
said = f"{len(cmap.functions)} functions, {len(cmap.aws)} AWS calls"
|
|
130
|
+
except Exception as exc: # noqa: BLE001 any failure becomes the page, never a dead server
|
|
131
|
+
page, said = _error_page(f"{type(exc).__name__}: {exc}", self.entry), f"failed: {type(exc).__name__}"
|
|
132
|
+
# the folder's name leads the tab title, to tell apart the tabs of several servers
|
|
133
|
+
page = page.replace("<title>", f"<title>{html.escape(self.root.name)} · ", 1)
|
|
134
|
+
page = page.replace("</body>", _LIVE_SCRIPT + "</body>", 1) if "</body>" in page else page + _LIVE_SCRIPT
|
|
135
|
+
with self.changed:
|
|
136
|
+
self.page = page
|
|
137
|
+
self.version += 1
|
|
138
|
+
self.changed.notify_all()
|
|
139
|
+
took = time.monotonic() - started
|
|
140
|
+
self.log(f"{time.strftime('%H:%M:%S')} {reason or 'drawn'}: {said} ({took:.1f}s)")
|
|
141
|
+
|
|
142
|
+
def wait_for_change(self, version: int, timeout: float) -> int:
|
|
143
|
+
"""
|
|
144
|
+
The version after `version`, once there is one, or `version` again after timeout.
|
|
145
|
+
"""
|
|
146
|
+
with self.changed:
|
|
147
|
+
self.changed.wait_for(lambda: self.version != version or self.stopping.is_set(), timeout)
|
|
148
|
+
return self.version
|
|
149
|
+
|
|
150
|
+
def watch(self) -> None:
|
|
151
|
+
"""
|
|
152
|
+
Checks the folder every POLL_SECONDS and builds the page again when a .py file changes.
|
|
153
|
+
"""
|
|
154
|
+
before = snapshot(self.root)
|
|
155
|
+
while not self.stopping.wait(POLL_SECONDS):
|
|
156
|
+
after = snapshot(self.root)
|
|
157
|
+
if after == before:
|
|
158
|
+
continue
|
|
159
|
+
time.sleep(SETTLE_SECONDS)
|
|
160
|
+
after = snapshot(self.root)
|
|
161
|
+
changed = changed_files(before, after)
|
|
162
|
+
before = after
|
|
163
|
+
if changed:
|
|
164
|
+
names = [os.path.relpath(path, self.root) for path in changed]
|
|
165
|
+
self.build(", ".join(names[:3]) + (f" and {len(names) - 3} more" if len(names) > 3 else ""))
|
|
166
|
+
|
|
167
|
+
def stop(self) -> None:
|
|
168
|
+
self.stopping.set()
|
|
169
|
+
with self.changed:
|
|
170
|
+
self.changed.notify_all()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _handler(live: LivePage) -> type[BaseHTTPRequestHandler]:
|
|
174
|
+
class Handler(BaseHTTPRequestHandler):
|
|
175
|
+
def do_GET(self) -> None: # noqa: N802 the name http.server calls
|
|
176
|
+
if self.path.split("?")[0] == "/events":
|
|
177
|
+
self._events()
|
|
178
|
+
elif self.path.split("?")[0] in ("/", "/index.html"):
|
|
179
|
+
body = live.page.encode("utf-8")
|
|
180
|
+
self.send_response(200)
|
|
181
|
+
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
182
|
+
self.send_header("Cache-Control", "no-store")
|
|
183
|
+
self.send_header("Content-Length", str(len(body)))
|
|
184
|
+
self.end_headers()
|
|
185
|
+
self.wfile.write(body)
|
|
186
|
+
elif self.path.split("?")[0] in ("/favicon.ico", "/favicon.svg"):
|
|
187
|
+
body = FAVICON_SVG.encode("utf-8")
|
|
188
|
+
self.send_response(200)
|
|
189
|
+
self.send_header("Content-Type", "image/svg+xml")
|
|
190
|
+
self.send_header("Cache-Control", "max-age=86400")
|
|
191
|
+
self.send_header("Content-Length", str(len(body)))
|
|
192
|
+
self.end_headers()
|
|
193
|
+
self.wfile.write(body)
|
|
194
|
+
else:
|
|
195
|
+
self.send_error(404)
|
|
196
|
+
|
|
197
|
+
def _events(self) -> None:
|
|
198
|
+
self.send_response(200)
|
|
199
|
+
self.send_header("Content-Type", "text/event-stream")
|
|
200
|
+
self.send_header("Cache-Control", "no-store")
|
|
201
|
+
self.end_headers()
|
|
202
|
+
version = live.version
|
|
203
|
+
try:
|
|
204
|
+
self.wfile.write(f"data: {version}\n\n".encode())
|
|
205
|
+
self.wfile.flush()
|
|
206
|
+
while not live.stopping.is_set():
|
|
207
|
+
newer = live.wait_for_change(version, HEARTBEAT_SECONDS)
|
|
208
|
+
message = f"data: {newer}\n\n" if newer != version else ": still here\n\n"
|
|
209
|
+
self.wfile.write(message.encode())
|
|
210
|
+
self.wfile.flush()
|
|
211
|
+
version = newer
|
|
212
|
+
except (BrokenPipeError, ConnectionResetError):
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
def log_message(self, format: str, *args: object) -> None: # noqa: A002 quiet: the watcher says what matters
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
return Handler
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class _Server(ThreadingHTTPServer):
|
|
222
|
+
daemon_threads = True
|
|
223
|
+
# on Windows, SO_REUSEADDR lets a second server take a port that's in use, so one folder would hide another
|
|
224
|
+
allow_reuse_address = os.name != "nt"
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def listen(handler: type[BaseHTTPRequestHandler], port: int | None = None) -> ThreadingHTTPServer:
|
|
228
|
+
"""
|
|
229
|
+
A server on 127.0.0.1: on `port` when one is given (0 is any free port), or else the first free port from
|
|
230
|
+
DEFAULT_PORT, so another code-node --serve already running elsewhere doesn't stop this one.
|
|
231
|
+
"""
|
|
232
|
+
if port is not None:
|
|
233
|
+
return _Server(("127.0.0.1", port), handler)
|
|
234
|
+
for candidate in range(DEFAULT_PORT, DEFAULT_PORT + PORTS_TRIED):
|
|
235
|
+
try:
|
|
236
|
+
return _Server(("127.0.0.1", candidate), handler)
|
|
237
|
+
except OSError:
|
|
238
|
+
continue
|
|
239
|
+
return _Server(("127.0.0.1", 0), handler)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def serve(
|
|
243
|
+
root: Path,
|
|
244
|
+
entry: EntryPoint,
|
|
245
|
+
options: PageOptions,
|
|
246
|
+
*,
|
|
247
|
+
port: int | None = None,
|
|
248
|
+
open_page: Callable[[str], None] | None = None,
|
|
249
|
+
) -> int:
|
|
250
|
+
"""
|
|
251
|
+
Serves the page on http://localhost:<port> until Ctrl-C, drawing it again on every change under root. Without a
|
|
252
|
+
port, it's the first free one from DEFAULT_PORT.
|
|
253
|
+
"""
|
|
254
|
+
live = LivePage(root, entry, options)
|
|
255
|
+
live.build()
|
|
256
|
+
try:
|
|
257
|
+
server = listen(_handler(live), port)
|
|
258
|
+
except OSError as exc:
|
|
259
|
+
print(
|
|
260
|
+
f"Can't listen on port {port if port is not None else DEFAULT_PORT} ({exc.strerror}): pick another with "
|
|
261
|
+
"--port."
|
|
262
|
+
)
|
|
263
|
+
return 1
|
|
264
|
+
url = f"http://localhost:{server.server_address[1]}/"
|
|
265
|
+
watcher = threading.Thread(target=live.watch, name="code-node-watch", daemon=True)
|
|
266
|
+
watcher.start()
|
|
267
|
+
print(f"Serving {entry.name} at {url} (watching {root}; Ctrl-C to stop)", flush=True)
|
|
268
|
+
if open_page is not None:
|
|
269
|
+
open_page(url)
|
|
270
|
+
try:
|
|
271
|
+
server.serve_forever(poll_interval=0.25)
|
|
272
|
+
except KeyboardInterrupt:
|
|
273
|
+
print("\nStopped.")
|
|
274
|
+
finally:
|
|
275
|
+
live.stop()
|
|
276
|
+
server.server_close()
|
|
277
|
+
return 0
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: code-node-graph
|
|
3
|
+
Version: 0.1.0
|
|
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
|
+
Project-URL: Homepage, https://github.com/utkarsh5026/code-node
|
|
6
|
+
Project-URL: Documentation, https://utkarsh5026.github.io/code-node/
|
|
7
|
+
Project-URL: Changelog, https://github.com/utkarsh5026/code-node/blob/main/CHANGELOG.md
|
|
8
|
+
Project-URL: Issues, https://github.com/utkarsh5026/code-node/issues
|
|
9
|
+
Author: Utkarsh Priyadarshi
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ast,aws,boto3,call graph,code map,lambda,static analysis,visualization
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
25
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: botocore>=1.35.72
|
|
28
|
+
Requires-Dist: pyyaml>=6.0
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
<div align="center">
|
|
32
|
+
|
|
33
|
+
<img src="https://raw.githubusercontent.com/utkarsh5026/code-node/main/docs/images/logo.svg" width="72" alt="">
|
|
34
|
+
|
|
35
|
+
# code-node
|
|
36
|
+
|
|
37
|
+
**See how Python code works, without running it.**
|
|
38
|
+
|
|
39
|
+
[](https://pypi.org/project/code-node-graph/)
|
|
40
|
+
[](https://pypi.org/project/code-node-graph/)
|
|
41
|
+
[](LICENSE)
|
|
42
|
+
|
|
43
|
+
[Docs](https://utkarsh5026.github.io/code-node/) · [Plugins](https://utkarsh5026.github.io/code-node/plugins.html) · [How it works](https://utkarsh5026.github.io/code-node/how-it-works.html) · [Changelog](CHANGELOG.md)
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
Point it at a repo. Get a page showing where the code starts, who calls whom, and what each path touches: AWS, HTTP,
|
|
48
|
+
environment variables.
|
|
49
|
+
|
|
50
|
+
<picture>
|
|
51
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/utkarsh5026/code-node/main/docs/images/graph-dark.webp">
|
|
52
|
+
<img alt="code-node's graph of a small Lambda function: an S3 event comes in, the handler calls its own functions, and they reach S3, DynamoDB, SNS and the web" src="https://raw.githubusercontent.com/utkarsh5026/code-node/main/docs/images/graph-light.webp">
|
|
53
|
+
</picture>
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install code-node-graph # or: uv tool install code-node-graph
|
|
59
|
+
cd your/repo && code-node
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
> [!NOTE]
|
|
63
|
+
> Nothing is run. Code is read with `ast`, AWS operations come from botocore's models on disk. No imports, no requests.
|
|
64
|
+
|
|
65
|
+
## What you get
|
|
66
|
+
|
|
67
|
+
| | |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| 🗺️ **Graph** | What comes in, who calls whom, what each path reaches. AWS calls coloured by kind (read, write, send, run); ↻ marks loops |
|
|
70
|
+
| 👣 **Step by step** | Walk the code in run order, like a debugger: Next, Skip call, Finish, arrow keys |
|
|
71
|
+
| 🔌 **Plugins** | boto3, Redis, SQL (sqlite3, psycopg, PyMySQL, asyncpg, SQLAlchemy), MongoDB, OpenAI, Anthropic |
|
|
72
|
+
| 📄 **Deploy checks** | Reads SAM `template.yaml` / `serverless.yml`: unset variables, IAM calls the role won't allow, timeouts that don't fit |
|
|
73
|
+
| 🚨 **Findings** | SQL or shell commands built from input, clients with no timeout, queries in loops |
|
|
74
|
+
| 🧜 **Mermaid** | Paste the graph into Markdown, a wiki or a PR |
|
|
75
|
+
| 🔁 **Live** | `--serve` redraws on every save |
|
|
76
|
+
|
|
77
|
+
<picture>
|
|
78
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/utkarsh5026/code-node/main/docs/images/steps-dark.webp">
|
|
79
|
+
<img alt="The Step by step tab: every step in the order the code runs on the left, and on the right the function the current one is in, its line lit" src="https://raw.githubusercontent.com/utkarsh5026/code-node/main/docs/images/steps-light.webp">
|
|
80
|
+
</picture>
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
code-node # find entry points and pick one
|
|
86
|
+
code-node --list # just list them
|
|
87
|
+
code-node src/app/main.py:run # start from a function
|
|
88
|
+
code-node src/app/store.py:Store.save # ...or a method
|
|
89
|
+
code-node -e save # part of a name
|
|
90
|
+
code-node --serve # live page on localhost:8765
|
|
91
|
+
code-node --mermaid # print the graph as Mermaid
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
<details>
|
|
95
|
+
<summary><b>Entry points it finds</b></summary>
|
|
96
|
+
|
|
97
|
+
Console scripts in any `pyproject.toml` (monorepos too), functions named in SAM / Serverless files, Lambda handlers,
|
|
98
|
+
FastAPI / Flask routes, Django `urls.py` files, Typer commands, `if __name__ == "__main__":` blocks, and functions named
|
|
99
|
+
`main`. Test files are skipped. A web app's routes are read as called with the request, so SQL built from a query
|
|
100
|
+
string is flagged.
|
|
101
|
+
|
|
102
|
+
</details>
|
|
103
|
+
|
|
104
|
+
<details>
|
|
105
|
+
<summary><b>All options</b></summary>
|
|
106
|
+
|
|
107
|
+
| Option | What it does |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| `path` | Folder to read, or a file to start from (`main.py`, `main.py:function`) |
|
|
110
|
+
| `-e`, `--entry` | `module.function`, `module.Class.method`, `file.py[:function]`, or part of a name |
|
|
111
|
+
| `-l`, `--list` | List entry points and stop |
|
|
112
|
+
| `--plugins` | List platforms, libraries and deploy files and stop |
|
|
113
|
+
| `--all` | Draw every function (default: 24 functions, 5 calls deep) |
|
|
114
|
+
| `--depth N`, `--functions N` | Your own limits |
|
|
115
|
+
| `--serve`, `--port N` | Serve on localhost, redraw on change |
|
|
116
|
+
| `--mermaid` | Print the graph as Mermaid |
|
|
117
|
+
| `-o`, `--out`, `--no-open` | Where to save (default `~/.cache/code-node/`), and whether to open it |
|
|
118
|
+
| `--platform NAME`, `--lambda` | Run a platform's checks on any entry point |
|
|
119
|
+
| `--editor vscode\|cursor\|none` | Editor the line links open (default `vscode`, WSL-aware) |
|
|
120
|
+
|
|
121
|
+
</details>
|
|
122
|
+
|
|
123
|
+
<details>
|
|
124
|
+
<summary><b>From Python</b></summary>
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from code_node import code_graph, map_code, read_project
|
|
128
|
+
|
|
129
|
+
project = read_project(".")
|
|
130
|
+
cmap = map_code(project.files, "pkg.cli.main", libraries=project.libraries())
|
|
131
|
+
print([call.label for call in cmap.aws])
|
|
132
|
+
print(code_graph(cmap).mermaid())
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
</details>
|
|
136
|
+
|
|
137
|
+
## Plugins that mix
|
|
138
|
+
|
|
139
|
+
One platform, any number of libraries and deploy files, checked together. On
|
|
140
|
+
[`examples/cart-service`](examples/cart-service):
|
|
141
|
+
|
|
142
|
+
<picture>
|
|
143
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/utkarsh5026/code-node/main/docs/images/plugins-mix-dark.svg">
|
|
144
|
+
<img alt="The cart example: template.yaml, AWS Lambda and its libraries read on_checkout(), and together find a role that doesn't allow two AWS calls, two unset variables, SQL injection, a client with no timeout, a connection on every call and a query run once per item" src="https://raw.githubusercontent.com/utkarsh5026/code-node/main/docs/images/plugins-mix-light.svg">
|
|
145
|
+
</picture>
|
|
146
|
+
|
|
147
|
+
## Limits
|
|
148
|
+
|
|
149
|
+
It can't follow values from outside the code (a client a library hands back, a function picked from a registry at
|
|
150
|
+
run time), installed libraries' own code, or `getattr` with a computed name. **A page can miss calls, but everything
|
|
151
|
+
it shows is in the code.**
|
|
152
|
+
|
|
153
|
+
## Develop
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
make install # .venv with the dev tools (needs uv)
|
|
157
|
+
make check # everything CI runs
|
|
158
|
+
make shots # remake screenshots (needs Chrome)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`python makefile.py <task>` does the same on any OS.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
<sub>Extracted from [aws-analyzer](https://github.com/utkarsh5026/aws-analyzer)'s `how_it_works()`. Apache 2.0.</sub>
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
code_node/__init__.py,sha256=P00enhUnqmeAPpAajqOLb8MJQ7ljxA1RS4mSfcurHPU,1106
|
|
2
|
+
code_node/__main__.py,sha256=0W2bmerF1S96rjlTNJZKVzO4FvVhN6ENzAl451A08sQ,101
|
|
3
|
+
code_node/cli.py,sha256=GI0vZY1t4Pf7qtZI2coTpWZi-ulQEWNREDJPkTtmxG0,12302
|
|
4
|
+
code_node/pages.py,sha256=2UOrHUz3G_vAjPO89phJxhF4652FysVLgrLtJ-Iw6p4,3234
|
|
5
|
+
code_node/project.py,sha256=1s6pBwQ-hiEf9v0YzE8b6TETT9E0XBnzQPWGjYDJsnw,16361
|
|
6
|
+
code_node/serve.py,sha256=T09QSIuO1uR40xe9wD7btCroVFwEScZadfS71KZB_yQ,11468
|
|
7
|
+
code_node/analysis/__init__.py,sha256=-KM77rYv9pPutUG29DsU8ZkAUtr2AU9RsqrDPFAjLoQ,103
|
|
8
|
+
code_node/analysis/aws_model.py,sha256=R2sHppdu9PmlsOGDXf6_6JHumbA5PqbiJ0JgnyB9Xcs,6648
|
|
9
|
+
code_node/analysis/findings.py,sha256=S1-VQ1iNi1PkC-2eigNzbJrULa_2Y46avmtwAlV9iqU,19010
|
|
10
|
+
code_node/analysis/graph.py,sha256=obWKdYHXGEe_yL6YZAAWq0BJ1bTG0oPv9TVYAABRkrU,33624
|
|
11
|
+
code_node/analysis/mapping.py,sha256=6wYkUqaQdM-Vio8XGf_Lw8N9dP_90YtbUxauH4IQ8Tg,5102
|
|
12
|
+
code_node/analysis/model.py,sha256=ORGT6BEQFonQjdRGPEbuGVGoyyK54nTEA4Th-7rEGvU,13614
|
|
13
|
+
code_node/analysis/reader.py,sha256=jVm4DSHlM9X7C2Rm1H7GpJ29_cXOSUNpr71Y1xZ35ws,88119
|
|
14
|
+
code_node/analysis/syntax.py,sha256=wyeUr68IDjGp3i9HYYYzcjw8-CvL6a6mmZdWz5LY8j8,7952
|
|
15
|
+
code_node/analysis/terms.py,sha256=ec0f51DkfDRixJSRX1hnyq9Hr5aLRhXuBRHlNJBlZD8,13782
|
|
16
|
+
code_node/deploys/__init__.py,sha256=QEH2oQNPACPekg6QSBpfSbqkuhrdNDpelhn_VaY0RNs,11572
|
|
17
|
+
code_node/deploys/sam.py,sha256=rFb8aQXmvb5IqGvJtc4840IL6CufanN8cOQriw5TPU0,15154
|
|
18
|
+
code_node/deploys/serverless.py,sha256=lX3fMKfhXpkGhAKbXoo3gxTzjUt37RYid5j1GLCMneo,5564
|
|
19
|
+
code_node/kit/__init__.py,sha256=yVmFVUR8Lj4TieIFUeIChvaqeE-RWRV67dYgBT5naog,150
|
|
20
|
+
code_node/kit/blocks.py,sha256=jup7Z5s1pqDZcJzFAfwVNG51ZhjEEjA7z2s6_CMTebg,5825
|
|
21
|
+
code_node/kit/fmt.py,sha256=T902zI5_m-biagzKfyldgjo35Siu-pq0oLQOcJC5a4Y,14309
|
|
22
|
+
code_node/kit/highlight.py,sha256=bZVJw3Bcu6IZLc4IBKcVoSP0iwn1__y7Tf4pr7pHBvE,4999
|
|
23
|
+
code_node/kit/render.py,sha256=JcSNjBIIP2uC7Jaun-tkerGIzALTU5TH-C_tkKizaeQ,49407
|
|
24
|
+
code_node/kit/text.py,sha256=Em8FrLury0hQplXQuIFgCHDtAbzN5PbxBnmvSt4yWKQ,2997
|
|
25
|
+
code_node/libraries/__init__.py,sha256=A2Z6Ien4EX7Wsk88RyLBcqHifO3WIxdQUNZQXt_mEWs,13979
|
|
26
|
+
code_node/libraries/llm.py,sha256=L_jaIT3-Y9HBK9A8uuaORyqTRGdwiqHLdslOkaE7EIw,4313
|
|
27
|
+
code_node/libraries/mongo.py,sha256=R47W8BW4zyukP_zhJ7VDHHuUWb-G3HA_9HDkAKF8Ptk,5289
|
|
28
|
+
code_node/libraries/redis.py,sha256=tbDV430dYX7CkMuJRnIKSMxAqzyCpFtPTgQ92QrvxLM,6957
|
|
29
|
+
code_node/libraries/sql.py,sha256=QS34ODcYBf1yWwQVDcu8HbqNzNHiaKO61QOVoAqMWA4,9210
|
|
30
|
+
code_node/platforms/__init__.py,sha256=dqQ7OZu8p0K5E8y76AGweC-5twESwXF1QYQlUjuQjl8,7993
|
|
31
|
+
code_node/platforms/aws_lambda.py,sha256=IoYxJ2IPNkuxXMWSRK5mxaaDtmKQsyq7Q1EkXCptkC4,12643
|
|
32
|
+
code_node/platforms/web.py,sha256=lNjwkz3D0zJkFcJhqD_ShcfTj4uF8miLNqKMtXu-t1U,11762
|
|
33
|
+
code_node/report/__init__.py,sha256=l3dfZFbLEo-7Fl7O_Rk2lvyAElQnyugSZPI_PzHrG_Q,76
|
|
34
|
+
code_node/report/brands.py,sha256=n32BfFz2d02ZVUHvy8L4wPHBtiD_1FCTKzdR4NyvU88,8372
|
|
35
|
+
code_node/report/build.py,sha256=F0wdC0ibu3WSAenUsovUWuKwLC0vVxnNYKJVXtU5jRk,4644
|
|
36
|
+
code_node/report/editor.py,sha256=TRUX6BTB-oe-m5Vf3xM9PppnC99sCLL4PObXkYAGKoU,1466
|
|
37
|
+
code_node/report/graph.py,sha256=bLbsB8YxByX7RWH9Hmyc76aq6VHsllgcPyIKQpszW6Y,60090
|
|
38
|
+
code_node/report/outline.py,sha256=3RRxU4FMUZKBVjFDsscKZkvDlj_W4ZeW4DwUztg4wd0,8539
|
|
39
|
+
code_node/report/style.py,sha256=rpubo57Qbgp1m4zsn4sfBikkzNSJuRTNXC__07qylEs,31021
|
|
40
|
+
code_node/report/tabs.py,sha256=K2XR16owfgEL1kfux61PI9nBcRsgjQurTx1ptMM5Mac,3016
|
|
41
|
+
code_node/report/walk.py,sha256=jumSV-x1X0D614V7K0zECCSWk69OkHNR7IuDt8K4ggg,27516
|
|
42
|
+
code_node/report/logos/README.md,sha256=8Evw8eApl_56QJrwY7LAtGgJMZE0mp0-HIpRz9bu1ME,613
|
|
43
|
+
code_node/report/logos/anthropic-icon.svg,sha256=-_25zx86lfDTm-ksVXkLE4uQ_ldzLbTd3U0z-wjT-JA,302
|
|
44
|
+
code_node/report/logos/aws-amplify.svg,sha256=62WXM0XCn4WrQYWcVZFz4ln62xidiqF8eO4ELlgO8hg,531
|
|
45
|
+
code_node/report/logos/aws-api-gateway.svg,sha256=1uSI8-aIPftVXFLydyaxdTiN0SRjVNfTUbiFjsWOrv8,1444
|
|
46
|
+
code_node/report/logos/aws-appflow.svg,sha256=19wSNebSBMVUaX56TbsEBc4EjQ1maHyvHzU-OqN2XB8,773
|
|
47
|
+
code_node/report/logos/aws-appsync.svg,sha256=KIhLt9OnaUkBNSTnNn_538YITt5yAOcIYZD_pcORAXs,2041
|
|
48
|
+
code_node/report/logos/aws-athena.svg,sha256=40qBlwQWqyZGeF4MFAMF7iOKObsEquppBdNIBxPjLUU,2651
|
|
49
|
+
code_node/report/logos/aws-backup.svg,sha256=ljFEzYRarauLzrsbLyCL_8Ycq_5kZeKTfZgkH-SlL1M,2820
|
|
50
|
+
code_node/report/logos/aws-batch.svg,sha256=lAzl_hbZeWfpljQU_YuH-2EOcgEeXNtzUl6z0z1IILA,2718
|
|
51
|
+
code_node/report/logos/aws-certificate-manager.svg,sha256=Ry_IVyMIQc9Ovt4hyW6nnlnuD4uM8xMqali9v_uXxI0,1369
|
|
52
|
+
code_node/report/logos/aws-cloudformation.svg,sha256=kt0knvLxq72vj4WR4PwyFUxN6wcTzVym9OXvZvT615A,1405
|
|
53
|
+
code_node/report/logos/aws-cloudfront.svg,sha256=vupkivjNK6JmH9lmxTcS9Qwd5aYbVEsI4P4wL4Qmh5I,2754
|
|
54
|
+
code_node/report/logos/aws-cloudtrail.svg,sha256=VjJnfFps965I6B6qGb7YQwaIDrGj4GDD3bdhVNBzV1w,1357
|
|
55
|
+
code_node/report/logos/aws-cloudwatch.svg,sha256=vOIflgaFfGRUHSsZO3XGZuAcRQjt9J4kxV8voeXY8RY,2283
|
|
56
|
+
code_node/report/logos/aws-codebuild.svg,sha256=4L2-irxxVRB7fQovxYCeZ3-WB88OiH44ukFSyoqSGpo,1724
|
|
57
|
+
code_node/report/logos/aws-codecommit.svg,sha256=ZQAAjKiWwACw88NBuFFHZLkEBdTY8tSVpb-Z9qMKlJQ,2394
|
|
58
|
+
code_node/report/logos/aws-codedeploy.svg,sha256=G2wgdValPw5aJh0G5A_vHEG-GX9yZkBDmIlyKY-nRWo,2248
|
|
59
|
+
code_node/report/logos/aws-codepipeline.svg,sha256=KlvhgB7kOSY9jklPI98e9ugYrOMq_wFc6Ffrb1H2iX8,1245
|
|
60
|
+
code_node/report/logos/aws-cognito.svg,sha256=EV3wCl9J_aZmTqvswMI9PaUEopsUyGimBAbSvvXuWzs,2291
|
|
61
|
+
code_node/report/logos/aws-config.svg,sha256=-fT0rm7FD6ZTf421RIHlr-mt-9pmrSJi3mRNGaJXilk,4471
|
|
62
|
+
code_node/report/logos/aws-documentdb.svg,sha256=E_bENeNQXJSMhQbaS7153GqxHACd25KKhT7CTHbkTOs,2477
|
|
63
|
+
code_node/report/logos/aws-dynamodb.svg,sha256=DNn9RRGN-hvvS9kmNYYea3qAjEQgdTmqs03SIXncbCg,2978
|
|
64
|
+
code_node/report/logos/aws-ec2.svg,sha256=XRiIlleAfwq-bZZbtiOr7h-x918My6-gTTMMluTyrQs,1374
|
|
65
|
+
code_node/report/logos/aws-ecs.svg,sha256=px8zEqU8iU0o-3a0Px4zmCW80lDgzkUnOvUWMg7aq-c,1275
|
|
66
|
+
code_node/report/logos/aws-eks.svg,sha256=CcHY79tH8GmFRDjij8IcG026xd10iRLgX0QGG3ceou4,1377
|
|
67
|
+
code_node/report/logos/aws-elasticache.svg,sha256=ejCdYM3q30-ffvV2qVlyNjjt49r9WmvmOTuKK1nBpoM,2596
|
|
68
|
+
code_node/report/logos/aws-elb.svg,sha256=c7fglRgp3iaMBeYZVMfiDuzepu7_X1zlxfZfHxdx_SA,1425
|
|
69
|
+
code_node/report/logos/aws-eventbridge.svg,sha256=BtMGX7kEz3DMPJesBHRFyQLCqpM_ZC5qLQBA0Y_-rSg,2371
|
|
70
|
+
code_node/report/logos/aws-glacier.svg,sha256=yX2AbETb66qcCOWOoIUdhXH-0L_R5zBVAUumLeWXOIc,2150
|
|
71
|
+
code_node/report/logos/aws-glue.svg,sha256=q56b6eK-LaGKU87k5dsI5Qt8-xXDRZLSNnX1Lkv9tPc,1775
|
|
72
|
+
code_node/report/logos/aws-iam.svg,sha256=El6lDU3CbtSOpUMsH5eHoQ955XHwoqlqTXzfLz-QAro,1339
|
|
73
|
+
code_node/report/logos/aws-keyspaces.svg,sha256=SEYsde1L___gNeI_t8Qqj86O4zXo_h-HDrPwZBfn0Ew,1845
|
|
74
|
+
code_node/report/logos/aws-kinesis.svg,sha256=76QAAYrmkJepQV315EdYNPVcZqPr4XWWT2l0SnOr1E8,945
|
|
75
|
+
code_node/report/logos/aws-kms.svg,sha256=mM3GEFjzZTSHLM52OSjkWt-SwmoYvPN0RWrzrUboh_0,2080
|
|
76
|
+
code_node/report/logos/aws-lake-formation.svg,sha256=K1o5c1a6YvLxShKoehoOODzrhbIJ-L2KFPxY9D8gZuo,3414
|
|
77
|
+
code_node/report/logos/aws-lambda.svg,sha256=mwyJV4rRlOs6-_sdHuUGk1Bsd3SP1sC1J0fzmgmtrOI,1050
|
|
78
|
+
code_node/report/logos/aws-lightsail.svg,sha256=L8lxDkSspYh8Lv2LfdBPxdOXxVMP5SBVVL8FmpbDPMc,2299
|
|
79
|
+
code_node/report/logos/aws-mq.svg,sha256=EulNJDLmxWmXKAQexDCvXEhm2pg-OYg5SE2L73o5-sI,3357
|
|
80
|
+
code_node/report/logos/aws-msk.svg,sha256=7Kw3yQE1p_1X67_6Sch74PwfqEERW65etHWlYHo7MfM,3141
|
|
81
|
+
code_node/report/logos/aws-neptune.svg,sha256=KrQi3hymBY42Sqc20O0y58DSUesIjQkd9u5wsqR1IHw,2659
|
|
82
|
+
code_node/report/logos/aws-open-search.svg,sha256=exX4xtyoiPcg-uQp0S8go4-Yd43Gei-mY4NususajP0,1860
|
|
83
|
+
code_node/report/logos/aws-quicksight.svg,sha256=FCLpU3HwYcwzU26g85C1k6YtYXvXomvCUNK-hhNLnvk,1567
|
|
84
|
+
code_node/report/logos/aws-rds.svg,sha256=ZtafvulHWLfQMCbJ7mxfXWyy5rDbpH5IYcWtB0MPgCs,1911
|
|
85
|
+
code_node/report/logos/aws-redshift.svg,sha256=vk2MVxOrO8LyZ3owG2ayjSHCxnFasU2Ue-NUcUMbd88,2181
|
|
86
|
+
code_node/report/logos/aws-route53.svg,sha256=FG2_WmbO4WRBq13LWNPPdjrFQtTlH9KLtVbbxAILnJ4,3960
|
|
87
|
+
code_node/report/logos/aws-s3.svg,sha256=k3MUnJTngmwlJJ8_QBM0KenY8Ycv-B-wy04nxq9WFZo,1416
|
|
88
|
+
code_node/report/logos/aws-secrets-manager.svg,sha256=YClUcq0MgLEQ0xHaoy07Quc1I5XHlOt2Yp3g7VoHnXU,2259
|
|
89
|
+
code_node/report/logos/aws-ses.svg,sha256=uHCToydJugqBfjOexq35OGdhKbLSRFwbCB27ryN0o_c,1781
|
|
90
|
+
code_node/report/logos/aws-shield.svg,sha256=qh4eM8_uYv1hQTQjmri2WD66w8RwvF14g2rbfhtgnrE,1519
|
|
91
|
+
code_node/report/logos/aws-sns.svg,sha256=O-LtYQUeppnUOeZ2wN682dj5_rME9sVaqsZsCd8aJAU,2301
|
|
92
|
+
code_node/report/logos/aws-sqs.svg,sha256=rQo26rosGBsIHtcJAEoyLP9gQzdZJCcCx2ZJohAEYww,2872
|
|
93
|
+
code_node/report/logos/aws-step-functions.svg,sha256=tMqVEQV8HbIj9E-HsX-8DvSbH0XLO6yc4LZu40y31Fg,1494
|
|
94
|
+
code_node/report/logos/aws-systems-manager.svg,sha256=L-zUMbM32_mRFj-6IUhnh1kCFl_L8wuGOzse5wpRHv4,3761
|
|
95
|
+
code_node/report/logos/aws-timestream.svg,sha256=mJnDvNe8idlbFOlNloN8RnU0OvfVFO7yzasIt7BqIRA,1760
|
|
96
|
+
code_node/report/logos/aws-waf.svg,sha256=5ZzlZz5CAakmMzKV0zsF6_wSv8jnD18qgI7iaF9q5zM,2755
|
|
97
|
+
code_node/report/logos/aws-xray.svg,sha256=lrdG0luFEnzZLvNKZd7_kxRBoFh4Qp69aNzryyleJ48,1649
|
|
98
|
+
code_node/report/logos/aws.svg,sha256=QK2E9VAarPAeqY9nPIsHtSOBJmUgNG4m7354m1DvkgI,3816
|
|
99
|
+
code_node/report/logos/azure-icon.svg,sha256=V_jDFsb_wV1JQhbCf7-bX_cwiuEi6173Ma9SfMGK7CU,672
|
|
100
|
+
code_node/report/logos/django-icon.svg,sha256=Vo72-jDTuKoIuM412ifkPBW7nb4rDLdU75nDX2gjLWc,763
|
|
101
|
+
code_node/report/logos/elasticsearch.svg,sha256=mB1yl87GGG2-A5S8n1gEViMnt3TYKkqDRvaSifPymC0,678
|
|
102
|
+
code_node/report/logos/fastapi-icon.svg,sha256=e-2qbAO3AGAa8tFgVOM0SXCXAyBwhiGATf_SaTbSUFs,279
|
|
103
|
+
code_node/report/logos/firebase-icon.svg,sha256=Lwow-aBMo6VNVQPh38gTsX3U_cJWwBxPEhtDwek5LSU,1106
|
|
104
|
+
code_node/report/logos/flask.svg,sha256=dy8JvASaGWzXSjzVWf_lzj5Y8Vscwg6rj35TnV2qN4U,15324
|
|
105
|
+
code_node/report/logos/google-cloud.svg,sha256=LDI8bsIyHoAFEdlSR8hm6ufqw-EGdAPNbyxUOj7_f9U,1087
|
|
106
|
+
code_node/report/logos/kafka-icon.svg,sha256=9xeKe2ApE73zAcw0uGhiY7hxYotPT6kQw4K29Dey6BA,3562
|
|
107
|
+
code_node/report/logos/mariadb-icon.svg,sha256=wpNeh_wZy-N7fiZFFHFtA8vEDriSfo1zs82UiDjmxnU,2925
|
|
108
|
+
code_node/report/logos/mongodb-icon.svg,sha256=aXNQkJqhV45-J1l7QpyTu48gGVnsCbDkXLYaM0rzqh0,587
|
|
109
|
+
code_node/report/logos/mysql-icon.svg,sha256=dUhFjDlirCgNA_bcQg8Lu8GxiaHE1arxPwDbe2tdnws,3137
|
|
110
|
+
code_node/report/logos/openai-icon.svg,sha256=f_D3lsnSFeRiRKGGGxssaQqi6dTOrS4cLrMfMDyP0JU,1607
|
|
111
|
+
code_node/report/logos/postgresql.svg,sha256=SkR7xpqMeD85TvFvXxyTsSUfrDPQ-v8yIoXsKJDZnSU,6757
|
|
112
|
+
code_node/report/logos/python.svg,sha256=wKOEWws_mrBWn8CXkUq4wntq5fiDX24rqXMYLiOGIOQ,1378
|
|
113
|
+
code_node/report/logos/rabbitmq-icon.svg,sha256=LQZ9tX7EJHHIUBWSd_xFhVCsXK13WM0MzqRD5HCXDMg,673
|
|
114
|
+
code_node/report/logos/redis.svg,sha256=M4HkYfqKmVtvB91Bx06xQBQ91vuUV0NxRMJbfQb_17w,2694
|
|
115
|
+
code_node/report/logos/serverless.svg,sha256=GRaQjw4RC7ssehpTPctDCWIeMzcJC7Jr30cBQdNNdlQ,327
|
|
116
|
+
code_node/report/logos/slack-icon.svg,sha256=ZbiLddqLGHPYgqzGkb5xOJTAs7FyvZY967S29deaD6k,1273
|
|
117
|
+
code_node/report/logos/snowflake-icon.svg,sha256=yf8W5iiJj2TQS2VwAAexcrhpAgQ6As9o0GuXvtScVII,2693
|
|
118
|
+
code_node/report/logos/sqlite.svg,sha256=UOvDiZ8-z9rbntML8tRql8-KscfitSLanfcRi_F84O8,1833
|
|
119
|
+
code_node/report/logos/stripe.svg,sha256=luAlfB_NYEL7LsUp6sxGKN5_niBXU94DHWtTRofb0-M,1663
|
|
120
|
+
code_node/report/logos/supabase-icon.svg,sha256=g_SXDosqHnGM7rB3WXZR3-dXObeND_DAy7_vTSCp5pw,994
|
|
121
|
+
code_node/report/logos/twilio-icon.svg,sha256=TtpHPSzG7bizAx1kL6TJ1-cf3_yGeTfaJ-cU5FwcVqw,878
|
|
122
|
+
code_node/report/logos/small/aws-api-gateway.svg,sha256=3-jQVY-kPNeWHNSu_bkED6Ic2Vrz4dUTHEjRoFi7XYw,626
|
|
123
|
+
code_node/report/logos/small/aws-appflow.svg,sha256=_KJSeWntyOlRgp_usJaqyn-wUQLby9ZynSQvhXjbqy0,648
|
|
124
|
+
code_node/report/logos/small/aws-appsync.svg,sha256=oWJj3fXzHlpGb3PPrvpkFAHBRQWscqKm3wAueRPxHVQ,618
|
|
125
|
+
code_node/report/logos/small/aws-athena.svg,sha256=4SndORM4NKA31MRcYETisJFvZXkJ6qEw7gsbFhEE5Dg,631
|
|
126
|
+
code_node/report/logos/small/aws-backup.svg,sha256=9a4Xvh3K2Vz6IZzO1nODlzZ5XTFcOe8wNbIB_L_HUGk,655
|
|
127
|
+
code_node/report/logos/small/aws-batch.svg,sha256=_f1DOC8UC5z1wDEuyR1dyO2eih2yvVkKS-XrBjyQBKU,641
|
|
128
|
+
code_node/report/logos/small/aws-certificate-manager.svg,sha256=F-jl9FGB9nkDnLDOUeL-laS3n4hXRpXarWJtKIvS4t0,623
|
|
129
|
+
code_node/report/logos/small/aws-cloudformation.svg,sha256=ZsfAkSQOgWpbEIkR_qlPYiEB_FpJoZF5HdctwJKpu9A,662
|
|
130
|
+
code_node/report/logos/small/aws-cloudfront.svg,sha256=Th0vqYr-BwY60Ft22osjqATEK9oY-9okCqI2QZjB7Sg,624
|
|
131
|
+
code_node/report/logos/small/aws-cloudtrail.svg,sha256=KkwfVqX7swq5I3JB-5Ah5yPwtr7XHXRLaw9hriPK_Xo,603
|
|
132
|
+
code_node/report/logos/small/aws-cloudwatch.svg,sha256=nLCKaYqAR8QZWVnESCmNShQ3x_g5C_t2fgOTRuvn6lQ,603
|
|
133
|
+
code_node/report/logos/small/aws-codebuild.svg,sha256=Bkju0NabgIPpIRlmLJm9Z7NqnrmJTKG4pUpVjeYgkxY,652
|
|
134
|
+
code_node/report/logos/small/aws-codecommit.svg,sha256=3iYb9NqtJ-pVZOlTPsn-R_CjX3F3oiJ2ny5O0N_GFDs,603
|
|
135
|
+
code_node/report/logos/small/aws-codedeploy.svg,sha256=1XwK63wRfX17gsG5IkK2IlHlZy4RwwTjvCxlyY5CAlY,654
|
|
136
|
+
code_node/report/logos/small/aws-codepipeline.svg,sha256=d1aVZQnrl2b790ve8Dy2BHf4mTt03rXcyufE1I9FH7o,658
|
|
137
|
+
code_node/report/logos/small/aws-cognito.svg,sha256=nOoYkTbMBsTJ0AecdW_8MUd1NWqt70OYH_1Yk_UtOtQ,599
|
|
138
|
+
code_node/report/logos/small/aws-config.svg,sha256=kWwT9gPz8Zb3tBfIbpl94NUh25fo_1l09wQpAtx4pAg,595
|
|
139
|
+
code_node/report/logos/small/aws-documentdb.svg,sha256=Kpx4cY4hQcC1U9QkNlrRooQp0VmQcLUN4b4SbGxr3Jw,639
|
|
140
|
+
code_node/report/logos/small/aws-dynamodb.svg,sha256=p1Tc5VFowbOJxjJdqRu30PQ1cdeGuKDP2bgm2-RjT7U,635
|
|
141
|
+
code_node/report/logos/small/aws-ec2.svg,sha256=EbTHAku8ZoaZA4tZ_Q-wcIkSKnBspd9CE67eJvafWJE,586
|
|
142
|
+
code_node/report/logos/small/aws-ecs.svg,sha256=iWf-Jx2k0DT-tt82oSm4LPx5WCOV8Fg1rpyJzPrO-L8,637
|
|
143
|
+
code_node/report/logos/small/aws-eks.svg,sha256=tPi4AbQmsglDNiVWxM5h_-Tdm3WmRj-ZW7ijfSk26Uw,637
|
|
144
|
+
code_node/report/logos/small/aws-elasticache.svg,sha256=faW9ZVnQWy9a9NHOWt2-ceI7gk1GNcBAXh70VjVbf1k,641
|
|
145
|
+
code_node/report/logos/small/aws-elb.svg,sha256=z7aZXi6-bssr1SQaa1oefXkr869awpZPoE9B6TfxBgA,610
|
|
146
|
+
code_node/report/logos/small/aws-eventbridge.svg,sha256=U6xaHXbv2v6bPs7IK_6XlxIZmTOPKGQS5TpJw5UWgwk,582
|
|
147
|
+
code_node/report/logos/small/aws-glacier.svg,sha256=AL_ZZm0slRFDyvXmYprhYeN-PW4w0hYy35sCGOj19pQ,657
|
|
148
|
+
code_node/report/logos/small/aws-glue.svg,sha256=-iJ0cGbvO8h9E0OVgjixWGEl7HeJL5Fbo2YrbsbEgjk,642
|
|
149
|
+
code_node/report/logos/small/aws-iam.svg,sha256=0V5CeVl-oZxL_LiVItWdCt7aNB9xKBs1Iq7N-wM0Md8,591
|
|
150
|
+
code_node/report/logos/small/aws-keyspaces.svg,sha256=w5Fh4oG08RxJOPscZLWwtm-WimZQtfL2Ao9AY48NqFo,637
|
|
151
|
+
code_node/report/logos/small/aws-kinesis.svg,sha256=cM2L3sXB7Cib4dCiNrzthQSHYQmN4G3mJIeJiJHCPBM,582
|
|
152
|
+
code_node/report/logos/small/aws-kms.svg,sha256=x8nNV3JkRE2a1xidVRcAfiLiyjz9iQgf7dAWiBX-9Lg,591
|
|
153
|
+
code_node/report/logos/small/aws-lake-formation.svg,sha256=ZAPis7GtVt0GisU8TR6N05HEMKeIHrc4NU1TzAdvRys,611
|
|
154
|
+
code_node/report/logos/small/aws-lambda.svg,sha256=0m6RFUMmWtB_kMP4Z5ItJHoNJ-OqtABoJOul_HM37sM,557
|
|
155
|
+
code_node/report/logos/small/aws-mq.svg,sha256=27h8S73GsZXDg-60vq-xYyI83ak2m9lz1mYZOe6hWZA,572
|
|
156
|
+
code_node/report/logos/small/aws-msk.svg,sha256=ux81Qq1Lt4RLv086U_C8yew7RdZ-uV4vqy1TA_700ZI,574
|
|
157
|
+
code_node/report/logos/small/aws-neptune.svg,sha256=4jb9cUuqkEQcRTESj6lsjgllD0jhwDed6WFop2AVbu4,633
|
|
158
|
+
code_node/report/logos/small/aws-open-search.svg,sha256=PvEffllxTxdWpcIuwoi6sHS7lJzgp6enis8AsCXynjE,605
|
|
159
|
+
code_node/report/logos/small/aws-quicksight.svg,sha256=AKOPcXZZA_TrfZNXDEORQQlr54r-6vk8pNdap_NOrKg,603
|
|
160
|
+
code_node/report/logos/small/aws-rds.svg,sha256=IbRjAe319gTkIVIHM9t4BSxU-IfXO2jNijLt6THbVLY,625
|
|
161
|
+
code_node/report/logos/small/aws-redshift.svg,sha256=iRchIExEvvD3L0lYLMyJLAoUGx92LnwCZV5OFqxvDOI,635
|
|
162
|
+
code_node/report/logos/small/aws-route53.svg,sha256=P3R9f6eX7xEwkco_r_QZYHraJiRLbz14mFzjJCHmZas,618
|
|
163
|
+
code_node/report/logos/small/aws-s3.svg,sha256=Kdza1CWVmmGg3exdv6p2HrWCqUZ9fy554s3sP1XuZJA,647
|
|
164
|
+
code_node/report/logos/small/aws-secrets-manager.svg,sha256=WBf9AFFVfbHFsOIe20ing8aWDSVSpTHQogn6DDMCr9A,615
|
|
165
|
+
code_node/report/logos/small/aws-ses.svg,sha256=LNvdHkR3K7w5nWdFlSm0iRjRemVJ91zS3AKqDWj4c-E,566
|
|
166
|
+
code_node/report/logos/small/aws-shield.svg,sha256=jE3-Fsr5WowxDU55p7nU57Cr_SbOFna0nBJg_2pqeOE,597
|
|
167
|
+
code_node/report/logos/small/aws-sns.svg,sha256=WIY3D72deIlxZ-WLc9bUww902AikItRdRW-kPMKyeXI,566
|
|
168
|
+
code_node/report/logos/small/aws-sqs.svg,sha256=UUoSeRNPBN_syhoCPS7q8VVGoNxrmTaj_Em7Tz9R5is,574
|
|
169
|
+
code_node/report/logos/small/aws-step-functions.svg,sha256=a-UUy6Oyn6LNgGlYyafv-PLhMytq-rw_zOzpDrsoKbU,662
|
|
170
|
+
code_node/report/logos/small/aws-systems-manager.svg,sha256=9VzzFdTjaRZO9ulR8Ulp9ms7EN6RYzUzGxfQStK5NJg,615
|
|
171
|
+
code_node/report/logos/small/aws-timestream.svg,sha256=yKdKIj7pbooFqDKClEQxW9h7dYV5mnDn0RCHnLUDf8Q,639
|
|
172
|
+
code_node/report/logos/small/aws-waf.svg,sha256=l9ZzBZAl2g8bK1-m6A50ga37l5g1t-9Mj70bxWDykWE,591
|
|
173
|
+
code_node/report/logos/small/aws-xray.svg,sha256=VKg_QtKGZKDCbZvKBRDn7eReuNVbJpduBUVV8MQrZZU,591
|
|
174
|
+
code_node_graph-0.1.0.dist-info/METADATA,sha256=h488K0OzyyEL7CfcJxU8_HOogMK3qIP2Qm_tfK3hwuk,7619
|
|
175
|
+
code_node_graph-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
176
|
+
code_node_graph-0.1.0.dist-info/entry_points.txt,sha256=eEArp-uk7ZzODIWr6l62rI0-mIA0-mLDaqJEyKikPJw,49
|
|
177
|
+
code_node_graph-0.1.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
178
|
+
code_node_graph-0.1.0.dist-info/RECORD,,
|