Documentation
¶
Overview ¶
Package parse is the structural layer: it walks a repo's source files and emits the skeleton — file / function / method / type nodes and the contains edges between them — using tree-sitter, config-driven per language.
It is deliberately NOT a call graph. Per docs/ARCHITECTURE.md the value lives in the contract layer (HTTP/gRPC/pub-sub), which pairs services by normalized keys and needs no cross-file call resolution. The skeleton is what the contract extractors hang their producer/consumer nodes onto, and what a reader navigates. Deterministic by design: no LLM, same input → same graph.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func LoadHTTPProfile ¶
LoadHTTPProfile reads a JSON array of HTTPClientProfile from path and appends the validated rows to the extension table. Returns the number loaded. A row with an empty name fails the whole load so a typo cannot silently disable an identity.
func LoadPubsubProfile ¶
LoadPubsubProfile reads a JSON array of PubsubPattern from path and appends the (validated) rows to the extension table. Returns the number of patterns loaded. The schema is exactly PubsubPattern's JSON form:
[{"pkg":"my/wrapper","ident":"PublishTo","shape":"call","argPos":1,"role":"produce"}]
Only shape ∈ {call, composite} and role ∈ {produce, consume} are accepted; a bad row fails the whole load so a typo can't silently disable a pattern.
func ParseDir ¶
ParseDir walks dir, parses every file a configured language owns, and returns the structural graph for one world (service/repo). World is the logical name of the service this directory IS (e.g. "checkout-service").
func ParseK8sManifests ¶
func ParseK8sManifests(dir string, preferEnvs []string) (*graph.HTTPRouting, error)
Rendered k8s manifests (`helm template` output, a GitOps deploy repo) carry a routing fact source code never has: the Ingress rules that mount a service's HTTP surface at an external base path. A client calls /public/widgets/1; the service's own routes and swagger document /widgets/{id}; only the Ingress knows /public belongs to that service. This file harvests that side input.
ParseK8sManifests walks dir recursively for YAML files (multi-doc streams supported) and reads two k8s-GENERIC object kinds — nothing here is specific to any house chart pipeline; whatever rendered the YAML, the objects are plain k8s API shapes:
- Ingress (networking.k8s.io/v1, v1beta1 backends tolerated): spec.rules[].host + http.paths[].{path, backend service} → one IngressRule per (host, prefix, service).
- Service (v1): metadata.name + spec.ports[].name — a port named "http" (or "http-*") marks the service as HTTP-hosting.
- Deployment / StatefulSet / DaemonSet (apps/v1): container env: entries with a LITERAL value whose name is host-shaped (contracts.IsHostKey) — the env-value table binding "this workload deploys with VAR = target-service". valueFrom (secret / configMapKeyRef) entries carry no literal and are skipped, never mis-harvested.
- ConfigMap (v1): flat data: string values that are host/URL-shaped under a host-shaped key. (Nested YAML-in-YAML config blobs are out of scope — a per-service inner format, low yield next to the uniform env: shape.)
preferEnvs biases the env-value table when a deploy tree renders SEVERAL environments side by side (…/staging/manifest.yaml, …/production/manifest.yaml): a binding whose file path contains one of these directory segments wins over one that doesn't; a (workload, var) still carrying multiple DISTINCT target values after that preference is dropped as ambiguous rather than guessed.
The result is a JOIN side input (graph.HTTPRouting) — manifests never become worlds or endpoint nodes. Documents of any other kind, malformed files, and non-YAML content are skipped silently: a deploy tree is full of CRDs this reader has no business failing on.
Types ¶
type HTTPClientProfile ¶
type HTTPClientProfile struct {
// Name is the instance identifier as it appears at call sites (the member-call
// operand): apiClient in apiClient.get("/x").
Name string `json:"name"`
// TreatAsClient marks Name as a client identity — its member calls route to the
// consumer path instead of the producer branch. Must be true for the entry to have
// effect (a false/omitted value is a no-op, reserved for future roles).
TreatAsClient bool `json:"treatAsClient"`
// PathPrefix is the static PATH component of the instance's baseURL, prepended to
// its relative call paths (optional; "" = none).
PathPrefix string `json:"pathPrefix"`
}
HTTPClientProfile is one profile entry AND the on-disk --http-profile schema (JSON, exported fields): it declares an instance name the static analysis cannot reach as an HTTP client, optionally with the static path prefix of its baseURL. A house wrapper whose client carries no axios/http token (so axios.create detection misses it) is expressible as data a caller supplies — never a code change here, and never a house name baked into lodestar (the profile file stays with the operator).
[{"name":"apiClient","treatAsClient":true,"pathPrefix":"/api/v2"}]
type Language ¶
Language is one language's extraction config: the file extensions it owns, the tree-sitter grammar, and an extractor that turns a parsed tree into nodes/edges. A new language is a new Language value — the grammars differ enough (Go's type name hangs off a nested type_spec; Python has no type decls) that a shared config DSL would guess wrong. Shared walking helpers live below.
type PubsubPattern ¶
type PubsubPattern struct {
// Pkg is an import-path substring the file must import for the pattern to apply
// ("" = any import — the bare-verb NATS/MQTT shapes). Scoping by import keeps two
// libraries' identically-named identifiers (sarama vs franz-go) from colliding.
Pkg string `json:"pkg"`
// Ident is the call's final selector segment (the verb) for Shape "call", or the
// composite-literal's type name for Shape "composite".
Ident string `json:"ident"`
// Shape is "call" (topic is a positional argument) or "composite" (topic is a
// struct field).
Shape string `json:"shape"`
// ArgPos is the positional index of the topic argument for Shape "call" —
// 0 for NATS nc.Publish("subj", …); 1 for a ctx-first wrapper Publish(ctx, "t", …).
ArgPos int `json:"argPos"`
// Field is the composite-literal field holding the topic for Shape "composite"
// ("Topic"); a dotted path ("TopicPartition.Topic") descends nested composites.
Field string `json:"field"`
// Role is "produce" or "consume".
Role string `json:"role"`
}
PubsubPattern describes one recognized producer/consumer call- or type-shape, data-driven so a new client library is a new table row, not a new switch case.
It is both the builtin table's element type and the on-disk `--pubsub-profile` schema (JSON, exported fields) — so a house wrapper the builtins can't know about (a ctx-first Publish at ArgPos 1, a functional-option SubscribeTopics) is expressible as data a caller supplies, never a code change here.
Source Files
¶
- config.go
- csharp.go
- csharp_grpc.go
- csharp_http.go
- csharp_pubsub.go
- golang.go
- golang_grpc.go
- golang_http.go
- golang_pubsub.go
- gomod.go
- helm.go
- http_resolve.go
- java.go
- java_grpc.go
- java_http.go
- java_pubsub.go
- k8smanifest.go
- openapi.go
- parse.go
- protobuf.go
- python.go
- python_decorators.go
- python_grpc.go
- python_http.go
- python_pkg.go
- python_pubsub.go
- sql_tables.go
- typescript.go
- typescript_grpc.go
- typescript_http.go
- typescript_pubsub.go
- urlexpr.go
- vue.go