class

EndpointOptimizer

Inherits Reference < Object

Endpoint optimization module that handles endpoint deduplication, URL combination, and path parameter extraction

Constants

ABSOLUTE_URL_RE = /\A[a-zA-Z][a-zA-Z0-9+.\-]*:\/\//

A URL that already carries its own scheme + authority (e.g. https://host/path). The HAR / OAS detectors emit these, so the optimizer must not prepend a target, collapse the // after the scheme, or treat the leading segment as a path.

AI_FOLD_PLACEHOLDER_RE = /\A(?::\w.*|\*\w*|\{[^}]*\}|<[^>]*>|\[[^\]]*\])\z/

:id, :id?, :id(\d+), *, *path, {id}, <int:id>, [id].

ANGLE_CONVERTER_TYPES = Set {"int", "str", "string", "slug", "uuid", "float", "bool", "path", "any"}

Converter names that can lead a <converter:name> placeholder. Same list as OutputBuilderOasCommon::PATH_CONVERTER_TYPES, so the two resolve a placeholder the same way.

ANGLE_PLACEHOLDER_RE = /<([^>]+)>/
BAZEL_DEPLOYABLE_RE = /\b[A-Za-z_]\w*_(?:binary|image)\s*\(/

A bare BUILD file is not a project. Every Bazel package has one, so treating the filename as a manifest would split one app per directory. The directory counts only when the file invokes a *_binary or *_image rule.

BRACE_CONSTRAINT_RE = /\A([A-Za-z_][A-Za-z0-9_]*):./
COLLECTION_NOISE_HEADERS = Set {"user-agent", "accept", "content-type", "host", "origin", "referer", "x-requested-with", "cookie"}

Generic request headers a browser/HTTP client always sends; they carry no endpoint-specific signal, so collection imports that surface them as params are treated as noise during dedup.

COLON_SEGMENT_RE = /\/:([^\/{}]+)/
CONVERTER_ARGS_RE = /\([^()]*\)/

Werkzeug converter arguments: int(signed=True), any(about, help).

CONVERTER_FIRST_TECHS = Set {"python_django", "python_flask", "python_quart"}

Frameworks whose <...> placeholders are always converter first: Django path() (<int:pk>) and Werkzeug-routed Flask/Quart (<int:id>). Their converters are user-extensible (register_converter(..., "yyyy"), app.url_map.converters), so the builtin list alone can't recognize a custom one; <yyyy:year> declares year, not yyyy.

GO_PACKAGE_PROBE_BYTES = 64 * 1024

How far into a Go file go_main_package? looks for the package clause.

PATH_PARAM_IDENT_RE = /\A[A-Za-z_][A-Za-z0-9_]*(?:-[A-Za-z0-9_]+)*/

A :name param name. Hyphens are part of the identifier — kebab-case path params are idiomatic in Clojure (/:artifact-id, /:group-id) and legal in several other route DSLs. Excluding - truncated artifact-id to artifact. A hyphen only joins the name when an identifier character follows it, though: Express's /:from-:to declares from and to around a literal -, not a param named from-.

PROJECT_MANIFEST_FILES = {"pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts", "shard.yml", "package.json", "go.mod", "Cargo.toml", "pyproject.toml", "requirements.txt", "setup.py", "setup.cfg", "Pipfile", "mix.exs"}
PROXY_ROUTE_TECHNOLOGIES = Set {"k8s_ingress", "envoy"}

Proxy configs whose ANY route is folded into the code routes at its path (expand_proxy_routes).

SPEC_ONLY_TECHNOLOGIES = Set {"oas2", "oas3", "openrpc", "raml", "smithy", "typespec"}

HTTP API contracts whose operations a code analyzer can report too. An endpoint only these (and other declarations) produced, in a scan where code analyzers did report routes, is tagged spec-only. gRPC, Thrift, GraphQL, WSDL and AsyncAPI are left out: no code analyzer emits their operations in a comparable form, so the tag would be on every one of them.

SPLAT_SEGMENT_RE = /\/\*([^\/]+)/
URL_AUTHORITY_RE = /\A[a-zA-Z][a-zA-Z0-9+.\-]*:\/\/[^\/]*/
WHOLE_SEGMENT_PLACEHOLDER_RE = /(?<=\/)(?:\{[A-Za-z_]\w*\??(?::(?:[^{}\/]|\{[^{}\/]*\})+)?\}|:[A-Za-z_]\w*|<(?:[A-Za-z_]\w*:)?[A-Za-z_]\w*>)(?=[\/?#]|\z)/

A whole-segment path placeholder in any framework's spelling: {id}, :id (Express/Rails/Postman) or <id> / <int:id> (Flask/Django). Constraints were stripped by normalize_url_shape, except a regex router's ({id:[0-9]+}, {sha:[a-f0-9]{7,64}}), which still unifies with a spec's {id}. An optional slot ({id?}: Laravel, ASP.NET) is the slot a spec lists as {id}.

Constructors

new(logger : NoirLogger, options : Hash(String, YAML::Any))
Source

Instance methods

add_path_parameters(endpoints : Array(Endpoint)) : Array(Endpoint)

Add path parameters by parsing URL patterns

Source
apply_pvalue(param_type, param_name, param_value) : String

Apply parameter values based on configuration. A rule naming the param beats a type-wide default whatever the flag order, so --pvalue query=1 --pvalue query=limit=10 gives limit 10.

Source
combine_url_and_endpoints(endpoints : Array(Endpoint)) : Array(Endpoint)

Combine target URL with endpoints

Source
normalize_url_shapes(endpoints : Array(Endpoint)) : Array(Endpoint)

Normalize cross-framework URL shapes the analyzers can't always resolve without language context. Rewrites a small set of well- known leaky forms into the canonical {name} placeholder so the downstream add_path_parameters pass picks them up as path params instead of literal noise.

Covered shapes:

  • (?P<name>pattern) — Python re_path-style named groups. Bleeds through Django's re_path route table; rewrite to {name} and drop the regex body.
  • ${name} / ${obj.field} — JS/TS template literals that analyzers can't statically resolve (handler files reference captured variables, not literal paths). Rewrite to {name} (or {field} for ${obj.field}) so the AI/output payload surfaces it as a path placeholder rather than as a literal ${...} segment.

Regex anchors (^, $, \Z) and \. escapes are not touched here: only the analyzer knows whether a route is a regex, so regex-route analyzers strip them with Noir::URLPath.strip_regex_anchors. A string route (Express /dollar$, Sinatra /^start) keeps them as path text.

  • Spring {name:regex} — strip the inline regex constraint so the placeholder is {name} regardless of framework dialect.
  • Postman / Express-style :name path segments — rewrite to {name} so collections merge with framework analyzers that already emit the canonical placeholder shape.
Source
optimize(endpoints : Array(Endpoint)) : Array(Endpoint)

Main optimization workflow - calls all optimization steps

Source
optimize_endpoints(endpoints : Array(Endpoint)) : Array(Endpoint)

Remove duplicated endpoints and parameters, validate HTTP methods, clean URLs

Source