Pake: link-handling and window layer carry the highest risk — 5 functions to fix first

Analysis of tw93/Pake at commit c01afad finds five critical-band functions — spanning a JavaScript event injection layer and a Rust window builder — all in the 'fire' quadrant, meaning structurally complex and actively changing right now.

Stephen Collins ·
Generated by hotspots · free & open source
pip
$ pip install hotspots-cli
Activity Risk16.81Low
Hottest FunctionfindBuildOutputFiles

Antipatterns Detected

god_function5long_function5complex_branching3exit_heavy3deeply_nested2

Run this on your own codebase

See if your own repo has a findBuildOutputFiles-style hotspot — run this in any local git repo:

pip
$ pip install hotspots-cli
npm
$ npm install -g @stephencollinstech/hotspots
Run in any repo
$ hotspots analyze .
★ Star on GitHub

Key Points

What is a god function and why does it matter in Pake?

A god function is a single function that has absorbed too many distinct responsibilities — it calls many other functions, handles many cases internally, and grows longer over time as each new requirement lands in the same place rather than being extracted. Every one of the top 5 hotspots in Pake is flagged as a god function, and four of the five are also flagged as long functions. The concrete problem is coupling: when `detectAnchorElementClick` calls 16 distinct functions and contains 25 independent execution paths, a change to any one of its callees can alter behavior in ways that are difficult to predict without reading the entire function. Testing a god function in isolation is also expensive — you either need to mock every callee or run integration tests that exercise the whole navigation stack.

How do I reduce cyclomatic complexity in Rust?

The most direct technique is extract-method refactoring: identify each logical branch — especially those guarded by `#[cfg(...)]` platform attributes or nested `if let` chains — and move them into named helper functions with clear, single purposes. A cyclomatic complexity above 15 is a strong signal to split; above 20 it's worth treating as a blocking refactor before the next feature lands on top of that function. For `build_window` in `src-tauri/src/app/window.rs` specifically, I'd start by extracting the macOS certificate-bypass URL logic and the Windows DPI-scaling block into their own functions, each returning the relevant value. That alone would remove roughly 6–8 decision points from `build_window` and make each platform path independently testable without constructing a full `AppHandle`.

Is Pake actively maintained?

Yes — and the data makes that concrete. Four of the five top hotspots have been touched 4 times in the last 30 days, and `build_window` in `src-tauri/src/app/window.rs` was modified as recently as the analysis snapshot itself. Every function in the repository sits in either the fire or watch quadrant; there are no dormant functions at all, which means development is broadly active across the codebase. Active maintenance and structural complexity accumulation are not mutually exclusive — the fire-quadrant pattern here reflects a project growing fast enough that some functions are taking on more responsibility than they should before a refactor pass catches up.

How do I reproduce this analysis?

The Hotspots CLI is available at github.com/hotspots-dev/hotspots. This analysis was run against tw93/Pake at commit `c01afad`. After checking out that commit with `git checkout c01afad`, run `hotspots analyze . --mode snapshot --explain-patterns --force` from the repository root. The same command works on any local git repository without additional configuration.

What does activity-weighted risk mean?

Activity-weighted risk multiplies a function's structural complexity — derived from cyclomatic complexity, nesting depth, and fan-out — by its recent commit frequency, so functions that are both hard to understand and actively changing score the highest. A function with a cyclomatic complexity of 80 that hasn't been touched in two years scores much lower than one with a cyclomatic complexity of 25 that is committed to every week, because the dormant function has low near-term regression probability regardless of its structural state. In Pake's case, all five top hotspots are in the fire quadrant precisely because they combine high structural complexity with real recent activity — not theoretical risks sitting in legacy corners of the codebase, but functions that engineers are modifying right now.

At commit c01afad, tw93/Pake — a Tauri-based tool for packaging web apps into desktop binaries — has 425 analyzed functions, 41 of which score in the critical band. Every one of the top 5 hotspots lands in the ‘fire’ quadrant: high structural complexity and active recent commits, making them live regression risks rather than backlog cleanup items. The top-ranked function, findBuildOutputFiles, carries a risk score of 16.81 with a cyclomatic complexity of 25 and a nesting depth of 9; detectAnchorElementClick sits just behind it and has been touched 4 times in the last 30 days. I’d treat both as priorities for anyone shipping code against this repo right now.

The table below ranks functions by activity-weighted risk — a score that multiplies structural complexity by recent commit frequency. A function that is both hard to understand (high cyclomatic complexity) and actively changing is a higher priority than one that is complex but untouched. CC = cyclomatic complexity (independent execution paths); ND = max nesting depth; FO = fan-out (distinct callees).

Top 5 Hotspots

FunctionFileRiskCCNDFO
findBuildOutputFilestests/index.js16.825923
detectAnchorElementClicksrc-tauri/src/inject/event.js15.525416
getFilenameFromUrlsrc-tauri/src/inject/event.js15.21789
build_windowsrc-tauri/src/app/window.rs14.924317
loadConfigFilebin/helpers/config-file.ts14.426311

Large Repo Analysis

Pake is a large repository. To stay within memory constraints, this analysis used hybrid touch mode: structural complexity — CC, ND, FO — is measured precisely for every function. Git activity is tracked at the function level (via git log -L) only for files with 5 or more commits in the last 30 days; other files use a file-level approximation. Rankings therefore surface functions that are both structurally complex and in the most actively-changing parts of the codebase. Dormant code with high structural complexity will rank lower than it would under a full per-function analysis — to surface it, run hotspots analyze . --per-function-touches on a machine with sufficient memory.

Codemod / Tooling Files in Results

The function findBuildOutputFiles in tests/index.js is part of the project’s own test infrastructure rather than a vendored third-party library, so it warrants the same review attention as production code. There are no vendored, node_modules, dist, or bundled library files in the provided data. No exclusion pattern is needed.

Quadrant and pattern overview

Triage Band Distribution
Fire128Watch297

425 functions analyzed

Every function in this repository sits in either the ‘fire’ or ‘watch’ quadrant — no dormant structural debt is waiting quietly in the background. The 128 fire-quadrant functions are all complex enough to warrant attention and all actively changing. The 297 watch-quadrant functions are active but structurally simpler; runSearch in src-tauri/src/inject/find.js and run_app in src-tauri/src/lib.rs are worth keeping an eye on as the codebase grows, but neither demands immediate refactoring.

Detected Antipatterns
God Function×5God Function
Calls an unusually large number of distinct functions (high fan-out), making it the structural centre of gravity for a subsystem.
Long Function×5Long Function
Function body is too long to review in a single pass; likely contains multiple distinct responsibilities.
Complex Branching×3Complex Branching
High cyclomatic complexity — many independent execution paths, each a potential bug surface and required test case.
Exit Heavy×3Exit Heavy
Multiple return or throw paths dispersed through the body — each exit needs separate test coverage.
Deeply Nested×2Deeply Nested
Control structures nested 4+ levels deep, making it hard to reason about the full execution state at inner branches.

The pattern picture is consistent across all five hotspots: every one is flagged as a god function and a long function. Three also carry complex branching, three are exit-heavy, and two are deeply nested. That’s not a coincidence — it points to a structural habit of accumulating logic inside single functions rather than distributing it across smaller, testable units.


findBuildOutputFiles — tests/index.js

findBuildOutputFiles
tests/index.js
16.81
critical
CC 25
ND 9
FO 23
touches/30d 1

findBuildOutputFiles is responsible for locating platform-specific build artifacts — .msi, .deb, .dmg, .app, and others — across a set of candidate directories derived from the target platform string. The source excerpt shows it building a searchLocations array through a chain of spread conditionals, one branch per platform (linux, win32, darwin), then walking each directory with fs.readdirSync and filtering results against per-platform extension patterns.

Cyclomatic Complexity 25
threshold: 10
Max Nesting Depth 9
threshold: 4
Fan-Out 23
threshold: 15

A cyclomatic complexity of 25 means 25 independent execution paths — each one a required test case for guaranteed coverage. The nesting depth of 9 is the most striking number: at that depth, the innermost logic is buried inside a for loop, a try block, a directory-existence guard, a filter callback, a stats.isDirectory() branch, and an Array.includes check. That’s very hard to reason about in isolation. The fan-out of 23 reflects the function’s direct dependence on fs.existsSync, fs.readdirSync, fs.statSync, path.join, path.relative, and a set of config accessors — a change to any of those call sites can affect behavior here.

External signals for this file show that its single commit in history was tagged as a bug fix, with a single author in the last 90 days. That’s too thin a commit history to draw defect conclusions from, but combined with the structural metrics it means this function has grown complex without the review feedback loop that would ordinarily surface simplification opportunities.

The most actionable step is to extract the platform-to-directory mapping into a pure data structure or a dedicated helper, and the per-directory file-scanning logic into its own function. That would bring the CC of the residual orchestration function below 10 and make the directory-walking path independently testable.


detectAnchorElementClick — src-tauri/src/inject/event.js

detectAnchorElementClick
src-tauri/src/inject/event.js
15.5
critical
CC 25
ND 4
FO 16
touches/30d 4

This function is the central click-event handler injected into the Tauri webview — every anchor click inside the packaged web app passes through here. The source excerpt shows it sequentially deciding: is the target a valid element? is the href a Pake-bypassed link? is it an OAuth/auth link? is the target _blank and internal? is the target _blank and external? is the target _new? — with early returns scattered throughout each branch.

Cyclomatic Complexity 25
threshold: 10
Fan-Out 16
threshold: 15

With 4 commits in the last 30 days and last changed 6 days ago, this is the most actively changing function in the link-handling layer. A cyclomatic complexity of 25 in a function touched roughly once a week is a live regression risk. Each new navigation case — OAuth flows, forceInternalNavigation, SPA _blank quirks, download links — adds another branch rather than being dispatched through a strategy or routing table. The exit-heavy pattern is visible in the excerpt: nearly every conditional block ends with an explicit return, which means test coverage requires exercising all 25 paths individually.

The fan-out of 16 means this single handler calls shouldBypassPakeLinkHandling, isAuthLink, openAuthNavigation, isInternalUrl, handleExternalLink, getFilenameFromUrl, and others — a change to any of those helpers has this function as a direct blast-radius surface.

Every one of the 4 commits to this file has been tagged as a bug fix. That’s consistent with the branching structure — each edge case discovered in the field requires a new conditional here. I’d extract each link-handling concern (auth links, blank-target internal links, blank-target external links, download links) into its own named function, then reduce detectAnchorElementClick to a dispatcher that calls each handler in sequence. That would cut the CC roughly in half and make each case independently testable.


getFilenameFromUrl — src-tauri/src/inject/event.js

getFilenameFromUrl
src-tauri/src/inject/event.js
15.15
critical
CC 17
ND 8
FO 9
touches/30d 4

getFilenameFromUrl lives in the same file as detectAnchorElementClick and is called by it. Its job is to derive a local filename from a URL — either by reading the URL path’s trailing segment, or by generating a timestamped fallback filename keyed to the detected image type.

Max Nesting Depth 8
threshold: 4
Cyclomatic Complexity 17
threshold: 10

The nesting depth of 8 is what stands out. The source excerpt shows the deepest branch handling data URI parsing: the function enters a try block, checks whether the URL starts with data:image/, locates the MIME subtype boundaries by scanning for semicolons and commas, splits on +, then maps jpeg to jpg. All of that is nested inside the fallback branch of the filename-extraction logic. The cyclomatic complexity of 17 comes from the chain of url.includes("jpg"), url.includes("png"), url.includes("gif"), and so on that follows the data URI branch.

Because this function shares the same file commit history as detectAnchorElementClick (4 touches in 30 days, all tagged as bug fixes, 2 authors in 90 days), both are co-evolving. The data URI MIME parsing logic and the extension-sniffing-from-URL-string logic are distinct concerns that belong in separate helpers. Extracting them would bring the nesting depth below 4 and the CC below 10, and would make it possible to unit-test the MIME parsing path without constructing a full event.


build_window — src-tauri/src/app/window.rs

build_window
src-tauri/src/app/window.rs
14.95
critical
CC 24
ND 3
FO 17
touches/30d 4

This is the only Rust function in the top 5, and also the most recently changed: it was modified as recently as the analysis snapshot. build_window takes an AppHandle, a PakeConfig, a Tauri Config, and a WindowBuildOptions struct and assembles the WebviewWindow that wraps the packaged web app.

Cyclomatic Complexity 24
threshold: 10
Fan-Out 17
threshold: 15

The structural driver is platform-conditional compilation: #[cfg(target_os = "macos")] and #[cfg(target_os = "windows")] blocks each introduce separate logic branches — certificate bypass, blank-page navigation on macOS, DPI-scaled window sizing on Windows. A cyclomatic complexity of 24 in Rust carries extra weight because the compiler’s ownership and lifetime rules are layered on top: each branch that conditionally constructs or moves a value (like the url rebinding under the macOS cert-bypass path) requires the reader to track ownership across the branch boundary, a cognitive cost that CC alone doesn’t fully capture.

The fan-out of 17 reflects calls into get_data_dir, serde_json::to_string, Url::parse, WebviewWindowBuilder::new, and a chain of builder methods — changes to any of those APIs require coordinated updates here. The god_function and exit_heavy patterns are consistent with a function that has absorbed every platform-specific window-configuration concern over time.

External signals show this function has attracted more code review discussion than any other in the top 5, suggesting reviewers have already flagged concerns here. With 4 touches in 30 days and last changed on the day of this snapshot, it is being actively modified right now. I’d extract each #[cfg(...)] block into a dedicated platform configuration helper — a function returning the appropriate WebviewUrl for macOS cert-bypass, another applying Windows DPI scaling — and leave build_window as a thin orchestrator. That decomposition would also make each platform path independently testable.


loadConfigFile — bin/helpers/config-file.ts

loadConfigFile
bin/helpers/config-file.ts
14.36
critical
CC 26
ND 3
FO 11
touches/30d 1

loadConfigFile is the CLI entry point for reading and validating a user-supplied JSON configuration file against the pake.schema.json schema. The source excerpt shows it doing several distinct jobs in sequence: verifying the file exists, parsing the JSON, asserting the top-level shape is an object, then iterating over every key and applying type validation, range validation, and rejection rules.

Cyclomatic Complexity 26
threshold: 10

A cyclomatic complexity of 26 is the highest in the top 5, driven by the per-key validation loop: each key can hit the $schema skip, the url special case, the REJECTED_KEYS check, the validKeys check, the type-match check, or the numeric range check — each an independent path. The exit-heavy pattern — throw new PakeError(...) at each validation failure — means test coverage requires exercising all 26 paths. The nesting depth stays manageable at 3 because the exit-heavy style keeps the happy-path logic relatively flat, but the number of throws scattered through the function makes reasoning about the full validation sequence non-trivial.

With only 1 touch in 30 days and a single author in 90 days, this is the least actively changing of the five hotspots — but a CC of 26 means it carries the highest structural risk per change whenever it is next modified. I’d extract the per-key validation logic — type checking, range checking, rejection rules — into a dedicated validateConfigEntry(key, value) helper. That would reduce loadConfigFile to a parsing-and-iteration shell with a CC closer to 6, and give the validation rules a home where they can be tested against individual key-value pairs without needing a full config file on disk.

Patterns Found

Antipatterns detected across the top functions in this snapshot:

PatternOccurrences
god_function5
long_function5
complex_branching3
exit_heavy3
deeply_nested2

These labels belong to two tiers — Tier 1 (structural): complex_branching, deeply_nested, exit_heavy, long_function, god_function. Tier 2 (relational/temporal): hub_function, cyclic_hub, middle_man, neighbor_risk, stale_complex, churn_magnet, shotgun_target, volatile_god.

See more analyses with these patterns: complex_branching, deeply_nested, exit_heavy, god_function, long_function.

Reproduce This Analysis

git clone https://github.com/tw93/Pake
cd Pake
git checkout c01afad4e258de6a27fa483908dfdeb6df3914ad
hotspots analyze . --mode snapshot --explain-patterns --force --hybrid-touches 5

To run the same analysis on your own codebase, run hotspots analyze . --mode snapshot in any local git repo — no configuration required.

I use Hotspots to highlight structural and activity risk — not “bad code.” I treat these findings as a prioritization aid, not a bug predictor. Editorial policy →

Was this useful? Let me know →

Related Analyses