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.
Files changed (178) hide show
  1. code_node/__init__.py +40 -0
  2. code_node/__main__.py +7 -0
  3. code_node/analysis/__init__.py +3 -0
  4. code_node/analysis/aws_model.py +197 -0
  5. code_node/analysis/findings.py +462 -0
  6. code_node/analysis/graph.py +780 -0
  7. code_node/analysis/mapping.py +114 -0
  8. code_node/analysis/model.py +289 -0
  9. code_node/analysis/reader.py +1926 -0
  10. code_node/analysis/syntax.py +220 -0
  11. code_node/analysis/terms.py +505 -0
  12. code_node/cli.py +316 -0
  13. code_node/deploys/__init__.py +321 -0
  14. code_node/deploys/sam.py +371 -0
  15. code_node/deploys/serverless.py +117 -0
  16. code_node/kit/__init__.py +4 -0
  17. code_node/kit/blocks.py +184 -0
  18. code_node/kit/fmt.py +389 -0
  19. code_node/kit/highlight.py +131 -0
  20. code_node/kit/render.py +1066 -0
  21. code_node/kit/text.py +81 -0
  22. code_node/libraries/__init__.py +333 -0
  23. code_node/libraries/llm.py +124 -0
  24. code_node/libraries/mongo.py +169 -0
  25. code_node/libraries/redis.py +253 -0
  26. code_node/libraries/sql.py +232 -0
  27. code_node/pages.py +92 -0
  28. code_node/platforms/__init__.py +203 -0
  29. code_node/platforms/aws_lambda.py +290 -0
  30. code_node/platforms/web.py +301 -0
  31. code_node/project.py +402 -0
  32. code_node/report/__init__.py +3 -0
  33. code_node/report/brands.py +244 -0
  34. code_node/report/build.py +119 -0
  35. code_node/report/editor.py +40 -0
  36. code_node/report/graph.py +1411 -0
  37. code_node/report/logos/README.md +8 -0
  38. code_node/report/logos/anthropic-icon.svg +1 -0
  39. code_node/report/logos/aws-amplify.svg +1 -0
  40. code_node/report/logos/aws-api-gateway.svg +1 -0
  41. code_node/report/logos/aws-appflow.svg +1 -0
  42. code_node/report/logos/aws-appsync.svg +1 -0
  43. code_node/report/logos/aws-athena.svg +1 -0
  44. code_node/report/logos/aws-backup.svg +1 -0
  45. code_node/report/logos/aws-batch.svg +1 -0
  46. code_node/report/logos/aws-certificate-manager.svg +1 -0
  47. code_node/report/logos/aws-cloudformation.svg +1 -0
  48. code_node/report/logos/aws-cloudfront.svg +1 -0
  49. code_node/report/logos/aws-cloudtrail.svg +1 -0
  50. code_node/report/logos/aws-cloudwatch.svg +1 -0
  51. code_node/report/logos/aws-codebuild.svg +1 -0
  52. code_node/report/logos/aws-codecommit.svg +1 -0
  53. code_node/report/logos/aws-codedeploy.svg +1 -0
  54. code_node/report/logos/aws-codepipeline.svg +1 -0
  55. code_node/report/logos/aws-cognito.svg +1 -0
  56. code_node/report/logos/aws-config.svg +1 -0
  57. code_node/report/logos/aws-documentdb.svg +1 -0
  58. code_node/report/logos/aws-dynamodb.svg +1 -0
  59. code_node/report/logos/aws-ec2.svg +1 -0
  60. code_node/report/logos/aws-ecs.svg +1 -0
  61. code_node/report/logos/aws-eks.svg +1 -0
  62. code_node/report/logos/aws-elasticache.svg +1 -0
  63. code_node/report/logos/aws-elb.svg +1 -0
  64. code_node/report/logos/aws-eventbridge.svg +1 -0
  65. code_node/report/logos/aws-glacier.svg +1 -0
  66. code_node/report/logos/aws-glue.svg +1 -0
  67. code_node/report/logos/aws-iam.svg +1 -0
  68. code_node/report/logos/aws-keyspaces.svg +1 -0
  69. code_node/report/logos/aws-kinesis.svg +1 -0
  70. code_node/report/logos/aws-kms.svg +1 -0
  71. code_node/report/logos/aws-lake-formation.svg +1 -0
  72. code_node/report/logos/aws-lambda.svg +1 -0
  73. code_node/report/logos/aws-lightsail.svg +1 -0
  74. code_node/report/logos/aws-mq.svg +1 -0
  75. code_node/report/logos/aws-msk.svg +1 -0
  76. code_node/report/logos/aws-neptune.svg +1 -0
  77. code_node/report/logos/aws-open-search.svg +1 -0
  78. code_node/report/logos/aws-quicksight.svg +1 -0
  79. code_node/report/logos/aws-rds.svg +1 -0
  80. code_node/report/logos/aws-redshift.svg +1 -0
  81. code_node/report/logos/aws-route53.svg +1 -0
  82. code_node/report/logos/aws-s3.svg +1 -0
  83. code_node/report/logos/aws-secrets-manager.svg +1 -0
  84. code_node/report/logos/aws-ses.svg +1 -0
  85. code_node/report/logos/aws-shield.svg +1 -0
  86. code_node/report/logos/aws-sns.svg +1 -0
  87. code_node/report/logos/aws-sqs.svg +1 -0
  88. code_node/report/logos/aws-step-functions.svg +1 -0
  89. code_node/report/logos/aws-systems-manager.svg +1 -0
  90. code_node/report/logos/aws-timestream.svg +1 -0
  91. code_node/report/logos/aws-waf.svg +1 -0
  92. code_node/report/logos/aws-xray.svg +1 -0
  93. code_node/report/logos/aws.svg +1 -0
  94. code_node/report/logos/azure-icon.svg +1 -0
  95. code_node/report/logos/django-icon.svg +1 -0
  96. code_node/report/logos/elasticsearch.svg +1 -0
  97. code_node/report/logos/fastapi-icon.svg +1 -0
  98. code_node/report/logos/firebase-icon.svg +1 -0
  99. code_node/report/logos/flask.svg +1 -0
  100. code_node/report/logos/google-cloud.svg +1 -0
  101. code_node/report/logos/kafka-icon.svg +1 -0
  102. code_node/report/logos/mariadb-icon.svg +1 -0
  103. code_node/report/logos/mongodb-icon.svg +1 -0
  104. code_node/report/logos/mysql-icon.svg +1 -0
  105. code_node/report/logos/openai-icon.svg +1 -0
  106. code_node/report/logos/postgresql.svg +1 -0
  107. code_node/report/logos/python.svg +1 -0
  108. code_node/report/logos/rabbitmq-icon.svg +1 -0
  109. code_node/report/logos/redis.svg +1 -0
  110. code_node/report/logos/serverless.svg +1 -0
  111. code_node/report/logos/slack-icon.svg +1 -0
  112. code_node/report/logos/small/aws-api-gateway.svg +1 -0
  113. code_node/report/logos/small/aws-appflow.svg +1 -0
  114. code_node/report/logos/small/aws-appsync.svg +1 -0
  115. code_node/report/logos/small/aws-athena.svg +1 -0
  116. code_node/report/logos/small/aws-backup.svg +1 -0
  117. code_node/report/logos/small/aws-batch.svg +1 -0
  118. code_node/report/logos/small/aws-certificate-manager.svg +1 -0
  119. code_node/report/logos/small/aws-cloudformation.svg +1 -0
  120. code_node/report/logos/small/aws-cloudfront.svg +1 -0
  121. code_node/report/logos/small/aws-cloudtrail.svg +1 -0
  122. code_node/report/logos/small/aws-cloudwatch.svg +1 -0
  123. code_node/report/logos/small/aws-codebuild.svg +1 -0
  124. code_node/report/logos/small/aws-codecommit.svg +1 -0
  125. code_node/report/logos/small/aws-codedeploy.svg +1 -0
  126. code_node/report/logos/small/aws-codepipeline.svg +1 -0
  127. code_node/report/logos/small/aws-cognito.svg +1 -0
  128. code_node/report/logos/small/aws-config.svg +1 -0
  129. code_node/report/logos/small/aws-documentdb.svg +1 -0
  130. code_node/report/logos/small/aws-dynamodb.svg +1 -0
  131. code_node/report/logos/small/aws-ec2.svg +1 -0
  132. code_node/report/logos/small/aws-ecs.svg +1 -0
  133. code_node/report/logos/small/aws-eks.svg +1 -0
  134. code_node/report/logos/small/aws-elasticache.svg +1 -0
  135. code_node/report/logos/small/aws-elb.svg +1 -0
  136. code_node/report/logos/small/aws-eventbridge.svg +1 -0
  137. code_node/report/logos/small/aws-glacier.svg +1 -0
  138. code_node/report/logos/small/aws-glue.svg +1 -0
  139. code_node/report/logos/small/aws-iam.svg +1 -0
  140. code_node/report/logos/small/aws-keyspaces.svg +1 -0
  141. code_node/report/logos/small/aws-kinesis.svg +1 -0
  142. code_node/report/logos/small/aws-kms.svg +1 -0
  143. code_node/report/logos/small/aws-lake-formation.svg +1 -0
  144. code_node/report/logos/small/aws-lambda.svg +1 -0
  145. code_node/report/logos/small/aws-mq.svg +1 -0
  146. code_node/report/logos/small/aws-msk.svg +1 -0
  147. code_node/report/logos/small/aws-neptune.svg +1 -0
  148. code_node/report/logos/small/aws-open-search.svg +1 -0
  149. code_node/report/logos/small/aws-quicksight.svg +1 -0
  150. code_node/report/logos/small/aws-rds.svg +1 -0
  151. code_node/report/logos/small/aws-redshift.svg +1 -0
  152. code_node/report/logos/small/aws-route53.svg +1 -0
  153. code_node/report/logos/small/aws-s3.svg +1 -0
  154. code_node/report/logos/small/aws-secrets-manager.svg +1 -0
  155. code_node/report/logos/small/aws-ses.svg +1 -0
  156. code_node/report/logos/small/aws-shield.svg +1 -0
  157. code_node/report/logos/small/aws-sns.svg +1 -0
  158. code_node/report/logos/small/aws-sqs.svg +1 -0
  159. code_node/report/logos/small/aws-step-functions.svg +1 -0
  160. code_node/report/logos/small/aws-systems-manager.svg +1 -0
  161. code_node/report/logos/small/aws-timestream.svg +1 -0
  162. code_node/report/logos/small/aws-waf.svg +1 -0
  163. code_node/report/logos/small/aws-xray.svg +1 -0
  164. code_node/report/logos/snowflake-icon.svg +1 -0
  165. code_node/report/logos/sqlite.svg +1 -0
  166. code_node/report/logos/stripe.svg +1 -0
  167. code_node/report/logos/supabase-icon.svg +1 -0
  168. code_node/report/logos/twilio-icon.svg +1 -0
  169. code_node/report/outline.py +219 -0
  170. code_node/report/style.py +358 -0
  171. code_node/report/tabs.py +73 -0
  172. code_node/report/walk.py +624 -0
  173. code_node/serve.py +277 -0
  174. code_node_graph-0.1.0.dist-info/METADATA +165 -0
  175. code_node_graph-0.1.0.dist-info/RECORD +178 -0
  176. code_node_graph-0.1.0.dist-info/WHEEL +4 -0
  177. code_node_graph-0.1.0.dist-info/entry_points.txt +2 -0
  178. 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
+ [![PyPI](https://img.shields.io/pypi/v/code-node-graph)](https://pypi.org/project/code-node-graph/)
40
+ [![Python](https://img.shields.io/pypi/pyversions/code-node-graph)](https://pypi.org/project/code-node-graph/)
41
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ code-node = code_node.cli:main