fix: add Webhook to web constants + reformat for CI

- Add Webhook entry to NODE_COLORS and NODE_SIZES in gitnexus-web constants
- Run prettier across all packages to fix format CI check
- Resolves typecheck-web and format CI failures on PR #512

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Test 2026-04-04 22:51:16 +02:00
parent bad1a12e4f
commit ce889889a7
124 changed files with 1232 additions and 890 deletions

View file

@ -38,6 +38,7 @@ export const NODE_COLORS: Record<NodeLabel, string> = {
Template: '#a78bfa', // Violet light - like Type
Route: '#f43f5e', // Rose - like Process
Tool: '#a855f7', // Purple - like Project
Webhook: '#06b6d4', // Cyan - event/integration handler
};
// Node sizes by type - clear visual hierarchy with dramatic size differences
@ -79,6 +80,7 @@ export const NODE_SIZES: Record<NodeLabel, number> = {
Template: 3, // Like Type
Route: 5, // Like Enum
Tool: 5, // Like Enum
Webhook: 5, // Like Enum - event handler
};
// Community color palette for cluster-based coloring

View file

@ -80,17 +80,20 @@ All notable changes to GitNexus will be documented in this file.
## [1.5.2] - 2026-04-01
### Fixed
- **`gitnexus-shared` module not found** — `gitnexus-shared` was a `file:` workspace dependency never published to npm, causing `ERR_MODULE_NOT_FOUND` when installing `gitnexus` globally. The build now bundles shared code into `dist/_shared/` and rewrites imports to relative paths (#613)
- **v1.5.1 publish regression** — npm's `prepare` lifecycle ran `tsc` after `prepack`, overwriting the rewritten imports before packing; both scripts now run the full build so the final tarball is always correct
## [1.5.1] - 2026-04-01 [YANKED]
### Fixed
- Incomplete fix for `gitnexus-shared` bundling — `prepare` script overwrote rewritten imports during publish
## [1.5.0] - 2026-04-01
### Added
- **Repo landing screen** — when the backend detects indexed repositories, the web UI now shows a landing page with selectable repo cards (name, stats, indexed date) instead of auto-loading the first repo; users can also analyze new repos directly from the landing screen (#607)
- **Unified web & CLI ingestion pipeline** — complete architectural migration of the web app from a self-contained WASM browser app to a thin client backed by the CLI server; new `gitnexus-shared` package for cross-package type unification (#536)
- New server endpoints: `/api/heartbeat` (SSE liveness), `/api/info`, `/api/repos`, `/api/file`, `/api/grep`, `/api/analyze` (SSE progress), `/api/embed`, `/api/mcp` (MCP-over-StreamableHTTP)
@ -106,19 +109,23 @@ All notable changes to GitNexus will be documented in this file.
- **ESLint v9** — flat config with `unused-imports` auto-removal, `@typescript-eslint` rules, React hooks rules, CI `lint` job (#564)
### Fixed
- **OpenCode MCP configuration** — corrected README MCP setup for OpenCode which requires `command` as an array containing both executable and arguments (#363)
- **litellm security** — excluded vulnerable versions 1.82.7 and 1.82.8 in eval harness `pyproject.toml` (#580)
### Changed
- **Reduced explicit `any` types** — 128 `no-explicit-any` warnings eliminated (689 → 561, 19% reduction) across `NodeProperties` index signature, ~80 `SyntaxNode` substitutions, typed worker protocol, and graphology community detection (#566)
### Docs
- Added `gitnexus-shared` build step to web UI quick start instructions (#585)
- Added enterprise offering section to README (#579)
## [1.4.10] - 2026-03-27
### Fixed
- **MCP server install via npx** — resolve tree-sitter peer dependency conflicts that broke `npx -y gitnexus@latest mcp` (#537, #538)
- Downgrade tree-sitter from ^0.25.0 to ^0.21.1 (only npm version where all 14 parsers agree)
- Align all parser versions to their highest ^0.21.x-compatible releases
@ -129,6 +136,7 @@ All notable changes to GitNexus will be documented in this file.
## [1.4.9] - 2026-03-26
### Added
- **COBOL language support** — standalone regex processor for fixed-format and free-format COBOL, JCL, COPY/REPLACING with pseudotext (#498)
- 95% language feature coverage: CALL USING, EXEC SQL/CICS/DLI, DECLARATIVES, SET, INSPECT, INITIALIZE, STRING/UNSTRING, SORT/MERGE with INPUT/OUTPUT PROCEDURE, GO TO DEPENDING ON, MOVE CORRESPONDING, nested programs with per-program scoping
- 90+ review findings resolved across 20 review cycles with 241 tests (180 unit + 61 integration)
@ -145,11 +153,13 @@ All notable changes to GitNexus will be documented in this file.
- **Filter panel** — additional node types (#519)
### Changed
- **BUILT_IN_NAMES** split into per-language provider fields (#523)
- **tree-sitter** upgraded to 0.25.0 with all grammar packages (#516)
- **Impact tool** — batched chunking and entry-point grouping for enrichment (#507)
### Fixed
- **COBOL CRLF** — all `split('\n')` calls use `/\r?\n/` for Windows compatibility
- **COBOL nested programs** — all graph edges (CALL, CANCEL, CICS, ENTRY, SQL, SEARCH) use `owningModuleId()` for correct attribution
- **COBOL callAccum** — multi-line CALL USING with verb boundary detection, Area A paragraph guard, EXEC entry flush, division/END PROGRAM flush
@ -162,6 +172,7 @@ All notable changes to GitNexus will be documented in this file.
## [1.4.8] - 2026-03-23
### Added
- **Type resolution Milestone D — Phases 10–13** consolidated into a single milestone with full integration test coverage across 11 languages (#387)
- Phase A/B/C: overload disambiguation via argument literal types, constructor-visible virtual dispatch via `constructorTypeMap`, `parameterTypes` extraction in `extractMethodSignature`
- Phase 14 enhancements: single-pass seeding, Tarjan's SCC for cyclic resolution, cross-file return types
@ -180,6 +191,7 @@ All notable changes to GitNexus will be documented in this file.
- **Pre-commit hooks** via husky for typecheck + unit tests
### Fixed
- **Python import alias resolution** — `import X as Y` now routes module aliases directly to `moduleAliasMap` in import processor (#417, #461)
- **Python module-qualified calls** resolved via `moduleAliasMap` (#337)
- **Python module-qualified constructor calls** (Issue #337)
@ -201,17 +213,20 @@ All notable changes to GitNexus will be documented in this file.
- **package-lock.json** synced with `onnxruntime-node ^1.24.0`
### Changed
- **Unified language dispatch** with compile-time exhaustive tables
- **Prepare script simplified** — removed `scripts/prepare.cjs`
- **Switched from .githooks to husky** for pre-commit hooks
- **`@claude` workflow** restricted to maintainers and above via `author_association` check
### Performance
- **O(1) per-chunk synthesis guard** using `boolean[]` instead of Set
- **`sizeBefore` optimization** in type resolution
- **Token truncation** improvements
### Chore
- Strengthened Python module-import tests, un-skipped match/case, added perf guard
- Added positive and negative tests for all 4 bug fixes
- E2e tests for stale detection, sequential enrichment, stability (#396)
@ -222,6 +237,7 @@ All notable changes to GitNexus will be documented in this file.
## [1.4.7] - 2026-03-19
### Added
- **Phase 8 field/property type resolution** — ACCESSES edges with `declaredType` for field reads/writes (#354)
- **Phase 9 return-type variable binding** — call-result variable binding across 11 languages (#379)
- `extractPendingAssignment` in per-language type extractors captures `let x = getUser()` patterns
@ -235,6 +251,7 @@ All notable changes to GitNexus will be documented in this file.
- **MCP tool/resource descriptions** updated to reflect Phase 9 ACCESSES edge semantics and `declaredType` property
### Fixed
- **mcp**: server crashes under parallel tool calls (#326, #349)
- **parsing**: undefined error on languages missing from call routers (#364)
- **web**: add missing Kotlin entries to `Record<SupportedLanguages>` maps
@ -243,12 +260,14 @@ All notable changes to GitNexus will be documented in this file.
- **docs**: corrected stale "single-pass" claims in type-resolution-system.md to reflect walk+fixpoint architecture
### Changed
- **Upgrade `@ladybugdb/core` to 0.15.2** and remove segfault workarounds (#374)
- **type-resolution-roadmap.md** overhauled — completed phases condensed to summaries, Phases 10–14 added with full engineering specs
## [1.4.6] - 2026-03-18
### Added
- **Phase 7 type resolution** — return-aware loop inference for call-expression iterables (#341)
- `ReturnTypeLookup` interface with `lookupReturnType` / `lookupRawReturnType` split
- `ForLoopExtractorContext` context object replacing positional `(node, env)` signature
@ -261,6 +280,7 @@ All notable changes to GitNexus will be documented in this file.
- `pendingCallResults` infrastructure (dormant — Phase 9 work)
### Fixed
- **impact**: return structured error + partial results instead of crashing (#345)
- **impact**: add `HAS_METHOD` and `OVERRIDES` to `VALID_RELATION_TYPES` (#350)
- **cli**: write tool output to stdout via fd 1 instead of stderr (#346)
@ -271,11 +291,13 @@ All notable changes to GitNexus will be documented in this file.
- **docs**: correct `pendingCallResults` description in roadmap and system docs
### Chore
- Add `.worktrees/` to `.gitignore`
## [1.4.5] - 2026-03-17
### Added
- **Ruby language support** for CLI and web (#111)
- **TypeEnvironment API** with constructor inference, self/this/super resolution (#274)
- **Return type inference** with doc-comment parsing (JSDoc, PHPDoc, YARD) and per-language type extractors (#284)
@ -297,10 +319,12 @@ All notable changes to GitNexus will be documented in this file.
- `hookTimeout: 120000` in vitest config for CI beforeAll hooks
### Changed
- **Migrated from KuzuDB to LadybugDB v0.15** (#275)
- Dynamically discover and install agent skills in CLI (#270)
### Performance
- Worker pool threshold — skip worker creation for small repos (<15 files or <512KB total)
- AST walk pruning via `SKIP_SUBTREE_TYPES` for leaf-only nodes (string, comment, number literals)
- Pre-computed `interestingNodeTypes` set — single Set.has() replaces 3 checks per AST node
@ -308,6 +332,7 @@ All notable changes to GitNexus will be documented in this file.
- Replace `.children?.find()` with manual for loops in `extractFunctionName` to eliminate array allocations
### Fixed
- Same-directory Python import resolution (#328)
- Ruby method-level call resolution, HAS_METHOD edges, and dispatch table (#278)
- C++ fixture file casing for case-sensitive CI

View file

@ -30,20 +30,20 @@ To configure MCP for your editor, run `npx gitnexus setup` once — or set it up
### Editor Support
| Editor | MCP | Skills | Hooks (auto-augment) | Support |
|--------|-----|--------|---------------------|---------|
| **Claude Code** | Yes | Yes | Yes (PreToolUse) | **Full** |
| **Cursor** | Yes | Yes | — | MCP + Skills |
| **Codex** | Yes | Yes | — | MCP + Skills |
| **Windsurf** | Yes | — | — | MCP |
| **OpenCode** | Yes | Yes | — | MCP + Skills |
| Editor | MCP | Skills | Hooks (auto-augment) | Support |
| --------------- | --- | ------ | -------------------- | ------------ |
| **Claude Code** | Yes | Yes | Yes (PreToolUse) | **Full** |
| **Cursor** | Yes | Yes | — | MCP + Skills |
| **Codex** | Yes | Yes | — | MCP + Skills |
| **Windsurf** | Yes | — | — | MCP |
| **OpenCode** | Yes | Yes | — | MCP + Skills |
> **Claude Code** gets the deepest integration: MCP tools + agent skills + PreToolUse hooks that automatically enrich grep/glob/bash calls with knowledge graph context.
### Community Integrations
| Agent | Install | Source |
|-------|---------|--------|
| Agent | Install | Source |
| -------------------- | ---------------------------- | ------------------------------------------------------- |
| [pi](https://pi.dev) | `pi install npm:pi-gitnexus` | [pi-gitnexus](https://github.com/tintinweb/pi-gitnexus) |
## MCP Setup (manual)
@ -115,36 +115,36 @@ The result is a **LadybugDB graph database** stored locally in `.gitnexus/` with
Your AI agent gets these tools automatically:
| Tool | What It Does | `repo` Param |
|------|-------------|--------------|
| `list_repos` | Discover all indexed repositories | — |
| `query` | Process-grouped hybrid search (BM25 + semantic + RRF) | Optional |
| `context` | 360-degree symbol view — categorized refs, process participation | Optional |
| `impact` | Blast radius analysis with depth grouping and confidence | Optional |
| `detect_changes` | Git-diff impact — maps changed lines to affected processes | Optional |
| `rename` | Multi-file coordinated rename with graph + text search | Optional |
| `cypher` | Raw Cypher graph queries | Optional |
| Tool | What It Does | `repo` Param |
| ---------------- | ---------------------------------------------------------------- | ------------ |
| `list_repos` | Discover all indexed repositories | — |
| `query` | Process-grouped hybrid search (BM25 + semantic + RRF) | Optional |
| `context` | 360-degree symbol view — categorized refs, process participation | Optional |
| `impact` | Blast radius analysis with depth grouping and confidence | Optional |
| `detect_changes` | Git-diff impact — maps changed lines to affected processes | Optional |
| `rename` | Multi-file coordinated rename with graph + text search | Optional |
| `cypher` | Raw Cypher graph queries | Optional |
> With one indexed repo, the `repo` param is optional. With multiple, specify which: `query({query: "auth", repo: "my-app"})`.
## MCP Resources
| Resource | Purpose |
|----------|---------|
| `gitnexus://repos` | List all indexed repositories (read first) |
| `gitnexus://repo/{name}/context` | Codebase stats, staleness check, and available tools |
| `gitnexus://repo/{name}/clusters` | All functional clusters with cohesion scores |
| `gitnexus://repo/{name}/cluster/{name}` | Cluster members and details |
| `gitnexus://repo/{name}/processes` | All execution flows |
| `gitnexus://repo/{name}/process/{name}` | Full process trace with steps |
| `gitnexus://repo/{name}/schema` | Graph schema for Cypher queries |
| Resource | Purpose |
| --------------------------------------- | ---------------------------------------------------- |
| `gitnexus://repos` | List all indexed repositories (read first) |
| `gitnexus://repo/{name}/context` | Codebase stats, staleness check, and available tools |
| `gitnexus://repo/{name}/clusters` | All functional clusters with cohesion scores |
| `gitnexus://repo/{name}/cluster/{name}` | Cluster members and details |
| `gitnexus://repo/{name}/processes` | All execution flows |
| `gitnexus://repo/{name}/process/{name}` | Full process trace with steps |
| `gitnexus://repo/{name}/schema` | Graph schema for Cypher queries |
## MCP Prompts
| Prompt | What It Does |
|--------|-------------|
| `detect_impact` | Pre-commit change analysis — scope, affected processes, risk level |
| `generate_map` | Architecture documentation from the knowledge graph with mermaid diagrams |
| Prompt | What It Does |
| --------------- | ------------------------------------------------------------------------- |
| `detect_impact` | Pre-commit change analysis — scope, affected processes, risk level |
| `generate_map` | Architecture documentation from the knowledge graph with mermaid diagrams |
## CLI Commands
@ -200,21 +200,21 @@ TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust, PHP, Kotlin, Swift,
### Language Feature Matrix
| Language | Imports | Named Bindings | Exports | Heritage | Type Annotations | Constructor Inference | Config | Frameworks | Entry Points |
|----------|---------|----------------|---------|----------|-----------------|---------------------|--------|------------|-------------|
| TypeScript | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| JavaScript | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ | ✓ |
| Python | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Java | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| Kotlin | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| C# | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Go | ✓ | — | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Rust | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| PHP | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ | ✓ | ✓ |
| Ruby | ✓ | — | ✓ | ✓ | — | ✓ | — | ✓ | ✓ |
| Swift | — | — | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| C | — | — | ✓ | — | ✓ | ✓ | — | ✓ | ✓ |
| C++ | — | — | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| Language | Imports | Named Bindings | Exports | Heritage | Type Annotations | Constructor Inference | Config | Frameworks | Entry Points |
| ---------- | ------- | -------------- | ------- | -------- | ---------------- | --------------------- | ------ | ---------- | ------------ |
| TypeScript | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| JavaScript | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ | ✓ |
| Python | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Java | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| Kotlin | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| C# | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Go | ✓ | — | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Rust | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
| PHP | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ | ✓ | ✓ |
| Ruby | ✓ | — | ✓ | ✓ | — | ✓ | — | ✓ | ✓ |
| Swift | — | — | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| C | — | — | ✓ | — | ✓ | ✓ | — | ✓ | ✓ |
| C++ | — | — | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
**Imports** — cross-file import resolution · **Named Bindings** — `import { X as Y }` / re-export tracking · **Exports** — public/exported symbol detection · **Heritage** — class inheritance, interfaces, mixins · **Type Annotations** — explicit type extraction for receiver resolution · **Constructor Inference** — infer receiver type from constructor calls (`self`/`this` resolution included for all languages) · **Config** — language toolchain config parsing (tsconfig, go.mod, etc.) · **Frameworks** — AST-based framework pattern detection · **Entry Points** — entry point scoring heuristics

View file

@ -1,6 +1,6 @@
---
name: gitnexus-cli
description: "Use when the user needs to run GitNexus CLI commands like analyze/index a repo, check status, clean the index, generate a wiki, or list indexed repos. Examples: \"Index this repo\", \"Reanalyze the codebase\", \"Generate a wiki\""
description: 'Use when the user needs to run GitNexus CLI commands like analyze/index a repo, check status, clean the index, generate a wiki, or list indexed repos. Examples: "Index this repo", "Reanalyze the codebase", "Generate a wiki"'
---
# GitNexus CLI Commands

View file

@ -1,6 +1,6 @@
---
name: gitnexus-debugging
description: "Use when the user is debugging a bug, tracing an error, or asking why something fails. Examples: \"Why is X failing?\", \"Where does this error come from?\", \"Trace this bug\""
description: 'Use when the user is debugging a bug, tracing an error, or asking why something fails. Examples: "Why is X failing?", "Where does this error come from?", "Trace this bug"'
---
# Debugging with GitNexus

View file

@ -1,6 +1,6 @@
---
name: gitnexus-exploring
description: "Use when the user asks how code works, wants to understand architecture, trace execution flows, or explore unfamiliar parts of the codebase. Examples: \"How does X work?\", \"What calls this function?\", \"Show me the auth flow\""
description: 'Use when the user asks how code works, wants to understand architecture, trace execution flows, or explore unfamiliar parts of the codebase. Examples: "How does X work?", "What calls this function?", "Show me the auth flow"'
---
# Exploring Codebases with GitNexus

View file

@ -1,6 +1,6 @@
---
name: gitnexus-guide
description: "Use when the user asks about GitNexus itself — available tools, how to query the knowledge graph, MCP resources, graph schema, or workflow reference. Examples: \"What GitNexus tools are available?\", \"How do I use GitNexus?\""
description: 'Use when the user asks about GitNexus itself — available tools, how to query the knowledge graph, MCP resources, graph schema, or workflow reference. Examples: "What GitNexus tools are available?", "How do I use GitNexus?"'
---
# GitNexus Guide
@ -19,8 +19,8 @@ For any task involving code understanding, debugging, impact analysis, or refact
## Skills
| Task | Skill to read |
| -------------------------------------------- | ------------------- |
| Task | Skill to read |
| -------------------------------------------- | ---------------------------- |
| Understand architecture / "How does X work?" | `gitnexus-exploring` |
| Blast radius / "What breaks if I change X?" | `gitnexus-impact-analysis` |
| Trace bugs / "Why is X failing?" | `gitnexus-debugging` |

View file

@ -1,6 +1,6 @@
---
name: gitnexus-impact-analysis
description: "Use when the user wants to know what will break if they change something, or needs safety analysis before editing code. Examples: \"Is it safe to change X?\", \"What depends on this?\", \"What will break?\""
description: 'Use when the user wants to know what will break if they change something, or needs safety analysis before editing code. Examples: "Is it safe to change X?", "What depends on this?", "What will break?"'
---
# Impact Analysis with GitNexus

View file

@ -1,6 +1,6 @@
---
name: gitnexus-pr-review
description: "Use when the user wants to review a pull request, understand what a PR changes, assess risk of merging, or check for missing test coverage. Examples: \"Review this PR\", \"What does PR #42 change?\", \"Is this PR safe to merge?\""
description: 'Use when the user wants to review a pull request, understand what a PR changes, assess risk of merging, or check for missing test coverage. Examples: "Review this PR", "What does PR #42 change?", "Is this PR safe to merge?"'
---
# PR Review with GitNexus
@ -43,23 +43,23 @@ description: "Use when the user wants to review a pull request, understand what
## Review Dimensions
| Dimension | How GitNexus Helps |
| --- | --- |
| **Correctness** | `context` shows callers — are they all compatible with the change? |
| **Blast radius** | `impact` shows d=1/d=2/d=3 dependents — anything missed? |
| **Completeness** | `detect_changes` shows all affected flows — are they all handled? |
| **Test coverage** | `impact({includeTests: true})` shows which tests touch changed code |
| Dimension | How GitNexus Helps |
| -------------------- | --------------------------------------------------------------------- |
| **Correctness** | `context` shows callers — are they all compatible with the change? |
| **Blast radius** | `impact` shows d=1/d=2/d=3 dependents — anything missed? |
| **Completeness** | `detect_changes` shows all affected flows — are they all handled? |
| **Test coverage** | `impact({includeTests: true})` shows which tests touch changed code |
| **Breaking changes** | d=1 upstream items that aren't updated in the PR = potential breakage |
## Risk Assessment
| Signal | Risk |
| --- | --- |
| Changes touch <3 symbols, 0-1 processes | LOW |
| Changes touch 3-10 symbols, 2-5 processes | MEDIUM |
| Changes touch >10 symbols or many processes | HIGH |
| Changes touch auth, payments, or data integrity code | CRITICAL |
| d=1 callers exist outside the PR diff | Potential breakage — flag it |
| Signal | Risk |
| ---------------------------------------------------- | ---------------------------- |
| Changes touch <3 symbols, 0-1 processes | LOW |
| Changes touch 3-10 symbols, 2-5 processes | MEDIUM |
| Changes touch >10 symbols or many processes | HIGH |
| Changes touch auth, payments, or data integrity code | CRITICAL |
| d=1 callers exist outside the PR diff | Potential breakage — flag it |
## Tools
@ -146,18 +146,22 @@ Structure your review as:
**Risk: LOW / MEDIUM / HIGH / CRITICAL**
### Changes Summary
- <N> symbols changed across <M> files
- <P> execution flows affected
### Findings
1. **[severity]** Description of finding
- Evidence from GitNexus tools
- Affected callers/flows
### Missing Coverage
- Callers not updated in PR: ...
- Untested flows: ...
### Recommendation
APPROVE / REQUEST CHANGES / NEEDS DISCUSSION
```

View file

@ -1,6 +1,6 @@
---
name: gitnexus-refactoring
description: "Use when the user wants to rename, extract, split, move, or restructure code safely. Examples: \"Rename this function\", \"Extract this into a module\", \"Refactor this class\", \"Move this to a separate file\""
description: 'Use when the user wants to rename, extract, split, move, or restructure code safely. Examples: "Rename this function", "Extract this into a module", "Refactor this class", "Move this to a separate file"'
---
# Refactoring with GitNexus

View file

@ -1361,12 +1361,20 @@ export function extractWebhooks(filePath: string, content: string, out: Extracte
const lowerPath = filePath.toLowerCase();
// Skip test files — they often mock webhook patterns (e.g. Stripe constructEvent in test mocks)
if (lowerPath.includes('/test') || lowerPath.includes('.test.') || lowerPath.includes('.spec.') || lowerPath.includes('__test')) return;
if (
lowerPath.includes('/test') ||
lowerPath.includes('.test.') ||
lowerPath.includes('.spec.') ||
lowerPath.includes('__test')
)
return;
// Count newlines before offset without allocating substring + split array
const lineAt = (offset: number): number => {
let n = 0;
for (let i = 0; i < offset; i++) { if (content.charCodeAt(i) === 10) n++; }
for (let i = 0; i < offset; i++) {
if (content.charCodeAt(i) === 10) n++;
}
return n;
};
@ -1374,33 +1382,58 @@ export function extractWebhooks(filePath: string, content: string, out: Extracte
const eventTypes: string[] = [];
const caseRe = /case\s+['"]([.\w-]+)['"]/g;
let m;
while ((m = caseRe.exec(content)) !== null) { eventTypes.push(m[1]); }
while ((m = caseRe.exec(content)) !== null) {
eventTypes.push(m[1]);
}
const idx = content.indexOf('constructEvent');
out.push({ filePath, name: 'stripe-webhook', kind: 'stripe', eventTypes,
lineNumber: idx > -1 ? lineAt(idx) : 0 });
out.push({
filePath,
name: 'stripe-webhook',
kind: 'stripe',
eventTypes,
lineNumber: idx > -1 ? lineAt(idx) : 0,
});
return;
}
const edgeFnMatch = filePath.match(/supabase\/functions\/([\w-]+)\/index\.ts$/);
if (edgeFnMatch && content.includes('Deno.serve')) {
out.push({ filePath, name: edgeFnMatch[1], kind: 'edge-function', eventTypes: [],
lineNumber: lineAt(content.indexOf('Deno.serve')) });
out.push({
filePath,
name: edgeFnMatch[1],
kind: 'edge-function',
eventTypes: [],
lineNumber: lineAt(content.indexOf('Deno.serve')),
});
return;
}
if (content.includes('postgres_changes')) {
const channelRe = /\.channel\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
let m;
while ((m = channelRe.exec(content)) !== null) {
out.push({ filePath, name: `realtime:${m[1]}`, kind: 'realtime', eventTypes: [],
lineNumber: lineAt(m.index) });
out.push({
filePath,
name: `realtime:${m[1]}`,
kind: 'realtime',
eventTypes: [],
lineNumber: lineAt(m.index),
});
}
}
if (lowerPath.includes('webhook') || lowerPath.includes('hook')) {
const hasSigVerify = content.includes('x-webhook-signature') || content.includes('x-hub-signature')
|| content.includes('verify');
const hasSigVerify =
content.includes('x-webhook-signature') ||
content.includes('x-hub-signature') ||
content.includes('verify');
if (hasSigVerify) {
const pathParts = filePath.split('/');
const name = pathParts[pathParts.length - 1].replace(/\.(ts|js|tsx|jsx)$/, '');
out.push({ filePath, name: `webhook:${name}`, kind: 'generic', eventTypes: [], lineNumber: 0 });
out.push({
filePath,
name: `webhook:${name}`,
kind: 'generic',
eventTypes: [],
lineNumber: 0,
});
}
}
}
@ -1409,13 +1442,25 @@ export function extractWebhooks(filePath: string, content: string, out: Extracte
* Extract message-queue producer/consumer patterns from source code.
* Detects: BullMQ, RabbitMQ (amqplib), Kafka (kafkajs), AWS SQS, Celery.
*/
export function extractQueuePatterns(filePath: string, content: string, out: ExtractedQueuePattern[]): void {
export function extractQueuePatterns(
filePath: string,
content: string,
out: ExtractedQueuePattern[],
): void {
const lowerPath = filePath.toLowerCase();
if (lowerPath.includes('/test') || lowerPath.includes('.test.') || lowerPath.includes('.spec.') || lowerPath.includes('__test')) return;
if (
lowerPath.includes('/test') ||
lowerPath.includes('.test.') ||
lowerPath.includes('.spec.') ||
lowerPath.includes('__test')
)
return;
const lineAt = (offset: number): number => {
let n = 0;
for (let i = 0; i < offset; i++) { if (content.charCodeAt(i) === 10) n++; }
for (let i = 0; i < offset; i++) {
if (content.charCodeAt(i) === 10) n++;
}
return n;
};
@ -1423,59 +1468,132 @@ export function extractQueuePatterns(filePath: string, content: string, out: Ext
const bullQueueRe = /new\s+Queue\s*\(\s*['"`]([^'"`]+)['"`]/g;
let m;
while ((m = bullQueueRe.exec(content)) !== null) {
out.push({ filePath, role: 'producer', queueName: m[1], method: 'Queue', lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'producer',
queueName: m[1],
method: 'Queue',
lineNumber: lineAt(m.index),
});
}
const bullWorkerRe = /new\s+Worker\s*\(\s*['"`]([^'"`]+)['"`]/g;
while ((m = bullWorkerRe.exec(content)) !== null) {
out.push({ filePath, role: 'consumer', queueName: m[1], method: 'Worker', lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'consumer',
queueName: m[1],
method: 'Worker',
lineNumber: lineAt(m.index),
});
}
const bullProcessRe = /\.process\s*\(\s*['"`]([^'"`]+)['"`]/g;
while ((m = bullProcessRe.exec(content)) !== null) {
out.push({ filePath, role: 'consumer', queueName: m[1], method: 'process', lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'consumer',
queueName: m[1],
method: 'process',
lineNumber: lineAt(m.index),
});
}
// RabbitMQ (amqplib) — sendToQueue / publish = producer, consume / assertQueue + consume = consumer
const amqpSendRe = /\.sendToQueue\s*\(\s*['"`]([^'"`]+)['"`]/g;
while ((m = amqpSendRe.exec(content)) !== null) {
out.push({ filePath, role: 'producer', queueName: m[1], method: 'sendToQueue', lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'producer',
queueName: m[1],
method: 'sendToQueue',
lineNumber: lineAt(m.index),
});
}
const amqpPublishRe = /\.publish\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*['"`]([^'"`]+)['"`]/g;
while ((m = amqpPublishRe.exec(content)) !== null) {
out.push({ filePath, role: 'producer', queueName: `${m[1]}/${m[2]}`, method: 'publish', lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'producer',
queueName: `${m[1]}/${m[2]}`,
method: 'publish',
lineNumber: lineAt(m.index),
});
}
const amqpConsumeRe = /\.consume\s*\(\s*['"`]([^'"`]+)['"`]/g;
while ((m = amqpConsumeRe.exec(content)) !== null) {
out.push({ filePath, role: 'consumer', queueName: m[1], method: 'consume', lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'consumer',
queueName: m[1],
method: 'consume',
lineNumber: lineAt(m.index),
});
}
// Kafka (kafkajs) — producer.send({topic: '...'}) = producer, consumer.subscribe({topic: '...'}) = consumer
const kafkaSendRe = /\.send\s*\(\s*\{[^}]*topic\s*:\s*['"`]([^'"`]+)['"`]/g;
while ((m = kafkaSendRe.exec(content)) !== null) {
out.push({ filePath, role: 'producer', queueName: m[1], method: 'send', lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'producer',
queueName: m[1],
method: 'send',
lineNumber: lineAt(m.index),
});
}
const kafkaSubRe = /\.subscribe\s*\(\s*\{[^}]*topic\s*:\s*['"`]([^'"`]+)['"`]/g;
while ((m = kafkaSubRe.exec(content)) !== null) {
out.push({ filePath, role: 'consumer', queueName: m[1], method: 'subscribe', lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'consumer',
queueName: m[1],
method: 'subscribe',
lineNumber: lineAt(m.index),
});
}
// AWS SQS — sendMessage/sendMessageBatch with QueueUrl = producer, receiveMessage = consumer
const sqsSendRe = /sendMessage(?:Batch)?\s*\(\s*\{[^}]*QueueUrl\s*:\s*['"`]([^'"`]+)['"`]/g;
while ((m = sqsSendRe.exec(content)) !== null) {
out.push({ filePath, role: 'producer', queueName: m[1], method: 'sendMessage', lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'producer',
queueName: m[1],
method: 'sendMessage',
lineNumber: lineAt(m.index),
});
}
const sqsRecvRe = /receiveMessage\s*\(\s*\{[^}]*QueueUrl\s*:\s*['"`]([^'"`]+)['"`]/g;
while ((m = sqsRecvRe.exec(content)) !== null) {
out.push({ filePath, role: 'consumer', queueName: m[1], method: 'receiveMessage', lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'consumer',
queueName: m[1],
method: 'receiveMessage',
lineNumber: lineAt(m.index),
});
}
// Celery (Python) — @app.task / .delay() / .apply_async()
const celeryTaskRe = /@(?:app|celery)\.task[^]*?def\s+(\w+)/g;
while ((m = celeryTaskRe.exec(content)) !== null) {
out.push({ filePath, role: 'consumer', queueName: m[1], method: 'task', handlerName: m[1], lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'consumer',
queueName: m[1],
method: 'task',
handlerName: m[1],
lineNumber: lineAt(m.index),
});
}
const celeryCallRe = /(\w+)\.(delay|apply_async)\s*\(/g;
while ((m = celeryCallRe.exec(content)) !== null) {
out.push({ filePath, role: 'producer', queueName: m[1], method: m[2], lineNumber: lineAt(m.index) });
out.push({
filePath,
role: 'producer',
queueName: m[1],
method: m[2],
lineNumber: lineAt(m.index),
});
}
}

View file

@ -452,14 +452,14 @@ export const streamAllCSVsToDisk = async (
);
break;
case 'Webhook': {
const eventTypes = node.properties.eventTypes as string[] || [];
const eventTypes = (node.properties.eventTypes as string[]) || [];
const eventTypesStr = `[${eventTypes.map((e: string) => `'${e.replace(/'/g, "''")}'`).join(',')}]`;
await webhookWriter.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
escapeCSVField(node.properties.filePath || ''),
escapeCSVField(node.properties.kind as string || ''),
escapeCSVField((node.properties.kind as string) || ''),
escapeCSVField(eventTypesStr),
].join(','),
);

View file

@ -3221,27 +3221,44 @@ export class LocalBackend {
};
}
private async webhookMap(repo: RepoHandle, params: { kind?: string; name?: string }): Promise<any> {
private async webhookMap(
repo: RepoHandle,
params: { kind?: string; name?: string },
): Promise<any> {
await this.ensureInitialized(repo.id);
const kindFilter = params.kind ? `AND n.kind = $kind` : '';
const nameFilter = params.name ? `AND n.name CONTAINS $name` : '';
const queryParams: Record<string, any> = {};
if (params.kind) queryParams.kind = params.kind;
if (params.name) queryParams.name = params.name;
const rows = await executeParameterized(repo.id, `
const rows = await executeParameterized(
repo.id,
`
MATCH (n:Webhook)
WHERE n.id STARTS WITH 'Webhook:' ${kindFilter} ${nameFilter}
RETURN n.id AS id, n.name AS name, n.filePath AS filePath, n.kind AS kind, n.eventTypes AS eventTypes
`, queryParams);
const triggerRows = await executeParameterized(repo.id, `
`,
queryParams,
);
const triggerRows = await executeParameterized(
repo.id,
`
MATCH (f:File)-[r:CodeRelation {type: 'TRIGGERS'}]->(w:Webhook)
RETURN f.filePath AS source, w.name AS target, r.reason AS reason
`, {});
`,
{},
);
const webhooks = rows.map((r: any) => ({
id: r.id, name: r.name, filePath: r.filePath, kind: r.kind, eventTypes: r.eventTypes || [],
id: r.id,
name: r.name,
filePath: r.filePath,
kind: r.kind,
eventTypes: r.eventTypes || [],
}));
const triggers = triggerRows.map((r: any) => ({
source: r.source, target: r.target, reason: r.reason,
source: r.source,
target: r.target,
reason: r.reason,
}));
return { webhooks, triggers, total: webhooks.length };
}

View file

@ -1,6 +1,8 @@
export class User {
save() {}
getName() { return ''; }
getName() {
return '';
}
}
export function getUser() {

View file

@ -1,5 +1,7 @@
export class Config {
validate(): boolean { return true; }
validate(): boolean {
return true;
}
}
export function getConfig(): Config {
return new Config();

View file

@ -1,5 +1,7 @@
export class Config {
validate(): boolean { return true; }
validate(): boolean {
return true;
}
}
export function getConfig(): Config {
return new Config();

View file

@ -1,6 +1,8 @@
export class User {
save(): void {}
getName(): string { return ''; }
getName(): string {
return '';
}
}
export function getUser(): User {
return new User();

View file

@ -1,2 +1,4 @@
import { Slot } from 'expo-router';
export default function AuthLayout() { return <Slot />; }
export default function AuthLayout() {
return <Slot />;
}

View file

@ -1,2 +1,6 @@
import { useRouter } from 'expo-router';
export default function Login() { const router = useRouter(); router.push('/'); return null; }
export default function Login() {
const router = useRouter();
router.push('/');
return null;
}

View file

@ -1,2 +1,4 @@
import { Slot } from 'expo-router';
export default function Layout() { return <Slot />; }
export default function Layout() {
return <Slot />;
}

View file

@ -1 +1,3 @@
export default function Home() { return null; }
export default function Home() {
return null;
}

View file

@ -1 +1,3 @@
export default function Profile() { return null; }
export default function Profile() {
return null;
}

View file

@ -1,2 +1,4 @@
import { Link } from 'expo-router';
export default function Settings() { return <Link href="/profile">Go</Link>; }
export default function Settings() {
return <Link href="/profile">Go</Link>;
}

View file

@ -1,2 +1,5 @@
import { useLocalSearchParams } from 'expo-router';
export default function UserDetail() { const { id } = useLocalSearchParams(); return null; }
export default function UserDetail() {
const { id } = useLocalSearchParams();
return null;
}

View file

@ -1 +1,6 @@
{"name": "expo-app-fixture", "dependencies": {"expo-router": "^3.0.0"}}
{
"name": "expo-app-fixture",
"dependencies": {
"expo-router": "^3.0.0"
}
}

View file

@ -1,7 +1,7 @@
# Test group for integration / fixture-driven tests
version: 1
name: test-group
description: "Test group for integration tests"
description: 'Test group for integration tests'
repos:
app/backend: test-backend

View file

@ -1 +1,4 @@
{ "name": "@monorepo/auth", "version": "1.0.0" }
{
"name": "@monorepo/auth",
"version": "1.0.0"
}

View file

@ -1 +1,4 @@
{ "name": "@monorepo/gateway", "version": "1.0.0" }
{
"name": "@monorepo/gateway",
"version": "1.0.0"
}

View file

@ -1 +1,4 @@
{ "name": "@monorepo/orders", "version": "1.0.0" }
{
"name": "@monorepo/orders",
"version": "1.0.0"
}

View file

@ -6,6 +6,9 @@ export async function GET() {
const grants = await fetchGrants();
return NextResponse.json({ data: grants, pagination: { page: 1, total: grants.length } });
} catch (err) {
return NextResponse.json({ error: 'Failed to fetch grants', message: String(err) }, { status: 400 });
return NextResponse.json(
{ error: 'Failed to fetch grants', message: String(err) },
{ status: 400 },
);
}
}

View file

@ -2,9 +2,11 @@ import { NextResponse } from 'next/server';
import { withAuth } from '../../../middleware/auth';
import { withRateLimit } from '../../../middleware/rate-limit';
export const GET = withAuth(withRateLimit(async (req: Request) => {
return NextResponse.json({ items: [], count: 0 });
}));
export const GET = withAuth(
withRateLimit(async (req: Request) => {
return NextResponse.json({ items: [], count: 0 });
}),
);
export const POST = async (req: Request) => {
const body = await req.json();

View file

@ -3,7 +3,9 @@ export async function GrantsList() {
const { data, pagination } = await res.json();
return (
<ul>
{data.map((g: any) => <li key={g.id}>{g.title}</li>)}
{data.map((g: any) => (
<li key={g.id}>{g.title}</li>
))}
<p>Page {pagination.page}</p>
</ul>
);

View file

@ -1,8 +1,5 @@
export async function useMulti() {
const [grantsRes, secureRes] = await Promise.all([
fetch('/api/grants'),
fetch('/api/secure'),
]);
const [grantsRes, secureRes] = await Promise.all([fetch('/api/grants'), fetch('/api/secure')]);
const grants = await grantsRes.json();
const secure = await secureRes.json();
return { grants: grants.data, secure: secure.items, meta: grants.meta };

View file

@ -1,5 +1,8 @@
export class ApiError extends Error {
constructor(public statusCode: number, message: string) {
constructor(
public statusCode: number,
message: string,
) {
super(message);
}
}

View file

@ -1,3 +1,6 @@
export async function fetchGrants() {
return [{ id: 1, title: 'Grant A' }, { id: 2, title: 'Grant B' }];
return [
{ id: 1, title: 'Grant A' },
{ id: 2, title: 'Grant B' },
];
}

View file

@ -1 +1,3 @@
export function helper1() { return 1; }
export function helper1() {
return 1;
}

View file

@ -1 +1,3 @@
export function helper10() { return 10; }
export function helper10() {
return 10;
}

View file

@ -1 +1,3 @@
export function helper11() { return 11; }
export function helper11() {
return 11;
}

View file

@ -1 +1,3 @@
export function helper12() { return 12; }
export function helper12() {
return 12;
}

View file

@ -1 +1,3 @@
export function helper13() { return 13; }
export function helper13() {
return 13;
}

View file

@ -1 +1,3 @@
export function helper2() { return 2; }
export function helper2() {
return 2;
}

View file

@ -1 +1,3 @@
export function helper3() { return 3; }
export function helper3() {
return 3;
}

View file

@ -1 +1,3 @@
export function helper4() { return 4; }
export function helper4() {
return 4;
}

View file

@ -1 +1,3 @@
export function helper5() { return 5; }
export function helper5() {
return 5;
}

View file

@ -1 +1,3 @@
export function helper6() { return 6; }
export function helper6() {
return 6;
}

View file

@ -1 +1,3 @@
export function helper7() { return 7; }
export function helper7() {
return 7;
}

View file

@ -1 +1,3 @@
export function helper8() { return 8; }
export function helper8() {
return 8;
}

View file

@ -1 +1,3 @@
export function helper9() { return 9; }
export function helper9() {
return 9;
}

View file

@ -1,21 +1,21 @@
class Animal {
speak() {
throw new Error("Not implemented");
}
speak() {
throw new Error('Not implemented');
}
static classify(name) {
return "mammal";
}
static classify(name) {
return 'mammal';
}
breathe() {
return true;
}
breathe() {
return true;
}
}
class Dog extends Animal {
speak() {
return "woof";
}
speak() {
return 'woof';
}
}
module.exports = { Animal, Dog };

View file

@ -2,4 +2,4 @@ const { Animal, Dog } = require('./animal');
const dog = new Dog();
const sound = dog.speak();
const category = Animal.classify("dog");
const category = Animal.classify('dog');

View file

@ -1,4 +1,6 @@
class BaseModel {
save() { return true; }
save() {
return true;
}
}
module.exports = { BaseModel };

View file

@ -1,6 +1,8 @@
const { BaseModel } = require('./Base');
class User extends BaseModel {
serialize() { return ''; }
serialize() {
return '';
}
}
module.exports = { User };

View file

@ -1,4 +1,6 @@
class Repo {
save() { return true; }
save() {
return true;
}
}
module.exports = { Repo };

View file

@ -1,5 +1,7 @@
class User {
save() { return true; }
save() {
return true;
}
process() {
this.save();
}

View file

@ -1,4 +1,6 @@
class BaseModel {
save() { return true; }
save() {
return true;
}
}
module.exports = { BaseModel };

View file

@ -1,4 +1,6 @@
class Repo {
save() { return true; }
save() {
return true;
}
}
module.exports = { Repo };

View file

@ -8,6 +8,8 @@ class User {
/**
* @returns {User[]}
*/
function getUsers() { return []; }
function getUsers() {
return [];
}
module.exports = { User, getUsers };

View file

@ -1,4 +1,6 @@
class Repo {
save() { return true; }
save() {
return true;
}
}
module.exports = { Repo };

View file

@ -1,4 +1,6 @@
class User {
save() { return true; }
save() {
return true;
}
}
module.exports = { User };

View file

@ -3,7 +3,9 @@
*/
class Address {
/** @returns {boolean} */
save() { return true; }
save() {
return true;
}
}
/**

View file

@ -3,6 +3,8 @@ const { User } = require('./models');
/**
* @returns {User}
*/
function getUser() { return new User(); }
function getUser() {
return new User();
}
module.exports = { getUser };

View file

@ -1,10 +1,10 @@
class Address {
city = '';
city = '';
}
class User {
name = '';
address = null;
name = '';
address = null;
}
module.exports = { Address, User };

View file

@ -1,7 +1,7 @@
const { User, Address } = require('./models');
function updateUser() {
const user = new User();
user.name = "Alice";
user.address = new Address();
const user = new User();
user.name = 'Alice';
user.address = new Address();
}

View file

@ -1,4 +1,4 @@
export function GrantsList({ slug }: { slug: string }) {
const data = fetch(`/api/organizations/${slug}/grants`).then(r => r.json());
const data = fetch(`/api/organizations/${slug}/grants`).then((r) => r.json());
return data;
}

View file

@ -1,4 +1,4 @@
export function useGrants() {
const data = fetch('/api/grants').then(r => r.json());
const data = fetch('/api/grants').then((r) => r.json());
return data;
}

View file

@ -1 +1,7 @@
{"autoload":{"psr-4":{"App\\\\":"src/"}}}
{
"autoload": {
"psr-4": {
"App\\\\": "src/"
}
}
}

View file

@ -1,7 +1,7 @@
{
"autoload": {
"psr-4": {
"App\\": "app/"
}
"autoload": {
"psr-4": {
"App\\": "app/"
}
}
}

View file

@ -1 +1,7 @@
{"autoload":{"psr-4":{"App\\\\":"src/"}}}
{
"autoload": {
"psr-4": {
"App\\\\": "src/"
}
}
}

View file

@ -1 +1,7 @@
{"autoload":{"psr-4":{"App\\\\":"src/"}}}
{
"autoload": {
"psr-4": {
"App\\\\": "src/"
}
}
}

View file

@ -1,5 +1,7 @@
{
"autoload": {
"psr-4": { "App\\": "app/" }
"psr-4": {
"App\\": "app/"
}
}
}

View file

@ -1,5 +1,7 @@
{
"autoload": {
"psr-4": { "App\\": "app/" }
"psr-4": {
"App\\": "app/"
}
}
}

View file

@ -1,5 +1,7 @@
{
"autoload": {
"psr-4": { "App\\": "app/" }
"psr-4": {
"App\\": "app/"
}
}
}

View file

@ -1,4 +1,4 @@
// TypeScript middleware — should NOT be resolved by Python imports
export function handleRequest(req: Request): Response {
return new Response("ok");
return new Response('ok');
}

View file

@ -3,5 +3,5 @@ import { NextResponse } from 'next/server';
export async function GET() {
const courses = await getCourses();
const articles = await getArticles();
return NextResponse.json({ 'courses': courses, 'articles': articles });
return NextResponse.json({ courses: courses, articles: articles });
}

View file

@ -1,5 +1,5 @@
export function GdprExport() {
const data = fetch('/api/gdpr/export', { method: 'POST' }).then(r => r.json());
const data = fetch('/api/gdpr/export', { method: 'POST' }).then((r) => r.json());
const link = document.createElement('a');
link.href = data.url;
document.body.appendChild(link);

View file

@ -1,5 +1,5 @@
export function SearchBar() {
const data = fetch('/api/search').then(r => r.json());
const data = fetch('/api/search').then((r) => r.json());
console.log(data.courses);
console.log(data.articles);
return null;

View file

@ -1,5 +1,5 @@
export function UserList() {
const res = fetch('/api/users').then(r => r.json());
const res = fetch('/api/users').then((r) => r.json());
const items = res.data;
const err = res.error;
return null;

View file

@ -1,8 +1,12 @@
import { User } from './user';
import { Repo } from './repo';
function getUser(): User { return new User(); }
function getRepo(): Repo { return new Repo(); }
function getUser(): User {
return new User();
}
function getRepo(): Repo {
return new Repo();
}
export function processEntities(): void {
const u: User = getUser();

View file

@ -1,5 +1,7 @@
import { Greeting } from './greeting';
export class A {
greet(): Greeting { return new Greeting(); }
greet(): Greeting {
return new Greeting();
}
}

View file

@ -1,8 +1,12 @@
import { User } from './user';
import { Repo } from './repo';
function getUser(): User { return new User(); }
function getRepo(): Repo { return new Repo(); }
function getUser(): User {
return new User();
}
function getRepo(): Repo {
return new Repo();
}
// Multi-hop forward-declared chain: a → b → c (source order)
// All three should resolve because the post-walk pass processes in order.

View file

@ -18,7 +18,7 @@ function processUndefined(x: User | undefined) {
}
}
const processFuncExpr = function(x: User | null) {
const processFuncExpr = function (x: User | null) {
if (x !== null) {
x.save();
}

View file

@ -1,8 +1,12 @@
import { User } from './user';
import { Repo } from './repo';
function findUser(): User | null { return new User(); }
function findRepo(): Repo | undefined { return new Repo(); }
function findUser(): User | null {
return new User();
}
function findRepo(): Repo | undefined {
return new Repo();
}
// Nullable type + assignment chain: the nullable union must be stripped
// before the alias can resolve to User.

View file

@ -1,6 +1,8 @@
export class Address {
city: string = '';
save(): boolean { return true; }
save(): boolean {
return true;
}
}
export class User {

View file

@ -1,2 +1,4 @@
import { User } from './models';
export function getUser(): User { return new User(); }
export function getUser(): User {
return new User();
}

View file

@ -1,5 +1,5 @@
function greet(name: string, greeting: string = "Hello"): string {
return greeting + ", " + name;
function greet(name: string, greeting: string = 'Hello'): string {
return greeting + ', ' + name;
}
function search(query: string, limit?: number): string[] {
@ -7,8 +7,8 @@ function search(query: string, limit?: number): string[] {
}
function process() {
greet("Alice");
greet("Bob", "Hi");
search("test");
search("test", 10);
greet('Alice');
greet('Bob', 'Hi');
search('test');
search('test', 10);
}

View file

@ -1,10 +1,10 @@
function lookup(id: number): string;
function lookup(name: string): string;
function lookup(key: number | string): string {
return String(key);
return String(key);
}
function process() {
lookup(42);
lookup("alice");
lookup(42);
lookup('alice');
}

View file

@ -1,5 +1,5 @@
export interface IRepository {
find(id: number): string;
find(name: string): string;
save(data: string): void;
find(id: number): string;
find(name: string): string;
save(data: string): void;
}

View file

@ -1,12 +1,12 @@
import { IRepository } from './repository';
export class SqlRepository implements IRepository {
find(id: number): string;
find(name: string): string;
find(arg: number | string): string {
return typeof arg === 'number' ? 'found-by-id' : 'found-by-name';
}
save(data: string): void {
console.log(data);
}
find(id: number): string;
find(name: string): string;
find(arg: number | string): string {
return typeof arg === 'number' ? 'found-by-id' : 'found-by-name';
}
save(data: string): void {
console.log(data);
}
}

View file

@ -19,9 +19,9 @@ class Dog extends Animal {
export function run(): void {
// Virtual dispatch: declared as Animal, constructed as Dog
const animal: Animal = new Dog();
animal.fetchBall(); // Only Dog has fetchBall — proves virtual dispatch override
animal.fetchBall(); // Only Dog has fetchBall — proves virtual dispatch override
// Direct type: no override needed
const dog: Dog = new Dog();
dog.fetchBall(); // Direct resolution to Dog#fetchBall
dog.fetchBall(); // Direct resolution to Dog#fetchBall
}

View file

@ -2,7 +2,7 @@ import { User, Address } from './models';
function updateUser(user: User) {
// Write access: user.name = "Alice"
user.name = "Alice";
user.name = 'Alice';
// Write access: user.address = new Address()
user.address = new Address();

View file

@ -1,7 +1,7 @@
import { ILogger } from './models';
export class ConsoleLogger implements ILogger {
log(message: string): void {
console.log(message);
}
log(message: string): void {
console.log(message);
}
}

View file

@ -1,11 +1,11 @@
export interface ILogger {
log(message: string): void;
log(message: string): void;
}
export class BaseService {
protected name: string = '';
protected name: string = '';
getName(): string {
return this.name;
}
getName(): string {
return this.name;
}
}

View file

@ -2,12 +2,12 @@ import { BaseService, ILogger } from './models';
import { ConsoleLogger } from './logger';
export class UserService extends BaseService implements ILogger {
log(message: string): void {
const logger = new ConsoleLogger();
logger.log(message);
}
log(message: string): void {
const logger = new ConsoleLogger();
logger.log(message);
}
getUsers(): string[] {
return [];
}
getUsers(): string[] {
return [];
}
}

View file

@ -1,3 +1,5 @@
export class Repo {
save() { return true; }
save() {
return true;
}
}

View file

@ -1,3 +1,5 @@
export class User {
save() { return true; }
save() {
return true;
}
}

View file

@ -1,3 +1,5 @@
export class Repo {
save() { return true; }
save() {
return true;
}
}

View file

@ -1,3 +1,5 @@
export class User {
save() { return true; }
save() {
return true;
}
}

View file

@ -1,9 +1,11 @@
export class Repo {
name: string;
constructor(name: string) { this.name = name; }
constructor(name: string) {
this.name = name;
}
save(): void {}
}
export function getRepos(): Repo[] {
return [new Repo("main")];
return [new Repo('main')];
}

View file

@ -1,9 +1,11 @@
export class User {
name: string;
constructor(name: string) { this.name = name; }
constructor(name: string) {
this.name = name;
}
save(): void {}
}
export function getUsers(): User[] {
return [new User("alice")];
return [new User('alice')];
}

View file

@ -1,3 +1,5 @@
export class BaseModel<T> {
save(): boolean { return true; }
save(): boolean {
return true;
}
}

Some files were not shown because too many files have changed in this diff Show more