sourcecode 1.38.0__tar.gz → 1.41.0__tar.gz
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.
- sourcecode-1.41.0/CHANGELOG.md +123 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/PKG-INFO +6 -3
- {sourcecode-1.38.0 → sourcecode-1.41.0}/README.md +5 -2
- {sourcecode-1.38.0 → sourcecode-1.41.0}/pyproject.toml +1 -1
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/cir_graphs.py +72 -7
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/cli.py +101 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/format_contract.py +1 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/mcp/registry.py +47 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/openapi_surface.py +32 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/repository_ir.py +249 -88
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/spring_impact.py +36 -12
- sourcecode-1.41.0/src/sourcecode/validation_surface.py +305 -0
- sourcecode-1.38.0/CHANGELOG.md +0 -54
- {sourcecode-1.38.0 → sourcecode-1.41.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/.gitignore +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/.ruff.toml +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/CONTRIBUTING.md +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/LICENSE +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/SECURITY.md +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/raw +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/cache.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/canonical_ir.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/error_schema.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/explain.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/file_chunker.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/flow_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/fqn_utils.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/license.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/mcp/orchestrator.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/mcp/server.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/migrate_check.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/pr_impact.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/rename_refactor.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/ris.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/security_config.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/spring_event_topology.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/spring_findings.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/spring_model.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/spring_security_audit.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/spring_semantic.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/spring_tx_analyzer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/version_check.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/supabase/.temp/cli-latest +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/supabase/functions/README.md +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/supabase/functions/get-license/index.ts +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/supabase/functions/telemetry/index.ts +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/supabase/sql/license_event_ordering.sql +0 -0
- {sourcecode-1.38.0 → sourcecode-1.41.0}/supabase/sql/telemetry_events.sql +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.41.0] — 2026-06-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Fase 21 — spec-recovered HTTP routes reach `impact-chain`.** In
|
|
7
|
+
openapi-generator "interface-only" repos (`@RestController implements XxxApi`,
|
|
8
|
+
mappings on the generated interface under `target/generated-sources`), the HTTP
|
|
9
|
+
surface is recovered from the OpenAPI spec. That recovery previously lived only in
|
|
10
|
+
the `endpoints` command path, so `impact-chain` on a repository/service symbol
|
|
11
|
+
reported `endpoints_affected = 0` even though the route existed. The spec→controller
|
|
12
|
+
linking is now shared and wired through the impact model end to end.
|
|
13
|
+
- **21-02** — `_recover_openapi_spec_routes` (repository_ir): single shared helper that
|
|
14
|
+
links spec operations to interface-defined controllers and emits both the
|
|
15
|
+
`endpoints`-command shape and the `route_surface` shape. `build_repo_ir` merges the
|
|
16
|
+
spec-sourced `route_surface` entries, so they flow `route_surface → CanonicalRepositoryIR
|
|
17
|
+
→ EndpointIndex → impact-chain`. The `endpoints` command output stays byte-identical.
|
|
18
|
+
- **21-03** — `impact-chain` BFS now crosses the interface DI boundary mid-chain.
|
|
19
|
+
`_bfs_callers` takes the `ImplementationGraph` and, for each implementation class it
|
|
20
|
+
reaches, folds in the reverse edges of that class's interfaces (callers inject the
|
|
21
|
+
interface type, so the `injects` edges sit on the interface node). Closes
|
|
22
|
+
`repo → serviceImpl → (service interface) → controller` so the spec-recovered endpoint
|
|
23
|
+
surfaces. CH-001b already did this for the seed; 21-03 extends it to every impl in the
|
|
24
|
+
traversal.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- **BUG-PARSER-002 — multi-line constructor/method signatures.** A signature whose
|
|
28
|
+
parameter list spans several physical lines (the canonical Spring constructor-injection
|
|
29
|
+
idiom, one param per line) lost its parameters: the per-line decl regex captured `[^)]*`
|
|
30
|
+
up to end-of-line only, so `param_types` was empty and no `injects` edges were emitted.
|
|
31
|
+
The pre-join pass now balances parentheses for declaration openers, mirroring the
|
|
32
|
+
existing multi-line class-declaration join.
|
|
33
|
+
- **BUG-PARSER-003 — wildcard-import dependency resolution.** A dependency type pulled in
|
|
34
|
+
via `import pkg.*` was never resolved to an FQN (`import_map` skips `.*`), so even a
|
|
35
|
+
single-line constructor produced no `injects` edge. `_build_relations` now receives the
|
|
36
|
+
global `{package → {simple → FQN}}` map and resolves wildcard-imported types against it.
|
|
37
|
+
|
|
38
|
+
### Why
|
|
39
|
+
Field test of spring-petclinic-rest issue #11 (weakness #2): `impact-chain` on a repo or
|
|
40
|
+
service symbol surfaced no affected HTTP endpoints in interface-only openapi-generator
|
|
41
|
+
repos, even though the `endpoints` command listed the routes. The break was structural at
|
|
42
|
+
several layers — the spec→controller linking never reached `route_surface`/the CIR, the DI
|
|
43
|
+
chain dead-ended at the service impl, and (the live-repo blocker) the very first hop
|
|
44
|
+
repo→service was missing because petclinic's `ClinicServiceImpl` uses a multi-line
|
|
45
|
+
constructor with a wildcard repository import. With all four fixes, the live E2E
|
|
46
|
+
`impact-chain VetRepository` on petclinic-rest goes from **0** affected endpoints to the
|
|
47
|
+
full transitive set (**35**, reaching every controller incl. the spec-recovered v2 routes).
|
|
48
|
+
|
|
49
|
+
## [1.40.0] — 2026-06-16
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
- **CH-001c — interface impact models implementors.** `impact-chain` over an
|
|
53
|
+
interface or abstract base now resolves its full in-repo descendant set:
|
|
54
|
+
concrete `implements` classes, `extends` sub-interfaces, and subclasses, traversed
|
|
55
|
+
transitively. A base interface query reaches impls hidden behind an intermediate
|
|
56
|
+
sub-interface (e.g. Spring Data repositories: `SpringDataVetRepository extends
|
|
57
|
+
VetRepository` alongside the JPA/JDBC impls).
|
|
58
|
+
- `ImplementationGraph` gains `subtypes_of()`, `supertypes_of()`, and
|
|
59
|
+
`all_subtypes_of()` (transitive, cycle-safe). `extends` edges are now captured;
|
|
60
|
+
`implements`-only indices (`implementations_of`/`primary_implementation`) keep their
|
|
61
|
+
strict DI-resolution semantics — sub-interfaces are not counted as bean implementations.
|
|
62
|
+
- `ImpactChainResult.implementations`: new output field listing the in-repo subtypes
|
|
63
|
+
of the queried type, making the implementation blast radius visible (previously the
|
|
64
|
+
impls were silent BFS seeds).
|
|
65
|
+
|
|
66
|
+
### Why
|
|
67
|
+
Field test of spring-petclinic-rest issue #11 surfaced the gap: `impact-chain
|
|
68
|
+
VetRepository` returned only the SpringData sub-interface, missing the JPA/JDBC impls —
|
|
69
|
+
exactly the "3 impls" graph the maintainer cared about. Interface impact did not model
|
|
70
|
+
implementors.
|
|
71
|
+
|
|
72
|
+
## [1.33.0] — 2026-05-29
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
- **Repositioned product identity** around persistent structural cache and ultra-fast repeated analysis for AI coding agents. Cache is now the central product story, not a performance feature.
|
|
76
|
+
- README rewritten: new intro emphasizing persistent context engine, cache performance benchmarks promoted above quickstart, agent workflow patterns section added, "Java/Spring analysis CLI" framing moved down.
|
|
77
|
+
- `pyproject.toml` description updated: "Persistent structural context and ultra-fast repeated analysis for AI coding agents".
|
|
78
|
+
- CLI `--help` updated: tagline, cold/warm latency numbers, cache commands section added prominently.
|
|
79
|
+
|
|
80
|
+
## [Unreleased]
|
|
81
|
+
|
|
82
|
+
### Added
|
|
83
|
+
- `prepare-context generate-tests --include-config`: opt-in flag to include tooling
|
|
84
|
+
config files (`.eslintrc*`, `karma.conf.js`, `jest.config.js`, etc.) in `test_gaps`.
|
|
85
|
+
By default these are now excluded (IMP-1).
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
- **BUG-1** `repo-ir` stdout: JSON is now written via `stdout.buffer` (UTF-8) so Unicode
|
|
89
|
+
characters (e.g. `→`) survive on Windows consoles with non-UTF-8 codecs.
|
|
90
|
+
`main_entry` also calls `stdout.reconfigure(encoding='utf-8')` on startup.
|
|
91
|
+
- **BUG-2** `--exclude` with a space-separated value (`--exclude "a,b"`) was silently
|
|
92
|
+
consumed as the repository path. Added `--exclude` to the options-with-value registry
|
|
93
|
+
so its argument is parsed correctly.
|
|
94
|
+
- **BUG-3** `prepare-context onboard --fast` returned only the git-changed file
|
|
95
|
+
(e.g. `.idea/vcs.xml`). Fast mode for `onboard` now always uses a shallow depth-2
|
|
96
|
+
scan so manifests and entry points are reliably discovered.
|
|
97
|
+
- **BUG-4** `angular_version: null` when `package.json` has `"dependencies": null`.
|
|
98
|
+
The merge now uses `or {}` so an explicit `null` key doesn't raise TypeError.
|
|
99
|
+
Also checks `peerDependencies` as a fallback source.
|
|
100
|
+
- **BUG-5** `lazy_routes_count: 0` in Angular projects. Counting now uses
|
|
101
|
+
`loadChildren:` and `loadComponent:` (property syntax) instead of the defunct
|
|
102
|
+
`loadChildren(` call syntax.
|
|
103
|
+
- **BUG-6** Angular `*.component.ts` files classified as Spring `@Service` in
|
|
104
|
+
`review-pr` and `prepare-context` output on fullstack Java+Angular repos.
|
|
105
|
+
Root cause: `"component"` was in `_SERVICE_KW` inside `_classify_changed_file`.
|
|
106
|
+
Fix: Angular detection block (by `.ts` stem suffix) now runs **before** the
|
|
107
|
+
Java/Spring heuristics. `"component"` removed from `_SERVICE_KW`. Added
|
|
108
|
+
`ng_component`, `ng_pipe`, `ng_directive`, `ng_guard`, `ng_interceptor`,
|
|
109
|
+
`ng_resolver`, `ng_service`, `ng_module` to `_ARTIFACT_CHANGE_EFFECT`.
|
|
110
|
+
`ast_extractor._detect_role` updated with the same Angular stem-suffix map.
|
|
111
|
+
- **BUG-7** `--compact` help text referenced `--slim (when available)` which is
|
|
112
|
+
not implemented and does not exist as a CLI option, causing user confusion
|
|
113
|
+
(`Error: No such option '--slim'`). Removed the reference (Option A: remove
|
|
114
|
+
mention rather than implement the flag this sprint).
|
|
115
|
+
|
|
116
|
+
### Regression tests added (`tests/test_bug_fixes_v13122.py`)
|
|
117
|
+
- 13 exit-code tests covering all commands reported as EXIT 255 — all verified
|
|
118
|
+
to return EXIT 0 (BUG-1 through BUG-7 of this audit cycle).
|
|
119
|
+
- 8 Angular classification tests locking `ng_component` / `ng_service` / `ng_*`
|
|
120
|
+
artifact types and `_ARTIFACT_CHANGE_EFFECT` entries.
|
|
121
|
+
- 3 `--slim` tests verifying the option is absent from help and CLI surface.
|
|
122
|
+
- 6 `angular_version` parsing tests covering `dependencies`, `devDependencies`,
|
|
123
|
+
`peerDependencies`, `null` JSON values, and version prefix stripping.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.41.0
|
|
4
4
|
Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Keywords: agents,ai,codebase,context,developer-tools,llm
|
|
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
**Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
|
|
42
42
|
|
|
43
|
-

|
|
44
44
|

|
|
45
45
|
|
|
46
46
|
---
|
|
@@ -114,7 +114,7 @@ pipx install sourcecode
|
|
|
114
114
|
|
|
115
115
|
```bash
|
|
116
116
|
sourcecode version
|
|
117
|
-
# sourcecode 1.
|
|
117
|
+
# sourcecode 1.39.0
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
---
|
|
@@ -149,6 +149,9 @@ sourcecode impact-chain OrderPlacedEvent /path/to/repo --type events
|
|
|
149
149
|
# REST endpoint surface
|
|
150
150
|
sourcecode endpoints /path/to/repo
|
|
151
151
|
|
|
152
|
+
# Request-body validation per endpoint: constraints + custom validators (free)
|
|
153
|
+
sourcecode validation /path/to/repo
|
|
154
|
+
|
|
152
155
|
# Onboard to an unfamiliar codebase
|
|
153
156
|
sourcecode onboard /path/to/repo
|
|
154
157
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|

|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -76,7 +76,7 @@ pipx install sourcecode
|
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
78
|
sourcecode version
|
|
79
|
-
# sourcecode 1.
|
|
79
|
+
# sourcecode 1.39.0
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
---
|
|
@@ -111,6 +111,9 @@ sourcecode impact-chain OrderPlacedEvent /path/to/repo --type events
|
|
|
111
111
|
# REST endpoint surface
|
|
112
112
|
sourcecode endpoints /path/to/repo
|
|
113
113
|
|
|
114
|
+
# Request-body validation per endpoint: constraints + custom validators (free)
|
|
115
|
+
sourcecode validation /path/to/repo
|
|
116
|
+
|
|
114
117
|
# Onboard to an unfamiliar codebase
|
|
115
118
|
sourcecode onboard /path/to/repo
|
|
116
119
|
|
|
@@ -25,22 +25,67 @@ class ImplementationGraph:
|
|
|
25
25
|
|
|
26
26
|
Built from implements edges where BOTH ends are known CIR symbols (internal
|
|
27
27
|
interface/class pairs). External framework interfaces are excluded.
|
|
28
|
+
|
|
29
|
+
Subtype indices (CH-001c): `extends` edges are also captured so that
|
|
30
|
+
interface-to-interface inheritance (`SubIface extends BaseIface`) and abstract
|
|
31
|
+
base classes (`SubClass extends BaseClass`) are modeled as descendants of the
|
|
32
|
+
supertype. The `implements`-only indices (`_impl_of`/`_ifaces_of`) are kept
|
|
33
|
+
separate to preserve DI resolution semantics (primary_implementation).
|
|
28
34
|
"""
|
|
29
35
|
_impl_of: dict[str, list[str]] = field(default_factory=dict)
|
|
30
36
|
_ifaces_of: dict[str, list[str]] = field(default_factory=dict)
|
|
37
|
+
# CH-001c: union of implements + extends descendants (impl classes, sub-interfaces,
|
|
38
|
+
# subclasses) keyed by supertype FQN, and its reverse.
|
|
39
|
+
_subtype_of: dict[str, list[str]] = field(default_factory=dict)
|
|
40
|
+
_supertype_of: dict[str, list[str]] = field(default_factory=dict)
|
|
31
41
|
|
|
32
42
|
# ---------------------------------------------------------------------------
|
|
33
43
|
# Queries
|
|
34
44
|
# ---------------------------------------------------------------------------
|
|
35
45
|
|
|
36
46
|
def implementations_of(self, interface_fqn: str) -> list[str]:
|
|
37
|
-
"""Return FQNs of classes that implement interface_fqn (in-repo only).
|
|
47
|
+
"""Return FQNs of classes that implement interface_fqn (in-repo only).
|
|
48
|
+
|
|
49
|
+
Strictly `implements` edges — excludes sub-interfaces/subclasses. Use
|
|
50
|
+
subtypes_of()/all_subtypes_of() for the full impact-relevant descendant set.
|
|
51
|
+
"""
|
|
38
52
|
return self._impl_of.get(interface_fqn, [])
|
|
39
53
|
|
|
40
54
|
def interfaces_of(self, class_fqn: str) -> list[str]:
|
|
41
55
|
"""Return FQNs of in-repo interfaces implemented by class_fqn."""
|
|
42
56
|
return self._ifaces_of.get(class_fqn, [])
|
|
43
57
|
|
|
58
|
+
def subtypes_of(self, type_fqn: str) -> list[str]:
|
|
59
|
+
"""Return direct in-repo subtypes of type_fqn.
|
|
60
|
+
|
|
61
|
+
Union of `implements` (concrete impls) and `extends` (sub-interfaces,
|
|
62
|
+
subclasses) children. This is the impact-relevant descendant set: a change
|
|
63
|
+
to type_fqn's contract propagates to all of these.
|
|
64
|
+
"""
|
|
65
|
+
return self._subtype_of.get(type_fqn, [])
|
|
66
|
+
|
|
67
|
+
def supertypes_of(self, type_fqn: str) -> list[str]:
|
|
68
|
+
"""Return direct in-repo supertypes of type_fqn (implemented/extended)."""
|
|
69
|
+
return self._supertype_of.get(type_fqn, [])
|
|
70
|
+
|
|
71
|
+
def all_subtypes_of(self, type_fqn: str) -> list[str]:
|
|
72
|
+
"""Return the transitive closure of in-repo subtypes (BFS, cycle-safe).
|
|
73
|
+
|
|
74
|
+
Covers multi-level hierarchies, e.g. a base interface → sub-interface →
|
|
75
|
+
concrete impl chain. Order is breadth-first from type_fqn; deduplicated.
|
|
76
|
+
"""
|
|
77
|
+
seen: set[str] = set()
|
|
78
|
+
out: list[str] = []
|
|
79
|
+
queue: list[str] = list(self._subtype_of.get(type_fqn, []))
|
|
80
|
+
while queue:
|
|
81
|
+
sub = queue.pop(0)
|
|
82
|
+
if sub in seen:
|
|
83
|
+
continue
|
|
84
|
+
seen.add(sub)
|
|
85
|
+
out.append(sub)
|
|
86
|
+
queue.extend(self._subtype_of.get(sub, []))
|
|
87
|
+
return out
|
|
88
|
+
|
|
44
89
|
def primary_implementation(self, interface_fqn: str) -> str | None:
|
|
45
90
|
"""Return the single implementation if unambiguous, else None.
|
|
46
91
|
|
|
@@ -89,9 +134,14 @@ class ImplementationGraph:
|
|
|
89
134
|
|
|
90
135
|
impl_of: dict[str, list[str]] = {}
|
|
91
136
|
ifaces_of: dict[str, list[str]] = {}
|
|
137
|
+
subtype_of: dict[str, list[str]] = {}
|
|
138
|
+
supertype_of: dict[str, list[str]] = {}
|
|
92
139
|
|
|
93
140
|
for edge in dependencies:
|
|
94
|
-
|
|
141
|
+
etype = edge.get("type")
|
|
142
|
+
# CH-001c: extends edges (sub-interface / subclass) are subtype relations
|
|
143
|
+
# too, even though they never feed the implements-only DI indices.
|
|
144
|
+
if etype not in ("implements", "extends"):
|
|
95
145
|
continue
|
|
96
146
|
from_fqn = (edge.get("from") or "").strip()
|
|
97
147
|
to_fqn = (edge.get("to") or "").strip()
|
|
@@ -110,12 +160,27 @@ class ImplementationGraph:
|
|
|
110
160
|
if ">" in from_fqn or "<" in from_fqn:
|
|
111
161
|
continue
|
|
112
162
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
163
|
+
# Subtype indices — both implements and extends contribute descendants.
|
|
164
|
+
if from_fqn not in subtype_of.get(to_fqn, []):
|
|
165
|
+
subtype_of.setdefault(to_fqn, []).append(from_fqn)
|
|
166
|
+
if to_fqn not in supertype_of.get(from_fqn, []):
|
|
167
|
+
supertype_of.setdefault(from_fqn, []).append(to_fqn)
|
|
168
|
+
|
|
169
|
+
# Implements-only indices — preserve DI resolution semantics. Sub-interfaces
|
|
170
|
+
# and subclasses (extends) must NOT count as "implementations" for
|
|
171
|
+
# primary_implementation() bean resolution.
|
|
172
|
+
if etype == "implements":
|
|
173
|
+
if from_fqn not in impl_of.get(to_fqn, []):
|
|
174
|
+
impl_of.setdefault(to_fqn, []).append(from_fqn)
|
|
175
|
+
if to_fqn not in ifaces_of.get(from_fqn, []):
|
|
176
|
+
ifaces_of.setdefault(from_fqn, []).append(to_fqn)
|
|
117
177
|
|
|
118
|
-
return cls(
|
|
178
|
+
return cls(
|
|
179
|
+
_impl_of=impl_of,
|
|
180
|
+
_ifaces_of=ifaces_of,
|
|
181
|
+
_subtype_of=subtype_of,
|
|
182
|
+
_supertype_of=supertype_of,
|
|
183
|
+
)
|
|
119
184
|
|
|
120
185
|
|
|
121
186
|
# ---------------------------------------------------------------------------
|
|
@@ -230,6 +230,8 @@ _SUBCOMMANDS: frozenset[str] = frozenset(
|
|
|
230
230
|
"cold-start",
|
|
231
231
|
# Spring semantic audit
|
|
232
232
|
"spring-audit",
|
|
233
|
+
# Request-body validation surface
|
|
234
|
+
"validation",
|
|
233
235
|
# Spring impact chain
|
|
234
236
|
"impact-chain",
|
|
235
237
|
# PR blast-radius report
|
|
@@ -3915,6 +3917,105 @@ def endpoints_cmd(
|
|
|
3915
3917
|
_nudge()
|
|
3916
3918
|
|
|
3917
3919
|
|
|
3920
|
+
@app.command("validation")
|
|
3921
|
+
def validation_cmd(
|
|
3922
|
+
path: Path = typer.Argument(
|
|
3923
|
+
Path("."),
|
|
3924
|
+
help="Repository path to scan for request-body validation (default: current directory)",
|
|
3925
|
+
),
|
|
3926
|
+
output_path: Optional[Path] = typer.Option(
|
|
3927
|
+
None, "--output", "-o",
|
|
3928
|
+
help="Write output to a file instead of stdout.",
|
|
3929
|
+
),
|
|
3930
|
+
format: str = typer.Option(
|
|
3931
|
+
"json",
|
|
3932
|
+
"--format",
|
|
3933
|
+
"-f",
|
|
3934
|
+
help="Output format: json (default) or yaml.",
|
|
3935
|
+
show_default=True,
|
|
3936
|
+
),
|
|
3937
|
+
copy: bool = typer.Option(
|
|
3938
|
+
False,
|
|
3939
|
+
"--copy",
|
|
3940
|
+
"-c",
|
|
3941
|
+
help="Copy output to system clipboard after a successful run.",
|
|
3942
|
+
),
|
|
3943
|
+
path_prefix: Optional[str] = typer.Option(
|
|
3944
|
+
None, "--path-prefix", "-p",
|
|
3945
|
+
help="Filter endpoints whose URL path starts with this prefix.",
|
|
3946
|
+
),
|
|
3947
|
+
gaps_only: bool = typer.Option(
|
|
3948
|
+
False, "--gaps-only",
|
|
3949
|
+
help="Report only endpoints/fields with no declared validation (the gaps section).",
|
|
3950
|
+
),
|
|
3951
|
+
) -> None:
|
|
3952
|
+
"""Map request-body validation per endpoint (constraints + custom validators).
|
|
3953
|
+
|
|
3954
|
+
\b
|
|
3955
|
+
Aggregates two sources of bean-validation truth so an agent knows exactly
|
|
3956
|
+
what a request body must satisfy before touching it:
|
|
3957
|
+
* declarative constraints on the DTOs (@Pattern/@Size/@NotNull, min/max,
|
|
3958
|
+
enum), recovered from the OpenAPI spec even when the DTOs are generated
|
|
3959
|
+
under target/generated-sources (not scanned);
|
|
3960
|
+
* hand-written custom validators (@Constraint + ConstraintValidator, e.g.
|
|
3961
|
+
PetAgeValidator), linked to fields via x-field-extra-annotation.
|
|
3962
|
+
|
|
3963
|
+
\b
|
|
3964
|
+
Output (JSON): per-endpoint validatedFields with their rules + custom
|
|
3965
|
+
validators, the discovered custom-validator catalog, and the set of body
|
|
3966
|
+
endpoints with no declared validation (gaps).
|
|
3967
|
+
|
|
3968
|
+
\b
|
|
3969
|
+
Examples:
|
|
3970
|
+
sourcecode validation .
|
|
3971
|
+
sourcecode validation . --gaps-only
|
|
3972
|
+
sourcecode validation . --path-prefix /owners
|
|
3973
|
+
sourcecode validation . --format yaml
|
|
3974
|
+
"""
|
|
3975
|
+
_enforce_format("validation", format)
|
|
3976
|
+
|
|
3977
|
+
target = path.resolve()
|
|
3978
|
+
if not target.exists() or not target.is_dir():
|
|
3979
|
+
_emit_error_json(
|
|
3980
|
+
INVALID_INPUT_CODE,
|
|
3981
|
+
f"'{target}' is not a valid directory.",
|
|
3982
|
+
path=str(target),
|
|
3983
|
+
hint="Pass an existing repository directory.",
|
|
3984
|
+
expected="A directory path.",
|
|
3985
|
+
)
|
|
3986
|
+
raise typer.Exit(code=1)
|
|
3987
|
+
|
|
3988
|
+
from sourcecode.validation_surface import build_validation_surface
|
|
3989
|
+
data = build_validation_surface(target)
|
|
3990
|
+
|
|
3991
|
+
if path_prefix:
|
|
3992
|
+
data["endpoints"] = [
|
|
3993
|
+
e for e in data.get("endpoints", [])
|
|
3994
|
+
if str(e.get("path", "")).startswith(path_prefix)
|
|
3995
|
+
]
|
|
3996
|
+
data["gaps"] = [
|
|
3997
|
+
g for g in data.get("gaps", [])
|
|
3998
|
+
if str(g.get("path", "")).startswith(path_prefix)
|
|
3999
|
+
]
|
|
4000
|
+
if gaps_only:
|
|
4001
|
+
data = {
|
|
4002
|
+
"gaps": data.get("gaps", []),
|
|
4003
|
+
"summary": data.get("summary", {}),
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4006
|
+
output = _serialize_dict(data, format)
|
|
4007
|
+
_summary = data.get("summary", {})
|
|
4008
|
+
_emit_command_output(
|
|
4009
|
+
output, output_path, copy,
|
|
4010
|
+
success_msg=f"Validation surface written to {output_path} "
|
|
4011
|
+
f"({_summary.get('endpoints_with_body', 0)} body endpoints, "
|
|
4012
|
+
f"{_summary.get('gaps', 0)} gaps)",
|
|
4013
|
+
)
|
|
4014
|
+
|
|
4015
|
+
from sourcecode.mcp_nudge import nudge_mcp_if_needed as _nudge
|
|
4016
|
+
_nudge()
|
|
4017
|
+
|
|
4018
|
+
|
|
3918
4019
|
# ── Spring Semantic Audit ─────────────────────────────────────────────────────
|
|
3919
4020
|
|
|
3920
4021
|
|
|
@@ -27,6 +27,7 @@ FORMAT_REGISTRY: "dict[str, tuple[str, ...]]" = {
|
|
|
27
27
|
"repo-ir": ("json", "yaml"),
|
|
28
28
|
"impact": ("json", "yaml"),
|
|
29
29
|
"endpoints": ("json", "yaml"),
|
|
30
|
+
"validation": ("json", "yaml"),
|
|
30
31
|
"impact-chain": ("json", "yaml"),
|
|
31
32
|
"pr-impact": ("text", "json"),
|
|
32
33
|
"migrate-check": ("json", "text"),
|
|
@@ -864,6 +864,31 @@ Returns: endpoints list with method, path, controller, handler fields;
|
|
|
864
864
|
"unknown" (no security signals detected).
|
|
865
865
|
Supports Spring MVC (@GetMapping etc.) and JAX-RS (@GET/@POST etc.).
|
|
866
866
|
repo_path: absolute path to the Java repository (default: current working directory).
|
|
867
|
+
"""
|
|
868
|
+
|
|
869
|
+
_GET_VALIDATION_DOC = """\
|
|
870
|
+
Request-body validation surface per endpoint. JAVA/SPRING ONLY.
|
|
871
|
+
|
|
872
|
+
Do NOT call this on non-Java repositories — it will return empty results.
|
|
873
|
+
|
|
874
|
+
Combines two sources of bean-validation truth so you know what a request body
|
|
875
|
+
must satisfy before generating a payload, a test, or reasoning about a 400:
|
|
876
|
+
* declarative constraints on the DTOs (@Pattern/@Size/@NotNull, minimum/maximum,
|
|
877
|
+
enum) — recovered from the OpenAPI spec even when DTOs are generated under
|
|
878
|
+
target/generated-sources (not scanned);
|
|
879
|
+
* hand-written custom validators (@Constraint + ConstraintValidator, e.g.
|
|
880
|
+
PetAgeValidator), linked to fields via x-field-extra-annotation.
|
|
881
|
+
|
|
882
|
+
Maps to: sourcecode validation <repo_path>
|
|
883
|
+
Returns: endpoints[] (method, path, controller, handler, schema, validatedFields[
|
|
884
|
+
{name, rules[{kind,value}], customValidators[{annotation,validators,message,resolved}]}]),
|
|
885
|
+
custom_validators[] (catalog: annotation, validators, message, validatedTypes, targets),
|
|
886
|
+
gaps[] (POST/PUT/PATCH endpoints with no declared validation),
|
|
887
|
+
summary, openapi_spec.
|
|
888
|
+
An unresolved custom annotation (referenced in the spec, no validator in source)
|
|
889
|
+
is reported with resolved=false.
|
|
890
|
+
repo_path: absolute path to the Java repository (default: current working directory).
|
|
891
|
+
gaps_only: when true, return only the gaps section (endpoints lacking validation).
|
|
867
892
|
"""
|
|
868
893
|
|
|
869
894
|
_CACHE_STATUS_DOC = """\
|
|
@@ -1083,6 +1108,27 @@ repo_path: absolute path to the repository (default: current working directory).
|
|
|
1083
1108
|
docstring_override=_GET_ENDPOINTS_DOC,
|
|
1084
1109
|
),
|
|
1085
1110
|
|
|
1111
|
+
# --- get_validation: clean alias replacing raw canonical (6 CLI params) ---
|
|
1112
|
+
_alias_spec(
|
|
1113
|
+
"get_validation",
|
|
1114
|
+
"Request-body validation surface per endpoint (constraints + custom validators). JAVA/SPRING ONLY.",
|
|
1115
|
+
("validation",),
|
|
1116
|
+
(
|
|
1117
|
+
ToolParamSpec("repo_path", "argument", str, required=False, default=".", is_path=True),
|
|
1118
|
+
ToolParamSpec("gaps_only", "option", bool, required=False, default=False,
|
|
1119
|
+
option_names=("--gaps-only",), is_flag=True,
|
|
1120
|
+
help="Return only endpoints/fields lacking validation."),
|
|
1121
|
+
),
|
|
1122
|
+
lambda inputs: (
|
|
1123
|
+
["validation", str(inputs.get("repo_path", "."))]
|
|
1124
|
+
+ (["--gaps-only"] if bool(inputs.get("gaps_only")) else [])
|
|
1125
|
+
),
|
|
1126
|
+
supported_targets=("repo_path",),
|
|
1127
|
+
unsupported_targets=("file_path",),
|
|
1128
|
+
validator=validate_repo_path,
|
|
1129
|
+
docstring_override=_GET_VALIDATION_DOC,
|
|
1130
|
+
),
|
|
1131
|
+
|
|
1086
1132
|
# --- cache management: curated aliases stripping CLI noise params ---
|
|
1087
1133
|
_alias_spec(
|
|
1088
1134
|
"cache_status",
|
|
@@ -1214,6 +1260,7 @@ _MCP_HIDDEN_CANONICAL_TOOLS: frozenset[str] = frozenset({
|
|
|
1214
1260
|
"modernize", # duplicate of modernize_context
|
|
1215
1261
|
# Raw CLI tools with output-format/noise params — clean alias with only repo_path exists
|
|
1216
1262
|
"endpoints", # 7 CLI params (output_path/format/copy/etc.); use get_endpoints
|
|
1263
|
+
"validation", # 6 CLI params (output_path/format/copy/path_prefix/gaps_only); use get_validation
|
|
1217
1264
|
"cache_status", # path + json_output flag; curated alias strips json_output, renames path→repo_path
|
|
1218
1265
|
"cache_warm", # path + compact/agent output flags; curated alias keeps only repo_path
|
|
1219
1266
|
"cache_clear", # path + yes/all_ destructive flags; curated alias keeps repo_path + include_ris only
|
|
@@ -68,6 +68,9 @@ class FieldConstraint:
|
|
|
68
68
|
fmt: Optional[str] = None
|
|
69
69
|
enum: Optional[list[Any]] = None
|
|
70
70
|
ref: Optional[str] = None # schema name when the field is an object/array ref
|
|
71
|
+
# Custom bean-validation annotations injected via openapi-generator's
|
|
72
|
+
# ``x-field-extra-annotation`` vendor extension (simple class names).
|
|
73
|
+
extra_annotations: "list[str]" = field(default_factory=list)
|
|
71
74
|
|
|
72
75
|
def to_dict(self) -> "dict[str, Any]":
|
|
73
76
|
out: "dict[str, Any]" = {"name": self.name, "required": self.required}
|
|
@@ -84,6 +87,8 @@ class FieldConstraint:
|
|
|
84
87
|
):
|
|
85
88
|
if val is not None:
|
|
86
89
|
out[key] = val
|
|
90
|
+
if self.extra_annotations:
|
|
91
|
+
out["extraAnnotations"] = self.extra_annotations
|
|
87
92
|
return out
|
|
88
93
|
|
|
89
94
|
|
|
@@ -255,6 +260,32 @@ def _ref_name(ref: Any) -> Optional[str]:
|
|
|
255
260
|
return None
|
|
256
261
|
|
|
257
262
|
|
|
263
|
+
def _extra_annotations(prop: "dict[str, Any]") -> "list[str]":
|
|
264
|
+
"""Extract simple class names from ``x-field-extra-annotation``.
|
|
265
|
+
|
|
266
|
+
openapi-generator injects custom bean-validation annotations on generated DTO
|
|
267
|
+
fields via this vendor extension, e.g.
|
|
268
|
+
``x-field-extra-annotation: "@com.x.PetAgeValidation"`` (string) or a list of
|
|
269
|
+
such entries. We keep the trailing simple name (``PetAgeValidation``).
|
|
270
|
+
"""
|
|
271
|
+
import re as _re
|
|
272
|
+
|
|
273
|
+
raw = prop.get("x-field-extra-annotation")
|
|
274
|
+
if raw is None:
|
|
275
|
+
return []
|
|
276
|
+
items = raw if isinstance(raw, list) else [raw]
|
|
277
|
+
names: "list[str]" = []
|
|
278
|
+
for item in items:
|
|
279
|
+
if not isinstance(item, str):
|
|
280
|
+
continue
|
|
281
|
+
# Each entry may carry several annotations; grab every @Name token.
|
|
282
|
+
for m in _re.finditer(r"@\s*([\w.]+)", item):
|
|
283
|
+
simple = m.group(1).rsplit(".", 1)[-1]
|
|
284
|
+
if simple and simple not in names:
|
|
285
|
+
names.append(simple)
|
|
286
|
+
return names
|
|
287
|
+
|
|
288
|
+
|
|
258
289
|
def _field_from_property(name: str, prop: Any, required: bool) -> FieldConstraint:
|
|
259
290
|
fc = FieldConstraint(name=name, required=required)
|
|
260
291
|
if not isinstance(prop, dict):
|
|
@@ -276,6 +307,7 @@ def _field_from_property(name: str, prop: Any, required: bool) -> FieldConstrain
|
|
|
276
307
|
enum = prop.get("enum")
|
|
277
308
|
if isinstance(enum, list):
|
|
278
309
|
fc.enum = list(enum)
|
|
310
|
+
fc.extra_annotations = _extra_annotations(prop)
|
|
279
311
|
if fc.type is None and prop.get("type") == "array":
|
|
280
312
|
items = prop.get("items")
|
|
281
313
|
if isinstance(items, dict):
|