From 992aa08cb71a9d7db660d4909ffef4a154888ded Mon Sep 17 00:00:00 2001 From: Matthias Huels Date: Sat, 31 Jan 2026 12:24:35 -0500 Subject: [PATCH] refactor: restructure codebase with pipeline package and improved tooling - Add unified pipeline package for parse/layout/render orchestration - Add session management for GitHub token persistence - Add observability hooks for structured logging - Reorganize package structure (core/, integrations/, graph/) - Update Go version to 1.25.0 and golangci-lint to v2.8.0 - Add fmt-check target for CI-style formatting verification - Fix staticcheck linter warnings (S1021, SA9003, QF1012) - Update CI workflow with aligned tooling versions - Regenerate blog showcase assets with new watermark styling - Simplify example JSON files --- .github/workflows/ci.yml | 8 +- .github/workflows/release.yml | 4 +- .gitignore | 3 + .goreleaser.yaml | 24 +- Makefile | 134 +++-- README.md | 225 ++++---- blogpost/android-chrome-192x192.png | Bin 48938 -> 12807 bytes blogpost/android-chrome-512x512.png | Bin 435954 -> 46873 bytes blogpost/apple-touch-icon.png | Bin 44272 -> 11362 bytes blogpost/favicon-16x16.png | Bin 386 -> 520 bytes blogpost/favicon-32x32.png | Bin 1111 -> 1365 bytes blogpost/favicon.ico | Bin 15406 -> 15406 bytes .../plots/showcase/javascript/ioredis.svg | 192 ++++--- .../showcase/javascript/ioredis_mobile.svg | 191 ++++--- blogpost/plots/showcase/javascript/knex.svg | 293 +++++----- .../plots/showcase/javascript/knex_mobile.svg | 292 +++++----- .../plots/showcase/javascript/mongoose.svg | 244 ++++---- .../showcase/javascript/mongoose_mobile.svg | 243 ++++---- blogpost/plots/showcase/javascript/pino.svg | 204 ++++--- .../plots/showcase/javascript/pino_mobile.svg | 203 ++++--- blogpost/plots/showcase/javascript/yup.svg | 122 ++-- .../plots/showcase/javascript/yup_mobile.svg | 121 ++-- blogpost/plots/showcase/python/fastapi.svg | 197 ++++--- .../plots/showcase/python/fastapi_mobile.svg | 196 ++++--- blogpost/plots/showcase/python/openai.svg | 266 +++++---- .../plots/showcase/python/openai_mobile.svg | 265 +++++---- blogpost/plots/showcase/python/pydantic.svg | 119 ++-- .../plots/showcase/python/pydantic_mobile.svg | 118 ++-- blogpost/plots/showcase/python/requests.svg | 114 ++-- .../plots/showcase/python/requests_mobile.svg | 113 ++-- blogpost/plots/showcase/python/typer.svg | 160 +++--- .../plots/showcase/python/typer_mobile.svg | 159 +++--- blogpost/plots/showcase/rust/diesel.svg | 271 ++++----- .../plots/showcase/rust/diesel_mobile.svg | 270 ++++----- blogpost/plots/showcase/rust/hyper.svg | 153 ++--- blogpost/plots/showcase/rust/hyper_mobile.svg | 152 ++--- blogpost/plots/showcase/rust/rayon.svg | 133 +++-- blogpost/plots/showcase/rust/rayon_mobile.svg | 132 +++-- blogpost/plots/showcase/rust/serde.svg | 143 +++-- blogpost/plots/showcase/rust/serde_mobile.svg | 142 +++-- blogpost/plots/showcase/rust/ureq.svg | 174 +++--- blogpost/plots/showcase/rust/ureq_mobile.svg | 177 +++--- cmd/stacktower/main.go | 56 ++ examples/real/cobra.json | 204 ++----- examples/real/com.google.guava_guava.json | 136 +---- examples/real/console.json | 285 ++-------- examples/real/flask.json | 254 ++------- examples/real/htmd.json | 449 --------------- examples/real/openai.json | 499 +++-------------- examples/real/rspec.json | 133 +---- examples/real/serde.json | 175 +----- examples/real/yargs.json | 270 ++------- go.mod | 13 +- go.sum | 23 +- internal/cli/auth.go | 245 ++++++++ internal/cli/cache.go | 77 ++- internal/cli/cache_test.go | 49 -- internal/cli/cli.go | 147 +++++ internal/cli/completion.go | 69 +++ internal/cli/layout.go | 113 ++++ internal/cli/log.go | 92 --- internal/cli/log_test.go | 134 ----- internal/cli/ordering.go | 117 ---- internal/cli/parse.go | 371 +++++------- internal/cli/parse_github.go | 194 +++++++ internal/cli/parse_test.go | 62 +- internal/cli/paths_test.go | 86 +++ internal/cli/pqtree.go | 38 +- internal/cli/render.go | 511 ++++++----------- internal/cli/render_test.go | 103 +--- internal/cli/root.go | 80 --- internal/cli/root_test.go | 34 -- internal/cli/spinner.go | 100 ++++ internal/cli/spinner_test.go | 82 +++ internal/cli/tui.go | 305 ++++++++++ internal/cli/ui.go | 192 +++++++ internal/cli/visualize.go | 103 ++++ main.go | 22 - pkg/buildinfo/version.go | 34 ++ pkg/cache/cache.go | 141 +++++ pkg/cache/cache_test.go | 201 +++++++ pkg/cache/errors.go | 68 +++ pkg/cache/file.go | 108 ++++ pkg/cache/hash.go | 24 + pkg/cache/null.go | 38 ++ pkg/cache/scoped.go | 49 ++ pkg/{ => core}/dag/crossings.go | 0 pkg/{ => core}/dag/crossings_test.go | 0 pkg/{ => core}/dag/dag.go | 52 ++ pkg/{ => core}/dag/dag_test.go | 0 pkg/{ => core}/dag/doc.go | 4 +- pkg/{ => core}/dag/example_test.go | 2 +- pkg/{ => core}/dag/perm/doc.go | 2 +- pkg/{ => core}/dag/perm/dot.go | 0 pkg/{ => core}/dag/perm/dot_test.go | 0 pkg/{ => core}/dag/perm/example_test.go | 2 +- pkg/{ => core}/dag/perm/perm.go | 0 pkg/{ => core}/dag/perm/perm_test.go | 0 pkg/{ => core}/dag/perm/pqtree.go | 0 pkg/{ => core}/dag/perm/pqtree_test.go | 0 pkg/{ => core}/dag/transform/cycles.go | 2 +- pkg/{ => core}/dag/transform/cycles_test.go | 2 +- pkg/{ => core}/dag/transform/doc.go | 0 pkg/{ => core}/dag/transform/example_test.go | 4 +- pkg/{ => core}/dag/transform/layering.go | 2 +- pkg/{ => core}/dag/transform/layering_test.go | 2 +- pkg/{ => core}/dag/transform/normalize.go | 2 +- pkg/{ => core}/dag/transform/reduction.go | 2 +- .../dag/transform/reduction_test.go | 2 +- pkg/{ => core}/dag/transform/result.go | 0 pkg/{ => core}/dag/transform/spans.go | 2 +- pkg/{ => core}/dag/transform/spans_test.go | 2 +- pkg/{ => core}/dag/transform/subdivide.go | 2 +- .../dag/transform/subdivide_test.go | 2 +- .../dag/transform/transform_test.go | 2 +- pkg/{ => core}/deps/deps.go | 3 + pkg/{ => core}/deps/deps_test.go | 0 pkg/{ => core}/deps/doc.go | 26 +- pkg/{ => core}/deps/example_test.go | 6 +- pkg/{ => core}/deps/golang/doc.go | 2 +- pkg/core/deps/golang/golang.go | 92 +++ pkg/{ => core}/deps/golang/golang_test.go | 76 ++- pkg/{ => core}/deps/golang/gomod.go | 4 +- pkg/{ => core}/deps/golang/gomod_test.go | 4 +- pkg/{ => core}/deps/java/doc.go | 2 +- pkg/{ => core}/deps/java/java.go | 10 +- pkg/{ => core}/deps/java/java_test.go | 0 pkg/{ => core}/deps/java/pom.go | 4 +- pkg/{ => core}/deps/java/pom_test.go | 4 +- pkg/{ => core}/deps/javascript/doc.go | 2 +- pkg/{ => core}/deps/javascript/javascript.go | 10 +- pkg/{ => core}/deps/javascript/npm_test.go | 4 +- pkg/{ => core}/deps/javascript/packagejson.go | 4 +- .../deps/javascript/packagejson_test.go | 4 +- pkg/{ => core}/deps/language.go | 22 +- pkg/{ => core}/deps/language_test.go | 15 +- pkg/core/deps/languages/languages.go | 42 ++ pkg/{ => core}/deps/manifest.go | 126 +++++ pkg/{ => core}/deps/manifest_test.go | 0 pkg/{ => core}/deps/metadata/doc.go | 2 +- pkg/{ => core}/deps/metadata/example_test.go | 25 +- pkg/{ => core}/deps/metadata/github.go | 15 +- pkg/{ => core}/deps/metadata/provider.go | 2 +- pkg/{ => core}/deps/metadata/provider_test.go | 2 +- pkg/{ => core}/deps/metadata/schema.go | 1 + pkg/core/deps/metadata/typed.go | 208 +++++++ pkg/{ => core}/deps/php/composer.go | 4 +- pkg/{ => core}/deps/php/composer_test.go | 4 +- pkg/{ => core}/deps/php/doc.go | 2 +- pkg/{ => core}/deps/php/packagist_test.go | 4 +- pkg/{ => core}/deps/php/php.go | 10 +- pkg/{ => core}/deps/python/doc.go | 2 +- pkg/{ => core}/deps/python/poetry.go | 4 +- pkg/{ => core}/deps/python/poetry_test.go | 4 +- pkg/{ => core}/deps/python/pypi_test.go | 4 +- pkg/{ => core}/deps/python/python.go | 10 +- pkg/{ => core}/deps/python/requirements.go | 4 +- .../deps/python/requirements_test.go | 4 +- pkg/core/deps/registry.go | 11 + pkg/{ => core}/deps/resolver.go | 42 +- .../deps/resolver_integration_test.go | 0 pkg/{ => core}/deps/resolver_test.go | 0 pkg/{ => core}/deps/ruby/doc.go | 2 +- pkg/{ => core}/deps/ruby/gemfile.go | 4 +- pkg/{ => core}/deps/ruby/gemfile_test.go | 4 +- pkg/{ => core}/deps/ruby/ruby.go | 10 +- pkg/{ => core}/deps/ruby/rubygems_test.go | 4 +- pkg/{ => core}/deps/rust/cargo.go | 4 +- pkg/{ => core}/deps/rust/cargo_test.go | 4 +- pkg/{ => core}/deps/rust/crates_test.go | 4 +- pkg/{ => core}/deps/rust/doc.go | 2 +- pkg/{ => core}/deps/rust/rust.go | 10 +- pkg/{ => core}/render/convert.go | 0 pkg/{ => core}/render/doc.go | 12 +- pkg/core/render/nodelink/convert.go | 67 +++ pkg/core/render/nodelink/doc.go | 61 ++ pkg/{ => core}/render/nodelink/dot.go | 6 +- pkg/{ => core}/render/nodelink/dot_test.go | 2 +- .../render/nodelink/example_test.go | 4 +- pkg/{ => core}/render/tower/doc.go | 12 +- pkg/core/render/tower/feature/brittle.go | 80 +++ .../render/tower/feature/brittle_test.go | 34 +- pkg/{ => core}/render/tower/feature/doc.go | 0 .../render/tower/feature/example_test.go | 4 +- .../render/tower/feature/nebraska.go | 9 +- .../render/tower/feature/nebraska_test.go | 2 +- pkg/{ => core}/render/tower/layout/block.go | 0 .../render/tower/layout/block_test.go | 0 pkg/core/render/tower/layout/convert.go | 241 ++++++++ pkg/core/render/tower/layout/convert_test.go | 372 ++++++++++++ pkg/{ => core}/render/tower/layout/doc.go | 12 +- .../render/tower/layout/example_test.go | 6 +- pkg/{ => core}/render/tower/layout/layout.go | 52 +- .../render/tower/layout/layout_test.go | 2 +- pkg/{ => core}/render/tower/layout/width.go | 2 +- .../render/tower/layout/width_test.go | 2 +- .../render/tower/ordering/barycentric.go | 2 +- .../render/tower/ordering/barycentric_test.go | 2 +- pkg/{ => core}/render/tower/ordering/doc.go | 0 .../render/tower/ordering/example_test.go | 4 +- .../render/tower/ordering/optimal.go | 13 +- .../render/tower/ordering/optimal_test.go | 2 +- .../render/tower/ordering/ordering.go | 2 +- .../render/tower/ordering/position.go | 2 +- pkg/{ => core}/render/tower/sink/doc.go | 26 +- .../render/tower/sink/example_test.go | 35 +- pkg/{ => core}/render/tower/sink/pdf.go | 4 +- pkg/{ => core}/render/tower/sink/png.go | 4 +- pkg/{ => core}/render/tower/sink/svg.go | 77 ++- pkg/core/render/tower/sink/svg_nebraska.go | 139 +++++ .../render/tower/sink/svg_popups.go | 22 +- pkg/{ => core}/render/tower/sink/svg_test.go | 4 +- pkg/{ => core}/render/tower/styles/doc.go | 2 +- .../styles/handdrawn/brittle-bg-base64.txt | 0 .../tower/styles/handdrawn/brittle_texture.go | 0 .../render/tower/styles/handdrawn/colors.go | 0 .../tower/styles/handdrawn/colors_test.go | 0 .../render/tower/styles/handdrawn/doc.go | 0 .../tower/styles/handdrawn/handdrawn.go | 29 +- .../tower/styles/handdrawn/handdrawn_test.go | 11 +- .../render/tower/styles/handdrawn/paths.go | 0 .../tower/styles/handdrawn/paths_test.go | 0 pkg/{ => core}/render/tower/styles/simple.go | 0 .../render/tower/styles/simple_test.go | 0 pkg/{ => core}/render/tower/styles/style.go | 0 pkg/{ => core}/render/tower/styles/text.go | 0 .../render/tower/styles/text_test.go | 0 pkg/{ => core}/render/tower/transform/doc.go | 4 +- .../render/tower/transform/example_test.go | 6 +- .../render/tower/transform/merge.go | 4 +- .../render/tower/transform/merge_test.go | 4 +- .../render/tower/transform/randomize.go | 26 +- .../render/tower/transform/randomize_test.go | 4 +- pkg/deps/golang/golang.go | 60 -- pkg/doc.go | 127 +++-- pkg/errors/errors.go | 154 +++++ pkg/errors/errors_test.go | 179 ++++++ pkg/errors/validation.go | 201 +++++++ pkg/errors/validation_test.go | 272 +++++++++ pkg/fonts/LICENSE | 12 + pkg/fonts/fonts.go | 51 ++ pkg/fonts/xkcd-script.ttf | Bin 0 -> 52764 bytes pkg/fonts/xkcd-script.woff | Bin 0 -> 40056 bytes pkg/graph/doc.go | 85 +++ pkg/{io => graph}/example_test.go | 30 +- pkg/graph/graph.go | 81 +++ pkg/graph/graph_test.go | 238 ++++++++ pkg/graph/layout.go | 166 ++++++ pkg/graph/types.go | 268 +++++++++ pkg/httputil/cache.go | 176 ------ pkg/httputil/cache_test.go | 212 ------- pkg/httputil/doc.go | 52 -- pkg/httputil/example_test.go | 183 ------ pkg/httputil/retry.go | 103 ---- pkg/httputil/retry_test.go | 211 ------- pkg/integrations/client.go | 74 ++- pkg/integrations/client_test.go | 85 +-- pkg/integrations/common.go | 53 +- pkg/integrations/crates/client.go | 20 +- pkg/integrations/crates/client_test.go | 13 +- pkg/integrations/doc.go | 6 +- pkg/integrations/example_test.go | 2 +- pkg/integrations/github/client.go | 49 +- pkg/integrations/github/client_test.go | 13 +- pkg/integrations/github/content.go | 529 ++++++++++++++++++ pkg/integrations/github/oauth.go | 204 +++++++ pkg/integrations/github/types.go | 98 ++++ pkg/integrations/github/validate.go | 59 ++ pkg/integrations/gitlab/client.go | 20 +- pkg/integrations/goproxy/client.go | 19 +- pkg/integrations/goproxy/client_test.go | 8 +- pkg/integrations/maven/client.go | 64 ++- pkg/integrations/maven/client_test.go | 8 +- pkg/integrations/maven/doc.go | 2 +- pkg/integrations/npm/client.go | 19 +- pkg/integrations/npm/client_test.go | 13 +- pkg/integrations/packagist/client.go | 19 +- pkg/integrations/packagist/client_test.go | 13 +- pkg/integrations/pypi/client.go | 56 +- pkg/integrations/pypi/client_test.go | 8 +- pkg/integrations/rubygems/client.go | 19 +- pkg/integrations/rubygems/client_test.go | 8 +- pkg/io/doc.go | 97 ---- pkg/io/export.go | 115 ---- pkg/io/export_test.go | 106 ---- pkg/io/import.go | 88 --- pkg/io/import_test.go | 118 ---- pkg/observability/hooks.go | 192 +++++++ pkg/observability/hooks_test.go | 94 ++++ pkg/pipeline/layout.go | 125 +++++ pkg/pipeline/parse.go | 140 +++++ pkg/pipeline/pipeline.go | 388 +++++++++++++ pkg/pipeline/pipeline_test.go | 244 ++++++++ pkg/pipeline/render.go | 199 +++++++ pkg/pipeline/runner.go | 296 ++++++++++ pkg/render/nodelink/doc.go | 42 -- pkg/render/tower/feature/brittle.go | 72 --- pkg/render/tower/sink/json.go | 256 --------- pkg/render/tower/sink/json_test.go | 373 ------------ pkg/render/tower/sink/svg_nebraska.go | 126 ----- pkg/session/file.go | 171 ++++++ pkg/session/session.go | 193 +++++++ scripts/{test_e2e.sh => test_cli_e2e.sh} | 164 ++++-- 303 files changed, 13719 insertions(+), 9039 deletions(-) create mode 100644 cmd/stacktower/main.go delete mode 100644 examples/real/htmd.json create mode 100644 internal/cli/auth.go delete mode 100644 internal/cli/cache_test.go create mode 100644 internal/cli/cli.go create mode 100644 internal/cli/completion.go create mode 100644 internal/cli/layout.go delete mode 100644 internal/cli/log.go delete mode 100644 internal/cli/log_test.go delete mode 100644 internal/cli/ordering.go create mode 100644 internal/cli/parse_github.go create mode 100644 internal/cli/paths_test.go delete mode 100644 internal/cli/root.go delete mode 100644 internal/cli/root_test.go create mode 100644 internal/cli/spinner.go create mode 100644 internal/cli/spinner_test.go create mode 100644 internal/cli/tui.go create mode 100644 internal/cli/ui.go create mode 100644 internal/cli/visualize.go delete mode 100644 main.go create mode 100644 pkg/buildinfo/version.go create mode 100644 pkg/cache/cache.go create mode 100644 pkg/cache/cache_test.go create mode 100644 pkg/cache/errors.go create mode 100644 pkg/cache/file.go create mode 100644 pkg/cache/hash.go create mode 100644 pkg/cache/null.go create mode 100644 pkg/cache/scoped.go rename pkg/{ => core}/dag/crossings.go (100%) rename pkg/{ => core}/dag/crossings_test.go (100%) rename pkg/{ => core}/dag/dag.go (92%) rename pkg/{ => core}/dag/dag_test.go (100%) rename pkg/{ => core}/dag/doc.go (96%) rename pkg/{ => core}/dag/example_test.go (99%) rename pkg/{ => core}/dag/perm/doc.go (99%) rename pkg/{ => core}/dag/perm/dot.go (100%) rename pkg/{ => core}/dag/perm/dot_test.go (100%) rename pkg/{ => core}/dag/perm/example_test.go (98%) rename pkg/{ => core}/dag/perm/perm.go (100%) rename pkg/{ => core}/dag/perm/perm_test.go (100%) rename pkg/{ => core}/dag/perm/pqtree.go (100%) rename pkg/{ => core}/dag/perm/pqtree_test.go (100%) rename pkg/{ => core}/dag/transform/cycles.go (97%) rename pkg/{ => core}/dag/transform/cycles_test.go (98%) rename pkg/{ => core}/dag/transform/doc.go (100%) rename pkg/{ => core}/dag/transform/example_test.go (98%) rename pkg/{ => core}/dag/transform/layering.go (97%) rename pkg/{ => core}/dag/transform/layering_test.go (98%) rename pkg/{ => core}/dag/transform/normalize.go (98%) rename pkg/{ => core}/dag/transform/reduction.go (97%) rename pkg/{ => core}/dag/transform/reduction_test.go (99%) rename pkg/{ => core}/dag/transform/result.go (100%) rename pkg/{ => core}/dag/transform/spans.go (99%) rename pkg/{ => core}/dag/transform/spans_test.go (99%) rename pkg/{ => core}/dag/transform/subdivide.go (98%) rename pkg/{ => core}/dag/transform/subdivide_test.go (99%) rename pkg/{ => core}/dag/transform/transform_test.go (99%) rename pkg/{ => core}/deps/deps.go (97%) rename pkg/{ => core}/deps/deps_test.go (100%) rename pkg/{ => core}/deps/doc.go (87%) rename pkg/{ => core}/deps/example_test.go (96%) rename pkg/{ => core}/deps/golang/doc.go (92%) create mode 100644 pkg/core/deps/golang/golang.go rename pkg/{ => core}/deps/golang/golang_test.go (70%) rename pkg/{ => core}/deps/golang/gomod.go (97%) rename pkg/{ => core}/deps/golang/gomod_test.go (97%) rename pkg/{ => core}/deps/java/doc.go (92%) rename pkg/{ => core}/deps/java/java.go (91%) rename pkg/{ => core}/deps/java/java_test.go (100%) rename pkg/{ => core}/deps/java/pom.go (97%) rename pkg/{ => core}/deps/java/pom_test.go (97%) rename pkg/{ => core}/deps/javascript/doc.go (92%) rename pkg/{ => core}/deps/javascript/javascript.go (86%) rename pkg/{ => core}/deps/javascript/npm_test.go (65%) rename pkg/{ => core}/deps/javascript/packagejson.go (96%) rename pkg/{ => core}/deps/javascript/packagejson_test.go (95%) rename pkg/{ => core}/deps/language.go (85%) rename pkg/{ => core}/deps/language_test.go (91%) create mode 100644 pkg/core/deps/languages/languages.go rename pkg/{ => core}/deps/manifest.go (51%) rename pkg/{ => core}/deps/manifest_test.go (100%) rename pkg/{ => core}/deps/metadata/doc.go (98%) rename pkg/{ => core}/deps/metadata/example_test.go (84%) rename pkg/{ => core}/deps/metadata/github.go (80%) rename pkg/{ => core}/deps/metadata/provider.go (91%) rename pkg/{ => core}/deps/metadata/provider_test.go (98%) rename pkg/{ => core}/deps/metadata/schema.go (90%) create mode 100644 pkg/core/deps/metadata/typed.go rename pkg/{ => core}/deps/php/composer.go (96%) rename pkg/{ => core}/deps/php/composer_test.go (95%) rename pkg/{ => core}/deps/php/doc.go (92%) rename pkg/{ => core}/deps/php/packagist_test.go (62%) rename pkg/{ => core}/deps/php/php.go (86%) rename pkg/{ => core}/deps/python/doc.go (93%) rename pkg/{ => core}/deps/python/poetry.go (97%) rename pkg/{ => core}/deps/python/poetry_test.go (96%) rename pkg/{ => core}/deps/python/pypi_test.go (64%) rename pkg/{ => core}/deps/python/python.go (88%) rename pkg/{ => core}/deps/python/requirements.go (96%) rename pkg/{ => core}/deps/python/requirements_test.go (95%) create mode 100644 pkg/core/deps/registry.go rename pkg/{ => core}/deps/resolver.go (90%) rename pkg/{ => core}/deps/resolver_integration_test.go (100%) rename pkg/{ => core}/deps/resolver_test.go (100%) rename pkg/{ => core}/deps/ruby/doc.go (91%) rename pkg/{ => core}/deps/ruby/gemfile.go (96%) rename pkg/{ => core}/deps/ruby/gemfile_test.go (95%) rename pkg/{ => core}/deps/ruby/ruby.go (86%) rename pkg/{ => core}/deps/ruby/rubygems_test.go (64%) rename pkg/{ => core}/deps/rust/cargo.go (96%) rename pkg/{ => core}/deps/rust/cargo_test.go (95%) rename pkg/{ => core}/deps/rust/crates_test.go (64%) rename pkg/{ => core}/deps/rust/doc.go (91%) rename pkg/{ => core}/deps/rust/rust.go (86%) rename pkg/{ => core}/render/convert.go (100%) rename pkg/{ => core}/render/doc.go (76%) create mode 100644 pkg/core/render/nodelink/convert.go create mode 100644 pkg/core/render/nodelink/doc.go rename pkg/{ => core}/render/nodelink/dot.go (95%) rename pkg/{ => core}/render/nodelink/dot_test.go (98%) rename pkg/{ => core}/render/nodelink/example_test.go (96%) rename pkg/{ => core}/render/tower/doc.go (78%) create mode 100644 pkg/core/render/tower/feature/brittle.go rename pkg/{ => core}/render/tower/feature/brittle_test.go (72%) rename pkg/{ => core}/render/tower/feature/doc.go (100%) rename pkg/{ => core}/render/tower/feature/example_test.go (97%) rename pkg/{ => core}/render/tower/feature/nebraska.go (93%) rename pkg/{ => core}/render/tower/feature/nebraska_test.go (99%) rename pkg/{ => core}/render/tower/layout/block.go (100%) rename pkg/{ => core}/render/tower/layout/block_test.go (100%) create mode 100644 pkg/core/render/tower/layout/convert.go create mode 100644 pkg/core/render/tower/layout/convert_test.go rename pkg/{ => core}/render/tower/layout/doc.go (86%) rename pkg/{ => core}/render/tower/layout/example_test.go (95%) rename pkg/{ => core}/render/tower/layout/layout.go (65%) rename pkg/{ => core}/render/tower/layout/layout_test.go (99%) rename pkg/{ => core}/render/tower/layout/width.go (98%) rename pkg/{ => core}/render/tower/layout/width_test.go (97%) rename pkg/{ => core}/render/tower/ordering/barycentric.go (99%) rename pkg/{ => core}/render/tower/ordering/barycentric_test.go (99%) rename pkg/{ => core}/render/tower/ordering/doc.go (100%) rename pkg/{ => core}/render/tower/ordering/example_test.go (96%) rename pkg/{ => core}/render/tower/ordering/optimal.go (96%) rename pkg/{ => core}/render/tower/ordering/optimal_test.go (99%) rename pkg/{ => core}/render/tower/ordering/ordering.go (94%) rename pkg/{ => core}/render/tower/ordering/position.go (95%) rename pkg/{ => core}/render/tower/sink/doc.go (69%) rename pkg/{ => core}/render/tower/sink/example_test.go (72%) rename pkg/{ => core}/render/tower/sink/pdf.go (85%) rename pkg/{ => core}/render/tower/sink/png.go (88%) rename pkg/{ => core}/render/tower/sink/svg.go (65%) create mode 100644 pkg/core/render/tower/sink/svg_nebraska.go rename pkg/{ => core}/render/tower/sink/svg_popups.go (74%) rename pkg/{ => core}/render/tower/sink/svg_test.go (97%) rename pkg/{ => core}/render/tower/styles/doc.go (96%) rename pkg/{ => core}/render/tower/styles/handdrawn/brittle-bg-base64.txt (100%) rename pkg/{ => core}/render/tower/styles/handdrawn/brittle_texture.go (100%) rename pkg/{ => core}/render/tower/styles/handdrawn/colors.go (100%) rename pkg/{ => core}/render/tower/styles/handdrawn/colors_test.go (100%) rename pkg/{ => core}/render/tower/styles/handdrawn/doc.go (100%) rename pkg/{ => core}/render/tower/styles/handdrawn/handdrawn.go (86%) rename pkg/{ => core}/render/tower/styles/handdrawn/handdrawn_test.go (95%) rename pkg/{ => core}/render/tower/styles/handdrawn/paths.go (100%) rename pkg/{ => core}/render/tower/styles/handdrawn/paths_test.go (100%) rename pkg/{ => core}/render/tower/styles/simple.go (100%) rename pkg/{ => core}/render/tower/styles/simple_test.go (100%) rename pkg/{ => core}/render/tower/styles/style.go (100%) rename pkg/{ => core}/render/tower/styles/text.go (100%) rename pkg/{ => core}/render/tower/styles/text_test.go (100%) rename pkg/{ => core}/render/tower/transform/doc.go (94%) rename pkg/{ => core}/render/tower/transform/example_test.go (96%) rename pkg/{ => core}/render/tower/transform/merge.go (96%) rename pkg/{ => core}/render/tower/transform/merge_test.go (98%) rename pkg/{ => core}/render/tower/transform/randomize.go (86%) rename pkg/{ => core}/render/tower/transform/randomize_test.go (98%) delete mode 100644 pkg/deps/golang/golang.go create mode 100644 pkg/errors/errors.go create mode 100644 pkg/errors/errors_test.go create mode 100644 pkg/errors/validation.go create mode 100644 pkg/errors/validation_test.go create mode 100644 pkg/fonts/LICENSE create mode 100644 pkg/fonts/fonts.go create mode 100644 pkg/fonts/xkcd-script.ttf create mode 100644 pkg/fonts/xkcd-script.woff create mode 100644 pkg/graph/doc.go rename pkg/{io => graph}/example_test.go (84%) create mode 100644 pkg/graph/graph.go create mode 100644 pkg/graph/graph_test.go create mode 100644 pkg/graph/layout.go create mode 100644 pkg/graph/types.go delete mode 100644 pkg/httputil/cache.go delete mode 100644 pkg/httputil/cache_test.go delete mode 100644 pkg/httputil/doc.go delete mode 100644 pkg/httputil/example_test.go delete mode 100644 pkg/httputil/retry.go delete mode 100644 pkg/httputil/retry_test.go create mode 100644 pkg/integrations/github/content.go create mode 100644 pkg/integrations/github/oauth.go create mode 100644 pkg/integrations/github/types.go create mode 100644 pkg/integrations/github/validate.go delete mode 100644 pkg/io/doc.go delete mode 100644 pkg/io/export.go delete mode 100644 pkg/io/export_test.go delete mode 100644 pkg/io/import.go delete mode 100644 pkg/io/import_test.go create mode 100644 pkg/observability/hooks.go create mode 100644 pkg/observability/hooks_test.go create mode 100644 pkg/pipeline/layout.go create mode 100644 pkg/pipeline/parse.go create mode 100644 pkg/pipeline/pipeline.go create mode 100644 pkg/pipeline/pipeline_test.go create mode 100644 pkg/pipeline/render.go create mode 100644 pkg/pipeline/runner.go delete mode 100644 pkg/render/nodelink/doc.go delete mode 100644 pkg/render/tower/feature/brittle.go delete mode 100644 pkg/render/tower/sink/json.go delete mode 100644 pkg/render/tower/sink/json_test.go delete mode 100644 pkg/render/tower/sink/svg_nebraska.go create mode 100644 pkg/session/file.go create mode 100644 pkg/session/session.go rename scripts/{test_e2e.sh => test_cli_e2e.sh} (66%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53b4f62..4df2ba9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,8 @@ name: CI on: push: - branches: [main] + branches-ignore: + - "dependabot/**" # Skip dependabot branches (they create PRs) pull_request: concurrency: @@ -21,18 +22,19 @@ jobs: - uses: actions/setup-go@v5 with: - go-version-file: 'go.mod' + go-version-file: "go.mod" cache: true - name: Lint uses: golangci/golangci-lint-action@v7 with: - version: v2.1.6 + version: v2.8.0 - name: Test run: go test -race -coverprofile=coverage.txt -timeout=2m ./... - name: Upload coverage to Codecov + if: github.ref == 'refs/heads/main' uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e786e3..2894db6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: Release on: push: tags: - - 'v*' + - "v*" workflow_dispatch: # Manual trigger for re-running failed releases (requires existing tag) @@ -20,7 +20,7 @@ jobs: - uses: actions/setup-go@v5 with: - go-version-file: 'go.mod' + go-version-file: "go.mod" cache: true - name: Run GoReleaser diff --git a/.gitignore b/.gitignore index e887019..863ce65 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ # Test binary, built with `go test -c` *.test +# GoReleaser output +dist/ + # Output of the go coverage tool *.out coverage.out diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 79dc6b1..fc5e2f6 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -11,8 +11,9 @@ before: - go test ./... builds: + # CLI binary (stacktower) - for end users - id: stacktower - main: ./main.go + main: ./cmd/stacktower binary: stacktower env: - CGO_ENABLED=0 @@ -25,12 +26,15 @@ builds: - arm64 ldflags: - -s -w - - -X main.version={{.Version}} - - -X main.commit={{.Commit}} - - -X main.date={{.Date}} + - -X github.com/matzehuels/stacktower/pkg/buildinfo.Version={{.Version}} + - -X github.com/matzehuels/stacktower/pkg/buildinfo.Commit={{.Commit}} + - -X github.com/matzehuels/stacktower/pkg/buildinfo.Date={{.Date}} archives: + # CLI archive (for Homebrew, manual download) - id: stacktower + builds: + - stacktower formats: - tar.gz name_template: >- @@ -50,7 +54,7 @@ archives: - README.md checksum: - name_template: 'checksums.txt' + name_template: "checksums.txt" snapshot: version_template: "{{ incpatch .Version }}-next" @@ -60,11 +64,11 @@ changelog: use: github filters: exclude: - - '^docs:' - - '^test:' - - '^chore:' - - '^ci:' - - 'README' + - "^docs:" + - "^test:" + - "^chore:" + - "^ci:" + - "README" - Merge pull request - Merge branch groups: diff --git a/Makefile b/Makefile index 1df93cc..ace9c06 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,53 @@ -.PHONY: all build clean fmt lint test cover e2e e2e-test e2e-real e2e-parse blog blog-diagrams blog-showcase install-tools snapshot release help +.PHONY: all build clean fmt fmt-check lint test cover vuln e2e blog install-tools snapshot release help + +# ============================================================================= +# Variables +# ============================================================================= BINARY := stacktower +VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") +COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown") +DATE := $(shell date -u +%Y-%m-%dT%H:%M:%SZ) +LDFLAGS := -X github.com/matzehuels/stacktower/pkg/buildinfo.Version=$(VERSION) \ + -X github.com/matzehuels/stacktower/pkg/buildinfo.Commit=$(COMMIT) \ + -X github.com/matzehuels/stacktower/pkg/buildinfo.Date=$(DATE) + +# ============================================================================= +# Default Target +# ============================================================================= all: check build +# ============================================================================= +# Build Targets +# ============================================================================= + +build: + @echo "Building CLI (bin/$(BINARY))..." + @go build -ldflags "$(LDFLAGS)" -o bin/$(BINARY) ./cmd/stacktower + +install: + @echo "Installing CLI..." + @go install -ldflags "$(LDFLAGS)" ./cmd/stacktower + +clean: + @rm -rf bin/ dist/ coverage.out output/ tmp/ + +# ============================================================================= +# Quality Checks +# ============================================================================= + check: fmt lint test vuln fmt: @gofmt -s -w . - @goimports -w -local stacktower . + @goimports -w -local github.com/matzehuels/stacktower . + +fmt-check: + @echo "Checking formatting..." + @test -z "$$(gofmt -l .)" || (echo "Files not formatted:"; gofmt -l .; exit 1) + @test -z "$$(goimports -l -local github.com/matzehuels/stacktower .)" || (echo "Imports not formatted:"; goimports -l -local github.com/matzehuels/stacktower .; exit 1) + @echo "Formatting OK" lint: @golangci-lint run @@ -20,23 +59,33 @@ cover: @go test -race -coverprofile=coverage.out ./... @go tool cover -func=coverage.out -build: - @go build -o bin/$(BINARY) . +vuln: + @govulncheck ./... -install: - @go install . +install-tools: + @go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest + @go install golang.org/x/tools/cmd/goimports@latest + @go install golang.org/x/vuln/cmd/govulncheck@latest + +# ============================================================================= +# End-to-End Tests +# ============================================================================= e2e: build - @./scripts/test_e2e.sh all + @./scripts/test_cli_e2e.sh all e2e-test: build - @./scripts/test_e2e.sh test + @./scripts/test_cli_e2e.sh test e2e-real: build - @./scripts/test_e2e.sh real + @./scripts/test_cli_e2e.sh real e2e-parse: build - @./scripts/test_e2e.sh parse + @./scripts/test_cli_e2e.sh parse + +# ============================================================================= +# Blog Assets +# ============================================================================= blog: blog-diagrams blog-showcase @@ -46,13 +95,9 @@ blog-diagrams: build blog-showcase: build @./scripts/blog_showcase.sh -install-tools: - @go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest - @go install golang.org/x/tools/cmd/goimports@latest - @go install golang.org/x/vuln/cmd/govulncheck@latest - -vuln: - @govulncheck ./... +# ============================================================================= +# Release +# ============================================================================= snapshot: @goreleaser release --snapshot --clean --skip=publish @@ -60,23 +105,42 @@ snapshot: release: @goreleaser release --clean -clean: - @rm -rf bin/ dist/ coverage.out +# ============================================================================= +# Help +# ============================================================================= help: - @echo "make - Run checks and build" - @echo "make check - Format, lint, test, vulncheck (same as CI)" - @echo "make fmt - Format code" - @echo "make lint - Run golangci-lint" - @echo "make test - Run tests" - @echo "make cover - Run tests with coverage" - @echo "make build - Build binary" - @echo "make e2e - Run all end-to-end tests" - @echo "make e2e-test - Render examples/test/*.json" - @echo "make e2e-real - Render examples/real/*.json" - @echo "make e2e-parse - Parse packages to examples/real/" - @echo "make blog - Generate all blogpost diagrams" - @echo "make blog-diagrams - Generate blogpost example diagrams" - @echo "make blog-showcase - Generate blogpost showcase diagrams" - @echo "make vuln - Check for vulnerabilities" - @echo "make clean - Remove build artifacts" + @echo "Stacktower Makefile" + @echo "" + @echo "BUILDING:" + @echo " make build - Build CLI binary (bin/stacktower)" + @echo " make install - Install CLI to GOPATH" + @echo "" + @echo "QUALITY:" + @echo " make check - Run all checks (fmt, lint, test, vuln)" + @echo " make fmt - Format code" + @echo " make fmt-check - Check formatting (CI-style, no writes)" + @echo " make lint - Run golangci-lint" + @echo " make test - Run tests" + @echo " make cover - Run tests with coverage" + @echo " make vuln - Check for vulnerabilities" + @echo "" + @echo "TESTING:" + @echo " make e2e - Run all CLI end-to-end tests" + @echo " make e2e-test - Run test examples" + @echo " make e2e-real - Run real package examples" + @echo " make e2e-parse - Run parse tests" + @echo "" + @echo "BLOG:" + @echo " make blog - Generate all blog assets" + @echo " make blog-diagrams - Generate blog diagrams" + @echo " make blog-showcase - Generate blog showcase" + @echo "" + @echo "RELEASE:" + @echo " make snapshot - Build release locally (no publish)" + @echo " make release - Build and publish release" + @echo "" + @echo "OTHER:" + @echo " make clean - Remove build artifacts" + @echo " make install-tools - Install development tools" + @echo " make help - Show this help" diff --git a/README.md b/README.md index e161774..b31a74e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Release](https://img.shields.io/github/v/release/matzehuels/stacktower)](https://github.com/matzehuels/stacktower/releases) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) -Inspired by [XKCD #2347](https://xkcd.com/2347/), Stacktower renders dependency graphs as **physical towers** where blocks rest on what they depend on. Your application sits at the top, supported by libraries below—all the way down to that one critical package maintained by *some dude in Nebraska*. +Inspired by [XKCD #2347](https://xkcd.com/2347/), Stacktower renders dependency graphs as **physical towers** where blocks rest on what they depend on. Your application sits at the top, supported by libraries below—all the way down to that one critical package maintained by _some dude in Nebraska_.

FastAPI dependency tower @@ -36,7 +36,7 @@ go install github.com/matzehuels/stacktower@latest ```bash git clone https://github.com/matzehuels/stacktower.git cd stacktower -go build -o stacktower . +go build -o bin/stacktower ./cmd/stacktower ``` ## Quick Start @@ -88,6 +88,7 @@ stacktower parse go examples/manifest/go.mod -o deps.json When the argument exists on disk or matches a known manifest filename, Stacktower automatically parses it as a manifest. The project name (root node) is auto-detected from the manifest or a sibling file: + - **Cargo.toml**: `[package].name` - **go.mod**: `module` directive - **package.json**: `name` field @@ -103,27 +104,6 @@ stacktower parse python requirements.txt --name="my-project" -o deps.json stacktower parse ruby Gemfile -n my-rails-app -o deps.json ``` -#### Explicit Mode - -Force registry or manifest parsing when auto-detection isn't enough: - -```bash -# Force registry lookup -stacktower parse python registry pypi fastapi -stacktower parse java registry maven org.springframework:spring-core -stacktower parse go registry goproxy github.com/spf13/cobra - -# Force manifest type -stacktower parse python manifest poetry examples/manifest/poetry.lock -stacktower parse python manifest requirements examples/manifest/requirements.txt -stacktower parse rust manifest cargo examples/manifest/Cargo.toml -stacktower parse javascript manifest package examples/manifest/package.json -stacktower parse ruby manifest gemfile examples/manifest/Gemfile -stacktower parse php manifest composer examples/manifest/composer.json -stacktower parse java manifest pom examples/manifest/pom.xml -stacktower parse go manifest gomod examples/manifest/go.mod -``` - #### Metadata Enrichment By default, Stacktower enriches packages with GitHub metadata (stars, maintainers, last commit) for richer visualizations. Set `GITHUB_TOKEN` to enable this: @@ -132,8 +112,8 @@ By default, Stacktower enriches packages with GitHub metadata (stars, maintainer export GITHUB_TOKEN=your_token stacktower parse python fastapi -o fastapi.json -# Disable enrichment if you don't have a token -stacktower parse python fastapi --enrich=false -o fastapi.json +# Skip enrichment if you don't have a token +stacktower parse python fastapi --skip-enrich -o fastapi.json ``` ### Rendering @@ -144,6 +124,14 @@ The `render` command generates visualizations from parsed JSON graphs: stacktower render [flags] ``` +This is a shortcut that combines `layout` and `visualize` in one step. For more control, you can run them separately: + +```bash +# Two-step workflow with intermediate layout +stacktower layout examples/real/flask.json -o flask.layout.json +stacktower visualize flask.layout.json -o flask.svg +``` + #### Visualization Types ```bash @@ -155,9 +143,6 @@ stacktower render examples/real/serde.json --style simple --randomize=false --po # Traditional node-link diagram (uses Graphviz DOT) stacktower render examples/real/yargs.json -t nodelink -o yargs.svg - -# Multiple types at once (outputs flask_tower.svg, flask_nodelink.svg) -stacktower render examples/real/flask.json -t tower,nodelink -o flask ``` #### Output Formats @@ -177,18 +162,16 @@ stacktower render examples/real/flask.json -f png -o flask.png # Multiple formats at once (outputs flask.svg, flask.json, flask.pdf) stacktower render examples/real/flask.json -f svg,json,pdf -o flask - -# Combine multiple types and formats -stacktower render examples/real/flask.json -t tower,nodelink -f svg,json ``` Output path behavior: + - **No `-o`**: Derives from input (`input.json` → `input.`) - **Single format**: Uses exact path (`-o out.svg` → `out.svg`) -- **Multiple formats**: Strips extension, adds format (`-o out.svg -f svg,json` → `out.svg`, `out.json`) -- **Multiple types**: Adds type suffix (`-t tower,nodelink` → `out_tower.svg`, `out_nodelink.svg`) +- **Multiple formats**: Strips extension, adds format (`-o out -f svg,json` → `out.svg`, `out.json`) > **Note:** PDF and PNG output requires [librsvg](https://wiki.gnome.org/Projects/LibRsvg): +> > - macOS: `brew install librsvg` > - Linux: `apt install librsvg2-bin` @@ -213,51 +196,45 @@ stacktower render examples/test/diamond.json -o diamond.svg ### Global Options -| Flag | Description | -|------|-------------| +| Flag | Description | +| ----------------- | -------------------------------------------------------- | | `-v`, `--verbose` | Enable debug logging (search space info, timing details) | ### Parse Options -| Flag | Description | -|------|-------------| -| `-o`, `--output` | Output file (stdout if empty) | -| `-n`, `--name` | Project name for manifest parsing (auto-detected from manifest if not set) | -| `--max-depth N` | Maximum dependency depth (default: 10) | -| `--max-nodes N` | Maximum packages to fetch (default: 5000) | -| `--enrich` | Enrich with GitHub metadata (default: true, requires `GITHUB_TOKEN`) | -| `--refresh` | Bypass cache | +| Flag | Description | +| ---------------- | -------------------------------------------------------------------------- | +| `-o`, `--output` | Output file (stdout if empty) | +| `-n`, `--name` | Project name for manifest parsing (auto-detected from manifest if not set) | +| `--max-depth N` | Maximum dependency depth (default: 10) | +| `--max-nodes N` | Maximum packages to fetch (default: 5000) | +| `--skip-enrich` | Skip metadata enrichment (GitHub descriptions, etc.) | +| `--no-cache` | Disable caching | ### Render Options -| Flag | Description | -|------|-------------| -| `-o`, `--output` | Output file or base path for multiple types/formats | -| `-t`, `--type` | Visualization type(s): `tower` (default), `nodelink` (comma-separated) | -| `-f`, `--format` | Output format(s): `svg` (default), `json`, `pdf`, `png` (comma-separated) | -| `--normalize` | Apply graph normalization: break cycles, remove transitive edges, assign layers, subdivide long edges (default: true) | +| Flag | Description | +| ---------------- | --------------------------------------------------------------------------------------------------------------------- | +| `-o`, `--output` | Output file or base path for multiple formats | +| `-t`, `--type` | Visualization type: `tower` (default), `nodelink` | +| `-f`, `--format` | Output format(s): `svg` (default), `json`, `pdf`, `png` (comma-separated) | +| `--normalize` | Apply graph normalization: break cycles, remove transitive edges, assign layers, subdivide long edges (default: true) | #### Tower Options -| Flag | Description | -|------|-------------| -| `--width N` | Frame width in pixels (default: 800) | -| `--height N` | Frame height in pixels (default: 600) | -| `--style handdrawn\|simple` | Visual style (default: handdrawn) | -| `--randomize` | Vary block widths to visualize load-bearing structure (default: true) | -| `--merge` | Merge subdivider blocks into continuous towers (default: true) | -| `--popups` | Enable hover popups with package metadata (default: true) | -| `--nebraska` | Show "Nebraska guy" maintainer ranking panel | -| `--edges` | Show dependency edges as dashed lines | -| `--ordering optimal\|barycentric` | Crossing minimization algorithm (default: optimal) | -| `--ordering-timeout N` | Timeout for optimal search in seconds (default: 60) | -| `--top-down` | Width flows from roots down; by default width flows from sinks up | - -#### Node-Link Options - -| Flag | Description | -|------|-------------| -| `--detailed` | Show all node metadata in labels | +| Flag | Description | +| --------------------------------- | --------------------------------------------------------------------- | +| `--width N` | Frame width in pixels (default: 800) | +| `--height N` | Frame height in pixels (default: 600) | +| `--style handdrawn\|simple` | Visual style (default: handdrawn) | +| `--randomize` | Vary block widths to visualize load-bearing structure (default: true) | +| `--merge` | Merge subdivider blocks into continuous towers (default: true) | +| `--popups` | Enable hover popups with package metadata (default: true) | +| `--nebraska` | Show "Nebraska guy" maintainer ranking panel | +| `--edges` | Show dependency edges as dashed lines | +| `--ordering optimal\|barycentric` | Crossing minimization algorithm (default: optimal) | +| `--ordering-timeout N` | Timeout for optimal search in seconds (default: 60) | +| `--no-cache` | Disable caching | ## JSON Format @@ -267,11 +244,7 @@ The render layer accepts a simple JSON format, making it easy to visualize **any ```json { - "nodes": [ - { "id": "app" }, - { "id": "lib-a" }, - { "id": "lib-b" } - ], + "nodes": [{ "id": "app" }, { "id": "lib-a" }, { "id": "lib-b" }], "edges": [ { "from": "app", "to": "lib-a" }, { "from": "lib-a", "to": "lib-b" } @@ -281,34 +254,34 @@ The render layer accepts a simple JSON format, making it easy to visualize **any ### Required Fields -| Field | Type | Description | -|-------|------|-------------| -| `nodes[].id` | string | Unique node identifier (displayed as label) | -| `edges[].from` | string | Source node ID | -| `edges[].to` | string | Target node ID | +| Field | Type | Description | +| -------------- | ------ | ------------------------------------------- | +| `nodes[].id` | string | Unique node identifier (displayed as label) | +| `edges[].from` | string | Source node ID | +| `edges[].to` | string | Target node ID | ### Optional Fields -| Field | Type | Description | -|-------|------|-------------| -| `nodes[].row` | int | Pre-assigned layer (computed automatically if omitted) | -| `nodes[].kind` | string | Internal use: `"subdivider"` or `"auxiliary"` | -| `nodes[].meta` | object | Freeform metadata for display features | +| Field | Type | Description | +| -------------- | ------ | ------------------------------------------------------ | +| `nodes[].row` | int | Pre-assigned layer (computed automatically if omitted) | +| `nodes[].kind` | string | Internal use: `"subdivider"` or `"auxiliary"` | +| `nodes[].meta` | object | Freeform metadata for display features | ### Recognized `meta` Keys These keys are read by specific render flags. All are optional—missing keys simply disable the corresponding feature. -| Key | Type | Used By | -|-----|------|---------| -| `repo_url` | string | Clickable blocks, `--popups`, `--nebraska` | -| `repo_stars` | int | `--popups` | -| `repo_owner` | string | `--nebraska` | -| `repo_maintainers` | []string | `--nebraska` | -| `repo_last_commit` | string (date) | `--popups`, brittle detection | -| `repo_last_release` | string (date) | `--popups` | -| `repo_archived` | bool | `--popups`, brittle detection | -| `summary` | string | `--popups` (fallback: `description`) | +| Key | Type | Used By | +| ------------------- | ------------- | ------------------------------------------ | +| `repo_url` | string | Clickable blocks, `--popups`, `--nebraska` | +| `repo_stars` | int | `--popups` | +| `repo_owner` | string | `--nebraska` | +| `repo_maintainers` | []string | `--nebraska` | +| `repo_last_commit` | string (date) | `--popups`, brittle detection | +| `repo_last_release` | string (date) | `--popups` | +| `repo_archived` | bool | `--popups`, brittle detection | +| `summary` | string | `--popups` (fallback: `description`) | The `--detailed` flag (node-link only) displays **all** meta keys in the node label. @@ -323,16 +296,34 @@ The `--detailed` flag (node-link only) displays **all** meta keys in the node la The ordering step is where the magic happens. Stacktower uses an optimal search algorithm that guarantees minimum crossings for small-to-medium graphs. For larger graphs, it gracefully falls back after a configurable timeout. +## GitHub Authentication + +For parsing repositories directly from GitHub, you can authenticate using the device flow: + +```bash +# Login with GitHub (opens browser for device authorization) +stacktower github login + +# Check current session +stacktower github whoami + +# Logout +stacktower github logout + +# Parse a manifest from a GitHub repository +stacktower parse github owner/repo -o deps.json +``` + ## Environment Variables -| Variable | Description | -|----------|-------------| -| `GITHUB_TOKEN` | GitHub API token for `--enrich` metadata | -| `GITLAB_TOKEN` | GitLab API token for `--enrich` metadata | +| Variable | Description | +| -------------- | ------------------------------------------------ | +| `GITHUB_TOKEN` | GitHub API token for metadata enrichment | +| `GITLAB_TOKEN` | GitLab API token for metadata enrichment | ## Caching -HTTP responses are cached in `~/.cache/stacktower/` with a 24-hour TTL. Use `--refresh` to bypass the cache for a single request. +HTTP responses are cached in `~/.cache/stacktower/` with a 24-hour TTL. Use `--no-cache` to disable caching for a single request. ```bash # Clear the entire cache @@ -348,10 +339,10 @@ Stacktower can be used as a Go library for programmatic graph visualization. ```go import ( - "github.com/matzehuels/stacktower/pkg/dag" - "github.com/matzehuels/stacktower/pkg/dag/transform" - "github.com/matzehuels/stacktower/pkg/render/tower/layout" - "github.com/matzehuels/stacktower/pkg/render/tower/sink" + "github.com/matzehuels/stacktower/pkg/core/dag" + "github.com/matzehuels/stacktower/pkg/core/dag/transform" + "github.com/matzehuels/stacktower/pkg/core/render/tower/layout" + "github.com/matzehuels/stacktower/pkg/core/render/tower/sink" ) // Build a graph @@ -369,10 +360,12 @@ svg := sink.RenderSVG(l, sink.WithGraph(g), sink.WithPopups()) 📚 **[Full API documentation on pkg.go.dev](https://pkg.go.dev/github.com/matzehuels/stacktower)** Key packages: -- [`pkg/dag`](https://pkg.go.dev/github.com/matzehuels/stacktower/pkg/dag) — DAG data structure and crossing algorithms -- [`pkg/dag/transform`](https://pkg.go.dev/github.com/matzehuels/stacktower/pkg/dag/transform) — Graph normalization pipeline -- [`pkg/render/tower`](https://pkg.go.dev/github.com/matzehuels/stacktower/pkg/render/tower) — Layout, ordering, and rendering -- [`pkg/deps`](https://pkg.go.dev/github.com/matzehuels/stacktower/pkg/deps) — Dependency resolution from registries + +- [`pkg/core/dag`](https://pkg.go.dev/github.com/matzehuels/stacktower/pkg/core/dag) — DAG data structure and crossing algorithms +- [`pkg/core/dag/transform`](https://pkg.go.dev/github.com/matzehuels/stacktower/pkg/core/dag/transform) — Graph normalization pipeline +- [`pkg/core/render/tower`](https://pkg.go.dev/github.com/matzehuels/stacktower/pkg/core/render/tower) — Layout, ordering, and rendering +- [`pkg/core/deps`](https://pkg.go.dev/github.com/matzehuels/stacktower/pkg/core/deps) — Dependency resolution from registries +- [`pkg/pipeline`](https://pkg.go.dev/github.com/matzehuels/stacktower/pkg/pipeline) — Complete parse → layout → render pipeline ## Contributing @@ -386,16 +379,16 @@ make check # Run all CI checks locally (fmt, lint, test, vuln) make build # Build binary to bin/stacktower ``` -| Command | Description | -|---------|-------------| -| `make check` | Format, lint, test, vulncheck (same as CI) | -| `make fmt` | Format code with gofmt and goimports | -| `make lint` | Run golangci-lint | -| `make test` | Run tests with race detector | -| `make cover` | Run tests with coverage report | -| `make vuln` | Check for known vulnerabilities | -| `make e2e` | Run end-to-end tests | -| `make snapshot` | Build release locally (no publish) | +| Command | Description | +| --------------- | ------------------------------------------ | +| `make check` | Format, lint, test, vulncheck (same as CI) | +| `make fmt` | Format code with gofmt and goimports | +| `make lint` | Run golangci-lint | +| `make test` | Run tests with race detector | +| `make cover` | Run tests with coverage report | +| `make vuln` | Check for known vulnerabilities | +| `make e2e` | Run end-to-end tests | +| `make snapshot` | Build release locally (no publish) | Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/). diff --git a/blogpost/android-chrome-192x192.png b/blogpost/android-chrome-192x192.png index 2706f787436d5a22b550697f676337e4d8103bb0..b842284f768867c705404a2b12c1487af433f75c 100644 GIT binary patch literal 12807 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 NsomFBu2M9A{eRIS=6wJF literal 435954 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 (xwf+7;yE-_Ec1u^q~WrJs#f3 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%0D{ssz6bFZr7^o+?%I)uJxr#Z1u)< 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�rC+y5^6*$qaU!=u&=UkivdE_T@SwF|x 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 D2KX?( diff --git a/blogpost/apple-touch-icon.png b/blogpost/apple-touch-icon.png index 3aaa75cd85b3df4486daeb5aa8fd823e951313fc..571ab67a54cc5977e96243cae97f865bcd5c559c 100644 GIT binary patch literal 11362 zcmXwSIOCkgMWn5F67Hn!jW&r16q*v}QsbF`3ts$p77F0y{6dudbqb9$tvU z6s7zBdmcLC$hOjFGqd=Ym@UjG|Lz!Fpnae0==4;s%!6?VF#O~u$1m*u>IY1YH%#k9 zggflb$v&@={z-?X(jvc-$#m`3$`dU$@a z&4qRHcT@VM(e!>Pa_ss28oE~V{N(ea`Mr%tS1d7$pM6(l39Lj%^FBaMqItg?o#YPY z*4wbZd23KF%ldI+fs5+pc={C3^WWp1R&x*-Zou+?4Ri9cSFLH``%b=4k!TLhSLwYs z2oNFq3;Gf;P}7n*1#FT*VtyM*9O?VHi~%pY@NBso9T3U0Vgm0J`2=^=3}Z!zK}J|r~e2<n{J2I@vRPe(%Qj*Yr2C)VVRWw$a2TP}P%J5x_ z$34ngvF{8r_;lriOt&o(p(;;3w{Okm+YQ{&uhBLAX(A&lu#rS>)v9$r*gAoTSDuBEB12Muk{ZFXdm2$$vL0Z( zXbL)bQK}Mf`WSnrn$v+Y)cOy3jJ4Y8c;>63fI^4~`#bG)h&?;Kv@n|cTO#{h0Z}*#4M<_Bk*%GkH>Pyo3 zAfoX&5bF*PKa#u7)5hEfec#>prU5)AIJ(fpo9LLwpjz^ku7N^e(|}r9TBbwQw&RRC zUaSCNQ_Y7|3*DvP*~JvUTYYbn&>g3bvu0=qM^u*bV?NxYiSo;XW!XusR-Yvy+5^oz z<|q9DDhNaT^trI0=q|<^PiLI*5M6wJ^Ajqz4;WkT-@KhkEBFZE8nrVxIZ)ajSkVbJ zIAP)m3p_DS^@!mNh%HNZC*;x~*|ea?m=Japk`sC|&dlD;1L%k9O>nyZEMiI?=qSkW z0#;afV!j<4U5Vy#3eapc1XF|WEC_+A+XnIgsf9D%vMp8}=kvzysA8TlVToUueZpS0 zC590R0}E$VfwPS|6O|OOA81@u6%}nuP*+xaKJ&LNdbMAh+;yZW(!K1C*rs$AMxGPc zoQarjLR|0ySNoo6Q?=OLO5(bZvGi~Hq#zChEk3?sE!jrgm?pS1c)|m1)o5@UgMXBX zddSwoh>=NsJL;q#Uh;m+rO^fxa7Mg!}SeZhMIBTHM+5l8?kh7CNZT9lr~va$-)H>N|mXc!yI-vinn}z?y{=f zN%0!94a_skhZTJ1|MED4s@N{$Nv}AS0cG?$XlTA9Z(%jY6)M?tBfWOWaAQ_6E9z4KrXveb;vGHuKO&r>KYU>_ucl~84cOqi z$8pb3tzJ-QqollYjbJHki?Xt?M8X9T&V1jP_ejRyMxwDS&u3u_?|hx57Q9m zeb9+nHHOa=CxZcuoa!U&(~4G_2UNL2A@+S)L~Yj1pLEQtU^n>wVVq$(PyNiMu$kGut(61f%g8?8i*Bxm zkg8E}PYidB+olaa`$&edZ6Pl7#$b&~{(VY%hK;6qIz8cMv-t0WD@~23FG5ZR zAQv-krK_CsbTcRMSC%$AX}PqTfB1Ej4+y#IzZfa)gz%BgqLSlhEz^|Zxt={^W_R4U z&`G3j|BkBTQZZgK=WPQ87_gahN_(K@)O|0sM5A4`=Um?JRD0tW#xkOvEuGJ316Hv{ zP@Recz7gTW;qpO8Pt0zam6YPekMn;$Pxuzuo~X9&JsEW2n*}@;^zAu~`b-Lt%;6Tm ztaA{q;AcJPwMiw+N^MIyciEywl>&uguprl<%Hwd(&gK<-8B5ktO$4x{o7aCGNj44% zr$)A*oojZLNa++s;8o5Fd7{saErhGxx|jDK>O4TRob5R z2GQ|#+#JkWh!gi`9!iVc=p$*tLn%uE=G+Or4`kHaB57RUr$7N3IS_GYcR?gobk;2? zrOZKa#`T5P_W~QSnE1pklTuaY$z(sHvRT!=jA1L8aAt*e18ht&9@o0YaltK|0#E;1j$30be-6lg zor|$TF8|9gIjtOZ{x#u`d}PPFZb6BcPdsFtq%o>MaEcmmW8-?y^`s1xhbTAvTZ2;w zteG@123gCzWPUs%CA#4+vwhNhtFD^o)0KBLBL5w6&1^eL6FCsaot2QQQzdCO^6W`v zjCo#fNyDuo_~z5ENpLkdY%B$^z{8~@o31hSQ6EWR7&R`9r|SVX^@YviBWpieVh&*Q z*I8w~>b*msKs0k>yoG%ip(f(kg3I{zHEczr5j<_-b_su`@#cy9x-q9IhjWqHO`lVk z3wMsMuWoTg3NHg%`#uOd*Y;QYH6A{z4Y$WHH0;#u&L94}@w-Lh&peU-*TSHIIeI>p z>Gqa=o1rjaydB^)!*xW&;UZ*OB}J-I@uK`{w8wIRr}#gpdba_XN=mX%(MN|j)beTC zFTUgqEcm*j0L}Mj;_BPTKhRt>P~OG5bZ0JLqQd8VBNdBKlqeH<;8pf{0vX6xsNg+I zcjI21I$3C$QmI81yVnV zefDKzI9(kfi!HLZZ*YlFOZPKZ@~ZLL?|Q+Tkc{cJhA>xwwR{F&G~c_n&kg`Mg>UN2 z9B7GJ`RimE>t768cnV8!?^5Ge^JaRZ?nGGDZepe%^s#d&@&L=#$;{OGDzO?zM-zfN zH)G-2=CkdtBB6eM?opp1M5beW?kgIpXZA~zI?_fR{hyXcsH}PJH`}-Ig#)84FC-a4 zNaC0S6cf0jvOlxS`bn}gN?mJIvzjH^_6OV)+a)h09Jr%(Jrqn1(otRNPY07Q-Q4Jy z%2PhmQxH|-d!zc#4{+(5@1@A9fq|SRF~(7su9h-Uc$8q&yvP&69fXNDJs$B~cKOJ+ z`i_sCBi`H6;wRtr2tl3`sRIr(=-+J_4s?!1_O{lOL@h^mP8bCfRS4w|E#?cY3BzwJY|X`G zBisDP2bZ`^oKTWFIeU7tPjN|y;;sW@#H(F^V4q4#|=#jfg z4PTJ0_pxH`%S4u>;2pbi%6V<|d9KTS4oQ=O&d>h|}<@dX=b0YNpG?mo4nZ zKDPuIWbHjzchotBTnwoF?!`AwkJ@Sm>G|rXAuO}l!{%2$l`d?*3x%m0(O_RiK7Tb9)@C&V7#U8|K|d5Qgd~67_0}O z`WCdRc4)r!&+HEMz0=vL7jE!-s&7|fS9M?M;+ETe^%nn^7qGIP4H2X4pM}*Wno-z` zmONLh*1}luWj1D)$w|1z4J4$V-cUL)Tl$bT3z0c^{NUZ?3b( zOoBKyl02x0zMHjv=g;3QK*VJx>OP?{;eY@x%!_|`a`jEJ(B#3>9ZS}8#r3veyXU9A z8*-p2CG|FMsF%toW^YR7ph%sOQ*T5K4{b{-NGQZ4-tak+0(g`3)l~&5@h;?$FX)!C zSFY-noo-a)t`BLay_)KOb6PVyJ44UkULhpM(uV_7Xo10(r}W>xP6G%!w-9P~ZU3E; z-Vk;u~*RLdyA6{&$=xtb?~hJgK{1-@4A-jH_wVF{=)*;j>3v<+BO&t-}^2eFk7&u25`iu{s@(ETUEUWI>*z zKm>Fv+)~j8ptQ@rPRWT={)-i#1kHxxWhOIgX%+fF&p?KY6k@ zh9>49W*`Wrq<`I55Y)0=SKgH zDmW|-@0#u#&UD6YAZPQ_{Ze3gdS%cjQb*CLYe==K+*bk?$C@ts6%#b`LYSEf9Eps( z<ta~CTBUx zC%O87aS|%Nknm2RkR$58E9GoggOD$e0>k|pjWv_qj&9XK8y zF|tzVlg@eKw3SC$nA2S6LS%gV(@K=kSJ|w$yYi#93p~a}UvsFM!Eu95{~d3YTFYBm zw=S4s)_`n^RQ$2c+wk@ozGM?s=CelGWC?XESgGn?bgU?~vn}mk-&Q5$=Z(OFbimRP z&X&R;BhHCp%d>Daz4U;d?hW6YM29%w^QvA6-@6wJIf$k7K1>T#DYBkr>5`<=#qxHk_T_VUbzvztZm&qj#N z*m{YssQ`4TTyh*GXlhV9dY=lP_~H8q$4HU?XW*Ojxr?Ozl#MssJJg4fzDuxz256!k z-ln?zS&2VLvCKJ=P%SB87=piOy>1vu^EQiw)x;Lp-xfc^may5Dc-ofq+!mSH#tu7A zm>AC47}+LiwL(f`g>S0MJ3sZ#Osv79c-?}G2s35_SXzm_e!p;a{qy@Ng%Fq54xmF^ zePW~3u^HR(r0zz0L=?aziYXw9B_|4nJDa_enSJ-ntnGF<^7{>oK}!f)=hb=U!daUO zyjYr!CDo$smDNuZ8wtCXf}waFbx3h8z3X6_lS&5JYgvp@qZrV-k-O*ZVZ6~bZedGB zraZ@fFg?;o8O0Cj#ILeypMxx}KyUxIkHe)ih_`%WArpJrI<4&9LJ$MR;@3uZA|n}^ z9s$>%)2xDMj!uKmRiwP{jMxrQJp_h1s+=NY9AdGqc3QOZjU{wmOi2efcF8cnH4)nn z+yqE)$37!*H;0bD!W5A!>-ie#pO?9l@A+czH`6$fNY-5o0RFD;G(MVIq27;Qwr{%F8!!eU-09Vz7oF_xnLWwgwxi(5 z`Cco1-p(!mDq^_ya*c&f(ja<=IkB-6wyBc-P9aX+s0$Of~Vf#Tu zO-bbWiN7~?vJqPP(k5;b)UfMd6AD%`gc4eJJ&rfy7oO{Ji!t7)D>FB&ZEs|=qWSBT z<&x$OrWb~2e*&N6{Z7i`F*;f83_y=wyv9d#Z`Bmc`Sk^j3vVnYN= zxaNuW$aR7+SDZnCd8)IIZ-`#@qdVpHcd!~a^?Y3bRLdB)QU2i)%7P3(WfTMn4~zYj z#ntI>X4TDZ8e{)Zkvmla=C0+s3{~;BfX${0Tn6GYL@2cXP#DC$^t~_x>KbgbLuH4Z z;b52*i*YTio2AfIv}jJjA?LjnMKe=H^_XWdd&Wlv5(Yrn3dv6=EYndR22t}>+H3|+_fXO z5+^UE!SQbHQmur!6=`@7ULK<{(4Jf4rcSylhLfL0@S8uVt^B&DouP8sLXw#Bx2^9! zmhd@XUIP7WijQhoxMjS-&-q+KD_Yt*-YG;h=0waTZ`%x%zb?eGP@jl}_uJzbbIXQ>mUZrbuWtZh ztOf_mN|9p!H=SqvX{E8pbpet=7GJ&Lvl}y^3>gZ*3vCwfmN^Jy#%V#+0(2p{&@ANS zp%cx$py>1}!0ET5{gW$tkz;D_?PfD*(>s^+h>>M|K%~srefthgyS`Eu1A zD!+u!^Sw&QVtk@;S%#flj(w0^j#}uU^8T-`@5@v1!MX%wIN^iilrs$yx1I-V!L=L^ zM=N>7ocC)I?Qkq~J~S1@#QOBMKyhP2f^SDV*kc?nC^|i(Hc$@O@yF7AcKdfRr|qSQ zJUM`m;&59lKc_;7UHqGmbg~M+dFPAN5B{(Ac=ppCS`Z^V>Ydr)7EpG9jmVyQ*1rdH z>DC3!ZX!X}Oyjx^V&U~{_4;LilnD*+e}8obI5C!FX(;0p-uG~M5NcZzjsqI06)NqA znVHG0k(Hnv12aVmCY+eS-Cb2x;&{r0pPS_^FE)%YmXe*~C-R zhDWXe_up4f4x2a=7S`}n9&v>Xz|^Iq?3bxd@E8t{WG43xsb6R_iv=z7Kx3lWa7EM1 z1&0C~#&&k4(EfUS&Z?Z^n?G4-{YEj}lwCMXW(AkyBe$s)1bnc`chg2$r#tGP;4fD{ z9=yN1So3f;m#=Ub!~ShWn92Fw&+~X_=VJPxIvr26u*&d4!i)MD?89X1TkE4U#5Vn! z8H#u2YP7-aS+1RZH~jnX>M93J=nw3Z+)ZipVDJ`)%mSmr`KU!P9=R_Ni>HCrRHAkX zbpK7grDV|%6nHx8*~?ZY(@T}VmX|m_ap=dwK}*5{Jk5AV4{O1rF}UhSs1ufeeL8VJ zFwvm2peYSF50V)a{{EbK+z<7BGvmWtC@~{#MUd#Y=AYp`s6Capmyn@Xs<50t*dc-< z3R5$7{ho$O$oX;YH5mkJ*>PROGw^y0jlOF5sR{ArJ^?w}6(c9;$1vbu!!w&)vrCqZ zF`z&2^X&E{Ut%U!n;u5n$t3NM_;Dw}JpW^Fc6=-~al5(hiJ#09DTHKl@QiZ1CR~qn9n_hdbZ=X5=;VW#*)uH7$p}|D<78_z`Z2R*V!sTY>m#7X znSL{imcIM&3u)DaSy}j97N(|*h4_+#E0$xtKu@soshR%!JU7a2bY-7+E@u>22Wr}xTDPAN<~*&Xcu)uy z*zEImn*X-DxYIur3CA2=YOOEXv;ufJ3+&HlRUIfrr|5(aee#v!?!jY`_Z2gUxnr|; zTR5_rZYTPGlEv0O8W6?x5XB1?oz-q)Jy&oPktoihsID}a!n_blgYO7n&;OpIH99)` zW%yUoKwb>{@;o~4q*vfihLCxG-oJ9+GW&i9+4B_fFEo zB|?Gzy;|16+plEf>y!x=Do@+3V(WDUOs=y*J+gi;J$g1>GS8*B2c=HGmYJ)M>&;urSwvFKLsO4FzsvICG^oJvNBmr`Yc+D=adqdS3sN#iI^Kx~=PY0yf=W}t;CF}DpPa_-g5_?Bq{cH%3XD%xsr6oo2zKkaw1mKTT1RBs8R~S|AB&ig zQNN~pQuK=Ge6aLq1LicVp*%Bj(YT#1?-kdTN#)Wqe>8-0o_whC3KrbQeP!A|iurU! z(!v*J@Q1I0R(3^LQdfqf>?;w0zU@hO?>TD-%z3A8@!We$S_99vfgq;_m54sD5CHWx z8ltmb&N^BNJ1@&v5@m(-$Ps;@lw+sjmVUFy*%2`9oiL~>>XcaC+ShRX{_O!3&Gn%5 zT8BM?Y#V_`YggZpugGz0#tWqtJtpB{uw319CB%^#{wWMwZjY_Zsggbs3e27xi*p4T zGb;4M3^V79K-uxJ{$mpPXE~)BZENj{<8^@gB>CFfu(9cR)5$ZXIC8|mslO1+xR zU!gtTBMR{~SPQ@rx*u*=DyX(%t5%9KTONLlzju5iP+uH3oWd zRDMM?{|~zP8zG52KNyF*SctEK|8a6PhL@cdMl0V~LC6j&@#y1dmItdWVaePN$>(~& zigd*0i#<-f2H`s7D*y7jnDvY+CuFWCt)2BBX$QM85W3>J)@r@eu)dTOuMko`6LG%^|U$k-XH!+{~k!bh#~U$$&gsx|~WTY3)^NQRaHi??Swvdhpq(qe+$ z+L{Z3yl1$Lujud+-#`(9!98_(=d?cSWHDwNmxVhD!k$hTKNiu`0h2|Xm8POko1n-?Xq*ryuuIp)2jJnqwS zY?87zsKL%#GbB;(e7~e+UJaXV?yNFZ-+i|WCmbhXhj}_SMp9$U84Z3@I5GUkM`|f_ zLt3N~vHG0j>t$a54S-0hwVA5NBs{LoQmdzql36!2Nqeb?=|hBW$@_nNwI$ERM(KJ; zev>J=OphH8&b5A!o)h z{*j(U{(T1Hj9Fh1>h>t$AnM>o)?twTRuGpsG$09YKzUf?{8PhZHcdqC3*+#T-n6+k zNEN4iG8n1xm-dXR#x18FODvR#75O&S%kZ6noEV(uOit|QXhpLjEOu@O&NNQ4MvW1^ zi}}-B?SN%4l=#50Vyh)p^}oFlAo5xJNGUJU_vzp6V31;Nve^zY2e!j$ zdf8v^qVQE z4iEj?qjE`R8^c7h$@Py%cI*kO2FjvrT*;|>iu?T`vUHL?#nIR9n?<$6$a;!Blwm3I(VA3OS-$RxEPpV>H)%-HOeB$_gUsWUT~%)+ z{no+_Eo?{x5yKAJ*wEHZ%mRpxy(X1$j>zb!*YzA^p36!a@97}jHY|$*!8c08*7j$W zB@9`EAzqsP+2Pkkp796S$1J-U0t4k4Hgmb6J+z6x7mxy3~*l)wk{ru zdn>T;N<^)H`HY#UFbCIOTE_BEih)L+zUCgutrua|8VXBO(q6FvcQV41?XYri(0_r= zKo(9Ec#$v}@;zr?kwv_zE_YQ(?KeFrw5>DA0Sl@kZ=M}XMh}bCwsi4nzzq3oRnDo1 zS!bAyvMr&>=La!8PuzK=TiE?E`v;UgPE*P&hR34P8cn_!fKmX}&uo4Av6nB%LE7kD ztiuk|iQwxVWh~r~C-%Umk6p4JsNZFwxIS|Yy@Ce$kbAxkQ~wVpg)T}p>?W=v%NRle zIQb0J#Q4+-7fGN3%tEFuV-r3hNU=IZ>*dIEB`>!Oim5M$b&lKisY@O^!hzV1$9uH7 z>nYN-@;_Qrp~EIKuHZ5C0Vqz+)5LMvMEuTMHEhwQz42XlMia`M*K%YxzZ&)HXy>LP zf>%JVto@l*l5@(c%YEI-@OMDk1=&hHbTRT9hZ5EEOq`ix8~{N}(0?6Z_PG4j>om{X zax9B}LpGtt#-AFZ=?9#78A)|bL0jqdAOdc3#E{x(F)t|lv)#FDX|wf2h93sBbz&VE z4v$TsbipLB!OdW;8;1#!&MD)rtCJX35|WLPjFY6jsV94Fk3WulvHPVav-` z7(HGjv-XA6@6fx&(2v;7-+F2ctO4c+glc2j(S3(M3yy!$jJL|Mx?(KUa}XKwzv||X z<@K9ym`iFw#DY6uYJoJrG$vH0yeh^nj|g|x^1K^Mpae%>4&jMLJ?`<`a7@QR1KQH` zh%!=*QT8`vWv2-Rk5bf*zXgw}-mi(()^)!sJ6Q-z4BxzUle&xe@dLlADj1y_vi){& zDqa<{Yb*$! zbcdG%6iPeBW-) zK2_cN{J7cN{lCl}ujgLBh)&P|nbaolv%O(Xp1^K~wcix)4>x~+=?zj+g@Iv0wU zcydy&^q$}Govig@;XT)dbNpk?%)4Tl_rk?vEO=q$rtU@b1!`VI zE|mQ@a}!^@nq%%g^_CQ8-)FD$BJ(HjB6uX63%4-eF?OM@!v*+@cd^!XvCj2E*{rkT z7e=n#Me1E-o{n8ScdZh;Ks@`6?z-f!I;AgC`{HOWu{!gMBiz)#P@J1a>kH#z=FWV@ zn|^UFj0=U4yZlqlnO!{fW2aX1sZcxfZqm@+${ZS_j3XT1yNh8NipjMzO15=uZ71!B zsX0530+O0JM`As9uc7lhHi^vGd7>RH93mMbl!=-t=UE+?HNzlmFQ3zhKk(7Tg>yI@ z7^Tn>g2il2u;D5~9Np{3E+|8ImhB00UcE7;4nv(cQOs~bGf9A*R}S&>J#*Qv+V;AX zvqn*4yDZyv&El9L5iyV5UTOi;IeR5KR3Q)@M#fra84Mj;+yJaw2L`xv-9xEOjDARs zfia=e`I?p>adM9_d?uO^HBUXqQDMC9Pn;<#!}5xT_#z#A7Q!7yD0n~-cz!IsL_-_X`Pl!Y19{)0JCfdLO8R+G0}8658^W|p1jTzoB|<( zq3E_5(_0UUL~5N)CfG5I<4u?esB5oS0M?fqEkuM;jI$GN=1{J+WZ9M!VYU;p4Tev7 zrET#sKpFq`PFfR&y0SOfE)gwp+gNls%#4w4ZQI1vZQC|&eHZ}eV*uKViWpn3TI<$a zdFSR(C}AS3#LfjgfGQ=FX1i>{aXIi(3P=|I(1BOP{o3qAYGY?!RE5W77b7vv#x&Dv zbs8(gVoC-{=eVmo!Q?K05%L2vDr5Q#IltMwD|K zO6@Ug7loYKGdi`xEGd5?bDo8li!->}czt0%lUx{+(J`wpZaK`GE0=lu>;|g}d31P< z5GtPJ2>4|T5NU>#3ZgBJJrUHx2=D7mxatQifmoW8VeT^LaNvH}+P3UI#P%7>m#o>= zY*SCABf(`_9BfTw*%kwfqqZbG>>QJBp(T2Rc{hZ6^u@E$4u@LQw__*XdLy1^s3vY( zP8czkZLID&pz`J>;wMrVJdTQ+Ny!qsLDHMNxIx0VGs4Q60NcZ$H#gPd9AqD(*`MTk zh(&vK_LzF-I?tGi$>pPVYD)uzdtq8e((dbtv&{6O-)dh^pmn){fs&V}^>VKL7Qh4C-x6 zM>XPfRl}|I)halO_z6J3Y<_%XW1e7`)$55+)^YG=mDmlEU|E}U0j<^gl1TQn(00dd zbfgxZI7I{|DOFe{s~xh90M3q!#rb6%BIXQgzVH=OZJ;*ZYO`JvEi0|hB32|=Qih-e z;5;;KFr_soR^>vpSTk!M0WX(|0~$%6I;(a+TSdCM4cSew(FkR04LOH3U${i;T1%EW zYuoBJN?9(Qp%KXrHqe?MIa1xKHdS_Ceg?Zy+sfl~0XvZc4Yy=%sGReIZGZ?W^P&V+ z=7__QloOlk%dV6na1etOfI*yF0;kE;iQ5jr7^Maj_%pal=Hz16e&EKo!WF4#5JkaLQ$La}qDvj3`>K=D~a1x=+g zG}y0lFTu$=7ekW~KVltQcrkYO_fJdq$ahn&y3I}S=B9R%B{h)Kue ztfr6Z$%pD95FDAk6R|=dsjV0?&r(qA9>v@?^Cu@8CqkLZsA=PXRb8~@8Z)2B!_m?y zQ4poGuGrC*YhFwb{W;+$JBpW*-8U)*HmQJ0u zAB)-WVaUcqVn>QI^NM0}6A(r;AYC>-yjqK_xRJ`&v3|Ho%e8RbSV52?qY09&JQ*Zc zS6brHr^0-36SCJFMYF+}NOF^72gBQIRFFZGU~?vl+UqNe%>1ZYLvFO<04$A3EF-cCaYlGbEzycgO-wo!;hii=i`O$s zxnV0u-3-}TkMXfPmLmA{_C=Wx)Xlj)IIEo>m>gKepX;%kv!bQRiH6GF$iYtzR>Awg zbisH3oIx-hNnY_*9rP)+VgOmOGN~?Ww&ff^RqCLfg}0p>V_5=Uw(Vu2vD1S^K=z)x z<-~x5okhJLtrp&#GU4sL)5^7Y?8)$VTZyO|7?D0;fI|w7AjDHEMfI! z%a<(MA{(K@wXHnnRRO%LuOhJOwSu)%bBMglnCIQPNp^_iNn-qwVT#d+2ZJIV^wyuf zh^k;owvjA-v>vMFjE4*@%+%{sMYKM@CYo4jzIg9wJS(qaGDkp7x-@g6R*q;Z$8KxX zk6&cq(zR`j)lI4;=Be`|clE(3+Qc^I15EsQG}IyQdhB=^fnuQhbmK&doH;w8#u~&V+8cSh9U}{gW(TD% zA3A@?qmLq}Cl zoLyEgAO$hP$(pkLh^e2Od?cPAoQ@ByV|wZx5iGcwvq%}@6dWVKluR;ByJJ;r;;}Q! zbd_Z&t*NoNjjC|jngo!-JD&;-s9NTk`I}D?^2!oIy>i7svf1#Hk6Y}r9WUa>vO;fV z2VG(lkE}>|Rba^Wq_wWaCO*;J?>i+JG@B>FQ03YH*m*V1I!{v*vcrWS%eJJ5v|ddz zU!tPrLW{UlhB?H3ud@`Y3+SKLYI1C4#xIaLTA5xU$9aVyxN$DkDSFfN<#A zFzJI?v;IKBHfOJMjoC&6r66WKv2%U1r-%S;qx!7O9g($2)`lIvu(Q&7Llw<<^W7tB zNZ1A>By#L&FNJTnGoq_#4HP1YjmL8e;Z_GL-HXW^*J)HK(*vww8s$ub#lN zZ6XL`LFAR5^I%T|!f76X>Kto7vyKT8T$=&Rj#f3BL&b6k(Po(CvCj3{Lx!N?p-E>< zp8#=JtyQG8?U1H4Ry2_IvW~WZG*0MAHDo7H67KSrt^iLQ$qsg`6;Cv4-kC~kl;y_R z#kR~1(f}tJ!d_}r?U7(`l@)InyUnObUmtkz8OdP8<^s`YeZfp3s`#-f6B-FvbPmLZ zfa&llgV`No<>4)jsiMKN(lDZS0R^JVc1eY(Rhog-9jC|Ndy;lXoN54ygqtvM6ip{+Bv0BwEo|zHLVB=V1~7U?PBty# zsXOwqL6V)Dt$B9mf`YG`XPGol9Tqsw+l=k^{e zBf%WLMO*}6qzwGz9(W~;E8?LBgQYsR7$;&wD&LA(dF8JG9JBF^#$!sk9;&3^Lnh*k zOd2>QkZhv{LdRsa0;J<;wWD3}z@$_vHqQq}lhfBBoE6JaWRHdcfnJt^6_g=sl4X-m zV)6q^=7tsx0`tkX^7Bp(5mb^WgaypF_bNbwO@Lkj{E*9~USS!+iAF3QCw@|Z6lu5+ zrDX$=*h?~RVSOUNmMRggw9dENFK%F@>iVF@21Z*p9`XV@r5&%`3N!E!tFB9$xhU&qz7@ zFeGwpE3H-R;6t%`VAmAmq(bAe4gkr9foWOQuoXL8L}}r0Vr3FDqF|ALNMjhBzfs4Y z8LQ~5z_hcj-ONO@_b?CS!;pD&jWKU%yy6#IDqFJ|O;J0Bl+RxvbeCkrkm`VW#*~x6 z>PL!3GIi@jPvwYqMq*|tyat@v8$VR-R17@W*kFxfjdmD8zkO25000mGNklBP4c8OC&B92T-iewZTPx5RCsrmkw-~z=V|u4VpabMU*;LJ# z0j&Q+6cS_4nR;*84nk6V8xD0UAD#3P?2cgY_CN*(2cMA~yQ3@b04|Paz%^Pl8w(lp zRd8{+>xi-dM%#>1RN@hC391a1fi#VjHaZSpR&tca9wFlg+e+sNo_ItqQmdGdyP671 zx>h^q077(F;PibRwh7XSGfK-BXB48?#nhPOWfZS3`3iK%d4lX*e4pf5vR%SbMI}VC zW}9`_Sxj_Sw&BitlWT(^MMCFiv;+4=ZrH*y42jU1PHDEashd#|RCoAgz_E`Ju3FV; z#Ms_zNSX`NoQH$J+`F+NC_zFmbvPbn4@4c zy`b{vKx$hA>8dg@3bpd6#h0J-q2ll~lPr~}TL(gFpF}$x$wni#v30(UoWdx~&P^^^ zX<&(!q%1zL%~_$VV@KPT?U*Imf4t~aFK`ZpQ8R8hEK|8A6bRHpU{dM{n4AP^1vN!dBzsf z3(BTN17%bnK)@u)^;id2Qx%J$;vz1IKp%|DR#XP14J^wx&QM-4?AQWR?>#gOJ(1DqyRL)EVh7?h?q_t7wK7?O& zFzQusrW6J%+Fp|+7+L<(!B_z*?{cK=-EL0m>>vhI#_q25x||-%q+n~7#<877EZY^d zT{a5e5|wa~oqf z)@w^n6&ejD*&6AE^Db7D4MjGR2OtrJC}0K(auq~th252W2UvqO`kf9 zHJv^826$i{!E+Y4EG1r95(=tRgZ4(sw3$GW>7J-m=``koV>fS~2kuzk)?jmS=*cQ4 z8Tf5yv^zi51|L^)T^g6{b<1}Cq^jFE-p~cWq3;_VYd2lLC@Ef?@`jF~QHVh7MzbNi zZOxXNh7-3##*AdzA=}Cmq2?K)4KRUWR?jvuF58%7`bZI_g+<4r26@=F+a|@fL{~MJ8K_;OBB=D3GuXp0hb+WqQwJ5 zGX{~$$Pmft4H4F9v)6ToLzgl{MiR6)+_KSzvu=0H6c}j26l0X_sPhGYSa;)$15Rx~ z1n`T@LBt#k9h2f$3>#1*p%Tp&lxo#goS1`XM=*E= z7=+^YI)X*2_J}p5?C_?K3KYSt8Ws=cvG?O+RWKX|bEha~_Cf$lf{tk&c%{M%NesyB zq_fC!DxIP73+W-0Wm!B@6rEmpd}ARSOS3U>SFd^$DP19k^ze|}yy33vJ19&TDC0n< zN5f=&@glo9*k7E`5tG06wC^?PBrzT1bg(#_jL=d$YQ{>LGBJpS=%%K839b!?Se{V0 zQr$#YNkhq=SyKhu1#FfUGYOYaXXdcl(!^CQc=O6VKg@)1jsX@}cm`7|{wOWhc8-hR zd2Yqn(7=jhnb9ql!6PURA>)$l3-)qLhZ%{Jd1FV%UtDuB;|EV%bBjY+7?JtsgHA#5=yEoGwnOk#-;amJ+R zv=w;}$#o(C?U)xFjHrqiTbPg}na9v<#OaDiRyRIMU^3FVfW-yaxxjXzSdwi-@7IoeHlGRHlp`L$3MYz#&6r+?#KvOJ zAR8->;e^Nfl)4jQDe`m=zu>7<+M!?*f4ItmNd7v{H?goQ^|0>9%*z z;VsWGm=i44l7ewktL!1!Wo4HoSwkVC%GNJ?8ewkj|2@LP))+(QDY2556uq+u%m ztv6=XJNX_bO9@O0WX5C1pAl^n1!{{G)&aM7GG;<*uEdH!M%%54%Jl%G+nZ(xO(YjC zIFeqaYLF}%WSe>FxGdX%^l;2T*MvZI;CpIz9aDuTE?Q0nCBT9ha-plNL<#^JBV$R* z_H<>D88~Z+Zi%;-21*s%a7|GJ>|BQ&bilwAN~f(b7FBB_5H3o^D~A~&F-2HPvn!zD z=-6y*uX;8XJwH_gOYubmvumIhemZVc193*t zM}v%5Ltv~tW;P0%j?1BvxiD%E>{Lm-#Uc|@do{u-?HcC*D+R%#*t{sGIVjEI;aG}Q zH3yc`1_P3l@WO!7vnGKxZdIc=^fC!iYY3?P)F?x6Cx|s62bBsRO6&S%Wl+a7+rT9U z{s@AOs)KV`*(z>D+n(3IJ^};VO|(4Md0b&tnCDqE?E1?|$3=0l^+OBPDuzy{mQ4&h z+z95mUemxk2VU+_BdeygvOdk$^@eQAyec#DHc}(>QLGy6veyuA$UN9bcK-uAchTr|L;hdNT4PJ?&{v(=(p_ zGy3_T`#C-P**{Klx+*(I5ShUi9DpyI%Oh-$q`jKm3C~&|kgzuWAvs zf_ZHQr(&}(I>EiiO{@XSZea)Wrb!iXwgQ654L|@VE-_=0!$>Y8@th|S96CPZ-s9Ry z3S>DdV01~(ks8xQ6^m(CRm(QGl(8;b?N1VT{EfF|SzJ*VVy{Ko?tFN`Wejq2@^(1^a_Xhs1P+C#^IX@$ zS{q)r7>SKbKYFHuLDGF<jwS)@Be;1^O?`oul?Gu={J7k*Y)ea{_A?d z3!blE<|cpkv!Bfk{cJt`=}*^_pZsL~*pK~~9{ZTb=+TdUwEpAweV@MjyT4oC_U+%U zfB7%JRp0V0->h%^#($1{1K79e`-ng02Y*nHd)(vnpMLy5>8GCfQ+nc)o}?dn!V~mw z$$RmOUz|luvGQOzo(Jo+J>yZ<`YMg%k%0zc$BKd6QV$uibG?rQd*%^408fv%cdQgH zY-mj0gh2P(3tP309#qZc9N7VxB5De;>O*$36Yw1DvS!SDq?Bq#+uR%@v=ZB;_n~HK zPTg=iayiwa6+U?dEY4-M@>Y`SRzI3c6>_TvI7ibk(=4okIr3_3Tgp>1{F7WdJi zTKg8i0%68M;@bdp+#*IN&z@ibl%cRaF*JQfT2(F0c%Z?-Msrg}MT$eB;h{20Wmw&Y zmKA?cBf7497F7lh|^TZ^? zX;2R*SOhb@=E8uYWY)?nP6j%0!eR+g@_Yoh4HiM2_8r=dV>AgsYA;l}=mp zE~_)$Dy8Ynr}lMb)q47iLTqUvx)(Zy&))XMs7E6qj^EVMms-1O$fS~5t7bMhp! z@rS^rzxazcmS%Bi%aZa6tvs>d&~?Ze5V1|vvJsh-L{Xq^xpKF&O}0y96(*X^?Zv)R z?|@%tM`Avc+ZG*xqjjQ?u+s^sM3OgEJ5Z}))UcCp@lAl_bOuQWUj0N26DmY{{G<7f{{9H|IiZz+-CVKRa~UE{+{N zu%l7*c=?=N2bNrbUI(>=12um3L@6@5=X=Yxj$?Sxp=BhWdN}ne{w_Xh#0@*OipzSg zzr*#q+gQ-0F?wc+-Sqg>9q%tOJTLnQaxcCRYRwRNa zB&e!Uig$^3?Rl9$0cThehZ@Um)6@ZROXVeiAC*^_`GOcFDPupDp|Q`J0ePNn<|Hs0c&-aJk;GRSgPuWm`ea-Lx! z;p3j~!GnwpqQG33(g+rA*$#l-5SJuW2TFu(vscD_fvJN}46DW*=z&+gmWGc2r3Ya@r0EJ^Du&#pMrz3+W*-Twg((EaXv zKRw|7575Uw@ZERE5xE}Vfhw0-#_HT=Z000mGNkl%QPBG)%r-#1ns+CwL%uSqr~d5tE751u?qlTyT7QCLbw* zOKf5YTTvIH?8==tWDW_I?2MsVd2pSSMn>(yaZbc;q^6xU2Oo}8?}S#hibKJyBX&3? zM0YOhQQnCtYc7n0A*rURK~h61x9p+YUw^&w%A6a!-mWE?$HtY*9JT}KY&(-T%eD^u zAes1WZgU%b&Pqv6DxYsv}yqeydyEN&wUy%A888jmD_sQ^h%hijxGhFwu;Vm~o1s8wggoNCGT9 zY8J#~4x`csXE*XLGP%K|L?cNFIW?tvF@mo&_{@Se@`ft$s^H2SVCTDSDI#de@~z^1sYyix#>I7x*EiGVTjTDxu8CV-%Aba|~)7k74Y z%d*yuP;LflMzy#VqrEYn@7b_Y%Hdh|5@ENfnrqA&BI2+D*x+lvr4vBHyW<`2h((82 za|u4~#tn>OY9{Xw)F&c<~>J>VM^MsLk z#G5jv<{~&IzFk(&6O0Ty^C}kjfpz9ndh;|JsneQ@aIFuw=B5T_^~RGC;obp-UiEE@ z4$^otZ~^NKjZMdud0JbZ(9l`TH!;sWn7L#m{{U`QMM)e(6QNO!ysIA zn;;>(^}a;Hz|~i1LC$3!EFB4fbw$ZuHE^dLmBTqqE)hZ0X|r5zcx9Xp38e@J!Hiri zRzY8sRW`IPV};%Wy9oh4s74361_jK`WwRSNgI6a4ppY4J!>#qy#Rf}<4h>EW4OLxq zV%a05WCv}Li41Yxmn3lE+jCCv4f|?7a%8Tt_Ndw5h9A$yDT&ttqMn+QVd8|bWV>Vr zPslM>j3_xlA~k;%V^PW9EYU?vj7?*aGe54B!(d1YDjwL*rBv1vC=xqD71fzKC|=eP zi5rYO%El`>a4v!CrC?1GprUigm-F%wUG=132wj)AOcWw{b_fwj!BF<}u}^!_YB7MO zzHO}Tv3iq|9Z$!G&vOo+3VR5H4%j_*cTVdlk59@y0+up|cgDE6f zU@=3r!Q#|~rVh3pEEhqtt=z-{vaM~iQ0hjlJ&*bXY%^EqhSQ9W4FJN7-Pd(%kDSzk z#0=EHkON92*+yB%)^@hCV@WB8Su4<-KuZc%@KGNhT()G#vZ{@Y)5fHlbaa(glo%&s zH_2|DC+{%~M%RofHbv;!wF>50v+kjJ04u!@RZ3Pwbug7gg&-)vSSWf9*KR+XjvuK+ z;;T2-Ub9c^0+^d6p7NS;d8I~^lAEMGv0{+E!*y)IM#reV@SbDgjW*VHMw69aYFxn& zfrr#(k9JTeOmu4V<4876{nW56dY!9bCX@?g#}hFtukeyv4^^`b!d?fj)*$)JK8AJ0 zl~-vaVjDEwvK^x&mTj=OLs%GSFskXmyUSs4YYjYvQ6ffHn(e5p9x1ce-ms!9&dyr<)C1J(;H-r9U+2uz9%P$8EQ`S>hU94PYM^8 zy%e@c6-?#KVAR48pMCOzYGdEMyVv^Qc`UwswKuGu(t8#OW&>j9%i*{~A;iIQy@3#ABV zCM=bKG?dkkgoeT!pc4C0W0+XoE2jwxzUJcHU#kTRCAEUZEh!}yIQwt*=(wx0kku2N9=u_AFfyVTS$UKfZ9^xQUo{;u*3W^zuPAFwUjgDVje48O4!uzCTUZ@kKrf`&&i{7VoEZdhyiz1ssmBgl{ z_JO7spd__*+Q_l-R-EI8M?aZbYC$F2EL7F7N@uW_w>0RfnRtY`1b)Wn=fTCTc_uYs+d9-mh7_EOq9l+P?#B5= z+r;Ujart0awprWUcWh!Hh1*tgMN2-4votXwtwpn)QRacM_neO%)D!bLhV6+n|87qR zWpxuz_Y-l4EA!Q4cn6Jj8t2bh}Mt@YPD6-96eEbylHD3hv0P(3mdsjgD=qUf9mtR!EJ9=; z)pvu$w$IMaDq_1{S#+*hF5j|UIhmuNPOmfCF~O-ILz(P})!-?i<5?$}7zYqqyo%$b z7D@`RawYZj#S{xsbK4>dmA+uZk5TOo)KY%3{WbF>{GmAHWrzIyEa$ z1JRvtA7ckzEtAD;#j9b)rz$A(W!+grfKiY}dyPl=2@^rH*E^ z&03|@JaBffb!=m&dgBZ=sun7pn(b!|wu_rR9{8r^SR)p(%81=!xeOakkLwJV^RgN6 zsMJo<$bghi06Uu)#T)TzEx1+9AU0!nupt|VT#H9+w9s_e={Xl>9F#&D#~?d)zQG8q zBrxDq1<8`_5>nE2dCQ5O$BY|62~EDDSw?FXEL!~x?WvWp>xD`ucmed&( za!F_wrqTu%Jz1p5=1rsOCUO)Tez~K@HH12P;QWs<+cKmG3L7gtoZ_Qbhq~vc!WLi! zSyVoHZ5#PugMA(HWTDnTfNJTsdyW7pLf)lI2LT5`j7Zy-!@vwD`RJ-+aWkLrNnE&O zz4yKE6D3d9Nup@vnK%~V#*)V};Z_P!6ON$hlO?7=jyR!8up8@fvQc$4(czWNjNK=g z0J(`}YbjQ1*~7~~Q+$X4u{jfRlZRs6BZ~Ca8Ev>2T@M4dNsMuc+ZM0Tl6@LobtVQ` zG&{a?Z>rlKkP>3ZWYdxGsZ^tAMPc{J3a`oqPau*LK%d44?|tM7#Y=(c2t`PVMdeca zKxnsPtiy^Oi9a$Yj!#w@d(7n6B9@OFmo8n>s2f?z4Q_X|C+=4s+lqf+ttoArY^euk z1RV9`m4am}5%XlRo0~P|3DX|s$Pw+Po8BeC!<21Ah#{m`6`eLH(`8;372k#r+6YGV zK^>_h=y2woL$vs)%wk)11OPpTF}mBQHrR5(ZJZQ~t`I&tdre(T9XJ=e@%XlTE;%kc z(6qES7h(x+YDKnfI+kZ`3kZ@VYqn+NC?=$SRxK~dc6eDiUa4@n*a!S0v8+s929wz2 zMGAlqR_3_@PW-82R@IZd#zZzXYJTiJE)mdb=3>}gZ0#y;*@KaMM(9IVY-h4Jc|$W}#?JoZ1khn0um*ao3@2#9=eMk%qf)eSpFVi9JC()sm&eM^}{LJ`|5 zV(a}{6(~v#q;#Rni9uNv@X)rEhb?7oI@g8{yrR`p5WM1Z!MMlX)0J;qc?f-Ok>v+D zlIt)li{S`MR<#`>_mqn(+a*_C#jl;&!&+8&gEb!joGS35A_Hv9&Dd5R!H?o;Eqt!SNZ- z^%LljS1?wveu5?yD%n-sDjyf{T(^mxAfe}#Mh1+7I_+6^Bu~hE9_ELI3foEOL(DYs zDsT%msy0D;vQVi>c7}=q>WN^NNx|iN}NwT^{&xDZ!tiY%kRE{uLs9c3+ zCxL0G#n=Jp1$PKuR+NyjVNuB39V{l`E~K?hybUS5tZ{H%lx9omXq&o~!~hl^)vMcy zu~up^uCEqUEk(=7Kokxei&H|i(iw(}6fsCO`Qg zNe{w{uGXaSMYAp2VxXgyX4S`xeIDlE;^m2@)3)dfW4;47oRu|_gTIF`%B{I@=-FFi zB4g#K+Wx`;KfIh2rXbP>Vs#s(Duxwq1XFf%N?`_%5D=~k=MK&q;zuVhVmYclCX^8} zVzW+we9X!j6)TA>W3czCPfaLUa;XfYaRW-;c-D|)Gt?7KH1pa)0W2{uo~l{piRQA5 zMOo&FVOyJRLfTu_;^{4|$w*AAkr&@Svf^{uB(R`sNwsnXNhmskoy~{_pOKaA8Kex^ z9M-5v*|z(}5^dr&AFiF65u9|)KFpLI9@`V5`q3S^PQ;wO1&g&E*P ziVt@|;m*2jaMXg<@>BsB=)=4*k~h|dc!mw;fDyPP&-P&DFvNz!QYLH8-7511M;c%a zV%j*CIaneAD&Oj6bFe8|s|6#_JPA`(gfVNm>dGszTo*BWxS4M~UjO>n>kV&sy`tav z#y4WWL2rEH8}z0(y-9!hmw$=<7ZrQm>t3fDZ@jT2cI40h{LfYTb+0X*zsmHQKY5K_ z{pwfiJ$yx%{W4*97L^;?cKJ3M$*W=kS%2+gdM9=xYa}lw^>Si!lj{HOoxluBZLBhO ze9lU=n5djhY8>+Yc;ZF2Z0o@FoRm_MvqBx)0KtRH1;L;jsT$~x)>!AT<2`E_5!qf5eDh3bQ5p*7f+=c}thY(}^>Ii}43J`l;Wt!i*`bCoUEqvo{AwvWqM zwXy}xmNGVEMFO_A4bmc>?G@Qe9f*Ojn5v1nckh;L9ArZTw`30w-S&33YY$o(>?Jqu zD_;I`z4T=-(@S6a(xd#*AJw1rdil#=rk4?W#VcN}$SYs*3iK=LPlUbt)qkv4z3P>E z1*l_(qs+ZdLrAbP4)jJW?tJ-Jh8IKGKsrndZlbj^dn2O0hF6-NQo;A zzg)|>wnfJ}_C7aAKt|Fn_oXohJ%&UQ2>0iZd8CIBn~*jJ+gfeJz_-Eb{<7>TiE#mn z6B|6L#jR>=hV9M;V>OH?-P6t-_U=uzRc(+98ZS@A_?F{l56E#MoZ6&jt<+t_+{{)E zG<;;eVq*d)RkoYH#?1SE(5P31!68%<+|W?u=WD4|FbEPC6?(6otRzH&Bi>A|vAXAf zY+XyS&3xh_Z8{`pilL_7CH#CGIn=w6sI!#-a~AQ731%d|>VL33+cx6tg1o`OrdElh zd)@0^GCICuv4*zS+Cjnaly)HB;niMaIWSuuvats)>c~G3!()-Ft`tHCxk+#YiE9 zN1~06H+AZp8#Zq5nIQQ}K-nhs$itK%*_PG#y*~P0y6(DbdGY`>%5@pC!w3csmAZB? zi>bPL`I3Nb2GMFB<~v~12aq=tK2||L`B`nrp798{2Nqfm*4MSH#>Df$ffo+*pH}hfA=~b`VLW+qO5^ zE|IgJp#!hdXJ==+;e&6`7k$wet7#}CAI0-B$o}F=-9v z&tNcY8=qe8OpI$YBJ_k1&nV_e-{*i>b}s{lks&)eBIS=TKISox(bsZ|k> zU-6IhRbTZL`o?ekMm_eikI^sx@-Oj&+spMAZ+L_L_uJm4Yi@gu9{k`3>*1gCx%z_7 z{{mjMzffPyE9e)0(IfSxU-G5;vM+m-zVa)-O8?{^|6_gK*L}VI+1LLwYW;J4%m4k& z`qqE>t@^fa`&at*Z~xc&*Z=z8=sUmjyYz4W-M=gSZQu6o`qpp#R^(slo4@&+^(g9m z>Zg9HKJWvtWzDRWp1UYZ-(m&rKYO<9ZKHay)K*0?V_p z=pA7k$n0j0gyWK>Ee2BO8O=>#!$QV<6~-fS?4IV*ae3p3BD+)%tf>t~)?jllA;~)* zBG?p0db=3(B3hfBG$|Wr56Ybz#2N6q55*rz0^^{@(#bdtys1S6>?i%q*@NLU+;M!R9xT{t!(A(escKyhY{D}U|cYKHb z@DKk`?|8>M_3n!ok$3Cjg^POgo8PQI_=7*t&;IPQ^gZA6z53>F`4;`tulc8X)T187 ztMixWQD6S$`X^uWPxKAn@D0SinL7VQ-}`;vs~>voWA*sQKVCon6F;t>{^_Ub>CbqE z^5<}$j(#S!p7E?_=^4*>x}J&utY+3#6Q8JOJ@c9Rg{mI5K7^cbSvgP7i|HWTwU%QcWO07N4>Hs(Wn$Yw7lsX3KPWcT zBXAqX(aaGqAyQ&WC$&T%>*8g+)|pU5Ck=|i(`q3AMs<0dDl!Rqn~(3)thw>bvL!Rz zscoK71U3UkQ0b`=E#idBTm#32xweF+19VTJRcGRREf{-<0}mdNCJGiiH~mD^tv!dw zdVcFSe^W1d(Tj8ipHpp6=Qap)DEp6f;5*io+lXJu_s~~gb+xXz@(S5j{s#2B-uW(W z{I}>$|MP#=_sy^3JLEt7|Nc4n`{qFDScYf!0;QxxA{R_|5^U28{I?rGB&v)8?{r11s zd*1UN<>OfXL!~8ImpAK1zUzMGGoGm*{m~!QyWjn8?gtJoi_bO^T zI(dwQW)Fi9`8j7BE|i>6Bv^>W0*}25T*)LemaVcGs}m`m=@PaVV%hGPxVO&LwK}vk zG}gvgXTwooo=Xr3xz8z-nI!yWTWN>fEisU!My&a5fsV~?ejz7j`6Ss5+L;fa0_=Ic zFU4<4toIYUgEo3@O6s*1q|?^**TMh4|LwoYCXb$ra$wGZn6>1Gv0N-~XR=eT>l}DT z$}cmTd#P?3!lFbSW^`(0rp&VuH1*~&b`i$JQS!Fwj3-p=1M5Ff-?lP`XsLygz>;HU z2K8LjJtT)UXFaBt?fS*ct6udgRf7(tOcpEih;!VL;}Xhh%T%wmucd7=T!GZjN1MzA zs9dp#J>#tsDt?BXXB8y2? zjhRGyg8s(FTFFnr$YnGSDini5J0BakYmudULQvJh%9w-E;iIa@H{+*gwMG5igUwmRfIk8Lekd|uO62qj&}|nF^ZmMQ5zK;>HiREziXETT zjtQV_uv!Bk_Z1MwIAnw4L?yhDCV|C?n2n1zN-|1y=$OR?*S_pHBu61o7$v*0)LWmc z0yQd{wQcR?qN$pXd8u#7JVavZTE`Y&0CYtQj%Kq9t<7eBm1|T>XlBSSK8|w_<;G3j zdX}GUx{{9;dG*X&`?gL0iZfk-#J9~JHYt_O2xmLn3bVKT{069Mwq?6*M%&60VzYG? z?q+S+Hn>!;+GpDtb6rvPwryqSUi zT9$}2(6J@l*4kT6gBtVZk+AEgr&8vPXf@WbOtiy2ji1=|tOMo^GTkK^F=n?ddfjTa z8;!=a+&=leC)f20dGoAV-0)a5a}gVgJ4lU=Q#yXgXwIm3#m8Uqo&+F01)`CoIS&QQ z?kteH34y4|2ss!a&PfN)ya(2Kf|gmKxfo}`A(o#=%1#ZI|29I*?aGwAZTg@)+(8d{ z@I&->9{hLsCgUN>-+Ip9ZvI5Rdi(f~{{(#kvh;%<_#oZ?{twW{e9Xs`xhR$vr--{Cj-ZJ@kJN3+G<A9Fw5{{i=}zaRavAN#R--~&HSAJ5l+`3f-pecdOP000mGNklOAC?ZxH)m)>@$8JF z?5u4Ni)MDL*+W6L&9Ysf?XpqjuOa#8?SZ?{A>*YR%P}sO1x6#b&oSd=9edi04|HL! zu?Mi*-S)Pe=s~#z4q@O^V`@Z9kE z4*FjAy0=>MT{Jg*c=v#Rk9&N$a+819huyvYj&%NR^qp?FlkRk8F^AIvXX?*5@4s(XIaJ$3cfSF?{X_k|iKHzhD#*;{T3`*;ZUvX>5XC}&}J4ZF2C zY0+$a^!~1kEwHHE7r8%d>P@k+D|j*h(RaYc+mIl!qfca;nB5$^b>^K`vzgqswgj?S zt(JZ5%r@XDRCacom~#3CTHbQL$k8Nbrb1=MV38_#dnX`_RU4AQn)wk5jI+%xKCV(cNf zGQ}X=jvdVnQkoz=bJ?slAvBPLO)o7lrq|vGv>$`|1nk(^yX34(_>ye&2xlnoK7CCG zIM|qRb+cJR7j9sU@RAp?!x8z^%|#V;N`oJ8=+L>3rvsmkKgf|P>lWTcQ({Q6!$9J& z7Td~0m$j@V(B+zmDCLCC37?_YLcuAV`dH_>ZP_mD7{@Pc%-G;ZlEEZ{fOrz6 zft>)Kbnufno@^sj2M^IUDuSBb#-~r0z4U$q+1wa2w^7Y{I@h3q!zDX`T5F6Gv7Cj- zMuX>uM$yS*j-V*cYR)v3od~pZogiH&p?%J}cZarjZ5co7++r9vjtGEP(rNMB02L=#KOTO$G#0XU;)x`+Zz}VL# zeZU7?qr)Yh3}E3MSW{SbYHbRZ%q5A9aF7*?Z{s0lZ`dSwt|u6DkgRD`O}j5%&NM&7 zRgg$)iLg$a?Iah}^>SqfnBWqVw4GU*(>C6yA80Ke7NgXIW@G-kL(sK2Y7wzd$YnBA zTuN#5xU~ru_U{7 zor}6{twgZRP{OkesA`*u1;mRfVsoz&wq{FVvZTeGI(}l2dO~QnCbOr(rUu^(1h7ol z8CSjrNV*xYquT1+d4LpYfAVv<(}Yd$#bZeMkBnmU-lSZ#tWVXF$vkCqQP`~Qtf-Jg z%V0=O#Mn%pUQd*Ey9Vj9sdKUz`^4u-6@G5=qEwod$EX+<{BUfeEZe0SYQDj{&R`<5 z$4D-G{2}HUVNHQki>!$(+Z{OJgi=rN8H%zj%_pj3SC(rOHRxS7$b?hJa*!6xb^TMn z5}3En6LQwuL_iGa+Gf6|-k1m~QLgqf@K)}IJ$o046P|r?5GI)e+ha#6YdT19p~xFK z>eh>FJ7Xra6JN>6pe~cy(++VSboeprB7-MuaB=N$ohd=PXWnq*g%`d2W!uhV(9WwT4mqvF0N=vru>{KUpa?Un?Lr*_yjDoer!;aFCpuE_EE)SKfT@MhfqN9N z%>YvU)CRCSmNUpYY4h@jJOg`)({abw8ZRKA{;$r9{i?|4JE48#x$&Lv-TDf zfi3cs=A$9o;xmKN*2Gz8{3V4`-tvOZYhv`qVz91yg#y7;rof~RnMbgynOg{_ZNhY1 z4kjzGpz4?lHX;l{Mw0V;(T80^)Uf~VP#-M&BGGq_X z_MY(WV2nJ0qS>QFuFkh@B~+Z9O^tO6Y&Dko_5SU^T&o;Q+XlXhzV~Ux#JT`V2w@O94!xcfcB)BsbWkdAHpvk!ORHIfR$S@2-unWlB~nxyccYsE-8cBZ>@ zbN@$DL*DWHJ1&Yp7+G7Hw@}2kY)i7tlg^%dRkwi;QqBm+y`KPCmoZW{=QVJG0afP? zDSi0>4f>d{c~f6@Lm=ERZSP|yaeFLDk@n70L)%ugo^{l=4K%kAhTOTREVvd5r$OmZ z9KnJhDH}u09xMW0ydk@NO*MEG3wcyLRtE+@T(vj-fro@C;DQf{8i9XT8>bzLa&Re3c?nK&k~Bvu?FAIlQ5E#og7lk_3rm@*G7vHD|yXin)N zO`>h(bF`f71a!3#LNFp@F0q5<@lzixnPVOi#5?w(o#jUJ(N@fnb9ER9q)?j%mI=DjYL3TN8E`d?L?RgWaZwM&f_@;pQ zZSNcm;i+nmQcBA@Gq;#KwqTK}BO@jWsCC0(R@gZ|;}K5oQ4WB!CqB*GU^#I!$ANc{ zo+rT}yVsmw5T#B{z+z1}f|89Dsl)-~#;_`fm~FfWiu{b3v+FTdZsHNiJhJ~(i>KKZ zE%B5IXNGo_Sn;T_gE0UTi<#WgW&~$s5sW?I3?~=?r`y=byrBybKE^S(e0T*iYydfg zf@X2a^$4B~?67F2)&vF@-!O|e7nx9e%0iZGtJ~DY%MwS$MhA~$fJue05vEgOZ{OGJF8lhO_GTDZk^I;WDSb>1V!P$HnQVuoz^OEf~lKISZZg(H6<_Tz1b4Cqc zC92k|kZd!E76g72Ub^L|T~0z^9ZP!63$nURjJq(~F*PFLM8Y{=N3yN7jNy#j;M|af z0n!!W8gpEioddapiGrFNu55g}GLk|W?D*g*WLx!>n^g5sz|A34cIpUcu$G%|<{J%2 zl@8{*$w&R;o;?9ON)S%EO)ZS%#>3Uh4x1}QL}14%s#ZKWaRzfo#Elz2 zvG`QvCJ7=cNO=_tCfc^KNwh>|GggfPWV>z(MqwM9e=j zlx~C&kXZwkw1c5lptwq-4oIvi=e+_s3sVGYxtObR`v7)k>s;s)Z$+pL#z8QwT(j6I zRGQf8w$;B_AzEaT#3V{9Tjga@x~!2~Nb+13LmZUIYLf_Ex6mBUnuB743wJqf5WoC~ z45-kCY@=7lpQh&p%*F#nTaKw#qjX-~uw_n1pw$OAg-OCXr6fg;36g~}~HU;WJ)q!0X?-gQ%vl7ms*Hrld{o*gqaElY(PWl0EKvw5=; zj!cMkF!x-X1J;6Kv{*c5ULzT~PJ0mtU=OV_W-UwNM{IJK2L+coA>`anV+7)s^!*5F zmM0aQ#T_pQ5iXEeU}GVJ)eMxv)h)F3b)F6PBwqAj6SgtvJ7wA6H6LqPvGSKwx~j3} zqqaE!YAmj8(MytITlv^Tvu@^4$*Z*_r`R;3(^&51l8W1_)ltT~G{KibqXD>f=8 z91`iH%;SJhA|o#b7*wiQfSfah4&0PkeDMXJw7XAkF#v~Z4YdCnIDBy-NvDpGY8ANwal3fo*3)KI_)+%q?`{69IMgvnF9f<6LUQZC;JJ!B}7-5yz)S-u{yt z=|crfr&jW8Mr80?(R{SZ8WJAdLCG=2ECY>V9C&i}+wG`wc~TM-L43$KwsrIT?>mCb zrKF-5kf0FTQI%&fqP?+e_H*LyW*L+?^3QW^7iF7T)IHiHrY=f6c{XW zvK8hy}GQe90Ml47@>&sxML(W1g*oO)%ej$IXUNKcWF^LCaQOpr*}rfGj3X0&5-npzQ1q-HRTux#&AbW`8qgP>Ur-Q)Vo?&Ej?Fuk zF(c5SWCTbuN(q?P2K>s4U$|wr$Q%LjnrrEB5EC@IVrV;F8GIaKF8G@^gug z;IT$V3GM-#(yFzvC$bpTjfL2j@lwaeihvkNjXEDgpjYUajg@GG;8e*`a+4E`7ogM@ z$y?nA351cL6kM}qoiPV%j*gI~QGv-1xMb?=DQ5B5Q*0Jj%I^8fVF-62wv|V)P>oH$+c413^`7^Md{9JKpgQz3+YRQx=>*rx3|U5OecH&bBjc zX9)TX9qwD-`W9vRq8+ja^x!5+E*p$DC3}oH7;f6XNzMD0AmCUpe5>qrg~^gS;I#kSCEmiHas(I^34d_c_!y zLDyS$_7nl24^>EsXT7WpAfLewyej-TH}orC@e1|7kMeT#%k{^9{KqBvV~qK`aj$&k zEA`4(y-KfmWq6V1)xViSES%T8<~8-FdXxL=KYq0;m)I*_@k+f4{qmQ;TrYd+%kp9PPj(+~5ge_2VrKUM2JpF8HlPlx8`Bh5m`GCGvQQqFq0H3erD zTJY!+5^Y!rr<>P`f>jApKB8tV5p%6Yi;Yq!xwkYMr4&pw%CQCv$TeRD<0JIAo6W=! z4!K(aPiBud-B2YPE}dP`8&mnrLyZb&&`Aqz3Gm<R3%qn;;k{SvInskkKD zO5tm|D7^m>WdcY$a%1!vu)FrS6x|uQ{(EZudc7&uHfspyc*{MUwP#fD*uWr zw4I$PZIE|73d@dcmCGu1+KTH_B=!+l{Ng2aPQqi=>6oZvlsV(rTo=}pIwi4@#E#OL zmQ@|K)?X(4rFGJP%Nu;`NuHR?X1>#87|Gqx8U&@dxdm9x=!r8n%17%c!|n^C7Q<=mO_(;K>j(xd)zX$+-IJD|O8^9{?N=MK`NN%`p~|TC5YR zy6s+QdlG)lt+bHQQ0N$|$73JVv3QWfD4Q`NSIt$&#t(+JryW1#;!ZpKNZ=Ochrh6B zT-cQ)8x19R-O%wPNw&Qc8~_Q?#$(}iMW>l76(|}Si>E3^_;q1d063V)D=7{G-F5R| z(5n_Yw3AfDul$NpIYeq&wfucvRSO$~I04y~aeDwPXXw)&_Aou*0r#)J51rbBnEW=X#l#mLoyII7>RUnqISS>e7@o41|P&kPHeIi8O%}N zxZt_YXDD%IRBqgLHlF)G;}zS^x(7G&HM-%3J1TV;N+oaEr86+UBC(Oo8Q&S0AiixP z=}RANmWY$pM-q<)I@iGn*$m)a6O828wK(Ro6E}NA6E3&bp=!{f4h6TY{7_n00MX)D z;6_9{AeVQa0;8Q(pgmDLUEP{v5StMZb`~BpqLa4!eEw!nvVDxkoW9M4B4Pthu^SVR zdQdf%kon54Se=6e-v)>%*w7uWzrOw%ivR1MeS;qOzz6EuYp>I7ZgXqh?sm7U*bR5Q zL3h049ZT}}X;`r6vaNcclVO`+1eLx_^J+;# zCJWqR*-Uh97v_#i)H2E=V-m@Pht(G&25}t5S%WxEb;_1bF;^ul1I@gt6rPtuekjgb zq2A^;x6uP1^gw;nKmVrs=P7>jCx1#m`XfK8AO7LT>oGt082!Kx{6OjYCno;WkJn$v z|M8#riTbA|p7yk-=_yZns-mCv)Bjn|dginAb3gZUCC_^1GxgM`K9%@W^b&Ex)0IfK9i_C!Pjd-YU@g+|5O6w+ zgp8X7q{@Id_~g0M0ByWfM%fIp$>BI_v@gSO6HDchX9-3x+*Bii&)B(*$n3JC1)m$L z28a>adgnXeso(kS-`3Ck%+KgaPkNG`{FEox&GnS0JVigxw+Fxat1r+Cf9HjI$xB|s zHw`bx5Ui_jL=|%tj zMan-n@ms(3o60v0zxsj~=y}ikmHKuf|HjF${_3ylH;Mh%-}|rneZIYT>C0YLb@R_w zy!CBwV{Px^Rr_Xb{CYZnj_lfNuhsR}-$8f2)17qZyWFMzYH9vd?)~m}KYiTCJx~w$ z*pJmm-|Jrb&=2`g-SEM8)E(|{eZ7O+;SP7ezoGt8>Ai_xcinY*1>es6Hv7&Rp8MSA z>bbxC%k@uOJomZ3f_^URe4bwT!WZhzZ+$DrRHuc`p>cwox!qWhh?Peyf>DSO;EeMU z4cS&b&=aiqejAj>K)mjwu(1tpa4HWoQHg_Gw@BH6R1BpXnx#a<$rn-zjqvlDA?pZ_49MnmS& zk>luT+Sc3O{&s!u_kORQ^yDYkzh#mO_+S3Zf6;IL=5OkST;g|NZ}u9{uP?*Qa zM`Qoc;~uNWJ?@9~xF7!E%6;q)J+{{O_^j>mkJpd=*pKQdPkwSekuX-c3IG5Q07*na zRDbs8epWxrJ4V*^bFp~`dDhQ8OHX>@PwDHw_UrW9FZ}IwY7*Csqbq;Zn1NFT*9rte zcs+xU5h4^tY6t*+(vvP?c9Ar=5$)J;ms&TFj*aNf6?axG+oU)3)QNTH?vuwktx>$OI_|$KI3}6EqH%b;Y+WcLmvo*WMXF3??UKqPOj= z;#D1-s*grD-gqOSA=g?l>CMK-wK3Nw#p6x^l0u4zHjv`-%@EhS z_C(B(ikny{h)q4NHDOYc*K|F`S5=j4t6F$-Bemgxtim=N$(tob)Qwmf$vd&*2DjlZ0my1ldJ`WP@=vtc zeQ*oUoVJx8o>H#-Bp_5$rat55C_ccJ3(RCT4qDaP$Y@)G{SeiGi-=p7%@j)Q1F--$ zJQD0pm{lc1Y@cn~*mvd})R2i}ab>h!5N0}MQc`2*jQS&u+zv{;LG-{Y;34sl1J1%X{EC3Hqc(~La+G(1NKw&jVcJhYA;&W5!q`mI7e_sD#&bDoGL~yg{$qCk` zxe*5@lDepTm%AAUyY?HCIUrS^At?qipIP3lZ99{MZ7DB{rEKn#Aru&U%nd8oL$jrH z#0K{Ui9p%Lz$Mr>=ZLePjh#3c)i&lA8mfdAZ6ls?n3ZSn!h!?ZAWmr1XM1fp;I-${ z4CL7CA=AW063=yB*UIiZDv?k`SQG07fzzj*i0vZ>i>PeoX`}3zY*wgwJxewgL;hk-pHpGTQU;kJ4&6jRw{sWTzM)-hE0aD>x zwk3Mya=~+B<;jz}V$Fm4*^kYQsLns|#2g5&Y?nw@Y6uNWhr^|k5nd*fWW!b3kWrD! z362EJUJlF|$rCSr>Rrii60g4MD!zOE0Nv)+w=TIg-`wQeo7>#_Ho7feHD1HljHx#y zWglW*JF`d^Zn}x`*!Z?(8&;T+wg>wKteC~a%GOh+B(aEzaYbWdMNy;bNjBG&c~xzc zZ@az1$t+#axnZ)7Sn~b`Zb)3Ns5wc@C0z+Gi|GrFb8ZvyZPx-B;I+?ffpP@J z6EpximdxySIa)1Vt#?c$KqPKqIHkpli$SM@b+$1t^=vDTx`0^MU3V>CXFZ6ow;ra? z{_M}zBOdVxeg5ZtKJt0`T)r0jtk3#;`s7dkWPQRXe1h(GzmGxgtNYyhKFYTZ_y3rW z(cgLSgXeeP2=?t9<+ zDgUU@13&KL^xy|SM4$XApQ2Cuw1?@FKIx&#KSXr@``@2=?yY;@_rAKd93{rpZf@X*5CVk`t;BE41MaS zej4(rC7;3^pZrOm!d#!MtmmN*eW)J%kcZU2#o~EBgC_^arFZac5XX})JJ&g9>4dn> zqc9sh*ALVtHYD@NRgJ6Fj6sZXvSHMP1Oc}pppzPSWJm~Y8`yZ=Lt_Cr7NMzQ8=Ibe zp!PoD`13zbV$l??LYmQb;7kK$Vq4Du-aW4xS)ALgk>?xw6p0H;4SIGu1(jDT!mC$2 zPSR_xGJ;5(#S*kWhh<)!v@7{xC$1so$rAg(E6lb0Lga%!=!10iRkzaFwk4`dyjou3 z6+P2m!7oNWfLFHsvgOWqzO(Lfm%G;2VRuE}z%N&><14XS-|E)d>f?o!J>Q(%`FA`r z-&I#$Rj-IQeDDpr>)r0AyWaI~O07HH>CXD#8*b3G*Iug+_yAr>nJ2HDsd=Y6-U;4a zb+^0TrRKZ)-S4iuBl*{@?sDh5=nfxr2VKLf?A2G_N@CT6H6OScZ{`;@H#7I8`Inv! z;9Nc(7;+|@G;Ap!f#P3%)z!*>AHr2u+IGenaJCmOT&!RE1WVm$sjmRxcGn1;x>cBY z7Ch}OZR3Ooo=^fTeiuiLM;TX_OrY#G3qC0 zso2+iz%{br(c@=Lsh_vzDifS|Fe#Tebq5v!@S$$D3$v~4FL=bZjt!*zA#Yu)&t~kD zvu&eJ`?h7fW9YRuK(OG(9EkDRdN$UkqU@d{kqY5w-jD~+aNM0|lMw;2tt+m)g4co# zs)Oi|XYt5thi-b;yENhx5n@}_mM(_@*vx~agm7a@Wu%s*1SAw|rM~`_W|=55P;}d} z&Gs5dMax9G=Q_02@wTC3 zXmV@r1?P<$~qEY_s8zW~1jUVdj-=xprbf zr(S+sB=d>c*{S+)P;fSHpxD|n$hmnCX~M>tB6mStD>nq`5?^kr>k%kaEiz@Bq?BAD%%RziMKDGfI^-|(TaHmuv!Ao_RbhaN zu$SE?$z+yLL)hurfF;=(Rc*H_Kb0D>*#nFqsOpy4Hv#Q!0O91$ieOe9wA}@gBv?xi zrQvOkq53y7;FgmiFn&MdP3BOj9@q{cV(=Wd1hqxXm&qM`$i@)29T=n1#y-Rl9KPHa z4eo)P!tR%`5HE-)NMiN3qY?gsf zPs&jsc_RigsGE4+zEX44W>PgM1p2_N8#49q%}Pf=>~np_7j}ssl%TQ_$?ikQI;~nM zBc0lGnFk(yTE<+x=4!xpz1=IiaLJh=MMPb26utQb>C*-k-lVEp_|&1xws=FJ$YkKB zbn!AwI3^@i*Ij3^!iAD+@j!FOLhtC}nwys>I6afkYVUo|d$5L*hEb{F8trzB$U(AZ z?hxzdnxQ2I_$j&Of>!+y5DNxl_vWNh`x))&Pk!=Nj-A+0sgN^YX~{f9vt8@Xwa$|P zh;HOu9}+|c?V)L~6;((9;E8RaQV_EpV&MT7BsN_}7EKzxTuq*48{FUnj=?8Xiv=pz zcIgaxg^Dhj6B)ftPI7F|&yNHk1{=(AKv<{{|CKnmn*{+o~8?=EhZ>CEWOw#}}Nwy^Lp@ zsIj`+ti4Uyjn5^3t_oCTe*k+wzAz5*8qS<4VOu?;Xah+HjA{uuxTx*zJDo1mQS?|M zsbeP%xgLu=+OT-nw`^;*9PF|s%ciEaw@@PidVd|vMuJKB^nCp`X9*&2fRge=OJ4Xn zMtf7wiATWDEbO`V@Nmi0Qc4SMjNHVdURrTr1dnujub}^vM^1zKVIBC)8XaD0*vyyM z)WKFfoIH!kmfex$+r|&8rFfV&xQs<-dVAK!6?SUHR2B$rJKH#uLxpbUhPJFj*3u*; zC1&bV4%0|?%aj{C-*yKWQ^Ph49D3(s&~sh~p1hdVQ`=?fSSz<scAP}Zt z$Yw-}SDINN*b5gN(TsGrv8f4-Cy<$TXbVO-b!@91=<>54t2yRLO*A-kETbsKnu8f+ zDCgKxJ`+)jNIb*uZnwQr(?kzE85=U+$LGVJdQM_OD8pJMOr+;jni-~S6~sq}FV>ai z2f0Nd&t_Ob?By+8q3L9gVH z7>P|ukXrc|P>G8}xYj_tAUHrAA_mbRbWq`*%@B|uVFOE(2 zNM>%xL#B|p(NY2!?lC`B))!o|Y@_nz*i`$#r;5_TDH}?ymx9p|66=iFDq6U%d@+0S zq9de&XG8`f%eGG2Trtpr7|L7K#M-N+R{SOQu|_^F_MAi)&RX%R+g2WNvi22P+U8sj ziAKzTfNeHL+^sccb=#u%n#yRi#(gD4JsB*~vjCm6&?B=95f+n`)nO6Y10)me?v!e? zteFFnR6e)VQo^zgDqVBUH5@AS@a$j+j=pv&ZH<@E61N3v6!rxjgSfw zTeN7-EAJesVWOav9Lke8XKov4kjjd~k6V~@w(%?`I$h?o@&f#CxcBaYYm1OwS-y45lwf2I}Lsc7um@>(+Dt|io>2?ugBH_jlCn6oVSqRYE7psRg{Xu-;(q5_z zaJncr$gwdblUk&ABAa zI%#m1XA?1(L8OV@T!j**I;{@t>Xh_fe}$?3ns}Xp!2Oc zr&#JYudMgW>DbD!x!}{;Yh@EjMhB~m`0%n}dy{!=n?dSa`-X0}fr|8C6QIXSnbeG3 zP|a~n+2bAL`A|sc`^Ae>HQ<;v*cgDtWLQ`@5kj-WgFE;|%XwVvhBdGjrBQ>)3R}8w z)C5~hG&LfHP`C8PNPf9|4!m^f=KAkTfXE=K~;ts}N%rBzxDlHJ%=dhEVvxhW*e=75zA4Mw_E z7;vt&%p;q;^uC)VY|WON-6ofX=an*Y=~BP?0F#?trEU^Z8y_wK03&o%L6~9MT`zVZ zXFlUW${K9TMkr^aHv}E7WQiNu=z-wWn7r66tbtc{Mpe+N7C&P5{MpN(R$jFo;(*2) zZf1X|T&?*(23QAf)cTkf3K5iSM#9H4j47F@mDXtzqT1 zQ)31*Bp0PRX8@Z4z_!*k%Q6e~rqc#Rwsk~d(M2^ySMi)ckgNzYrZ_8V0?^BclgVPc zGBrohjA;5MPpoz9TIbs2?sW+=C*?7ccPO19YqOib;>s*I`AnD#70%jS-lnLa)mGfL z?3V3}^^u<#P*H7SZPi#eiPbq2wfdpe;0sK&nx7@xa1fyG*g1hM7lip-Xf};S--t&7 z<3=GJ_jbHoVjj|!W;e6ex5e(gWS&SkgICaG+r*I#UcTmxfP_*qR#Cjz$&cp3afDe% zRBI@}VU)!%WLw5cH*<^_6WNH7P)OrF28d;M=Gtd4W+U7dhGr=>j;fwD4CNgndt{D* zRD@wvK(lz2Vv~bchr*aM8;n3!umguBTCgR!^g-RKz)g&%P8O1l*%X4&ofyX86@v^) z5~H&aYy#B-2;U`{N3@i&Ezwi{R##uGT-XScYCAiVO;E>X$7iM_0NYj`nDxWcgmqxg zwK4$P?H7<;U^p3)UB{?%jo3OD%q*qCS-0gn7b=uKcOJ|YPGVwCS8Z1=WF{sO4rk@9 z@g5Tz{H&bXp$`Vj85u6|Ths%2wzbX%m~{==v9vckz9;S-XND7dnsDbb7BDqz))*%h zYER4`Kf))Tnj2Q!lJH`LW?LI#=WJI*5|~cg3Job{*fL2+o@JYPMrkZGJsJo$+LO^Z(QJCf)XKS9#y_yhm5*IE}awJ^(aK#~73Wh(a(%Fsf@Y5=0>*VGuY;gAakz z$ne*(Dn~5@1XPgFiW~75wD3UEMMMRX?s@(D&9&aW_xVd+XU#R|eC9LfD&Dn=bFS_c zF*@k#?$t9`TFeYJYkeeA--xv`^T?4PIu!&kPQnXomkz29Bb(N(d1j|3h1VMRBjEsm zgQ4wWm*`XL7|p`e?W%Z<7Hje7#uc%p6GM5WS>J#$50EE9A9R>y#+HI~(Z&PSGQ2LU z@6>uDY(Q`Tq%&sl2T3@B?o)NnQe3E-XvlWZ2*Hnz5zA2lNO zstoKv5!J-8*NX$MNy|RNY~UzP+}RPXl>)AM8$RsVYG0~#wOcv0_KYfHG5k) zlV)*46BEIV>*~?b`aaXb)&4OzeWtbMp76)L53@n?hzq(nH+`O90*DxD z_r@QccGXQw-PZ@UEtgB17H;_q0dohd(Ks#({WESK?_{jGnnLLdsDU2`R#y73_9 z^5M<`22I+=)-tiih)l#Htc;XF+r{-bGOfSaj`Mr~Y5BmKt#VPZxNpF(-b%q|bBN%7bp>+iGUhF0)ZQwwn}a1LPjW3mT1+JiHUPhCQ3 zo7j=H#=O!XZUoA*E@MK?E9|F(p7h*_?6^kJxcQBCz`| zfk%)!C%HI7)t)?oJ8|+m3<5WtWZVnZ+@;2Rb!eO6qO#lfzWcqgJV;-FR(yKm#kl7X z9|E~8`_O?TuIRQ+Tg@fgZVP@vojnL(jops52-KXl3*GSM@azKpT8|_+(*5@!Juku# z=y0C7xFO!MR1F~qam4^qw?YpR4Av+>Zh zQMtviinu9ph@-ASzQs@9N;v5f7$05pz=BrVX4+QTvsR22P$Bu;GHJx@K)6j?U6!#k zw`x28PgVw7IN;AD9mkW<@LO;_@USkfK)m>EU(|JeX>FlVC@^P#xCRg+QJiB8G5E`u zzw&^mKh}gcn1i@=6n&*#UfWssdCkqbt?GQozL7Fda90N1P+Ia>Gm3IDGT5>*t%XC~ zsWJJ=x+EV^RlPXuCK3+n|VJHvS5ndD_4Q%M}j@ykBPg>?gLQKV}F*oxF z1P+OGD4eKjJ-|b$oN$@@~`{mffsL!54U2 zq9;N}IRzFSoR~Ss)`VH^8GQ(rLY-+n$BfSHfHTc}WgLey!yAme(6klI>22YRnWC=w zFw-&@hWx`9EC~23`qUlmw47&ECv`vpC zKIE2lG3T|Wgcw-6v=4$+rCq%CNLy*>Of&8o>Bv_rgJZN!(P31#j7(_!nXe7zv|vh- zKFkqXON=#QrvBi!SoN_dg&$gvbyh8F-De%_1AOM0xgb*O0LPmC#4pF8R-c-u?p0!$wz089jSjd5VaiERbP*87)rIxv49bEvkU3BTi#B7^9Fy>zX=T)ZTs`(c zgs#4P$rrM;7h_q>i@~&<-F?#Xu#WQ3ClF>o6{xJ8oYWy7cE;ynbVQw0P9&MY2pcFTdXiM32($I-m;?7fq-XaE2Z07*na zRDX<-4E*OexX_1NZ~QT>a6;Y^k-Jsg))CBglQrrA$0VCerGX3`QsK7ZOiXXF!F~5^ zvH56}LrR{(i_tn_(4Tn$ps3B-XVeQ-b8BnwXWHhNeIz}OOrw;UL63jVID;4Z2D1)1 zt$C0LFtXvHCitPX+aSntLFgM>>qy$gSz~A&O#aC{i&yZ~AJ=DUYwS$uImJ#S>7lU*(oxmDrV}npw{B%JK+SF;`ZFfQV7>6|CFuh}YNm=;V zfVVC`wdCoL7Ki>UgW&DLfdh*!4p!T-+u~)}!6q-f(_rNp9pY5So~mJ@qzACF_FziG zPw#%Pha0dYJ&sIkfvHGRdA|4W&=XCJM+zLYeSM;y6KYnyIn$FUbr?Y%w{+?DGLnP2 zM4zv(N7Yj(vTHCaK+_g&zcf#Ke|jCq+Q*fmZsAcTX;w5z%R-5gij7ZbT_TxEn9nGh z-Lw?3s~6<>imbdDH*YKM$Ewwo&H9};)JO0=KyzfUB*EcmJo}I_)+=2WPD$Ow?|%2Y zX#t{~a}oT-R1YRAUtIULvJ3N!^|YRz);Y+9IqEd4m?T)bmGc}_yzGaD;W>0jbc2(^ zy6Ew8JLmIos4O@+f!aL#pq(e!#ORr_7}jYU%#l6hYR2QhyoaAo!^MGs$tT>o>pz1m z+_7!G_3dw`Ekfwljb09RU9W=88gYlOzOfAHs1Z8LbiPEMdk?K`TK96M`1UL2{+LDR zCg)|5qxrl@(Or3qagE3Gz91Lzh?nbR*=sS$HEj~B5Q)vgtBM{%4p>v{)w}JX5fgp( zfdc5FX4B6+Ok){7b(q7ky(+Lx6OsUB8or#dABOM0@kY`_al`U&dm<;aqX*;aQfD1| zPHiLRnXer4t^kv6Y{}hu-gR5gF5z*p;OS}QopB6pT(osj^m8s-+q4b051ky%DxkX6 z$GN)PZBv1BJTr;x!=7mtu}r64Szpu4*Dy%#%m1D9tcCQvXkFgMovw2Z5Addl7@s;~ zII6_+*aaX>9L?0MVopgfZMPovVpx$xZ6$SVoJm`7>R`J|VfM7ZwqP6$7j8|A4`YoO z_DO`Mt?c0G3h|v#yIle8sb$`Ra&V}uB2t9fVk7CUfTC9(dgWsfZW%Pu&eiIlY0=0@ z%n4=7m0UhyA+#5uB?_ zB0<>vr(@04mVd4aiE@l4Ct$a=X%K6=E@6U|X@;D4Vj$&Kx5cu|oF?4m&uGF#kF+8S zklA!1?qNa6Xb)jysHrzY0vq1t;L)vh)xGP`JVdy59@rb-|9;x$ zcseqdqX!q4auo3JLL}nnIbc0V7 z6o$Z=wCq(EX_~7Yd2k+{$?Q8c3o$3wf*$4=tPlCdlnXNVwh;nT)2h zy5Y1qc4W7dJ|8gpS2ZVSH70pw=8 zMz$lA9v$s)x2QtVXGGrcB_Qo1OCBIyDrx~zkcF-Dncubck*qhXhAo{eDyxy8F^YjS zVk}+|0g9IaGp$}S3lva*20r7O1Bu=d4?DCX|MkA24Rg-QDfB?T%{8$;MBw52zqC7! zC-BCv0WJE8EyhO8wJIJCWFQaq`X2<9Id_;i^_-(g%di7N+0AM#F}s=+$n=g#n>R0V z*Saf|^01{kcYTplRNLUClcrrVSi^yu6>ub-m&by^;fl&Vh_yr%=5~&gg*@}SMcdgM zQ^r=fYKkX2mT5#9p6xg@5FQXWO~JI~9snJL`M_JeCaBot-D`sgpqet0!yH?`sMO```h0MnK=$JiG|6GKl8f=0YYbpER%Hy4@y?MBFhUgbJZ8el456Q z*RX?=Jp-}bpQK6u(_ilhyEu&lIrjXul2=-xv0YK6vM7K@=EXq8`=C#VNej(Or?X z%($MJ9taBkOxyS~YXb@1tYcr^wmF`j65p$fqCk}~PsV#c$@%8Dz6t-JO5?j(+xSlp z|M9#3m>=FwxL4#H!*!0qX@q?GxEFo^H2fTaQHGs2tfU2tl4;&eb6NKID@2_24nTSZ z$!nV^R1U3a?9S=Aw4Hq&oF<*j*w%xOO!yK*tG=^JBM~TVRkz%B`2#Ykhq&x(H2JcV z@{_As8@nyz>Y8p=&+x2e!ZQFC*AH}d+|zn-rnU;XM=^Pm6rKj-iM?(g!|uYEP&_{KNl|NdeA+!Y#= zwG0mnIe8Jh!Cw--|Gn?$`T32sjq@En@Xvhzjqm3>{QdXa$Tz?F&8hdD?|diD{SW`} z4^du!{q=n9YhPmxug_Zkj=9GNfBoxVXxH;~?5}_A>v{d}{~oUT_5A(czn<4$|N7+r z?ce@w{^NiAf8&~y&OSW{9^BY|s)hCChqU2=x9%^{xT#rfBV5{mH_ezc`n>dUUi8zz z%u3YaNXeY?*b`!yHv@3HVrU9Y{#dZin0Jm_M)pSh7<61g!Yb%M*9tMk3D5;ci300m zzS1c!LVPyCM7m8?OEBqaJ0j@2-lpn~nBTG1aB^9+PKcD(WC^FwCtjKsHu44^rJ%&m z{HOo)|K;m9PHvr$05=XFvOyeC~6f&FB8Z ze~3K&PczS_nOpnwpZ`Deg+Kj5{`3o9$e;h&pXE=rl|TE_{Ka4V#mxW3FMcs!`tld^ zW!CXUi#4woR53KtsNuBWy-1vH0&@oF){(g?li;sWwpCd6qe ztYFUppmP;Q%T>hMg2c5ktll<)&Ypv(hpC9b?Uly*to+{8It_%+k+^98;G^N!=^w+DCf~CowzI z+Qv&$LhZ)UCl~4szHU5wIXBUEHUG#H_ZE#4~Nny4E@{GygCm_|%=1vcAxs zo{qFlvCASBduTofGHSnD@zmxDu}JoK&i&K28n*Q|`|piq2Ob!**v`2a%p|!jNA4!^0d+_47;7{=aghq76Yt5Al$M^^~1k2BU z*PasVJsnTzW`cVZPsNfMvD>j?ZEI3!Vr|nlNpm1Y?8Z|N$>8HnsIjh%RdowQ_UeKYzVO*V@wy`FPtWbo`gC;b-_!!JbH2SU4j2&82 zk}$3Zd0lLvm;Fo&S5SRwWU%(am!Ius+R1TjGQPXdA}sK6e7Ta)?v)-7vh8eq)iG;T z$<+>w8$=B|Iqet(SH~NJC60bEO#Tcv*mNDyE9b|4>__u^|N8gy<3Il6*K)8;ZBqaM z5CBO;K~$nq9)4(T)7C}eJ#>n!cTO-KY|}jPRU&ZLLv2ht(zM0SO>?3-o``Jx+5DlL zQ5{^etA^*MZ>`YU4yV}S>cUs_LtITsP12}o9K4lHqCu3O2xEe`D>mPe(;Ub0PcGz- z{K${wfByAf&oBPsFNT7|4s_Cb;q#(-u2WNVl}OMnLu^aCFTG1^k4*T)9D~%Q0e-Mw z6G3m`ZNFpzxm1o?tBLVEHZVemuP`)`HDXM+RtssoHMm$s&(IYWlP{hN+>ZEqQC^32 z&2woJN}6%8PNsPp)*kJETs~&n#nryVOZd-z{Ga8I{^*bLfBd_Dm*4xn-^+(T^r5`} z{qN5QKJbB?{@{l`IR1x-Y5&^)^lSO8-~R1<{NtaEn!ocqzmwnm&EL#NKKxfBRpk{fGJFAN)bYUH5z5^B!uvKkxs*`}4l{Q|tX7$a~-S-n{$W z?~eO@{|DZek5F6g2ifOuv%f$7-~Vm?_>cc>e(vXfZodDwFVE0F{dtzRbDjsWIAZ1Q zxA;CPiC!SJn0kxcB?~zBSK8Bhh`MR)zQ{teqZjh4w;(@5jbHhf|8jEP@%DG*=YReesQ0sZE47^CSE=Qtdp8&I zU0lAm|LWUk4&O1~`m@gc(|PO9zLmV6&o900m+}k0@C*6p|NJNNlg#s$x4ebR`14cy zz3+K%-u>?PjJyZ?J=A;ed*3^k`90Kq&%58pn%|vwyyG2l9oG4g|Nck#igBjpJn5dH z(sYioohy;@BOZBPAfj!fIge_F5C%)~IEL0XtujthQYVKuE^vK6$*gyMdW6{s0W_5}u>CJ58Uram!5QIG56 zqhpZ!U{O5ztQBi@Ym}PnCE8U3l}COAGGk}NKD3Y~LTy!<#=#b2)z0882kqnpu(H~@ zrqwwUC|L`@!GfCm!#Jbw+RNNoWLSy#<+gg+?a@x!j$m;<=l-`z&Rnf6X5l0}IHww$ z?Vw96H+^NbO+(v&+|y^LuMo1ILvnY5jeB4;gcJ#NmNtL=k_w-%FtOFx%FWnVTy&wZy6|sN<7I@}eg3Gq20MVr{>Ww3bHMZ^t+U~}! zr^_rMe5*C@b_7ly^3*d(o%xgnk7u`rc`q+m37A#a!i%m|~5`{Tp5BO9>pdJa zaq8UG=z+?z>Y4|xDUe){GI0nkgU(I#Pnc@X1zbMI%XxWmPLRA3hsPRD)-5kITtO_m zwbmNW2_)Bp3G)lBs!91-HF9rFe79mmb6lYn5Os0e?NjWUgYb(kUh3Ol1aKyuMUakn zb$~7#>xHp=f+V($pAxRjIb0J^vY*JZ!?f@>X1;UD7gD#z^MeiCc&M->t65|$4RYb> zo_zmRFEO^PYo1jL>@kFW!wKCwi@b2mfiHheH;J<{ap(3NhIkEEB1>G5ndB{paqM%7 z%y1D;Vsz;!*U!5L$I33L7K6?`u(mv6EYnXRLC#}Dn?aS~#_6bCI?LAYMCgl>l=@E-r^q+>F^kD{WceSJbfk&T!v)y=^pb#8t?cE zJ>hK&?)@2noy-qL>69Ok>5p&P!fm6D4O68I?4W~K3Y9!y+58rR9@;6WjkHa3vBc3tsDDVMC3cdoiq6+cSSq7iy*ZNae6_X93~{dAzh(mJiN z#-H#dP~EOAwUv_cGsYf-@3j|rdpOm%e$yf;08#g-IX)1*dmhX{Rn3J8yw*X8T6C=u z;7Ew=ObcLb1*pZ3vM-3HLTpi*q2OQLy`1}9@w`Cv;$x%^xm0LJWVBz{zdG zdsd6Bgw`dk2KGb0pcQ!>B`sfTNtvo+u5nBTU$8C`b9Y{k%bEpxR+oGF3`n;l14BA_ zX}Ljt?qsgNH0WRWrC9|qlQJe(k*r3oWvsCif4Ml{WhM&Tc1*FhX%Uc_Yixbyf(yJm zPBqWW4{jny0}aZSr)Rf5#0P>(`Rsc^G%R7NO*jVOfPydR8eQA9&7q`M$iq#${KY$O zezNgg4gcKsF4*3&V_v5xUzaYDdt*ho{#n%eD;ogAR1|&r7_;`aDG#+pBfJkJ6vx~v zS~Wek3u~SH6iWAx_4qth-J~U|z@f_EuFDmu2Vj83c&06yjy}1id)tZA0ow@;@0wlg zF4q|lJr{alrr6t0R)wX*vIv~a)XECsrSqbl-~y+^md2m$@F1QsALFYwAUwt*9N8#M z#Cg~H7OE|IF9pWx@|NYu9ITg)gUv|Un76&LD?ng$lV?9#y&18caSmXejNSuxVX%;^ z(M~{f$%A`>%$m{VQnWk(9vtqiw^zbqPhew@`&#=++B;usT!t3U68W#Up-{-WC@%P! z@`FKAk)$n>q?vXyp0j2F*00arm*)tuyL&w;tj;bl42n=TS6gvT5bj_T_s_uI5fFUF z7rj$&1+H4^!a8^;+L$?fhCWN3Owt+;7_Ib7J2%M%58y!k@u6AAbfBnnYfal6{G4fQ zYfOM+d;Bha3T$aNa}>wG0bJ{l(oTTqv9bsYC&Gwp6I;;*^DGhx#6w%0GwV~RMD{}? z$y_M6&I(pK2;n$=Tf9p!ACe{n6vJe;O^+ij8Zq=J)*N30X*({De(e#=_3Be(#h|nd zdRu8K?FBUcgmFn|?gquW>`b8ZLe^Z#Ds$d6$n=M;#?~?mqJ0y5dQv(DJ+{*UX$kB? z-Z`Kby_0x{%v&j}UuQzF0CjnXjwxA9=(1LVo0|E$@OB%zm@NaJTV|F@1t0#L5)Rr6 zYkVFA(0*-TtzPn$1!E=8`8xMK1dF*^F5DDTYb@OS7EA*vVh4&fEIZf?=4CbfmOKd{ z?3!_RVq+0kFPD*Y#`_u#vd>Wr#!wSiUm?#y@Z=ru-18Z~>~?3Wko#J?UT|P-(;^gY zXsl)Bj|Xt?3)hFFx^l)&cxi15H8t<&P@v;*^qJt$XZ>iXY@@S|qzTflT<0u)rgg@d zox#}J_IYERFi4G(EdJ8Vo->e?-LS3E_Qqay&+&bq-%J-yI&2B-9UPL0#k8LecY(^r z^jBc=D0`<D{SYOZl(F_;HQ@{-Mc9iY?EQ&Y^$3lZg|~Ra3u(A=!X}Eu{I(M^{T+ zXY1{WAb9YveOvG3-JSrQ7~{MFpr-@1e%V_Gv`bEC2I7fBXN=8FsCyeUUdYKB1TG!2 zk6qmKbFZo3u^CgaueD>|x;Z@dwBChtzc`kC?zuc0Ir`9Uxid#Ixqj_+r&;afxlL(; z37jhLOpX-F`vP% zZy3AX2KK$5s*DQKJrV7l6%SJEC?_KGl7oO2>yXHPOV{=7RIku4ndG=^hq^vFDu&w;(7QsNJc) zE!FZ?8u`pGI^sGo9<$H=?hCxS!Bt}U=1{C{jw7@)?IN6b9_ESLa-O{K^#=wU?SMV0 z+6whlw?p*c0;z4CTGlx1U4u_LqhuwSI3|7LnO1M6mi*c4 zFhtnf^Jt7@^De$qtYD~Jhuqq@9h{M>)a(^q_OdC@2{8-?3)K@391)f&0tIxPtpO&NFB z1ebU62jjLYN)bwJ5(mio+Qt{&x$W%MlGE*|E?Z4ZXzE+@5^Her>e~j`OsaVPQCYJ+B+FfMu;wKGpf>)kT zpalgZG#zhgaWc-F_)U41W&b!SL+gmK*LKCOwR!GDcR#b}%y^sYbTK27YdK|S5Saed zBMV+5S@_Gwh;_Gj%M?3q3prj5Z=RJf594NFJ7eo2ajvvWM`I20!%pyQZ`qkPdxFkgRvwyy7j0 zf>UatCU-Q07?rT-QxE;vnUFV^MX4VQNBmc6f~-BV*(-1MPsU?_uD}2Q z3@u4SK~%|iS?O|OVKDpnw@;_@F=QAy5&Jo@=gmEV+G`dZI}@A;)8la`Fj_cZbTGS_ z91}4;rQRovO)_PLn`hP?N*Ep-qZK&QssZTjwoe?o>p0p4K2um=iarpwxeg5udDEZy z_?}GP83swlp8}aCeIBmHj!s|?12e(mEO6Ga%o(5E9M*8rdAJR0J`Rc+mmTb(kubIN zcW}+axMRlK9VDsMHUWoj{26Ww4y>=_wwE(4u{^eu1#SNaVhdjx@Rob$<1^jz4POrS zZC-2QL!EA|h9B_?oq8H+i;PLla~oS79mBdO7j~i!%0wJG&z&+lW6R&pD2J9>bSBXq zo)gcT;2;ys8V6V$%=zR)``j#JjdsoJQg~WMZ@IVmL2ve*sA{T73bv}n6p zzJl-JY0kWJY4nz~67F`Ze@4}68#^5k4<0rBis2j&_Rg8P2wj;o?d1eMou>?gqxbup z{^VlNuRXRn^dVlu&2QQxX?|$yV}J$d&sPL$mWDryL>`z!_o6lc)}-QCRnZpXUs{|+JH5VP1H8@ zDD5Jex6KJZ+Po=jtXaP>ex z(ib;r?-*WeKZrOBA5jK9KlE_+=EmN0w_G&h*Toz?(3!Ip%#zOxZ=3ZSuEi-L3S z67(*;4_3%J>L17fywS0rugUj%v-N3#HTmoz)67&7zp4pMI#9VZoJ?%T1)il-Zb0Z^;g?jZh2J`9fY>)P=bg>_WIs6?5L@(9TJ8F3{LGOh+UsKL@quAO@cy z0ngP2VrG&sa|?~LT8YO z%mfx2gAEcJd=G5}(>4XWh4D#CD@tH@>x%1Yt6lxcr7+i+8LeYppJQ{B(o%<#5?XB?8C%4*c~dh7wdo;Fk2<`FVxxrTx=j@;J%P;BiL!CN6F#iskQ+%|3T5H@fv5MX)>u7$pRmj?1#p_q^i z@}$gRkKusq`9hqY>yQ2BDY>;pl(Vc`fVI9v&a6!5^1xQ@a}wGWTh5Hbwyx`CvD0Ne zQWul)0W=5XMPFY>%)a1lZ?bDLGOL-;4Dp@n!dtM-$6|esM8R8LFvPApZ9`8Q+6<{2 zyff=kp4hV~HMvWjL>FJ5L(Hmqj!Q7@${#@LzN9?Ofvj2PGGlV7J^M$`3tJX>EdwM* zQ$$CPzq~yzgdHw_ifS%Au~5jh*t{ETd*g(r6CUkwvv-Y)YVFevu~;`hwQuC$l0b|; z;V|DBuqn{A9!_>^09ed)4l>a%FHMZttP^P3H^HaO4VH5!?|k#*bVa(hJ{_tn@*M=+E@(j3={=)8k83kbFlVsJ1IYeQvIAc z$ZIQ<7EQvOd$0#(7?Xe+PLKO)DUsFy(?H=UNT7>dd&Z>`Wei+nWAZjM2cWZs@K_5! z(>DGL_-KL0k5mIpbUSGMn~*;?x?{w&Wr+!%xQHgZ83i(@rAEwYinTlRZQT{&mf?=v zEYX)C5P-GEb#Ri~rbXBf21{U9+Nt5RODBz=R++XI0~2(B$!jm}s^4yZ(HD`ix2&XX zY);v%AALnnt<>Q`j-5veohSw94~#WwON=u%aNqkG;pH1Adb;K_nDb0UKJ%wwL?9@%I@US4UZKDgXv!WP^$lFmO`ea`ar{k`@5bnMO zg>TUR9)?;uIcxLU#;;pvRt$nKvy7b@m_X07h+h($winw7+~@fNsMILSYpc<%%jK@5Y$`Z zcdyA8hS_4!F{ih8$C69TCCnn|ly#q2^y}EO)4Giv3QH$3ya9=H>}wcgL)wZl*SD4- zvP8_-O@Mc5)|9Nt#U`FKV}NfAoThDFG;3TC_KE>KQ-c=(7PUZ$(J{dH9Lt$8f!H$t zVh0xTgMIdJ6`JrF(#D#cQ`2I4IP|81Bjzg)P+I0?O@j}MT=F!x(i~|z+JQ7Kw260& zOhbXzcu_(-c$a7;aS5ncqSs{IuUvh_7%5qH($L6Z94sPJYa+Q?tG|hY|uYW#*nHA$Hu1B4UhH0_Fa z{6V$Sf+Lxmjty#wi*a_{-1B-XXWvk!%Nrw9wIiX{c@M0xBe7de;;(W%+rViVi2EwJ z&Z}+c$zdUcLQ+yLgD$?-d987i2}o8@lZ$;`LC-0x zkut`~ya!H8!XTGsKCty>N~K-HXen;q3D$P9a_RAU+iq+U;Zo6lru>L!k}NJRLZoVZ zHa1~g7>x9ZF%W1QewpU1iLwbsY&dM#QE^^%CZ)t-ogx4#WH?}V1kesPih0^v0@oTS zJo9Exx?6S%-kG*xU?Z7!vPO`Uqq*`tTvn86XNVQaYUFn;0%&teZfGqL`h34#fegez zmyrQNTL1=2kvrBkG=4hem1c_a2)9o7P~*Bxz&n)s)0H)=W@wMJ8zj_th6L!i(>voH z;2J{lycX83oMXASv0a(1T3Ml!eJXnEd{@lh4ccY5OV3LUv&WGZaOoy-PIlI63S5Q*qAr`3Cf(A z6-r66!h$knO4(T|3-b^gEKEhl1+pMZWx?n3>l7JI@8>+<_j$hOIp;m^`#JRSWi5@e zsF82SdxfvqieMKO1NMl;V2$uRUN-nfx*1NP7uF#*V2#)V)_?STM~8v~NJrGeF#Lo@xP;f^ibNCW86eg{mJPNL$4~)-kOUu%1PXmn z1ukdH2G>lYp#c8F2FyV&dElcxi(O#6Oq--=0ByfGOC4ZJ35f$bonm zhrbYEgH*&mtbam2EI>ABR4<_uArodnk6xciwZS||o7@m5!9qFl2wZC=@c_!enjXC# zuz4Gh53O*^gBbAexUFaC)&RGy8|~NV(Ywq%WP@acYpo?-KqG3C$3WfhcSiHF1qdnHX+1(NK5P55a=kWvp0RR7% kjP7#)000I_L_t&o0Hif&pPOH;Jpcdz07*qoM6N<$g6%@+J^%m! delta 359 zcmV-t0hs=X1cC#QB!4$aL_t(|0i=>aYlA=#h2NTxLJ}~+5Pw9xDDe^nN&mVR5kcy~ zN=&0(^seGbvURsDq*bwyf#*DSW-BmXz&n0+`Zx39@6RUy00960Ebm5100006Nkl< diff --git a/blogpost/favicon-32x32.png b/blogpost/favicon-32x32.png index bf7dc7ddac18cf856fd8d499d73ec53f483f7f2b..74bf31885f507d30af00d001a0197ecc6bad8c7e 100644 GIT binary patch delta 1347 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 literal 15406 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 + + @@ -26,48 +32,49 @@ - @ioredis/commands + @ioredis/commands - cluster-key-slot + cluster-key-slot - debug + debug - denque + denque - ioredis + ioredis - lodash.defaults + lodash.defaults - lodash.isarguments + lodash.isarguments - ms + ms - redis-errors + redis-errors - redis-parser + redis-parser - standard-as-callback + standard-as-callback + diff --git a/blogpost/plots/showcase/javascript/ioredis_mobile.svg b/blogpost/plots/showcase/javascript/ioredis_mobile.svg index f643b90..4cb99de 100644 --- a/blogpost/plots/showcase/javascript/ioredis_mobile.svg +++ b/blogpost/plots/showcase/javascript/ioredis_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -26,48 +32,49 @@ - @ioredis/commands + @ioredis/commands - cluster-key-slot + cluster-key-slot - debug + debug - denque + denque - ioredis + ioredis - lodash.defaults + lodash.defaults - lodash.isarguments + lodash.isarguments - ms + ms - redis-errors + redis-errors - redis-parser + redis-parser - standard-as-callback + standard-as-callback + diff --git a/blogpost/plots/showcase/javascript/knex.svg b/blogpost/plots/showcase/javascript/knex.svg index aeb772c..21e330c 100644 --- a/blogpost/plots/showcase/javascript/knex.svg +++ b/blogpost/plots/showcase/javascript/knex.svg @@ -1,13 +1,19 @@ - + + @@ -45,92 +51,93 @@ - colorette + colorette - commander + commander - debug + debug - escalade + escalade - esm + esm - function-bind + function-bind - get-package-type + get-package-type - getopts + getopts - hasown + hasown - interpret + interpret - is-core-module + is-core-module - knex + knex - lodash + lodash - ms + ms - path-parse + path-parse - pg-connection-string + pg-connection-string - rechoir + rechoir - resolve + resolve - resolve-from + resolve-from - supports-preserve-symlinks-flag + supports-preserve-symlinks-flag - tarn + tarn - tildify + tildify + diff --git a/blogpost/plots/showcase/javascript/knex_mobile.svg b/blogpost/plots/showcase/javascript/knex_mobile.svg index b4639f4..602a20f 100644 --- a/blogpost/plots/showcase/javascript/knex_mobile.svg +++ b/blogpost/plots/showcase/javascript/knex_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -45,92 +51,93 @@ - colorette + colorette - commander + commander - debug + debug - escalade + escalade - esm + esm - function-bind + function-bind - get-package-type + get-package-type - getopts + getopts - hasown + hasown - interpret + interpret - is-core-module + is-core-module - knex + knex - lodash + lodash - ms + ms - path-parse + path-parse - pg-connection-string + pg-connection-string - rechoir + rechoir - resolve + resolve - resolve-from + resolve-from - supports-preserve-symlinks-flag + supports-preserve-symlinks-flag - tarn + tarn - tildify + tildify + diff --git a/blogpost/plots/showcase/javascript/mongoose.svg b/blogpost/plots/showcase/javascript/mongoose.svg index 9b73b09..56f6021 100644 --- a/blogpost/plots/showcase/javascript/mongoose.svg +++ b/blogpost/plots/showcase/javascript/mongoose.svg @@ -1,13 +1,19 @@ - + + @@ -33,76 +39,77 @@ - @mongodb-js/.. + @mongodb-js/.. - @types/webidl-conversions + @types/webidl-conversions - @types/whatw.. + @types/whatw.. - bson + bson - kareem + kareem - memory-pager + memory-pager - mongodb + mongodb - mongodb-connection-string-url + mongodb-connection-string-url - mongoose + mongoose - mpath + mpath - mquery + mquery - ms + ms - punycode + punycode - sift + sift - sparse-bitfi.. + sparse-bitfi.. - tr46 + tr46 - webidl-conversions + webidl-conversions - whatwg-url + whatwg-url + diff --git a/blogpost/plots/showcase/javascript/mongoose_mobile.svg b/blogpost/plots/showcase/javascript/mongoose_mobile.svg index 4949b43..972d38f 100644 --- a/blogpost/plots/showcase/javascript/mongoose_mobile.svg +++ b/blogpost/plots/showcase/javascript/mongoose_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -33,76 +39,77 @@ - @mongodb-js/saslprep + @mongodb-js/saslprep - @types/webidl-conversions + @types/webidl-conversions - @types/whatwg-url + @types/whatwg-url - bson + bson - kareem + kareem - memory-pager + memory-pager - mongodb + mongodb - mongodb-connection-string-url + mongodb-connection-string-url - mongoose + mongoose - mpath + mpath - mquery + mquery - ms + ms - punycode + punycode - sift + sift - sparse-bitfield + sparse-bitfield - tr46 + tr46 - webidl-conversions + webidl-conversions - whatwg-url + whatwg-url + diff --git a/blogpost/plots/showcase/javascript/pino.svg b/blogpost/plots/showcase/javascript/pino.svg index ec71f96..c96ed38 100644 --- a/blogpost/plots/showcase/javascript/pino.svg +++ b/blogpost/plots/showcase/javascript/pino.svg @@ -1,13 +1,19 @@ - + + @@ -28,56 +34,57 @@ - @pinojs/redact + @pinojs/redact - atomic-sleep + atomic-sleep - on-exit-leak-free + on-exit-leak-free - pino + pino - pino-abstract-transport + pino-abstract-transport - pino-std-serializers + pino-std-serializers - process-warning + process-warning - quick-format-unescaped + quick-format-unescaped - real-require + real-require - safe-stable-stringify + safe-stable-stringify - sonic-boom + sonic-boom - split2 + split2 - thread-stream + thread-stream + diff --git a/blogpost/plots/showcase/javascript/pino_mobile.svg b/blogpost/plots/showcase/javascript/pino_mobile.svg index ddfeb8d..4fcf616 100644 --- a/blogpost/plots/showcase/javascript/pino_mobile.svg +++ b/blogpost/plots/showcase/javascript/pino_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -28,56 +34,57 @@ - @pinojs/redact + @pinojs/redact - atomic-sleep + atomic-sleep - on-exit-leak-free + on-exit-leak-free - pino + pino - pino-abstract-transport + pino-abstract-transport - pino-std-serializers + pino-std-serializers - process-warning + process-warning - quick-format-unescaped + quick-format-unescaped - real-require + real-require - safe-stable-stringify + safe-stable-stringify - sonic-boom + sonic-boom - split2 + split2 - thread-stream + thread-stream + diff --git a/blogpost/plots/showcase/javascript/yup.svg b/blogpost/plots/showcase/javascript/yup.svg index 59b2772..abd092e 100644 --- a/blogpost/plots/showcase/javascript/yup.svg +++ b/blogpost/plots/showcase/javascript/yup.svg @@ -1,13 +1,19 @@ - + + @@ -19,28 +25,29 @@ - property-expr + property-expr - tagged-tag + tagged-tag - tiny-case + tiny-case - toposort + toposort - type-fest + type-fest - yup + yup + diff --git a/blogpost/plots/showcase/javascript/yup_mobile.svg b/blogpost/plots/showcase/javascript/yup_mobile.svg index c976667..b741e14 100644 --- a/blogpost/plots/showcase/javascript/yup_mobile.svg +++ b/blogpost/plots/showcase/javascript/yup_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -19,28 +25,29 @@ - property-expr + property-expr - tagged-tag + tagged-tag - tiny-case + tiny-case - toposort + toposort - type-fest + type-fest - yup + yup + diff --git a/blogpost/plots/showcase/python/fastapi.svg b/blogpost/plots/showcase/python/fastapi.svg index 70d66e2..73b1b03 100644 --- a/blogpost/plots/showcase/python/fastapi.svg +++ b/blogpost/plots/showcase/python/fastapi.svg @@ -1,13 +1,19 @@ - + + @@ -21,48 +27,49 @@ - annotated-doc + annotated-doc - annotated-types + annotated-types - anyio + anyio - exceptiongroup + exceptiongroup - fastapi + fastapi - idna + idna - pydantic + pydantic - pydantic-core + pydantic-core - starlette + starlette - typing-extensions + typing-extensions - typing-inspection + typing-inspection + diff --git a/blogpost/plots/showcase/python/fastapi_mobile.svg b/blogpost/plots/showcase/python/fastapi_mobile.svg index f7caddc..d4b1da3 100644 --- a/blogpost/plots/showcase/python/fastapi_mobile.svg +++ b/blogpost/plots/showcase/python/fastapi_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -21,48 +27,49 @@ - annotated-doc + annotated-doc - annotated-types + annotated-types - anyio + anyio - exceptiongroup + exceptiongroup - fastapi + fastapi - idna + idna - pydantic + pydantic - pydantic-core + pydantic-core - starlette + starlette - typing-extensions + typing-extensions - typing-inspection + typing-inspection + diff --git a/blogpost/plots/showcase/python/openai.svg b/blogpost/plots/showcase/python/openai.svg index 4e58c57..063bdf8 100644 --- a/blogpost/plots/showcase/python/openai.svg +++ b/blogpost/plots/showcase/python/openai.svg @@ -1,13 +1,19 @@ - + + @@ -29,76 +35,77 @@ - annotated-types + annotated-types - anyio + anyio - certifi + certifi - colorama + colorama - distro + distro - exceptiongroup + exceptiongroup - h11 + h11 - httpcore + httpcore - httpx + httpx - idna + idna - jiter + jiter - openai + openai - pydantic + pydantic - pydantic-core + pydantic-core - sniffio + sniffio - tqdm + tqdm - typing-extensions + typing-extensions - typing-inspection + typing-inspection + diff --git a/blogpost/plots/showcase/python/openai_mobile.svg b/blogpost/plots/showcase/python/openai_mobile.svg index df7836b..e48975b 100644 --- a/blogpost/plots/showcase/python/openai_mobile.svg +++ b/blogpost/plots/showcase/python/openai_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -29,76 +35,77 @@ - annotated-types + annotated-types - anyio + anyio - certifi + certifi - colorama + colorama - distro + distro - exceptiongroup + exceptiongroup - h11 + h11 - httpcore + httpcore - httpx + httpx - idna + idna - jiter + jiter - openai + openai - pydantic + pydantic - pydantic-core + pydantic-core - sniffio + sniffio - tqdm + tqdm - typing-extensions + typing-extensions - typing-inspection + typing-inspection + diff --git a/blogpost/plots/showcase/python/pydantic.svg b/blogpost/plots/showcase/python/pydantic.svg index f34cbdd..48beb55 100644 --- a/blogpost/plots/showcase/python/pydantic.svg +++ b/blogpost/plots/showcase/python/pydantic.svg @@ -1,13 +1,19 @@ - + + @@ -15,24 +21,25 @@ - annotated-ty.. + annotated-ty.. - pydantic + pydantic - pydantic-core + pydantic-core - typing-extensions + typing-extensions - typing-inspection + typing-inspection + diff --git a/blogpost/plots/showcase/python/pydantic_mobile.svg b/blogpost/plots/showcase/python/pydantic_mobile.svg index 8bb6dc1..b7dc374 100644 --- a/blogpost/plots/showcase/python/pydantic_mobile.svg +++ b/blogpost/plots/showcase/python/pydantic_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -15,24 +21,25 @@ - annotated-types + annotated-types - pydantic + pydantic - pydantic-core + pydantic-core - typing-extensions + typing-extensions - typing-inspection + typing-inspection + diff --git a/blogpost/plots/showcase/python/requests.svg b/blogpost/plots/showcase/python/requests.svg index 6ddeeed..5fff762 100644 --- a/blogpost/plots/showcase/python/requests.svg +++ b/blogpost/plots/showcase/python/requests.svg @@ -1,13 +1,19 @@ - + + @@ -15,24 +21,25 @@ - certifi + certifi - charset-normalizer + charset-normalizer - idna + idna - requests + requests - urllib3 + urllib3 + diff --git a/blogpost/plots/showcase/python/requests_mobile.svg b/blogpost/plots/showcase/python/requests_mobile.svg index 1abb8b3..abe07d3 100644 --- a/blogpost/plots/showcase/python/requests_mobile.svg +++ b/blogpost/plots/showcase/python/requests_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -15,24 +21,25 @@ - certifi + certifi - charset-normalizer + charset-normalizer - idna + idna - requests + requests - urllib3 + urllib3 + diff --git a/blogpost/plots/showcase/python/typer.svg b/blogpost/plots/showcase/python/typer.svg index 9643a1d..dd8202b 100644 --- a/blogpost/plots/showcase/python/typer.svg +++ b/blogpost/plots/showcase/python/typer.svg @@ -1,13 +1,19 @@ - + + @@ -19,40 +25,41 @@ - click + click - colorama + colorama - markdown-it-py + markdown-it-py - mdurl + mdurl - pygments + pygments - rich + rich - shellingham + shellingham - typer + typer - typing-extensions + typing-extensions + diff --git a/blogpost/plots/showcase/python/typer_mobile.svg b/blogpost/plots/showcase/python/typer_mobile.svg index 6ab6877..c0fcd78 100644 --- a/blogpost/plots/showcase/python/typer_mobile.svg +++ b/blogpost/plots/showcase/python/typer_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -19,40 +25,41 @@ - click + click - colorama + colorama - markdown-it-py + markdown-it-py - mdurl + mdurl - pygments + pygments - rich + rich - shellingham + shellingham - typer + typer - typing-extensions + typing-extensions + diff --git a/blogpost/plots/showcase/rust/diesel.svg b/blogpost/plots/showcase/rust/diesel.svg index cf6518d..f9e8331 100644 --- a/blogpost/plots/showcase/rust/diesel.svg +++ b/blogpost/plots/showcase/rust/diesel.svg @@ -1,13 +1,19 @@ - + + @@ -28,68 +34,69 @@ - darling + darling - darling_core + darling_core - darling_macro + darling_macro - diesel + diesel - diesel_derives + diesel_derives - diesel_table_macro_syntax + diesel_table_macro_syntax - downcast-rs + downcast-rs - dsl_auto_type + dsl_auto_type - either + either - fnv + fnv - heck + heck - ident_case + ident_case - proc-macro2 + proc-macro2 - quote + quote - syn + syn - unicode-ident + unicode-ident + diff --git a/blogpost/plots/showcase/rust/diesel_mobile.svg b/blogpost/plots/showcase/rust/diesel_mobile.svg index 705ce68..a201a85 100644 --- a/blogpost/plots/showcase/rust/diesel_mobile.svg +++ b/blogpost/plots/showcase/rust/diesel_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -28,68 +34,69 @@ - darling + darling - darling_core + darling_core - darling_macro + darling_macro - diesel + diesel - diesel_derives + diesel_derives - diesel_table_macro_syntax + diesel_table_macro_syntax - downcast-rs + downcast-rs - dsl_auto_type + dsl_auto_type - either + either - fnv + fnv - heck + heck - ident_case + ident_case - proc-macro2 + proc-macro2 - quote + quote - syn + syn - unicode-ident + unicode-ident + diff --git a/blogpost/plots/showcase/rust/hyper.svg b/blogpost/plots/showcase/rust/hyper.svg index 9618e60..8d0bb33 100644 --- a/blogpost/plots/showcase/rust/hyper.svg +++ b/blogpost/plots/showcase/rust/hyper.svg @@ -1,13 +1,19 @@ - + + @@ -17,32 +23,33 @@ - bytes + bytes - http + http - http-body + http-body - hyper + hyper - itoa + itoa - pin-project-lite + pin-project-lite - tokio + tokio + diff --git a/blogpost/plots/showcase/rust/hyper_mobile.svg b/blogpost/plots/showcase/rust/hyper_mobile.svg index 9741c86..dabe756 100644 --- a/blogpost/plots/showcase/rust/hyper_mobile.svg +++ b/blogpost/plots/showcase/rust/hyper_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -17,32 +23,33 @@ - bytes + bytes - http + http - http-body + http-body - hyper + hyper - itoa + itoa - pin-project-lite + pin-project-lite - tokio + tokio + diff --git a/blogpost/plots/showcase/rust/rayon.svg b/blogpost/plots/showcase/rust/rayon.svg index 584c2df..34fda5c 100644 --- a/blogpost/plots/showcase/rust/rayon.svg +++ b/blogpost/plots/showcase/rust/rayon.svg @@ -1,13 +1,19 @@ - + + @@ -16,28 +22,29 @@ - crossbeam-deque + crossbeam-deque - crossbeam-epoch + crossbeam-epoch - crossbeam-utils + crossbeam-utils - either + either - rayon + rayon - rayon-core + rayon-core + diff --git a/blogpost/plots/showcase/rust/rayon_mobile.svg b/blogpost/plots/showcase/rust/rayon_mobile.svg index 1afde0d..dbd09f8 100644 --- a/blogpost/plots/showcase/rust/rayon_mobile.svg +++ b/blogpost/plots/showcase/rust/rayon_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -16,28 +22,29 @@ - crossbeam-deque + crossbeam-deque - crossbeam-epoch + crossbeam-epoch - crossbeam-utils + crossbeam-utils - either + either - rayon + rayon - rayon-core + rayon-core + diff --git a/blogpost/plots/showcase/rust/serde.svg b/blogpost/plots/showcase/rust/serde.svg index 4c71fd3..d96274a 100644 --- a/blogpost/plots/showcase/rust/serde.svg +++ b/blogpost/plots/showcase/rust/serde.svg @@ -1,13 +1,19 @@ - + + @@ -16,28 +22,29 @@ - proc-macro2 + proc-macro2 - quote + quote - serde + serde - serde_derive + serde_derive - syn + syn - unicode-ident + unicode-ident + diff --git a/blogpost/plots/showcase/rust/serde_mobile.svg b/blogpost/plots/showcase/rust/serde_mobile.svg index 175d8e5..c89f417 100644 --- a/blogpost/plots/showcase/rust/serde_mobile.svg +++ b/blogpost/plots/showcase/rust/serde_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -16,28 +22,29 @@ - proc-macro2 + proc-macro2 - quote + quote - serde + serde - serde_derive + serde_derive - syn + syn - unicode-ident + unicode-ident + diff --git a/blogpost/plots/showcase/rust/ureq.svg b/blogpost/plots/showcase/rust/ureq.svg index 8efae5f..d4c2e6f 100644 --- a/blogpost/plots/showcase/rust/ureq.svg +++ b/blogpost/plots/showcase/rust/ureq.svg @@ -1,13 +1,19 @@ - + + @@ -21,44 +27,45 @@ - base64 + base64 - bytes + bytes - http + http - httparse + httparse - itoa + itoa - log + log - percent-encoding + percent-encoding - ureq + ureq - ureq-proto + ureq-proto - utf-8 + utf-8 + diff --git a/blogpost/plots/showcase/rust/ureq_mobile.svg b/blogpost/plots/showcase/rust/ureq_mobile.svg index a7fa313..0e2053a 100644 --- a/blogpost/plots/showcase/rust/ureq_mobile.svg +++ b/blogpost/plots/showcase/rust/ureq_mobile.svg @@ -1,13 +1,19 @@ - + + @@ -21,44 +27,45 @@ - base64 + base64 - bytes + bytes - http + http - httparse + httparse - itoa + itoa - log + log - percent-encoding + percent-encoding - ureq + ureq - ureq-proto + ureq-proto - utf-8 + utf-8 + diff --git a/cmd/stacktower/main.go b/cmd/stacktower/main.go new file mode 100644 index 0000000..e22cdbc --- /dev/null +++ b/cmd/stacktower/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "context" + "errors" + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/spf13/cobra" + + "github.com/matzehuels/stacktower/internal/cli" +) + +func main() { + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer cancel() + + if err := run(ctx); err != nil { + if errors.Is(err, context.Canceled) { + os.Exit(130) // Standard shell convention for SIGINT + } + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run(ctx context.Context) error { + var verbose bool + + // Create CLI - verbose flag will be handled via PersistentPreRun + // Create a temporary CLI to build the root command structure + c := cli.New(os.Stderr, cli.LogInfo) + root := c.RootCommand() + + root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "enable verbose logging") + + // Recreate CLI with correct log level before command execution + originalPreRun := root.PersistentPreRunE + root.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + level := cli.LogInfo + if verbose { + level = cli.LogDebug + } + // Update the CLI's logger + c.SetLogLevel(level) + + if originalPreRun != nil { + return originalPreRun(cmd, args) + } + return nil + } + + return root.ExecuteContext(ctx) +} diff --git a/examples/real/cobra.json b/examples/real/cobra.json index 4f52522..5590028 100644 --- a/examples/real/cobra.json +++ b/examples/real/cobra.json @@ -1,217 +1,135 @@ { "nodes": [ { - "id": "golang.org/x/crypto", + "id": "github.com/cpuguy83/go-md2man/v2", "meta": { - "version": "v0.46.0" + "version": "v2.0.7" } }, { - "id": "golang.org/x/term", + "id": "github.com/creack/pty", "meta": { - "version": "v0.38.0" + "version": "v1.1.24" } }, { - "id": "github.com/rogpeppe/go-internal", + "id": "github.com/google/go-cmp", "meta": { - "version": "v1.14.1" + "version": "v0.7.0" } }, { - "id": "golang.org/x/mod", + "id": "github.com/inconshreveable/mousetrap", "meta": { - "version": "v0.31.0" + "version": "v1.1.0" } }, { - "id": "github.com/creack/pty", + "id": "github.com/kr/pretty", "meta": { - "version": "v1.1.24" + "version": "v0.3.1" } }, { - "id": "github.com/google/go-cmp", + "id": "github.com/kr/text", "meta": { - "version": "v0.7.0" + "version": "v0.2.0" } }, { - "id": "github.com/yuin/goldmark", + "id": "github.com/rogpeppe/go-internal", "meta": { - "version": "v1.7.13" + "version": "v1.14.1" } }, { - "id": "golang.org/x/tools", + "id": "github.com/russross/blackfriday/v2", "meta": { - "version": "v0.40.0" + "version": "v2.1.0" } }, { - "id": "github.com/cpuguy83/go-md2man/v2", + "id": "github.com/spf13/cobra", "meta": { - "repo_archived": false, - "repo_language": "Go", - "repo_last_commit": "2025-12-22", - "repo_last_release": "2025-11-20", - "repo_maintainers": [ - "Roasbeef", - "guggero", - "yyforyongyu", - "cfromknecht", - "halseth" - ], - "repo_owner": "lightningnetwork", - "repo_stars": 8074, - "repo_topics": [ - "bitcoin", - "blockchain", - "cryptocurrency", - "cryptography", - "lightning", - "lightning-network", - "micropayments", - "payments", - "peer-to-peer", - "protocol" - ], - "repo_url": "https://github.com/lightningnetwork/lnd", - "version": "v2.0.7" + "version": "v1.10.2" } }, { - "id": "github.com/inconshreveable/mousetrap", + "id": "github.com/spf13/pflag", "meta": { - "version": "v1.1.0" + "version": "v1.0.10" } }, { - "id": "github.com/russross/blackfriday/v2", + "id": "github.com/yuin/goldmark", "meta": { - "version": "v2.1.0" + "version": "v1.7.16" } }, { - "id": "gopkg.in/check.v1", + "id": "go.yaml.in/yaml/v3", "meta": { - "version": "v1.0.0-20201130134442-10cb98267c6c" + "version": "v3.0.4" } }, { - "id": "github.com/kr/pretty", + "id": "golang.org/x/crypto", "meta": { - "version": "v0.3.1" + "version": "v0.47.0" } }, { - "id": "golang.org/x/text", + "id": "golang.org/x/mod", "meta": { "version": "v0.32.0" } }, { - "id": "github.com/kr/text", + "id": "golang.org/x/net", "meta": { - "version": "v0.2.0" + "version": "v0.49.0" } }, { - "id": "golang.org/x/sys", + "id": "golang.org/x/sync", "meta": { - "version": "v0.39.0" + "version": "v0.19.0" } }, { - "id": "golang.org/x/telemetry", + "id": "golang.org/x/sys", "meta": { - "version": "v0.0.0-20251222180846-3f2a21fb04ff" + "version": "v0.40.0" } }, { - "id": "github.com/spf13/cobra", + "id": "golang.org/x/telemetry", "meta": { - "repo_archived": false, - "repo_language": "TypeScript", - "repo_last_commit": "2025-12-22", - "repo_last_release": "2025-12-16", - "repo_maintainers": [ - "torkelo", - "bergquist", - "ryantxu", - "marefr" - ], - "repo_owner": "grafana", - "repo_stars": 71404, - "repo_topics": [ - "alerting", - "analytics", - "business-intelligence", - "dashboard", - "data-visualization", - "elasticsearch", - "go", - "grafana", - "hacktoberfest", - "influxdb", - "metrics", - "monitoring", - "mysql", - "postgres", - "prometheus" - ], - "repo_url": "https://github.com/grafana/grafana", - "version": "v1.10.2" + "version": "v0.0.0-20260128140115-fcf36f686977" } }, { - "id": "github.com/spf13/pflag", + "id": "golang.org/x/term", "meta": { - "repo_archived": false, - "repo_language": "Go", - "repo_last_commit": "2025-12-22", - "repo_last_release": "2025-11-20", - "repo_maintainers": [ - "Roasbeef", - "guggero", - "yyforyongyu", - "cfromknecht", - "halseth" - ], - "repo_owner": "lightningnetwork", - "repo_stars": 8074, - "repo_topics": [ - "bitcoin", - "blockchain", - "cryptocurrency", - "cryptography", - "lightning", - "lightning-network", - "micropayments", - "payments", - "peer-to-peer", - "protocol" - ], - "repo_url": "https://github.com/lightningnetwork/lnd", - "version": "v1.0.10" + "version": "v0.39.0" } }, { - "id": "go.yaml.in/yaml/v3", + "id": "golang.org/x/text", "meta": { - "version": "v3.0.4" + "version": "v0.33.0" } }, { - "id": "golang.org/x/net", + "id": "golang.org/x/tools", "meta": { - "version": "v0.48.0" + "version": "v0.41.0" } }, { - "id": "golang.org/x/sync", + "id": "gopkg.in/check.v1", "meta": { - "version": "v0.19.0" + "version": "v1.0.0-20201130134442-10cb98267c6c" } } ], @@ -232,14 +150,14 @@ "from": "github.com/spf13/cobra", "to": "go.yaml.in/yaml/v3" }, - { - "from": "github.com/cpuguy83/go-md2man/v2", - "to": "github.com/russross/blackfriday/v2" - }, { "from": "go.yaml.in/yaml/v3", "to": "gopkg.in/check.v1" }, + { + "from": "github.com/cpuguy83/go-md2man/v2", + "to": "github.com/russross/blackfriday/v2" + }, { "from": "gopkg.in/check.v1", "to": "github.com/kr/pretty" @@ -296,18 +214,6 @@ "from": "golang.org/x/mod", "to": "golang.org/x/tools" }, - { - "from": "golang.org/x/telemetry", - "to": "golang.org/x/mod" - }, - { - "from": "golang.org/x/telemetry", - "to": "golang.org/x/sync" - }, - { - "from": "golang.org/x/telemetry", - "to": "golang.org/x/sys" - }, { "from": "golang.org/x/net", "to": "golang.org/x/crypto" @@ -325,8 +231,16 @@ "to": "golang.org/x/text" }, { - "from": "golang.org/x/text", - "to": "golang.org/x/tools" + "from": "golang.org/x/telemetry", + "to": "golang.org/x/mod" + }, + { + "from": "golang.org/x/telemetry", + "to": "golang.org/x/sync" + }, + { + "from": "golang.org/x/telemetry", + "to": "golang.org/x/sys" }, { "from": "golang.org/x/crypto", @@ -340,6 +254,10 @@ "from": "golang.org/x/crypto", "to": "golang.org/x/term" }, + { + "from": "golang.org/x/text", + "to": "golang.org/x/tools" + }, { "from": "golang.org/x/term", "to": "golang.org/x/sys" diff --git a/examples/real/com.google.guava_guava.json b/examples/real/com.google.guava_guava.json index 73aa99b..ec28b4a 100644 --- a/examples/real/com.google.guava_guava.json +++ b/examples/real/com.google.guava_guava.json @@ -1,155 +1,39 @@ { "nodes": [ { - "id": "com.google.guava:guava", + "id": "com.google.errorprone:error_prone_annotations", "meta": { - "repo_archived": false, - "repo_language": "Java", - "repo_last_commit": "2025-12-18", - "repo_last_release": "2025-06-26", - "repo_maintainers": [ - "Randgalt", - "cammckenzie", - "tisonkun", - "dragonsinth", - "kezhuw" - ], - "repo_owner": "apache", - "repo_stars": 3165, - "repo_topics": [ - "consensus", - "curator", - "database", - "java", - "zookeeper" - ], - "repo_url": "https://github.com/apache/curator", - "version": "33.4.8-jre" + "version": "2.39.0" } }, { "id": "com.google.guava:failureaccess", "meta": { - "repo_archived": false, - "repo_language": "Scala", - "repo_last_commit": "2025-12-23", - "repo_maintainers": [ - "dongjoon-hyun", - "HyukjinKwon", - "zhengruifeng", - "mateiz", - "rxin" - ], - "repo_owner": "apache", - "repo_stars": 42526, - "repo_topics": [ - "big-data", - "java", - "jdbc", - "python", - "r", - "scala", - "spark", - "sql" - ], - "repo_url": "https://github.com/apache/spark", "version": "1.0.3" } }, { - "id": "com.google.guava:listenablefuture", + "id": "com.google.guava:guava", "meta": { - "repo_archived": false, - "repo_language": "Java", - "repo_last_commit": "2025-12-22", - "repo_last_release": "2025-12-17", - "repo_maintainers": [ - "gsmet", - "geoand", - "stuartwdouglas", - "gastaldi" - ], - "repo_owner": "quarkusio", - "repo_stars": 15356, - "repo_topics": [ - "cloud-native", - "hacktoberfest", - "java", - "kubernetes", - "reactive" - ], - "repo_url": "https://github.com/quarkusio/quarkus", - "version": "9999.0-empty-to-avoid-conflict-with-guava" + "version": "33.4.8-jre" } }, { - "id": "org.jspecify:jspecify", + "id": "com.google.guava:listenablefuture", "meta": { - "repo_archived": false, - "repo_language": "Java", - "repo_last_commit": "2025-12-22", - "repo_last_release": "2025-09-16", - "repo_maintainers": [ - "codahale", - "joschi", - "arteam", - "jplock" - ], - "repo_owner": "dropwizard", - "repo_stars": 8577, - "repo_topics": [ - "dropwizard", - "hibernate", - "java", - "jax-rs", - "jdbi", - "jersey-framework", - "jersey2", - "jetty", - "rest", - "restful-webservices", - "web-framework" - ], - "repo_url": "https://github.com/dropwizard/dropwizard", - "version": "1.0.0" + "version": "9999.0-empty-to-avoid-conflict-with-guava" } }, { - "id": "com.google.errorprone:error_prone_annotations", + "id": "com.google.j2objc:j2objc-annotations", "meta": { - "repo_archived": false, - "repo_language": "Java", - "repo_last_commit": "2025-12-19", - "repo_maintainers": [ - "normanmaurer", - "trustin", - "Scottmitch", - "netty-project-bot", - "chrisvest" - ], - "repo_owner": "netty", - "repo_stars": 34658, - "repo_url": "https://github.com/netty/netty", - "version": "2.39.0" + "version": "3.0.0" } }, { - "id": "com.google.j2objc:j2objc-annotations", + "id": "org.jspecify:jspecify", "meta": { - "repo_archived": false, - "repo_language": "Java", - "repo_last_commit": "2025-12-19", - "repo_maintainers": [ - "normanmaurer", - "trustin", - "Scottmitch", - "netty-project-bot", - "chrisvest" - ], - "repo_owner": "netty", - "repo_stars": 34658, - "repo_url": "https://github.com/netty/netty", - "version": "3.0.0" + "version": "1.0.0" } } ], diff --git a/examples/real/console.json b/examples/real/console.json index ed1eb03..f513009 100644 --- a/examples/real/console.json +++ b/examples/real/console.json @@ -1,102 +1,21 @@ { "nodes": [ { - "id": "symfony/polyfill-mbstring", - "meta": { - "author": "Nicolas Grekas", - "description": "Symfony polyfill for the Mbstring extension", - "license": "MIT", - "repo_archived": false, - "repo_language": "PHP", - "repo_last_commit": "2025-08-19", - "repo_maintainers": [ - "nicolas-grekas", - "fabpot", - "stof", - "IonBazan", - "keradus" - ], - "repo_owner": "symfony", - "repo_stars": 7859, - "repo_topics": [ - "compatibility", - "component", - "javascript", - "mbstring", - "polyfill", - "portable", - "shim", - "symfony", - "symfony-component", - "symfony-polyfill" - ], - "repo_url": "https://github.com/symfony/polyfill-mbstring", - "version": "v1.33.0" - } - }, - { - "id": "symfony/service-contracts", + "id": "psr/container", "meta": { - "author": "Nicolas Grekas", - "description": "Generic abstractions related to writing services", + "author": "PHP-FIG", + "description": "Common Container Interface (PHP FIG PSR-11)", "license": "MIT", - "repo_archived": false, - "repo_language": "PHP", - "repo_last_commit": "2025-12-13", - "repo_last_release": "2020-09-08", - "repo_maintainers": [ - "nicolas-grekas", - "fabpot", - "derrabus", - "kbond", - "xabbuh" - ], - "repo_owner": "symfony", - "repo_stars": 2630, - "repo_topics": [ - "contract", - "php", - "service", - "symfony", - "symfony-contract" - ], - "repo_url": "https://github.com/symfony/service-contracts", - "version": "v3.6.1" + "version": "2.0.2" } }, { - "id": "symfony/string", + "id": "symfony/console", "meta": { - "author": "Nicolas Grekas", - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "author": "Fabien Potencier", + "description": "Eases the creation of beautiful and testable command line interfaces", "license": "MIT", - "repo_archived": false, - "repo_language": "PHP", - "repo_last_commit": "2025-12-19", - "repo_last_release": "2025-12-07", - "repo_maintainers": [ - "nicolas-grekas", - "fabpot", - "derrabus", - "xabbuh", - "fancyweb" - ], - "repo_owner": "symfony", - "repo_stars": 1781, - "repo_topics": [ - "component", - "grapheme", - "i18n", - "php", - "string", - "symfony", - "symfony-component", - "unicode", - "utf-8", - "utf8" - ], - "repo_url": "https://github.com/symfony/string", - "version": "v8.0.1" + "version": "v8.0.4" } }, { @@ -105,62 +24,15 @@ "author": "Nicolas Grekas", "description": "A generic function and convention to trigger deprecation notices", "license": "MIT", - "repo_archived": false, - "repo_language": "PHP", - "repo_last_commit": "2025-05-25", - "repo_last_release": "2020-09-08", - "repo_maintainers": [ - "nicolas-grekas", - "fabpot", - "derrabus", - "xabbuh", - "cedric-anne" - ], - "repo_owner": "symfony", - "repo_stars": 2108, - "repo_topics": [ - "contract", - "deprecation", - "php", - "symfony", - "symfony-contract" - ], - "repo_url": "https://github.com/symfony/deprecation-contracts", "version": "v3.6.0" } }, { - "id": "symfony/polyfill-intl-normalizer", + "id": "symfony/polyfill-ctype", "meta": { - "author": "Nicolas Grekas", - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "author": "Gert de Pagter", + "description": "Symfony polyfill for ctype functions", "license": "MIT", - "repo_archived": false, - "repo_language": "PHP", - "repo_last_commit": "2025-08-19", - "repo_maintainers": [ - "nicolas-grekas", - "fabpot", - "derrabus", - "stof", - "DavidPrevot" - ], - "repo_owner": "symfony", - "repo_stars": 2071, - "repo_topics": [ - "compatibility", - "component", - "intl", - "javascript", - "normalizer", - "polyfill", - "portable", - "shim", - "symfony", - "symfony-component", - "symfony-polyfill" - ], - "repo_url": "https://github.com/symfony/polyfill-intl-normalizer", "version": "v1.33.0" } }, @@ -170,122 +42,43 @@ "author": "Nicolas Grekas", "description": "Symfony polyfill for intl's grapheme_* functions", "license": "MIT", - "repo_archived": false, - "repo_language": "PHP", - "repo_last_commit": "2025-08-19", - "repo_maintainers": [ - "nicolas-grekas", - "fabpot", - "gharlan", - "Ayesh", - "azjezz" - ], - "repo_owner": "symfony", - "repo_stars": 1733, - "repo_topics": [ - "compatibility", - "component", - "grapheme", - "intl", - "javascript", - "polyfill", - "portable", - "shim", - "symfony", - "symfony-component", - "symfony-polyfill" - ], - "repo_url": "https://github.com/symfony/polyfill-intl-grapheme", "version": "v1.33.0" } }, { - "id": "psr/container", + "id": "symfony/polyfill-intl-normalizer", "meta": { - "author": "PHP-FIG", - "description": "Common Container Interface (PHP FIG PSR-11)", + "author": "Nicolas Grekas", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "license": "MIT", - "repo_archived": false, - "repo_language": "PHP", - "repo_last_commit": "2024-02-05", - "repo_last_release": "2021-11-05", - "repo_maintainers": [ - "mnapoli", - "moufmouf", - "Ocramius", - "weierophinney", - "Jean85" - ], - "repo_owner": "php-fig", - "repo_stars": 10023, - "repo_url": "https://github.com/php-fig/container", - "version": "2.0.2" + "version": "v1.33.0" } }, { - "id": "symfony/polyfill-ctype", + "id": "symfony/polyfill-mbstring", "meta": { - "author": "Gert de Pagter", - "description": "Symfony polyfill for ctype functions", + "author": "Nicolas Grekas", + "description": "Symfony polyfill for the Mbstring extension", "license": "MIT", - "repo_archived": false, - "repo_language": "PHP", - "repo_last_commit": "2025-08-19", - "repo_maintainers": [ - "nicolas-grekas", - "BackEndTea", - "fabpot", - "cedric-anne", - "GrahamCampbell" - ], - "repo_owner": "symfony", - "repo_stars": 4053, - "repo_topics": [ - "compatibility", - "component", - "ctype", - "javascript", - "polyfill", - "portable", - "symfony", - "symfony-component", - "symfony-polyfill" - ], - "repo_url": "https://github.com/symfony/polyfill-ctype", "version": "v1.33.0" } }, { - "id": "symfony/console", + "id": "symfony/service-contracts", "meta": { - "author": "Fabien Potencier", - "description": "Eases the creation of beautiful and testable command line interfaces", + "author": "Nicolas Grekas", + "description": "Generic abstractions related to writing services", "license": "MIT", - "repo_archived": false, - "repo_language": "PHP", - "repo_last_commit": "2025-12-22", - "repo_last_release": "2025-12-07", - "repo_maintainers": [ - "fabpot", - "nicolas-grekas", - "xabbuh", - "derrabus", - "chalasr" - ], - "repo_owner": "symfony", - "repo_stars": 9834, - "repo_topics": [ - "cli", - "command-line", - "component", - "console", - "php", - "symfony", - "symfony-component", - "terminal" - ], - "repo_url": "https://github.com/symfony/console", - "version": "v8.0.1" + "version": "v3.6.1" + } + }, + { + "id": "symfony/string", + "meta": { + "author": "Nicolas Grekas", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "license": "MIT", + "version": "v8.0.4" } } ], @@ -302,14 +95,6 @@ "from": "symfony/console", "to": "symfony/string" }, - { - "from": "symfony/service-contracts", - "to": "psr/container" - }, - { - "from": "symfony/service-contracts", - "to": "symfony/deprecation-contracts" - }, { "from": "symfony/string", "to": "symfony/polyfill-intl-normalizer" @@ -325,6 +110,14 @@ { "from": "symfony/string", "to": "symfony/polyfill-intl-grapheme" + }, + { + "from": "symfony/service-contracts", + "to": "psr/container" + }, + { + "from": "symfony/service-contracts", + "to": "symfony/deprecation-contracts" } ] } diff --git a/examples/real/flask.json b/examples/real/flask.json index 98e303e..94942f3 100644 --- a/examples/real/flask.json +++ b/examples/real/flask.json @@ -1,254 +1,78 @@ { "nodes": [ { - "id": "importlib-metadata", + "id": "blinker", "meta": { - "description": "Read metadata from Python packages", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-12-21", - "repo_last_release": "2025-12-21", - "repo_maintainers": [ - "jaraco", - "warsaw", - "abravalheri", - "jherland", - "sjma3" - ], - "repo_owner": "python", - "repo_stars": 138, - "repo_url": "https://github.com/python/importlib_metadata", - "version": "8.7.1" + "author": "Jason Kirtland", + "description": "Fast, simple object-to-object and broadcast signaling", + "license": "MIT License", + "version": "1.9.0" } }, { - "id": "itsdangerous", + "id": "click", "meta": { - "description": "Safely pass data to untrusted environments and back.", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-06-14", - "repo_last_release": "2024-04-16", - "repo_maintainers": [ - "davidism", - "mitsuhiko" - ], - "repo_owner": "pallets", - "repo_stars": 3093, - "repo_topics": [ - "hmac", - "itsdangerous", - "pallets", - "python", - "security", - "serialization" - ], - "repo_url": "https://github.com/pallets/itsdangerous", - "version": "2.2.0" + "description": "Composable command line interface toolkit", + "version": "8.3.1" } }, { - "id": "werkzeug", + "id": "colorama", "meta": { - "description": "The comprehensive WSGI web application library.", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-12-02", - "repo_last_release": "2025-11-29", - "repo_maintainers": [ - "mitsuhiko", - "davidism", - "untitaker", - "DasIch" - ], - "repo_owner": "pallets", - "repo_stars": 6825, - "repo_topics": [ - "http", - "pallets", - "python", - "werkzeug", - "wsgi" - ], - "repo_url": "https://github.com/pallets/werkzeug", - "version": "3.1.4" + "description": "Cross-platform colored terminal text.", + "license": "BSD License", + "version": "0.4.6" } }, { - "id": "colorama", + "id": "flask", "meta": { - "description": "Cross-platform colored terminal text.", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-07-09", - "repo_maintainers": [ - "tartley", - "wiggin15", - "hugovk", - "njsmith", - "jdufresne" - ], - "repo_owner": "tartley", - "repo_stars": 3758, - "repo_url": "https://github.com/tartley/colorama", - "version": "0.4.6" + "description": "A simple framework for building complex web applications.", + "version": "3.1.2" } }, { - "id": "jinja2", + "id": "importlib-metadata", "meta": { - "description": "A very fast and expressive template engine.", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-06-14", - "repo_last_release": "2025-03-05", - "repo_maintainers": [ - "mitsuhiko", - "davidism", - "untitaker" - ], - "repo_owner": "pallets", - "repo_stars": 11342, - "repo_topics": [ - "jinja", - "jinja2", - "pallets", - "python", - "template-engine", - "templates" - ], - "repo_url": "https://github.com/pallets/jinja", - "version": "3.1.6" + "description": "Read metadata from Python packages", + "version": "8.7.1" } }, { - "id": "markupsafe", + "id": "itsdangerous", "meta": { - "description": "Safely add untrusted strings to HTML/XML markup.", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-09-27", - "repo_last_release": "2025-09-27", - "repo_maintainers": [ - "davidism", - "mitsuhiko" - ], - "repo_owner": "pallets", - "repo_stars": 682, - "repo_topics": [ - "html", - "html-escape", - "jinja", - "markupsafe", - "pallets", - "python", - "template-engine" - ], - "repo_url": "https://github.com/pallets/markupsafe", - "version": "3.0.3" + "description": "Safely pass data to untrusted environments and back.", + "license": "BSD License", + "version": "2.2.0" } }, { - "id": "zipp", + "id": "jinja2", "meta": { - "description": "Backport of pathlib-compatible object wrapper for zip files", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-10-19", - "repo_last_release": "2025-06-08", - "repo_maintainers": [ - "jaraco", - "DimitriPapadopoulos", - "layday", - "Avasam", - "barneygale" - ], - "repo_owner": "jaraco", - "repo_stars": 67, - "repo_url": "https://github.com/jaraco/zipp", - "version": "3.23.0" + "description": "A very fast and expressive template engine.", + "license": "BSD License", + "version": "3.1.6" } }, { - "id": "flask", + "id": "markupsafe", "meta": { - "description": "A simple framework for building complex web applications.", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-11-28", - "repo_last_release": "2025-08-19", - "repo_maintainers": [ - "davidism", - "mitsuhiko", - "untitaker", - "rduplain", - "greyli" - ], - "repo_owner": "pallets", - "repo_stars": 70955, - "repo_topics": [ - "flask", - "jinja", - "pallets", - "python", - "web-framework", - "werkzeug", - "wsgi" - ], - "repo_url": "https://github.com/pallets/flask", - "version": "3.1.2" + "description": "Safely add untrusted strings to HTML/XML markup.", + "version": "3.0.3" } }, { - "id": "blinker", + "id": "werkzeug", "meta": { - "author": "Jason Kirtland", - "description": "Fast, simple object-to-object and broadcast signaling", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-11-19", - "repo_last_release": "2024-11-08", - "repo_maintainers": [ - "davidism", - "jek", - "Secrus", - "pgjones" - ], - "repo_owner": "pallets-eco", - "repo_stars": 2008, - "repo_topics": [ - "blinker", - "python", - "signals" - ], - "repo_url": "https://github.com/pallets-eco/blinker", - "version": "1.9.0" + "description": "The comprehensive WSGI web application library.", + "version": "3.1.5" } }, { - "id": "click", + "id": "zipp", "meta": { - "description": "Composable command line interface toolkit", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-12-10", - "repo_last_release": "2025-11-15", - "repo_maintainers": [ - "davidism", - "mitsuhiko", - "Rowlando13", - "untitaker" - ], - "repo_owner": "pallets", - "repo_stars": 17074, - "repo_topics": [ - "cli", - "click", - "pallets", - "python" - ], - "repo_url": "https://github.com/pallets/click", - "version": "8.3.1" + "description": "Backport of pathlib-compatible object wrapper for zip files", + "version": "3.23.0" } } ], @@ -281,10 +105,6 @@ "from": "flask", "to": "werkzeug" }, - { - "from": "werkzeug", - "to": "markupsafe" - }, { "from": "jinja2", "to": "markupsafe" @@ -296,6 +116,10 @@ { "from": "importlib-metadata", "to": "zipp" + }, + { + "from": "werkzeug", + "to": "markupsafe" } ] } diff --git a/examples/real/htmd.json b/examples/real/htmd.json deleted file mode 100644 index 03ec20f..0000000 --- a/examples/real/htmd.json +++ /dev/null @@ -1,449 +0,0 @@ -{ - "nodes": [ - { - "id": "click", - "meta": { - "description": "Composable command line interface toolkit", - "version": "8.3.1" - } - }, - { - "id": "csscompressor", - "meta": { - "author": "Yury Selivanov", - "description": "A python port of YUI CSS Compressor", - "license": "BSD", - "version": "0.9.5" - } - }, - { - "id": "feedwerk", - "meta": { - "author": "phil", - "description": "The atom feed generator from werkzeug.", - "license": "BSD-3-Clause", - "version": "1.2.0" - } - }, - { - "id": "flask", - "meta": { - "description": "A simple framework for building complex web applications.", - "version": "3.1.2" - } - }, - { - "id": "pyyaml", - "meta": { - "author": "Kirill Simonov", - "description": "YAML parser and emitter for Python", - "license": "MIT", - "version": "6.0.3" - } - }, - { - "id": "frozen-flask", - "meta": { - "description": "Freezes a Flask application into a set of static files.", - "license": "Copyright (c) 2010-2012 by Simon Sapin and contributors. See AUTHORS\n for more details.\n \n Some rights reserved.\n \n Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions are\n met:\n \n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n \n * Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the following\n disclaimer in the documentation and/or other materials provided\n with the distribution.\n \n * The names of the contributors may not be used to endorse or\n promote products derived from this software without specific\n prior written permission.\n \n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.", - "version": "1.0.2" - } - }, - { - "id": "htmlmin2", - "meta": { - "author": "Dave Mankoff", - "description": "An HTML Minifier", - "license": "BSD", - "version": "0.1.13" - } - }, - { - "id": "beautifulsoup4", - "meta": { - "description": "Screen-scraping library", - "license": "MIT License", - "version": "4.14.3" - } - }, - { - "id": "soupsieve", - "meta": { - "description": "A modern CSS selector implementation for Beautiful Soup.", - "version": "2.8.1" - } - }, - { - "id": "typing-extensions", - "meta": { - "description": "Backported and Experimental Type Hints for Python 3.9+", - "version": "4.15.0" - } - }, - { - "id": "jinja2", - "meta": { - "description": "A very fast and expressive template engine.", - "version": "3.1.6" - } - }, - { - "id": "zipp", - "meta": { - "description": "Backport of pathlib-compatible object wrapper for zip files", - "version": "3.23.0" - } - }, - { - "id": "markdown", - "meta": { - "author": "Manfred Stienstra, Yuri Takhteyev", - "description": "Python implementation of John Gruber's Markdown.", - "version": "3.10" - } - }, - { - "id": "jsmin", - "meta": { - "author": "Dave St.Germain", - "description": "JavaScript minifier.", - "license": "MIT License", - "version": "3.0.1" - } - }, - { - "id": "htmd", - "meta": { - "virtual": true - } - }, - { - "id": "colorama", - "meta": { - "description": "Cross-platform colored terminal text.", - "version": "0.4.6" - } - }, - { - "id": "blinker", - "meta": { - "author": "Jason Kirtland", - "description": "Fast, simple object-to-object and broadcast signaling", - "version": "1.9.0" - } - }, - { - "id": "importlib-metadata", - "meta": { - "description": "Read metadata from Python packages", - "version": "8.7.0" - } - }, - { - "id": "flask-flatpages", - "meta": { - "author": "Simon Sapin, Igor Davydenko, Padraic Calpin", - "description": "Provides flat static pages to a Flask application", - "version": "0.8.3" - } - }, - { - "id": "pytz", - "meta": { - "author": "Stuart Bishop", - "description": "World timezone definitions, modern and historical", - "license": "MIT", - "version": "2025.2" - } - }, - { - "id": "watchdog", - "meta": { - "author": "Mickaël Schoentgen", - "description": "Filesystem events monitoring", - "license": "Apache-2.0", - "version": "6.0.0" - } - }, - { - "id": "werkzeug", - "meta": { - "description": "The comprehensive WSGI web application library.", - "version": "3.1.4" - } - }, - { - "id": "markupsafe", - "meta": { - "description": "Safely add untrusted strings to HTML/XML markup.", - "version": "3.0.3" - } - }, - { - "id": "itsdangerous", - "meta": { - "description": "Safely pass data to untrusted environments and back.", - "version": "2.2.0" - } - }, - { - "id": "six", - "meta": { - "author": "Benjamin Peterson", - "description": "Python 2 and 3 compatibility utilities", - "license": "MIT", - "version": "1.17.0" - } - }, - { - "id": "pygments", - "meta": { - "description": "Pygments is a syntax highlighting package written in Python.", - "license": "BSD-2-Clause", - "version": "2.19.2" - } - } - ], - "edges": [ - { - "from": "beautifulsoup4", - "to": "soupsieve" - }, - { - "from": "beautifulsoup4", - "to": "typing-extensions" - }, - { - "from": "htmd", - "to": "beautifulsoup4" - }, - { - "from": "click", - "to": "colorama" - }, - { - "from": "htmd", - "to": "click" - }, - { - "from": "htmd", - "to": "csscompressor" - }, - { - "from": "feedwerk", - "to": "werkzeug" - }, - { - "from": "feedwerk", - "to": "markupsafe" - }, - { - "from": "werkzeug", - "to": "markupsafe" - }, - { - "from": "htmd", - "to": "feedwerk" - }, - { - "from": "flask", - "to": "blinker" - }, - { - "from": "flask", - "to": "click" - }, - { - "from": "flask", - "to": "importlib-metadata" - }, - { - "from": "flask", - "to": "itsdangerous" - }, - { - "from": "flask", - "to": "jinja2" - }, - { - "from": "flask", - "to": "markupsafe" - }, - { - "from": "flask", - "to": "werkzeug" - }, - { - "from": "werkzeug", - "to": "markupsafe" - }, - { - "from": "click", - "to": "colorama" - }, - { - "from": "jinja2", - "to": "markupsafe" - }, - { - "from": "importlib-metadata", - "to": "zipp" - }, - { - "from": "importlib-metadata", - "to": "typing-extensions" - }, - { - "from": "htmd", - "to": "flask" - }, - { - "from": "flask-flatpages", - "to": "flask" - }, - { - "from": "flask-flatpages", - "to": "jinja2" - }, - { - "from": "flask-flatpages", - "to": "markdown" - }, - { - "from": "flask-flatpages", - "to": "pyyaml" - }, - { - "from": "flask-flatpages", - "to": "pytz" - }, - { - "from": "flask-flatpages", - "to": "six" - }, - { - "from": "flask", - "to": "blinker" - }, - { - "from": "flask", - "to": "click" - }, - { - "from": "flask", - "to": "importlib-metadata" - }, - { - "from": "flask", - "to": "itsdangerous" - }, - { - "from": "flask", - "to": "jinja2" - }, - { - "from": "flask", - "to": "markupsafe" - }, - { - "from": "flask", - "to": "werkzeug" - }, - { - "from": "jinja2", - "to": "markupsafe" - }, - { - "from": "werkzeug", - "to": "markupsafe" - }, - { - "from": "importlib-metadata", - "to": "zipp" - }, - { - "from": "importlib-metadata", - "to": "typing-extensions" - }, - { - "from": "click", - "to": "colorama" - }, - { - "from": "htmd", - "to": "flask-flatpages" - }, - { - "from": "frozen-flask", - "to": "flask" - }, - { - "from": "flask", - "to": "blinker" - }, - { - "from": "flask", - "to": "click" - }, - { - "from": "flask", - "to": "importlib-metadata" - }, - { - "from": "flask", - "to": "itsdangerous" - }, - { - "from": "flask", - "to": "jinja2" - }, - { - "from": "flask", - "to": "markupsafe" - }, - { - "from": "flask", - "to": "werkzeug" - }, - { - "from": "importlib-metadata", - "to": "zipp" - }, - { - "from": "importlib-metadata", - "to": "typing-extensions" - }, - { - "from": "werkzeug", - "to": "markupsafe" - }, - { - "from": "jinja2", - "to": "markupsafe" - }, - { - "from": "click", - "to": "colorama" - }, - { - "from": "htmd", - "to": "frozen-flask" - }, - { - "from": "htmd", - "to": "htmlmin2" - }, - { - "from": "htmd", - "to": "jsmin" - }, - { - "from": "htmd", - "to": "pygments" - }, - { - "from": "htmd", - "to": "watchdog" - } - ] -} diff --git a/examples/real/openai.json b/examples/real/openai.json index 35215c3..2ec9be2 100644 --- a/examples/real/openai.json +++ b/examples/real/openai.json @@ -1,380 +1,121 @@ { "nodes": [ { - "id": "h11", + "id": "annotated-types", "meta": { - "author": "Nathaniel J. Smith", - "description": "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1", - "license": "MIT", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-04-24", - "repo_maintainers": [ - "njsmith", - "pgjones", - "Lukasa", - "cdeler", - "lovelydinosaur" - ], - "repo_owner": "python-hyper", - "repo_stars": 538, - "repo_topics": [ - "http", - "python", - "sans-io" - ], - "repo_url": "https://github.com/python-hyper/h11", - "version": "0.16.0" + "description": "Reusable constraint types to use with typing.Annotated", + "license": "MIT License", + "version": "0.7.0" } }, { - "id": "exceptiongroup", + "id": "anyio", "meta": { - "description": "Backport of PEP 654 (exception groups)", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-11-21", - "repo_last_release": "2025-11-21", - "repo_maintainers": [ - "agronholm", - "jakkdl", - "Zac-HD", - "cfbolz" - ], - "repo_owner": "agronholm", - "repo_stars": 48, - "repo_url": "https://github.com/agronholm/exceptiongroup", - "version": "1.3.1" + "description": "High-level concurrency and networking framework on top of asyncio or Trio", + "version": "4.12.1" } }, { - "id": "httpx", + "id": "certifi", "meta": { - "description": "The next generation HTTP client.", - "license": "BSD-3-Clause", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-12-17", - "repo_last_release": "2024-12-06", - "repo_maintainers": [ - "lovelydinosaur", - "florimondmanca", - "sethmlarson", - "karpetrosyan" - ], - "repo_owner": "encode", - "repo_stars": 14857, - "repo_topics": [ - "asyncio", - "http", - "python", - "trio" - ], - "repo_url": "https://github.com/encode/httpx", - "version": "0.28.1" + "author": "Kenneth Reitz", + "description": "Python package for providing Mozilla's CA Bundle.", + "license": "Mozilla Public License 2.0 (MPL 2.0)", + "version": "2026.1.4" } }, { - "id": "jiter", + "id": "colorama", "meta": { - "description": "Fast iterable JSON parser.", - "repo_archived": false, - "repo_language": "Rust", - "repo_last_commit": "2025-11-09", - "repo_last_release": "2025-11-09", - "repo_maintainers": [ - "samuelcolvin", - "davidhewitt", - "Viicos", - "jessekrubin", - "msimacek" - ], - "repo_owner": "pydantic", - "repo_stars": 481, - "repo_topics": [ - "json", - "json-parser", - "pydantic", - "rust" - ], - "repo_url": "https://github.com/pydantic/jiter", - "version": "0.12.0" + "description": "Cross-platform colored terminal text.", + "license": "BSD License", + "version": "0.4.6" } }, { - "id": "pydantic-core", + "id": "distro", "meta": { - "description": "Core functionality for Pydantic validation and serialization", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-11-10", - "repo_last_release": "2025-11-04", - "repo_maintainers": [ - "samuelcolvin", - "davidhewitt", - "adriangb", - "sydney-runkle" - ], - "repo_owner": "pydantic", - "repo_stars": 1739, - "repo_topics": [ - "json-schema", - "parsing", - "pydantic", - "rust", - "schema", - "validation" - ], - "repo_url": "https://github.com/pydantic/pydantic-core", - "version": "2.41.5" + "author": "Nir Cohen", + "description": "Distro - an OS platform information API", + "license": "Apache Software License", + "version": "1.9.0" } }, { - "id": "typing-inspection", + "id": "exceptiongroup", "meta": { - "description": "Runtime typing introspection tools", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-10-01", - "repo_last_release": "2025-10-01", - "repo_maintainers": [ - "Viicos", - "samuelcolvin", - "cdce8p" - ], - "repo_owner": "pydantic", - "repo_stars": 57, - "repo_url": "https://github.com/pydantic/typing-inspection", - "version": "0.4.2" + "description": "Backport of PEP 654 (exception groups)", + "license": "MIT License", + "version": "1.3.1" } }, { - "id": "anyio", + "id": "h11", "meta": { - "description": "High-level concurrency and networking framework on top of asyncio or Trio", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-12-22", - "repo_last_release": "2025-11-30", - "repo_maintainers": [ - "agronholm", - "graingert", - "gschaffner", - "davidbrochart" - ], - "repo_owner": "agronholm", - "repo_stars": 2331, - "repo_topics": [ - "async-await", - "asyncio", - "trio" - ], - "repo_url": "https://github.com/agronholm/anyio", - "version": "4.12.0" + "author": "Nathaniel J. Smith", + "description": "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1", + "license": "MIT License", + "version": "0.16.0" } }, { - "id": "idna", + "id": "httpcore", "meta": { - "description": "Internationalized Domain Names in Applications (IDNA)", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-10-20", - "repo_last_release": "2025-10-12", - "repo_maintainers": [ - "kjd", - "hugovk", - "jdufresne", - "jribbens", - "slingamn" - ], - "repo_owner": "kjd", - "repo_stars": 273, - "repo_topics": [ - "dns", - "hacktoberfest", - "idna", - "python", - "unicode" - ], - "repo_url": "https://github.com/kjd/idna", - "version": "3.11" + "description": "A minimal low-level HTTP client.", + "license": "BSD License", + "version": "1.0.9" } }, { - "id": "certifi", + "id": "httpx", "meta": { - "author": "Kenneth Reitz", - "description": "Python package for providing Mozilla's CA Bundle.", - "license": "MPL-2.0", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-12-22", - "repo_maintainers": [ - "Lukasa", - "alex", - "sigmavirus24" - ], - "repo_owner": "certifi", - "repo_stars": 943, - "repo_url": "https://github.com/certifi/python-certifi", - "version": "2025.11.12" + "description": "The next generation HTTP client.", + "license": "BSD License", + "version": "0.28.1" } }, { - "id": "httpcore", + "id": "idna", "meta": { - "description": "A minimal low-level HTTP client.", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-12-06", - "repo_last_release": "2025-04-24", - "repo_maintainers": [ - "lovelydinosaur", - "florimondmanca", - "karpetrosyan", - "cdeler" - ], - "repo_owner": "encode", - "repo_stars": 527, - "repo_url": "https://github.com/encode/httpcore", - "version": "1.0.9" + "description": "Internationalized Domain Names in Applications (IDNA)", + "version": "3.11" } }, { - "id": "colorama", + "id": "jiter", "meta": { - "description": "Cross-platform colored terminal text.", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-07-09", - "repo_maintainers": [ - "tartley", - "wiggin15", - "hugovk", - "njsmith", - "jdufresne" - ], - "repo_owner": "tartley", - "repo_stars": 3758, - "repo_url": "https://github.com/tartley/colorama", - "version": "0.4.6" + "description": "Fast iterable JSON parser.", + "license": "MIT License", + "version": "0.12.0" } }, { "id": "openai", "meta": { "description": "The official Python library for the openai API", - "license": "Apache-2.0", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-12-19", - "repo_last_release": "2025-12-19", - "repo_maintainers": [ - "stainless-bot", - "RobertCraigie", - "hallacy", - "rachellim" - ], - "repo_owner": "openai", - "repo_stars": 29537, - "repo_topics": [ - "openai", - "python" - ], - "repo_url": "https://github.com/openai/openai-python", - "version": "2.14.0" + "license": "Apache Software License", + "version": "2.16.0" } }, { - "id": "distro", + "id": "pydantic", "meta": { - "author": "Nir Cohen", - "description": "Distro - an OS platform information API", - "license": "Apache License, Version 2.0", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-11-14", - "repo_last_release": "2024-01-14", - "repo_maintainers": [ - "nir0s", - "andy-maier", - "jdufresne", - "HorlogeSkynet", - "SethMichaelLarson" - ], - "repo_owner": "python-distro", - "repo_stars": 279, - "repo_topics": [ - "distribution", - "distro", - "hacktoberfest", - "linux", - "linux-distribution", - "python", - "python-library" - ], - "repo_url": "https://github.com/python-distro/distro", - "version": "1.9.0" + "description": "Data validation using Python type hints", + "version": "2.12.5" } }, { - "id": "pydantic", + "id": "pydantic-core", "meta": { - "description": "Data validation using Python type hints", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-12-21", - "repo_last_release": "2025-11-26", - "repo_maintainers": [ - "samuelcolvin", - "Viicos", - "sydney-runkle", - "dmontagu" - ], - "repo_owner": "pydantic", - "repo_stars": 26251, - "repo_topics": [ - "hints", - "json-schema", - "parsing", - "pydantic", - "python", - "python310", - "python311", - "python312", - "python313", - "python39", - "validation" - ], - "repo_url": "https://github.com/pydantic/pydantic", - "version": "2.12.5" + "description": "Core functionality for Pydantic validation and serialization", + "version": "2.41.5" } }, { "id": "sniffio", "meta": { "description": "Sniff out which async library your code is running under", - "license": "MIT OR Apache-2.0", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2024-02-25", - "repo_maintainers": [ - "njsmith", - "graingert", - "pquentin", - "agronholm", - "hugovk" - ], - "repo_owner": "python-trio", - "repo_stars": 144, - "repo_topics": [ - "async", - "asyncio", - "python", - "trio" - ], - "repo_url": "https://github.com/python-trio/sniffio", + "license": "Apache Software License", "version": "1.3.1" } }, @@ -383,85 +124,21 @@ "meta": { "description": "Fast, Extensible Progress Meter", "license": "MPL-2.0 AND MIT", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-05-22", - "repo_last_release": "2024-11-27", - "repo_maintainers": [ - "casperdcl", - "lrq3000", - "altendky", - "hadim", - "richardsheridan" - ], - "repo_owner": "tqdm", - "repo_stars": 30795, - "repo_topics": [ - "cli", - "closember", - "console", - "discord", - "gui", - "jupyter", - "keras", - "meter", - "pandas", - "parallel", - "progress", - "progress-bar", - "progressbar", - "progressmeter", - "python", - "rate", - "telegram", - "terminal", - "time", - "utilities" - ], - "repo_url": "https://github.com/tqdm/tqdm", - "version": "4.67.1" + "version": "4.67.2" } }, { "id": "typing-extensions", "meta": { "description": "Backported and Experimental Type Hints for Python 3.9+", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-12-02", - "repo_last_release": "2025-08-25", - "repo_maintainers": [ - "JelleZijlstra", - "AlexWaygood", - "srittau", - "brianschubert", - "Daraan" - ], - "repo_owner": "python", - "repo_stars": 543, - "repo_url": "https://github.com/python/typing_extensions", "version": "4.15.0" } }, { - "id": "annotated-types", + "id": "typing-inspection", "meta": { - "description": "Reusable constraint types to use with typing.Annotated", - "repo_archived": false, - "repo_language": "Python", - "repo_last_commit": "2025-11-10", - "repo_last_release": "2024-05-20", - "repo_maintainers": [ - "adriangb", - "samuelcolvin", - "Zac-HD", - "hugovk", - "Viicos" - ], - "repo_owner": "annotated-types", - "repo_stars": 585, - "repo_url": "https://github.com/annotated-types/annotated-types", - "version": "0.7.0" + "description": "Runtime typing introspection tools", + "version": "0.4.2" } } ], @@ -498,26 +175,6 @@ "from": "openai", "to": "typing-extensions" }, - { - "from": "httpx", - "to": "anyio" - }, - { - "from": "httpx", - "to": "certifi" - }, - { - "from": "httpx", - "to": "httpcore" - }, - { - "from": "httpx", - "to": "idna" - }, - { - "from": "tqdm", - "to": "colorama" - }, { "from": "anyio", "to": "exceptiongroup" @@ -546,29 +203,49 @@ "from": "pydantic", "to": "typing-inspection" }, - { - "from": "httpcore", - "to": "certifi" - }, - { - "from": "httpcore", - "to": "h11" - }, { "from": "exceptiongroup", "to": "typing-extensions" }, { - "from": "annotated-types", + "from": "typing-inspection", "to": "typing-extensions" }, { - "from": "typing-inspection", + "from": "annotated-types", "to": "typing-extensions" }, { "from": "pydantic-core", "to": "typing-extensions" + }, + { + "from": "httpx", + "to": "anyio" + }, + { + "from": "httpx", + "to": "certifi" + }, + { + "from": "httpx", + "to": "httpcore" + }, + { + "from": "httpx", + "to": "idna" + }, + { + "from": "tqdm", + "to": "colorama" + }, + { + "from": "httpcore", + "to": "certifi" + }, + { + "from": "httpcore", + "to": "h11" } ] } diff --git a/examples/real/rspec.json b/examples/real/rspec.json index 8c8bf71..5e8fbe3 100644 --- a/examples/real/rspec.json +++ b/examples/real/rspec.json @@ -1,70 +1,12 @@ { "nodes": [ - { - "id": "rspec-mocks", - "meta": { - "author": "Steven Baker, David Chelimsky, Myron Marston", - "description": "RSpec's 'test double' framework, with support for stubbing and mocking", - "downloads": 1086156206, - "license": "MIT", - "repo_archived": false, - "repo_language": "Ruby", - "repo_last_commit": "2025-12-12", - "repo_maintainers": [ - "myronmarston", - "JonRowe", - "dchelimsky", - "alindeman", - "xaviershay" - ], - "repo_owner": "rspec", - "repo_stars": 81, - "repo_url": "https://github.com/rspec/rspec", - "version": "3.13.7" - } - }, - { - "id": "rspec-support", - "meta": { - "author": "David Chelimsky, Myron Marson, Jon Rowe, Sam Phippen, Xaviery Shay, Bradley Schaefer", - "description": "Support utilities for RSpec gems", - "downloads": 1076638938, - "license": "MIT", - "repo_archived": false, - "repo_language": "Ruby", - "repo_last_commit": "2025-12-12", - "repo_maintainers": [ - "myronmarston", - "JonRowe", - "dchelimsky", - "alindeman", - "xaviershay" - ], - "repo_owner": "rspec", - "repo_stars": 81, - "repo_url": "https://github.com/rspec/rspec", - "version": "3.13.6" - } - }, { "id": "diff-lcs", "meta": { "author": "Austin Ziegler", "description": "Diff::LCS computes the difference between two Enumerable sequences using the\nMcIlroy-Hunt longest common subsequence (LCS) algorithm. It includes utilities\nto create a simple HTML diff output format and a standard diff-like tool.\n\nThis is release 1.6.1, providing a simple extension that allows for\nDiff::LCS::Change objects to be treated implicitly as arrays and fixes a number\nof formatting issues.\n\nRuby versions below 2.5 are soft-deprecated, which means that older versions are\nno longer part of the CI test suite. If any changes have been introduced that\nbreak those versions, bug reports and patches will be accepted, but it will be\nup to the reporter to verify any fixes prior to release. The next major release\nwill completely break compatibility.", - "downloads": 1116219630, + "downloads": 1128541527, "license": "MIT, Artistic-1.0-Perl, GPL-2.0-or-later", - "repo_archived": false, - "repo_language": "Ruby", - "repo_last_commit": "2025-12-01", - "repo_maintainers": [ - "halostatue", - "tiendo1011", - "Annih", - "JonRowe" - ], - "repo_owner": "halostatue", - "repo_stars": 300, - "repo_url": "https://github.com/halostatue/diff-lcs", "version": "1.6.2" } }, @@ -73,21 +15,8 @@ "meta": { "author": "Steven Baker, David Chelimsky, Myron Marston", "description": "BDD for Ruby", - "downloads": 938043191, + "downloads": 946513541, "license": "MIT", - "repo_archived": false, - "repo_language": "Ruby", - "repo_last_commit": "2025-12-12", - "repo_maintainers": [ - "myronmarston", - "JonRowe", - "dchelimsky", - "alindeman", - "xaviershay" - ], - "repo_owner": "rspec", - "repo_stars": 81, - "repo_url": "https://github.com/rspec/rspec", "version": "3.13.2" } }, @@ -96,21 +25,8 @@ "meta": { "author": "Steven Baker, David Chelimsky, Chad Humphries, Myron Marston", "description": "BDD for Ruby. RSpec runner and example groups.", - "downloads": 1091901505, + "downloads": 1102951587, "license": "MIT", - "repo_archived": false, - "repo_language": "Ruby", - "repo_last_commit": "2025-12-12", - "repo_maintainers": [ - "myronmarston", - "JonRowe", - "dchelimsky", - "alindeman", - "xaviershay" - ], - "repo_owner": "rspec", - "repo_stars": 81, - "repo_url": "https://github.com/rspec/rspec", "version": "3.13.6" } }, @@ -119,23 +35,30 @@ "meta": { "author": "Steven Baker, David Chelimsky, Myron Marston", "description": "rspec-expectations provides a simple, readable API to express expected outcomes of a code example.", - "downloads": 1092953359, + "downloads": 1103861051, "license": "MIT", - "repo_archived": false, - "repo_language": "Ruby", - "repo_last_commit": "2025-12-12", - "repo_maintainers": [ - "myronmarston", - "JonRowe", - "dchelimsky", - "alindeman", - "xaviershay" - ], - "repo_owner": "rspec", - "repo_stars": 81, - "repo_url": "https://github.com/rspec/rspec", "version": "3.13.5" } + }, + { + "id": "rspec-mocks", + "meta": { + "author": "Steven Baker, David Chelimsky, Myron Marston", + "description": "RSpec's 'test double' framework, with support for stubbing and mocking", + "downloads": 1097081204, + "license": "MIT", + "version": "3.13.7" + } + }, + { + "id": "rspec-support", + "meta": { + "author": "David Chelimsky, Myron Marson, Jon Rowe, Sam Phippen, Xaviery Shay, Bradley Schaefer", + "description": "Support utilities for RSpec gems", + "downloads": 1087843559, + "license": "MIT", + "version": "3.13.7" + } } ], "edges": [ @@ -151,10 +74,6 @@ "from": "rspec", "to": "rspec-mocks" }, - { - "from": "rspec-core", - "to": "rspec-support" - }, { "from": "rspec-expectations", "to": "diff-lcs" @@ -170,6 +89,10 @@ { "from": "rspec-mocks", "to": "rspec-support" + }, + { + "from": "rspec-core", + "to": "rspec-support" } ] } diff --git a/examples/real/serde.json b/examples/real/serde.json index 15337f1..4d8359b 100644 --- a/examples/real/serde.json +++ b/examples/real/serde.json @@ -1,183 +1,60 @@ { "nodes": [ { - "id": "serde_core", + "id": "proc-macro2", "meta": { - "description": "Serde traits only, with no support for derive -- use the `serde` crate instead", - "downloads": 57875040, - "repo_archived": false, - "repo_language": "Rust", - "repo_last_commit": "2025-12-22", - "repo_last_release": "2025-09-27", - "repo_maintainers": [ - "dtolnay", - "erickt", - "Mingun", - "oli-obk", - "mitsuhiko" - ], - "repo_owner": "serde-rs", - "repo_stars": 10241, - "repo_topics": [ - "derive", - "no-std", - "rust", - "serde" - ], - "repo_url": "https://github.com/serde-rs/serde", - "version": "1.0.228" + "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", + "downloads": 919778105, + "version": "1.0.106" } }, { - "id": "serde_derive", + "id": "quote", "meta": { - "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", - "downloads": 694132674, - "repo_archived": false, - "repo_language": "Rust", - "repo_last_commit": "2025-12-22", - "repo_last_release": "2025-09-27", - "repo_maintainers": [ - "dtolnay", - "erickt", - "Mingun", - "oli-obk", - "mitsuhiko" - ], - "repo_owner": "serde-rs", - "repo_stars": 10241, - "repo_topics": [ - "derive", - "no-std", - "rust", - "serde" - ], - "repo_url": "https://github.com/serde-rs/serde", + "description": "Quasi-quoting macro quote!(...)", + "downloads": 899531495, + "version": "1.0.44" + } + }, + { + "id": "serde", + "meta": { + "description": "A generic serialization/deserialization framework", + "downloads": 798236839, "version": "1.0.228" } }, { - "id": "proc-macro2", + "id": "serde_core", "meta": { - "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", - "downloads": 862284054, - "repo_archived": false, - "repo_language": "Rust", - "repo_last_commit": "2025-12-20", - "repo_last_release": "2025-10-23", - "repo_maintainers": [ - "dtolnay", - "alexcrichton", - "mystor", - "bjorn3", - "SergioBenitez" - ], - "repo_owner": "dtolnay", - "repo_stars": 885, - "repo_url": "https://github.com/dtolnay/proc-macro2", - "version": "1.0.103" + "description": "Serde traits only, with no support for derive -- use the `serde` crate instead", + "downloads": 89424375, + "version": "1.0.228" } }, { - "id": "quote", + "id": "serde_derive", "meta": { - "description": "Quasi-quoting macro quote!(...)", - "downloads": 843664819, - "repo_archived": false, - "repo_language": "Rust", - "repo_last_commit": "2025-12-20", - "repo_last_release": "2025-11-06", - "repo_maintainers": [ - "dtolnay", - "mystor", - "alexcrichton", - "dishmaker", - "SimonSapin" - ], - "repo_owner": "dtolnay", - "repo_stars": 1503, - "repo_topics": [ - "proc-macro", - "rust", - "syn" - ], - "repo_url": "https://github.com/dtolnay/quote", - "version": "1.0.42" + "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", + "downloads": 740018551, + "version": "1.0.228" } }, { "id": "syn", "meta": { "description": "Parser for Rust source code", - "downloads": 1251312086, - "repo_archived": false, - "repo_language": "Rust", - "repo_last_commit": "2025-12-20", - "repo_last_release": "2025-11-23", - "repo_maintainers": [ - "dtolnay", - "mystor", - "alexcrichton", - "taiki-e", - "CAD97" - ], - "repo_owner": "dtolnay", - "repo_stars": 3245, - "repo_topics": [ - "proc-macro" - ], - "repo_url": "https://github.com/dtolnay/syn", - "version": "2.0.111" + "downloads": 1331315986, + "version": "2.0.114" } }, { "id": "unicode-ident", "meta": { "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", - "downloads": 665270247, - "repo_archived": false, - "repo_language": "Rust", - "repo_last_commit": "2025-12-21", - "repo_last_release": "2025-10-30", - "repo_maintainers": [ - "dtolnay", - "Marcondiro", - "Jake-Shadle", - "sorairolake" - ], - "repo_owner": "dtolnay", - "repo_stars": 104, - "repo_url": "https://github.com/dtolnay/unicode-ident", + "downloads": 715962350, "version": "1.0.22" } - }, - { - "id": "serde", - "meta": { - "description": "A generic serialization/deserialization framework", - "downloads": 750943583, - "repo_archived": false, - "repo_language": "Rust", - "repo_last_commit": "2025-12-22", - "repo_last_release": "2025-09-27", - "repo_maintainers": [ - "dtolnay", - "erickt", - "Mingun", - "oli-obk", - "mitsuhiko" - ], - "repo_owner": "serde-rs", - "repo_stars": 10241, - "repo_topics": [ - "derive", - "no-std", - "rust", - "serde" - ], - "repo_url": "https://github.com/serde-rs/serde", - "version": "1.0.228" - } } ], "edges": [ diff --git a/examples/real/yargs.json b/examples/real/yargs.json index 0f63106..e884961 100644 --- a/examples/real/yargs.json +++ b/examples/real/yargs.json @@ -1,66 +1,39 @@ { "nodes": [ { - "id": "yargs", + "id": "ansi-regex", "meta": { - "description": "yargs the modern, pirate-themed, successor to optimist.", + "author": "Sindre Sorhus", + "description": "Regular expression for matching ANSI escape codes", "license": "MIT", - "repo_archived": false, - "repo_language": "JavaScript", - "repo_last_commit": "2025-12-20", - "repo_last_release": "2025-05-27", - "repo_maintainers": [ - "bcoe", - "maxrimue", - "nexdrew", - "mleguen" - ], - "repo_owner": "yargs", - "repo_stars": 11410, - "repo_url": "https://github.com/yargs/yargs", - "version": "18.0.0" + "version": "6.2.2" } }, { - "id": "y18n", + "id": "ansi-styles", "meta": { - "author": "Ben Coe", - "description": "the bare-bones internationalization library used by yargs", - "license": "ISC", - "repo_archived": false, - "repo_language": "JavaScript", - "repo_last_commit": "2025-12-15", - "repo_last_release": "2021-04-07", - "repo_maintainers": [ - "greenkeeperio-bot", - "maxrimue", - "bcoe" - ], - "repo_owner": "yargs", - "repo_stars": 151, - "repo_url": "https://github.com/yargs/y18n", - "version": "5.0.8" + "author": "Sindre Sorhus", + "description": "ANSI escape codes for styling strings in the terminal", + "license": "MIT", + "version": "6.2.3" } }, { - "id": "yargs-parser", + "id": "cliui", "meta": { "author": "Ben Coe", - "description": "the mighty option parser used by yargs", + "description": "easily create complex multi-column command-line-interfaces", "license": "ISC", - "repo_archived": false, - "repo_language": "JavaScript", - "repo_last_commit": "2025-12-15", - "repo_last_release": "2025-05-26", - "repo_maintainers": [ - "bcoe", - "elas7", - "juergba" - ], - "repo_owner": "yargs", - "repo_stars": 520, - "repo_url": "https://github.com/yargs/yargs-parser", - "version": "22.0.0" + "version": "9.0.1" + } + }, + { + "id": "escalade", + "meta": { + "author": "Luke Edwards", + "description": "A tiny (183B to 210B) and fast utility to ascend parent directories", + "license": "MIT", + "version": "3.2.0" } }, { @@ -69,19 +42,6 @@ "author": "Stefan Penner", "description": "[![Build Status](https://travis-ci.org/stefanpenner/get-caller-file.svg?branch=master)](https://travis-ci.org/stefanpenner/get-caller-file) [![Build status](https://ci.appveyor.com/api/projects/status/ol2q94g1932cy14a/branch/master?svg=true)](https://ci.a", "license": "ISC", - "repo_archived": false, - "repo_language": "TypeScript", - "repo_last_commit": "2023-09-27", - "repo_maintainers": [ - "stefanpenner", - "zomarg", - "mportuga", - "locks", - "dyniper" - ], - "repo_owner": "stefanpenner", - "repo_stars": 37, - "repo_url": "https://github.com/stefanpenner/get-caller-file", "version": "2.0.5" } }, @@ -91,215 +51,87 @@ "author": "Sindre Sorhus", "description": "Determine the East Asian Width of a Unicode character", "license": "MIT", - "repo_archived": false, - "repo_language": "JavaScript", - "repo_last_commit": "2025-09-11", - "repo_last_release": "2025-09-09", - "repo_maintainers": [ - "sindresorhus", - "fisker" - ], - "repo_owner": "sindresorhus", - "repo_stars": 43, - "repo_url": "https://github.com/sindresorhus/get-east-asian-width", "version": "1.4.0" } }, { - "id": "wrap-ansi", + "id": "string-width", "meta": { "author": "Sindre Sorhus", - "description": "Wordwrap a string with ANSI escape codes", + "description": "Get the visual width of a string - the number of columns required to display it", "license": "MIT", - "repo_archived": false, - "repo_language": "JavaScript", - "repo_last_commit": "2025-09-08", - "repo_last_release": "2025-09-08", - "repo_maintainers": [ - "sindresorhus", - "bcoe", - "SamVerschueren", - "coreyfarrell", - "kevva" - ], - "repo_owner": "chalk", - "repo_stars": 130, - "repo_url": "https://github.com/chalk/wrap-ansi", - "version": "9.0.2" + "version": "8.1.1" } }, { - "id": "ansi-styles", + "id": "strip-ansi", "meta": { "author": "Sindre Sorhus", - "description": "ANSI escape codes for styling strings in the terminal", + "description": "Strip ANSI escape codes from a string", "license": "MIT", - "repo_archived": false, - "repo_language": "JavaScript", - "repo_last_commit": "2025-10-01", - "repo_last_release": "2025-09-08", - "repo_maintainers": [ - "sindresorhus", - "Qix-", - "Richienb", - "arthurvr", - "ExE-Boss" - ], - "repo_owner": "chalk", - "repo_stars": 447, - "repo_topics": [ - "chalk" - ], - "repo_url": "https://github.com/chalk/ansi-styles", - "version": "6.2.3" + "version": "7.1.2" } }, { - "id": "string-width", + "id": "wrap-ansi", "meta": { "author": "Sindre Sorhus", - "description": "Get the visual width of a string - the number of columns required to display it", + "description": "Wordwrap a string with ANSI escape codes", "license": "MIT", - "repo_archived": false, - "repo_language": "JavaScript", - "repo_last_commit": "2025-09-01", - "repo_last_release": "2025-09-01", - "repo_maintainers": [ - "sindresorhus", - "fisker", - "coreyfarrell", - "BendingBender", - "adam2k" - ], - "repo_owner": "sindresorhus", - "repo_stars": 513, - "repo_url": "https://github.com/sindresorhus/string-width", - "version": "8.1.0" + "version": "9.0.2" } }, { - "id": "cliui", + "id": "y18n", "meta": { "author": "Ben Coe", - "description": "easily create complex multi-column command-line-interfaces", + "description": "the bare-bones internationalization library used by yargs", "license": "ISC", - "repo_archived": false, - "repo_language": "JavaScript", - "repo_last_commit": "2025-12-15", - "repo_last_release": "2025-03-17", - "repo_maintainers": [ - "bcoe", - "greenkeeperio-bot", - "coreyfarrell", - "nexdrew" - ], - "repo_owner": "yargs", - "repo_stars": 383, - "repo_url": "https://github.com/yargs/cliui", - "version": "9.0.1" - } - }, - { - "id": "escalade", - "meta": { - "author": "Luke Edwards", - "description": "A tiny (183B to 210B) and fast utility to ascend parent directories", - "license": "MIT", - "repo_archived": false, - "repo_language": "JavaScript", - "repo_last_commit": "2024-08-29", - "repo_last_release": "2024-08-29", - "repo_maintainers": [ - "lukeed", - "ljharb", - "NMinhNguyen", - "triannguyen" - ], - "repo_owner": "lukeed", - "repo_stars": 153, - "repo_url": "https://github.com/lukeed/escalade", - "version": "3.2.0" + "version": "5.0.8" } }, { - "id": "strip-ansi", + "id": "yargs", "meta": { - "author": "Sindre Sorhus", - "description": "Strip ANSI escape codes from a string", + "description": "yargs the modern, pirate-themed, successor to optimist.", "license": "MIT", - "repo_archived": false, - "repo_language": "JavaScript", - "repo_last_commit": "2025-09-08", - "repo_last_release": "2025-09-08", - "repo_maintainers": [ - "sindresorhus", - "jbnicolai", - "LitoMore", - "rmg", - "kevva" - ], - "repo_owner": "chalk", - "repo_stars": 430, - "repo_url": "https://github.com/chalk/strip-ansi", - "version": "7.1.2" + "version": "18.0.0" } }, { - "id": "ansi-regex", + "id": "yargs-parser", "meta": { - "author": "Sindre Sorhus", - "description": "Regular expression for matching ANSI escape codes", - "license": "MIT", - "repo_archived": false, - "repo_language": "JavaScript", - "repo_last_commit": "2025-09-08", - "repo_last_release": "2025-09-08", - "repo_maintainers": [ - "sindresorhus", - "Qix-", - "Akim95", - "arjunmehta", - "llimllib" - ], - "repo_owner": "chalk", - "repo_stars": 198, - "repo_url": "https://github.com/chalk/ansi-regex", - "version": "6.2.2" + "author": "Ben Coe", + "description": "the mighty option parser used by yargs", + "license": "ISC", + "version": "22.0.0" } } ], "edges": [ { "from": "yargs", - "to": "string-width" + "to": "cliui" }, { "from": "yargs", - "to": "y18n" + "to": "escalade" }, { "from": "yargs", - "to": "yargs-parser" + "to": "get-caller-file" }, { "from": "yargs", - "to": "cliui" + "to": "string-width" }, { "from": "yargs", - "to": "escalade" + "to": "y18n" }, { "from": "yargs", - "to": "get-caller-file" - }, - { - "from": "string-width", - "to": "get-east-asian-width" - }, - { - "from": "string-width", - "to": "strip-ansi" + "to": "yargs-parser" }, { "from": "cliui", @@ -328,6 +160,14 @@ { "from": "wrap-ansi", "to": "string-width" + }, + { + "from": "string-width", + "to": "strip-ansi" + }, + { + "from": "string-width", + "to": "get-east-asian-width" } ] } diff --git a/go.mod b/go.mod index fd09b2e..a5ff5ba 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,11 @@ module github.com/matzehuels/stacktower -go 1.24.11 +go 1.25.6 require ( github.com/BurntSushi/toml v1.5.0 + github.com/charmbracelet/bubbletea v1.3.10 + github.com/charmbracelet/lipgloss v1.1.0 github.com/charmbracelet/log v0.4.2 github.com/goccy/go-graphviz v0.2.9 github.com/spf13/cobra v1.10.1 @@ -12,11 +14,11 @@ require ( require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect - github.com/charmbracelet/lipgloss v1.1.0 // indirect - github.com/charmbracelet/x/ansi v0.8.0 // indirect + github.com/charmbracelet/x/ansi v0.10.1 // indirect github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/disintegration/imaging v1.6.2 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/flopp/go-findfont v0.1.0 // indirect github.com/fogleman/gg v1.3.0 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect @@ -24,7 +26,10 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.10 // indirect @@ -32,6 +37,6 @@ require ( github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/image v0.33.0 // indirect - golang.org/x/sys v0.30.0 // indirect + golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.31.0 // indirect ) diff --git a/go.sum b/go.sum index 26acffa..f7364bb 100644 --- a/go.sum +++ b/go.sum @@ -2,16 +2,22 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= +github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig= github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw= -github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= -github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ= +github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/corona10/goimagehash v1.1.0 h1:teNMX/1e+Wn/AYSbLHX8mj+mF9r60R1kBeqE9MkoYwI= @@ -21,6 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/flopp/go-findfont v0.1.0 h1:lPn0BymDUtJo+ZkV01VS3661HL6F4qFlkhcJN55u6mU= github.com/flopp/go-findfont v0.1.0/go.mod h1:wKKxRDjD024Rh7VMwoU90i6ikQRCr+JTHB5n4Ejkqvw= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= @@ -37,8 +45,14 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= @@ -65,9 +79,10 @@ golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQz golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= diff --git a/internal/cli/auth.go b/internal/cli/auth.go new file mode 100644 index 0000000..bbca83d --- /dev/null +++ b/internal/cli/auth.go @@ -0,0 +1,245 @@ +package cli + +import ( + "context" + "fmt" + "net/url" + "os" + "os/exec" + "runtime" + "time" + + "github.com/spf13/cobra" + + "github.com/matzehuels/stacktower/pkg/integrations/github" + "github.com/matzehuels/stacktower/pkg/session" +) + +// sessionTTL is the duration for CLI sessions (30 days). +const sessionTTL = 30 * 24 * time.Hour + +// githubCommand creates the github command with subcommands. +func (c *CLI) githubCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "github", + Short: "GitHub integration commands", + Long: `Authenticate with GitHub and interact with your repositories. + +Use the device flow to authenticate without needing a web browser callback. +Your session is stored in ~/.config/stacktower/sessions/`, + } + + cmd.AddCommand(c.githubLoginCommand()) + cmd.AddCommand(c.githubLogoutCommand()) + cmd.AddCommand(c.githubWhoamiCommand()) + + return cmd +} + +// githubLoginCommand creates the login subcommand. +func (c *CLI) githubLoginCommand() *cobra.Command { + return &cobra.Command{ + Use: "login", + Short: "Authenticate with GitHub using device flow", + Long: `Start the GitHub device authorization flow. + +You'll be given a code to enter at https://github.com/login/device. +Once authorized, your session will be saved locally for future commands.`, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + if existing, _ := loadGitHubSession(ctx); existing != nil { + printInfo("Already logged in as @%s", existing.User.Login) + printDetail("Run 'stacktower github logout' first to re-authenticate") + return nil + } + + _, err := c.runGitHubLogin(ctx) + return err + }, + } +} + +// githubLogoutCommand creates the logout subcommand. +func (c *CLI) githubLogoutCommand() *cobra.Command { + return &cobra.Command{ + Use: "logout", + Short: "Remove stored GitHub credentials", + RunE: func(cmd *cobra.Command, args []string) error { + if err := deleteGitHubSession(cmd.Context()); err != nil { + return fmt.Errorf("delete session: %w", err) + } + printSuccess("Logged out") + return nil + }, + } +} + +// githubWhoamiCommand creates the whoami subcommand. +func (c *CLI) githubWhoamiCommand() *cobra.Command { + return &cobra.Command{ + Use: "whoami", + Short: "Show the currently authenticated GitHub user", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + sess, err := loadGitHubSession(ctx) + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + + spinner := newSpinnerWithContext(ctx, "Verifying session...") + spinner.Start() + + client := github.NewContentClient(sess.AccessToken) + user, err := client.FetchUser(ctx) + if err != nil { + spinner.StopWithError("Session invalid") + return fmt.Errorf("verify session: %w", err) + } + spinner.Stop() + + printSuccess("GitHub Session") + printKeyValue("Username", "@"+user.Login) + if user.Name != "" { + printKeyValue("Name", user.Name) + } + if user.Email != "" { + printKeyValue("Email", user.Email) + } + printKeyValue("Logged in", sess.CreatedAt.Format("Jan 2, 2006")) + printKeyValue("Expires", sess.ExpiresAt.Format("Jan 2, 2006")) + + return nil + }, + } +} + +// ============================================================================= +// Session Management +// ============================================================================= + +// loadGitHubSession loads the GitHub session from disk. +func loadGitHubSession(ctx context.Context) (*session.Session, error) { + store, err := session.NewCLIStore() + if err != nil { + return nil, fmt.Errorf("open session store: %w", err) + } + + sess, err := store.GetSession(ctx) + if err != nil { + return nil, fmt.Errorf("get session: %w", err) + } + if sess == nil { + return nil, fmt.Errorf("not logged in (run 'stacktower github login' first)") + } + + return sess, nil +} + +func saveGitHubSession(ctx context.Context, token *github.OAuthToken, user *github.User) (*session.Session, error) { + store, err := session.NewCLIStore() + if err != nil { + return nil, fmt.Errorf("open session store: %w", err) + } + + sess, err := session.New(token.AccessToken, user, sessionTTL) + if err != nil { + return nil, fmt.Errorf("create session: %w", err) + } + + if err := store.SaveSession(ctx, sess); err != nil { + return nil, fmt.Errorf("save session: %w", err) + } + + return sess, nil +} + +func deleteGitHubSession(ctx context.Context) error { + store, err := session.NewCLIStore() + if err != nil { + return fmt.Errorf("open session store: %w", err) + } + return store.DeleteSession(ctx) +} + +// ============================================================================= +// Device Flow Login +// ============================================================================= + +func (c *CLI) runGitHubLogin(ctx context.Context) (*session.Session, error) { + clientID := os.Getenv("GITHUB_CLIENT_ID") + if clientID == "" { + clientID = github.DefaultClientID + } + + oauthClient := github.NewOAuthClient(github.OAuthConfig{ClientID: clientID}) + + loginCtx, cancel := context.WithTimeout(ctx, 5*time.Minute) + defer cancel() + + deviceResp, err := oauthClient.RequestDeviceCode(loginCtx) + if err != nil { + return nil, fmt.Errorf("request device code: %w", err) + } + + printNewline() + fmt.Println(StyleTitle.Render("GitHub Device Authorization")) + printNewline() + printKeyValue("Code", StyleNumber.Render(deviceResp.UserCode)) + printKeyValue("URL", StyleLink.Render(deviceResp.VerificationURI)) + printNewline() + + if err := openBrowser(deviceResp.VerificationURI); err != nil { + printDetail("Copy the URL above and paste it in your browser") + } else { + printDetail("Opening browser...") + } + printInline("Waiting for authorization...") + + token, err := oauthClient.PollForToken(loginCtx, deviceResp.DeviceCode, deviceResp.Interval) + if err != nil { + fmt.Println() + return nil, fmt.Errorf("authorization failed: %w", err) + } + + contentClient := github.NewContentClient(token.AccessToken) + user, err := contentClient.FetchUser(loginCtx) + if err != nil { + return nil, fmt.Errorf("fetch user: %w", err) + } + + sess, err := saveGitHubSession(ctx, token, user) + if err != nil { + return nil, fmt.Errorf("save session: %w", err) + } + + fmt.Println() + printSuccess("Logged in as @%s", user.Login) + + return sess, nil +} + +func openBrowser(rawURL string) error { + parsed, err := url.Parse(rawURL) + if err != nil { + return fmt.Errorf("invalid URL: %w", err) + } + if parsed.Scheme != "https" && parsed.Scheme != "http" { + return fmt.Errorf("URL scheme must be http or https, got %q", parsed.Scheme) + } + + var cmd *exec.Cmd + switch runtime.GOOS { + case "darwin": + cmd = exec.Command("open", rawURL) + case "linux": + cmd = exec.Command("xdg-open", rawURL) + case "windows": + cmd = exec.Command("cmd", "/c", "start", rawURL) + default: + return fmt.Errorf("unsupported platform: %s", runtime.GOOS) + } + return cmd.Start() +} diff --git a/internal/cli/cache.go b/internal/cli/cache.go index 01bdff5..67c515c 100644 --- a/internal/cli/cache.go +++ b/internal/cli/cache.go @@ -8,85 +8,84 @@ import ( "github.com/spf13/cobra" ) -// newCacheCmd creates the cache management command with subcommands for clearing and inspecting the cache. -// The cache stores HTTP responses from package registries to reduce network calls and improve performance. -func newCacheCmd() *cobra.Command { +// cacheCommand creates the cache management command. +func (c *CLI) cacheCommand() *cobra.Command { cmd := &cobra.Command{ Use: "cache", Short: "Manage the HTTP response cache", } - cmd.AddCommand(newCacheClearCmd()) - cmd.AddCommand(newCachePathCmd()) + cmd.AddCommand(c.cacheClearCommand()) + cmd.AddCommand(c.cachePathCommand()) return cmd } -// newCacheClearCmd creates the "cache clear" subcommand. -// It removes all non-directory files from the cache directory. -// If the cache directory does not exist, the command prints "Cache is empty" and succeeds. -// Failed removals are silently skipped; only successful deletions are counted. -func newCacheClearCmd() *cobra.Command { +// cacheClearCommand creates the "cache clear" subcommand. +func (c *CLI) cacheClearCommand() *cobra.Command { return &cobra.Command{ Use: "clear", Short: "Clear all cached HTTP responses", RunE: func(cmd *cobra.Command, args []string) error { dir, err := cacheDir() if err != nil { - return err + return fmt.Errorf("get cache dir: %w", err) } - entries, err := os.ReadDir(dir) - if os.IsNotExist(err) { - fmt.Println("Cache is empty") + if _, err := os.Stat(dir); os.IsNotExist(err) { + printInfo("Cache is empty") return nil } - if err != nil { - return err - } count := 0 - for _, entry := range entries { - if !entry.IsDir() { - if err := os.Remove(filepath.Join(dir, entry.Name())); err == nil { + err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil // Skip errors, continue walking + } + if path == dir { + return nil + } + if !info.IsDir() { + if err := os.Remove(path); err == nil { count++ } } + return nil + }) + if err != nil { + return err } - fmt.Printf("Cleared %d cached entries from %s\n", count, dir) + // Clean up empty subdirectories + _ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil || path == dir { + return nil + } + if info.IsDir() { + os.Remove(path) + } + return nil + }) + + printSuccess("Cleared %d cached entries", count) + printDetail("Directory: %s", dir) return nil }, } } -// newCachePathCmd creates the "cache path" subcommand. -// It prints the absolute path to the cache directory. -// The directory may not exist if no cached responses have been stored yet. -func newCachePathCmd() *cobra.Command { +// cachePathCommand creates the "cache path" subcommand. +func (c *CLI) cachePathCommand() *cobra.Command { return &cobra.Command{ Use: "path", Short: "Print the cache directory path", RunE: func(cmd *cobra.Command, args []string) error { dir, err := cacheDir() if err != nil { - return err + return fmt.Errorf("get cache dir: %w", err) } fmt.Println(dir) return nil }, } } - -// cacheDir returns the absolute path to the stacktower cache directory. -// The directory is located at $HOME/.cache/stacktower and follows XDG conventions. -// The directory is created on-demand by the deps package; this function only computes the path. -// -// It returns an error if the user's home directory cannot be determined. -func cacheDir() (string, error) { - home, err := os.UserHomeDir() - if err != nil { - return "", err - } - return filepath.Join(home, ".cache", "stacktower"), nil -} diff --git a/internal/cli/cache_test.go b/internal/cli/cache_test.go deleted file mode 100644 index e316631..0000000 --- a/internal/cli/cache_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package cli - -import ( - "os" - "path/filepath" - "strings" - "testing" -) - -func TestCacheDir(t *testing.T) { - dir, err := cacheDir() - if err != nil { - t.Fatalf("cacheDir() error: %v", err) - } - - if dir == "" { - t.Error("cacheDir() returned empty string") - } - - // Should be under home directory - home, _ := os.UserHomeDir() - if !strings.HasPrefix(dir, home) { - t.Errorf("cacheDir() = %q, should be under home %q", dir, home) - } - - // Should end with "stacktower" - if !strings.HasSuffix(dir, "stacktower") { - t.Errorf("cacheDir() = %q, should end with 'stacktower'", dir) - } - - // Should contain ".cache" in path - if !strings.Contains(dir, ".cache") { - t.Errorf("cacheDir() = %q, should contain '.cache'", dir) - } -} - -func TestCacheDirStructure(t *testing.T) { - dir, err := cacheDir() - if err != nil { - t.Fatalf("cacheDir() error: %v", err) - } - - // Verify the expected structure: $HOME/.cache/stacktower - home, _ := os.UserHomeDir() - expected := filepath.Join(home, ".cache", "stacktower") - if dir != expected { - t.Errorf("cacheDir() = %q, want %q", dir, expected) - } -} diff --git a/internal/cli/cli.go b/internal/cli/cli.go new file mode 100644 index 0000000..8661d9d --- /dev/null +++ b/internal/cli/cli.go @@ -0,0 +1,147 @@ +// Package cli implements the stacktower command-line interface. +package cli + +import ( + "io" + "os" + "path/filepath" + "strings" + + "github.com/charmbracelet/log" + "github.com/spf13/cobra" + + "github.com/matzehuels/stacktower/pkg/buildinfo" + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/pipeline" +) + +// ============================================================================= +// Constants +// ============================================================================= + +const ( + // appName is the application name used for directories and display. + appName = "stacktower" + + // defaultOrderTimeout is the default timeout for optimal ordering search (seconds). + defaultOrderTimeout = 60 +) + +// Log levels exported for use in main.go. +const ( + LogDebug = log.DebugLevel + LogInfo = log.InfoLevel +) + +// ============================================================================= +// CLI - Central CLI State +// ============================================================================= + +// CLI holds shared state for all commands. +type CLI struct { + Logger *log.Logger +} + +// New creates a new CLI instance with a default logger. +func New(w io.Writer, level log.Level) *CLI { + return &CLI{ + Logger: log.NewWithOptions(w, log.Options{ + ReportTimestamp: true, + TimeFormat: "15:04:05.00", + Level: level, + }), + } +} + +// SetLogLevel updates the logger's level. +func (c *CLI) SetLogLevel(level log.Level) { + c.Logger.SetLevel(level) +} + +// RootCommand creates the root cobra command with all subcommands registered. +func (c *CLI) RootCommand() *cobra.Command { + root := &cobra.Command{ + Use: "stacktower", + Short: "Stacktower visualizes dependency graphs as towers", + Long: `Stacktower is a CLI tool for visualizing complex dependency graphs as tiered tower structures, making it easier to understand layering and flow.`, + Version: buildinfo.Version, + SilenceUsage: true, + } + + root.SetVersionTemplate(buildinfo.Template()) + + // Register all subcommands + root.AddCommand(c.parseCommand()) + root.AddCommand(c.layoutCommand()) + root.AddCommand(c.visualizeCommand()) + root.AddCommand(c.renderCommand()) + root.AddCommand(c.cacheCommand()) + root.AddCommand(c.pqtreeCommand()) + root.AddCommand(c.githubCommand()) + root.AddCommand(c.completionCommand()) + + return root +} + +// ============================================================================= +// Runner Factory +// ============================================================================= + +// newRunner creates a pipeline runner for CLI use. +func (c *CLI) newRunner(noCache bool) (*pipeline.Runner, error) { + cache, err := newCache(noCache) + if err != nil { + return nil, err + } + return pipeline.NewRunner(cache, nil, c.Logger), nil +} + +func newCache(noCache bool) (cache.Cache, error) { + if noCache { + return cache.NewNullCache(), nil + } + dir, err := cacheDir() + if err != nil { + return cache.NewNullCache(), nil + } + return cache.NewFileCache(dir) +} + +// ============================================================================= +// Paths +// ============================================================================= + +// cacheDir returns the cache directory using XDG standard (~/.cache/stacktower/). +func cacheDir() (string, error) { + if cacheHome := os.Getenv("XDG_CACHE_HOME"); cacheHome != "" { + return filepath.Join(cacheHome, appName), nil + } + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(home, ".cache", appName), nil +} + +// ============================================================================= +// Options Helpers +// ============================================================================= + +// setCLIDefaults applies CLI-specific defaults on top of pipeline defaults. +func setCLIDefaults(opts *pipeline.Options) { + opts.SetLayoutDefaults() + opts.SetRenderDefaults() + // CLI-specific preferences (override pipeline defaults) + opts.Randomize = true + opts.Merge = true + opts.Normalize = true + opts.Popups = true +} + +// parseFormats parses a comma-separated format string into a slice. +func parseFormats(s string) []string { + if s == "" { + return []string{pipeline.FormatSVG} + } + return strings.Split(s, ",") +} diff --git a/internal/cli/completion.go b/internal/cli/completion.go new file mode 100644 index 0000000..a37d1b3 --- /dev/null +++ b/internal/cli/completion.go @@ -0,0 +1,69 @@ +package cli + +import ( + "os" + + "github.com/spf13/cobra" +) + +// completionCommand creates the completion command for generating shell completions. +func (c *CLI) completionCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "completion [bash|zsh|fish|powershell]", + Short: "Generate shell completion scripts", + Long: `Generate shell completion scripts for stacktower. + +To load completions: + +Bash: + $ source <(stacktower completion bash) + + # To load completions for each session, execute once: + # Linux: + $ stacktower completion bash > /etc/bash_completion.d/stacktower + # macOS: + $ stacktower completion bash > $(brew --prefix)/etc/bash_completion.d/stacktower + +Zsh: + # If shell completion is not already enabled in your environment, + # you will need to enable it. You can execute the following once: + $ echo "autoload -U compinit; compinit" >> ~/.zshrc + + # To load completions for each session, execute once: + $ stacktower completion zsh > "${fpath[1]}/_stacktower" + + # You will need to start a new shell for this setup to take effect. + +Fish: + $ stacktower completion fish | source + + # To load completions for each session, execute once: + $ stacktower completion fish > ~/.config/fish/completions/stacktower.fish + +PowerShell: + PS> stacktower completion powershell | Out-String | Invoke-Expression + + # To load completions for every new session, run: + PS> stacktower completion powershell > stacktower.ps1 + # and source this file from your PowerShell profile. +`, + DisableFlagsInUseLine: true, + ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, + Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), + RunE: func(cmd *cobra.Command, args []string) error { + switch args[0] { + case "bash": + return cmd.Root().GenBashCompletion(os.Stdout) + case "zsh": + return cmd.Root().GenZshCompletion(os.Stdout) + case "fish": + return cmd.Root().GenFishCompletion(os.Stdout, true) + case "powershell": + return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) + } + return nil + }, + } + + return cmd +} diff --git a/internal/cli/layout.go b/internal/cli/layout.go new file mode 100644 index 0000000..a0d85a6 --- /dev/null +++ b/internal/cli/layout.go @@ -0,0 +1,113 @@ +package cli + +import ( + "context" + "fmt" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + + "github.com/matzehuels/stacktower/pkg/graph" + "github.com/matzehuels/stacktower/pkg/pipeline" +) + +// layoutCommand creates the layout command for computing visualization layouts. +func (c *CLI) layoutCommand() *cobra.Command { + var ( + output string + noCache bool + orderTimeout int + ) + opts := pipeline.Options{} + setCLIDefaults(&opts) + + cmd := &cobra.Command{ + Use: "layout [graph.json]", + Short: "Compute visualization layout from a dependency graph", + Long: `Compute visualization layout from a dependency graph. + +The layout command takes a graph.json file (produced by 'parse') and computes +the layout for visualization. The output is a layout.json file (same format as +'render -f json') that can be rendered to SVG/PNG/PDF using the 'visualize' command. + +Supports both tower (-t tower) and nodelink (-t nodelink) visualization types. + +Results are cached locally for faster subsequent runs.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return c.runLayout(cmd.Context(), args[0], opts, output, noCache, orderTimeout) + }, + } + + // Common flags + cmd.Flags().StringVarP(&output, "output", "o", "", "output file (default:

NRIh)HZhrM*_`~vM@J;DPq|n$UXf~WOgW(D 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 Q0p^t~XXTst2Qb)Ja6Re81ONa4 literal 0 HcmV?d00001 diff --git a/pkg/graph/doc.go b/pkg/graph/doc.go new file mode 100644 index 0000000..daa5270 --- /dev/null +++ b/pkg/graph/doc.go @@ -0,0 +1,85 @@ +// Package graph provides serialization types for dependency graphs and layouts. +// +// This package defines the canonical wire format for Stacktower's graph data, +// used for JSON files, API responses, caching, and cross-tool interoperability. +// +// # Architecture +// +// The package sits at the serialization boundary between internal representations +// and external formats: +// +// - [Graph], [Layout]: Serialization types (this package) +// - pkg/core/dag.DAG: Internal graph representation +// - pkg/core/render/tower/layout.Layout: Internal layout (positions, metadata) +// +// Use [FromDAG]/[ToDAG] and Export/Parse methods to convert between them. +// +// # Core Types +// +// - [Graph]: Node-link format for dependency graphs +// - [Layout]: Unified format for visualization layouts (tower or nodelink) +// - [Node], [Edge]: Shared structural types +// - [Block]: Positioned element in tower visualizations +// +// # Constants +// +// This package is the single source of truth for visualization constants: +// +// graph.VizTypeTower // "tower" +// graph.VizTypeNodelink // "nodelink" +// graph.StyleSimple // "simple" +// graph.StyleHanddrawn // "handdrawn" +// +// # Graph Serialization +// +// Graphs use a simple node-link JSON format: +// +// { +// "nodes": [{"id": "app"}, {"id": "lib-a"}], +// "edges": [{"from": "app", "to": "lib-a"}] +// } +// +// Common operations: +// +// g, _ := graph.ReadGraphFile("deps.json") // File → DAG +// graph.WriteGraphFile(dag, "output.json") // DAG → File +// data, _ := graph.MarshalGraph(dag) // DAG → []byte +// parsed, _ := graph.UnmarshalGraph(data) // []byte → Graph +// +// # Layout Serialization +// +// Layouts are discriminated by VizType: +// +// layout, _ := graph.UnmarshalLayout(data) +// if layout.IsTower() { +// // Use layout.Blocks for positioned blocks +// } else { +// // Use layout.DOT for Graphviz rendering +// } +// +// # Converting Between Types +// +// For tower layouts: +// +// // Internal → Serialized (for JSON/API/cache) +// serialized, err := internalLayout.Export(dag) +// +// // Serialized → Internal (from JSON/API/cache) +// internal, err := layout.Parse(serializedLayout) +// +// # Node Metadata +// +// The meta object supports arbitrary key-value data. Recognized keys: +// +// repo_url Repository URL (clickable blocks) +// repo_stars Star count (popups) +// repo_owner Owner for Nebraska ranking +// repo_maintainers Maintainer list +// repo_last_commit Staleness detection +// repo_archived Archived flag +// description Popup content +// +// # Concurrency +// +// All functions are safe for concurrent reads but not concurrent writes. +package graph diff --git a/pkg/io/example_test.go b/pkg/graph/example_test.go similarity index 84% rename from pkg/io/example_test.go rename to pkg/graph/example_test.go index 19133f3..319d103 100644 --- a/pkg/io/example_test.go +++ b/pkg/graph/example_test.go @@ -1,4 +1,4 @@ -package io_test +package graph_test import ( "bytes" @@ -6,11 +6,11 @@ import ( "os" "path/filepath" - "github.com/matzehuels/stacktower/pkg/dag" - "github.com/matzehuels/stacktower/pkg/io" + "github.com/matzehuels/stacktower/pkg/core/dag" + "github.com/matzehuels/stacktower/pkg/graph" ) -func ExampleWriteJSON() { +func ExampleWriteGraph() { // Create a simple dependency graph g := dag.New(nil) _ = g.AddNode(dag.Node{ID: "app", Row: 0}) @@ -19,7 +19,7 @@ func ExampleWriteJSON() { // Write to a buffer (or any io.Writer) var buf bytes.Buffer - if err := io.WriteJSON(g, &buf); err != nil { + if err := graph.WriteGraph(g, &buf); err != nil { fmt.Println("Error:", err) return } @@ -50,7 +50,7 @@ func ExampleWriteJSON() { // } } -func ExampleReadJSON() { +func ExampleReadGraph() { // JSON input representing a dependency graph jsonData := `{ "nodes": [ @@ -63,7 +63,7 @@ func ExampleReadJSON() { }` // Parse the JSON - g, err := io.ReadJSON(bytes.NewReader([]byte(jsonData))) + g, err := graph.ReadGraph(bytes.NewReader([]byte(jsonData))) if err != nil { fmt.Println("Error:", err) return @@ -78,7 +78,7 @@ func ExampleReadJSON() { // Children of app: [lib] } -func ExampleExportJSON() { +func ExampleWriteGraphFile() { // Build a simple graph g := dag.New(nil) _ = g.AddNode(dag.Node{ID: "server"}) @@ -90,7 +90,7 @@ func ExampleExportJSON() { path := filepath.Join(tmpDir, "exported-graph.json") defer os.Remove(path) - if err := io.ExportJSON(g, path); err != nil { + if err := graph.WriteGraphFile(g, path); err != nil { fmt.Println("Error:", err) return } @@ -103,7 +103,7 @@ func ExampleExportJSON() { // Graph exported successfully } -func ExampleImportJSON() { +func ExampleReadGraphFile() { // Create a temporary JSON file tmpDir := os.TempDir() path := filepath.Join(tmpDir, "example-graph.json") @@ -127,7 +127,7 @@ func ExampleImportJSON() { defer os.Remove(path) // Import the graph - g, err := io.ImportJSON(path) + g, err := graph.ReadGraphFile(path) if err != nil { fmt.Println("Error:", err) return @@ -140,14 +140,14 @@ func ExampleImportJSON() { // Root has 2 children } -func ExampleReadJSON_withMetadata() { +func ExampleReadGraph_withMetadata() { // JSON with package metadata (as produced by dependency parsing) jsonData := `{ "nodes": [ { "id": "fastapi", "meta": { - "version": "0.100.0", + "version": "1.0.0", "description": "FastAPI framework", "repo_stars": 70000 } @@ -165,7 +165,7 @@ func ExampleReadJSON_withMetadata() { ] }` - g, _ := io.ReadJSON(bytes.NewReader([]byte(jsonData))) + g, _ := graph.ReadGraph(bytes.NewReader([]byte(jsonData))) node, _ := g.Node("fastapi") fmt.Println("Package:", node.ID) @@ -173,6 +173,6 @@ func ExampleReadJSON_withMetadata() { fmt.Println("Stars:", node.Meta["repo_stars"]) // Output: // Package: fastapi - // Version: 0.100.0 + // Version: 1.0.0 // Stars: 70000 } diff --git a/pkg/graph/graph.go b/pkg/graph/graph.go new file mode 100644 index 0000000..af34308 --- /dev/null +++ b/pkg/graph/graph.go @@ -0,0 +1,81 @@ +package graph + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" + + "github.com/matzehuels/stacktower/pkg/core/dag" +) + +// ============================================================================= +// Graph Serialization API +// ============================================================================= + +// MarshalGraph converts a DAG to JSON bytes. +// Nodes are sorted by ID for deterministic output. +func MarshalGraph(g *dag.DAG) ([]byte, error) { + var buf bytes.Buffer + if err := writeGraphTo(g, &buf); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// WriteGraphFile writes a DAG to a JSON file. +// The file is created with 0644 permissions. +func WriteGraphFile(g *dag.DAG, path string) error { + f, err := os.Create(path) + if err != nil { + return fmt.Errorf("create %s: %w", path, err) + } + defer f.Close() + return writeGraphTo(g, f) +} + +// WriteGraph writes a DAG as JSON to an io.Writer. +// Use MarshalGraph for in-memory serialization or WriteGraphFile for files. +func WriteGraph(g *dag.DAG, w io.Writer) error { + return writeGraphTo(g, w) +} + +// ReadGraphFile reads a JSON file and returns the decoded DAG. +// Returns validation errors for malformed graphs or DAG constraint violations. +func ReadGraphFile(path string) (*dag.DAG, error) { + f, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("open %s: %w", path, err) + } + defer f.Close() + return readGraphFrom(f) +} + +// ReadGraph decodes a JSON graph from an io.Reader into a DAG. +// Use ReadGraphFile for files or pass bytes.NewReader for in-memory data. +func ReadGraph(r io.Reader) (*dag.DAG, error) { + return readGraphFrom(r) +} + +// ============================================================================= +// Internal Implementation +// ============================================================================= + +func writeGraphTo(g *dag.DAG, w io.Writer) error { + out := FromDAG(g) + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + if err := enc.Encode(out); err != nil { + return fmt.Errorf("encode: %w", err) + } + return nil +} + +func readGraphFrom(r io.Reader) (*dag.DAG, error) { + var data Graph + if err := json.NewDecoder(r).Decode(&data); err != nil { + return nil, fmt.Errorf("decode: %w", err) + } + return ToDAG(data) +} diff --git a/pkg/graph/graph_test.go b/pkg/graph/graph_test.go new file mode 100644 index 0000000..02bed52 --- /dev/null +++ b/pkg/graph/graph_test.go @@ -0,0 +1,238 @@ +package graph + +import ( + "bytes" + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/matzehuels/stacktower/pkg/core/dag" +) + +func TestMarshalGraph(t *testing.T) { + tests := []struct { + name string + build func() *dag.DAG + wantNodes int + wantEdges int + check func(t *testing.T, g Graph) + }{ + { + name: "Empty", + build: func() *dag.DAG { return dag.New(nil) }, + wantNodes: 0, + wantEdges: 0, + }, + { + name: "Simple", + build: func() *dag.DAG { + g := dag.New(nil) + g.AddNode(dag.Node{ID: "a", Meta: dag.Metadata{"version": "1.0"}}) + g.AddNode(dag.Node{ID: "b", Meta: dag.Metadata{"version": "2.0"}}) + g.AddEdge(dag.Edge{From: "a", To: "b"}) + return g + }, + wantNodes: 2, + wantEdges: 1, + }, + { + name: "PreservesMetadata", + build: func() *dag.DAG { + g := dag.New(nil) + g.AddNode(dag.Node{ + ID: "test", + Meta: dag.Metadata{ + "version": "1.0", + "author": "test-author", + }, + }) + return g + }, + wantNodes: 1, + wantEdges: 0, + check: func(t *testing.T, g Graph) { + if g.Nodes[0].Meta["version"] != "1.0" { + t.Errorf("version = %v, want 1.0", g.Nodes[0].Meta["version"]) + } + if g.Nodes[0].Meta["author"] != "test-author" { + t.Errorf("author = %v, want test-author", g.Nodes[0].Meta["author"]) + } + }, + }, + { + name: "Diamond", + build: func() *dag.DAG { + g := dag.New(nil) + g.AddNode(dag.Node{ID: "a"}) + g.AddNode(dag.Node{ID: "b"}) + g.AddNode(dag.Node{ID: "c"}) + g.AddNode(dag.Node{ID: "d"}) + g.AddEdge(dag.Edge{From: "a", To: "b"}) + g.AddEdge(dag.Edge{From: "a", To: "c"}) + g.AddEdge(dag.Edge{From: "b", To: "d"}) + g.AddEdge(dag.Edge{From: "c", To: "d"}) + return g + }, + wantNodes: 4, + wantEdges: 4, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := tt.build() + + data, err := MarshalGraph(g) + if err != nil { + t.Fatalf("MarshalGraph: %v", err) + } + + var result Graph + if err := json.Unmarshal(data, &result); err != nil { + t.Fatalf("unmarshal: %v", err) + } + + if got := len(result.Nodes); got != tt.wantNodes { + t.Errorf("nodes = %d, want %d", got, tt.wantNodes) + } + if got := len(result.Edges); got != tt.wantEdges { + t.Errorf("edges = %d, want %d", got, tt.wantEdges) + } + + if tt.check != nil { + tt.check(t, result) + } + }) + } +} + +func TestReadGraph(t *testing.T) { + tests := []struct { + name string + input string + wantNodes int + wantEdges int + wantErr bool + check func(t *testing.T, g *dag.DAG) + }{ + { + name: "Valid", + input: `{ + "nodes": [ + {"id": "A", "meta": {"version": "1.0"}}, + {"id": "B"} + ], + "edges": [ + {"from": "A", "to": "B"} + ] + }`, + wantNodes: 2, + wantEdges: 1, + check: func(t *testing.T, g *dag.DAG) { + n, ok := g.Node("A") + if !ok { + t.Fatal("node A not found") + } + if n.Meta["version"] != "1.0" { + t.Errorf("version = %v, want 1.0", n.Meta["version"]) + } + }, + }, + { + name: "Empty", + input: `{ + "nodes": [], + "edges": [] + }`, + wantNodes: 0, + wantEdges: 0, + }, + { + name: "Invalid", + input: `{invalid json}`, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := strings.NewReader(tt.input) + g, err := ReadGraph(r) + + if tt.wantErr { + if err == nil { + t.Fatal("expected error, got nil") + } + return + } + + if err != nil { + t.Fatalf("ReadGraph: %v", err) + } + + if got := g.NodeCount(); got != tt.wantNodes { + t.Errorf("nodes = %d, want %d", got, tt.wantNodes) + } + if got := g.EdgeCount(); got != tt.wantEdges { + t.Errorf("edges = %d, want %d", got, tt.wantEdges) + } + + if tt.check != nil { + tt.check(t, g) + } + }) + } +} + +func TestReadGraphFile(t *testing.T) { + content := `{ + "nodes": [{"id": "A"}], + "edges": [] + }` + + dir := t.TempDir() + path := filepath.Join(dir, "test.json") + + if err := os.WriteFile(path, []byte(content), 0644); err != nil { + t.Fatal(err) + } + + g, err := ReadGraphFile(path) + if err != nil { + t.Fatalf("ReadGraphFile: %v", err) + } + + if g.NodeCount() != 1 { + t.Errorf("nodes = %d, want 1", g.NodeCount()) + } +} + +func TestReadGraphFileNotFound(t *testing.T) { + _, err := ReadGraphFile("nonexistent.json") + if err == nil { + t.Error("expected error for nonexistent file") + } +} + +func TestWriteGraph(t *testing.T) { + g := dag.New(nil) + g.AddNode(dag.Node{ID: "a"}) + g.AddNode(dag.Node{ID: "b"}) + g.AddEdge(dag.Edge{From: "a", To: "b"}) + + var buf bytes.Buffer + if err := WriteGraph(g, &buf); err != nil { + t.Fatalf("WriteGraph: %v", err) + } + + var result Graph + if err := json.Unmarshal(buf.Bytes(), &result); err != nil { + t.Fatalf("unmarshal: %v", err) + } + + if len(result.Nodes) != 2 { + t.Errorf("nodes = %d, want 2", len(result.Nodes)) + } +} diff --git a/pkg/graph/layout.go b/pkg/graph/layout.go new file mode 100644 index 0000000..ecf23b9 --- /dev/null +++ b/pkg/graph/layout.go @@ -0,0 +1,166 @@ +package graph + +import ( + "encoding/json" + "fmt" + "os" +) + +// ============================================================================= +// Layout - Unified Visualization Format +// ============================================================================= + +// Layout is the unified serialization format for all visualizations. +// +// This is a discriminated union type - check VizType to determine which +// fields are populated: +// +// Tower ("tower"): +// - Blocks: positioned blocks with coordinates +// - MarginX/Y, Seed, Randomize, Merged: tower-specific render options +// +// Nodelink ("nodelink"): +// - DOT: Graphviz DOT string for rendering +// - Engine: Graphviz layout engine (e.g., "dot") +// +// Shared fields (both types): +// - Width, Height: frame dimensions +// - Style: visual style ("handdrawn", "simple") +// - Nodes: structured node metadata +// - Edges: dependency edges +// - Rows: layer assignments (row → node IDs) +// - Nebraska: maintainer rankings (optional) +// +// For tower layouts, there is also an internal representation +// (pkg/core/render/tower/layout.Layout) optimized for computation. +// Use Export()/Parse() methods to convert between them. +type Layout struct { + // Discriminator + VizType string `json:"viz_type" bson:"viz_type"` + + // Common dimensions and style + Width float64 `json:"width" bson:"width"` + Height float64 `json:"height" bson:"height"` + Style string `json:"style,omitempty" bson:"style,omitempty"` + + // Graph structure (shared) + Nodes []Node `json:"nodes,omitempty" bson:"nodes,omitempty"` + Edges []Edge `json:"edges,omitempty" bson:"edges,omitempty"` + Rows map[int][]string `json:"rows,omitempty" bson:"rows,omitempty"` + Nebraska []NebraskaRanking `json:"nebraska,omitempty" bson:"nebraska,omitempty"` + + // Tower-specific + Blocks []Block `json:"blocks,omitempty" bson:"blocks,omitempty"` + MarginX float64 `json:"margin_x,omitempty" bson:"margin_x,omitempty"` + MarginY float64 `json:"margin_y,omitempty" bson:"margin_y,omitempty"` + Seed uint64 `json:"seed,omitempty" bson:"seed,omitempty"` + Randomize bool `json:"randomize,omitempty" bson:"randomize,omitempty"` + Merged bool `json:"merged,omitempty" bson:"merged,omitempty"` + + // Nodelink-specific + DOT string `json:"dot,omitempty" bson:"dot,omitempty"` + Engine string `json:"engine,omitempty" bson:"engine,omitempty"` +} + +// IsTower returns true if this is a tower layout. +func (l *Layout) IsTower() bool { return l.VizType == VizTypeTower } + +// IsNodelink returns true if this is a nodelink layout. +func (l *Layout) IsNodelink() bool { return l.VizType == VizTypeNodelink } + +// ============================================================================= +// Block - Tower Visualization Element +// ============================================================================= + +// Block represents a positioned block in a tower layout. +type Block struct { + ID string `json:"id" bson:"id"` + Label string `json:"label" bson:"label"` + X float64 `json:"x" bson:"x"` + Y float64 `json:"y" bson:"y"` + Width float64 `json:"width" bson:"width"` + Height float64 `json:"height" bson:"height"` + + // Metadata + URL string `json:"url,omitempty" bson:"url,omitempty"` + Brittle bool `json:"brittle,omitempty" bson:"brittle,omitempty"` + Auxiliary bool `json:"auxiliary,omitempty" bson:"auxiliary,omitempty"` + Synthetic bool `json:"synthetic,omitempty" bson:"synthetic,omitempty"` + Meta *BlockMeta `json:"meta,omitempty" bson:"meta,omitempty"` +} + +// BlockMeta contains enriched metadata (from GitHub, registries). +type BlockMeta struct { + Description string `json:"description,omitempty" bson:"description,omitempty"` + Stars int `json:"stars,omitempty" bson:"stars,omitempty"` + LastCommit string `json:"last_commit,omitempty" bson:"last_commit,omitempty"` + LastRelease string `json:"last_release,omitempty" bson:"last_release,omitempty"` + Archived bool `json:"archived,omitempty" bson:"archived,omitempty"` +} + +// ============================================================================= +// Nebraska - Maintainer Rankings +// ============================================================================= + +// NebraskaRanking contains maintainer ranking information. +type NebraskaRanking struct { + Maintainer string `json:"maintainer" bson:"maintainer"` + Score float64 `json:"score" bson:"score"` + Packages []NebraskaPackage `json:"packages" bson:"packages"` +} + +// NebraskaPackage represents a package maintained by someone. +type NebraskaPackage struct { + Package string `json:"package" bson:"package"` + Role string `json:"role" bson:"role"` // "owner", "lead", "maintainer" + URL string `json:"url,omitempty" bson:"url,omitempty"` +} + +// ============================================================================= +// Layout Serialization API +// ============================================================================= + +// MarshalLayout serializes a Layout to pretty-printed JSON bytes. +func MarshalLayout(l Layout) ([]byte, error) { + return json.MarshalIndent(l, "", " ") +} + +// UnmarshalLayout deserializes JSON bytes into a Layout. +// Validates that required fields are present for the viz type. +func UnmarshalLayout(data []byte) (Layout, error) { + var l Layout + if err := json.Unmarshal(data, &l); err != nil { + return Layout{}, fmt.Errorf("unmarshal layout: %w", err) + } + + if l.VizType == "" { + l.VizType = VizTypeTower + } + + if l.IsTower() && len(l.Blocks) == 0 { + return Layout{}, fmt.Errorf("tower layout must contain blocks") + } + if l.IsNodelink() && l.DOT == "" { + return Layout{}, fmt.Errorf("nodelink layout must contain DOT string") + } + + return l, nil +} + +// WriteLayoutFile writes a Layout to a JSON file. +func WriteLayoutFile(l Layout, path string) error { + data, err := MarshalLayout(l) + if err != nil { + return err + } + return os.WriteFile(path, data, 0644) +} + +// ReadLayoutFile reads a Layout from a JSON file. +func ReadLayoutFile(path string) (Layout, error) { + data, err := os.ReadFile(path) + if err != nil { + return Layout{}, fmt.Errorf("read %s: %w", path, err) + } + return UnmarshalLayout(data) +} diff --git a/pkg/graph/types.go b/pkg/graph/types.go new file mode 100644 index 0000000..237ec4f --- /dev/null +++ b/pkg/graph/types.go @@ -0,0 +1,268 @@ +package graph + +import ( + "encoding/json" + "fmt" + "slices" + + "github.com/matzehuels/stacktower/pkg/core/dag" + "github.com/matzehuels/stacktower/pkg/core/render/tower/feature" +) + +// ============================================================================= +// Constants - Single Source of Truth +// ============================================================================= + +// Visualization types. +const ( + VizTypeTower = "tower" + VizTypeNodelink = "nodelink" +) + +// Visual styles for rendering. +const ( + StyleSimple = "simple" + StyleHanddrawn = "handdrawn" +) + +// ProjectRootNodeID is the node ID used for the root of manifest-based graphs. +const ProjectRootNodeID = "__project__" + +// Node kinds. +const ( + KindSubdivider = "subdivider" + KindAuxiliary = "auxiliary" +) + +// Internal metadata keys for serialization. +const ( + metaLabel = "_label" // Stores display label for round-trip fidelity + metaRepoURL = "repo_url" // Repository URL extraction +) + +// ============================================================================= +// Graph - Dependency Graph Serialization +// ============================================================================= + +// Graph is the canonical serialization format for dependency graphs. +// Used for API responses, storage, caching, and cross-tool compatibility. +// +// The format is human-readable and designed for round-trip fidelity: +// import → transform → export → re-import produces identical results. +type Graph struct { + Nodes []Node `json:"nodes" bson:"nodes"` + Edges []Edge `json:"edges" bson:"edges"` +} + +// ============================================================================= +// Node - Unified Node Type +// ============================================================================= + +// Node is the unified node type for all serialization contexts. +// Used in both Graph and Layout types for consistency. +type Node struct { + ID string `json:"id" bson:"id"` + Label string `json:"label,omitempty" bson:"label,omitempty"` // Display label (defaults to ID) + Row int `json:"row,omitempty" bson:"row,omitempty"` // Layer/rank assignment + Kind string `json:"kind,omitempty" bson:"kind,omitempty"` // "subdivider", "auxiliary", or empty + Brittle bool `json:"brittle,omitempty" bson:"brittle,omitempty"` // At-risk package flag + MasterID string `json:"master_id,omitempty" bson:"master_id,omitempty"` + URL string `json:"url,omitempty" bson:"url,omitempty"` // Repository URL + Meta map[string]any `json:"meta,omitempty" bson:"meta,omitempty"` +} + +// IsSubdivider returns true if this is a subdivider node. +func (n *Node) IsSubdivider() bool { return n.Kind == KindSubdivider } + +// IsAuxiliary returns true if this is an auxiliary dependency. +func (n *Node) IsAuxiliary() bool { return n.Kind == KindAuxiliary } + +// DisplayLabel returns the label if set, otherwise the ID. +func (n *Node) DisplayLabel() string { + if n.Label != "" { + return n.Label + } + return n.ID +} + +// ============================================================================= +// Edge - Directed Dependency +// ============================================================================= + +// Edge represents a directed edge in the dependency graph. +type Edge struct { + From string `json:"from" bson:"from"` + To string `json:"to" bson:"to"` +} + +// ============================================================================= +// DAG ↔ Graph Conversion +// ============================================================================= + +// FromDAG converts a DAG to its serialization format. +// Nodes are sorted by ID for deterministic output. +// Extracts repository URL and computes brittle flag from metadata. +func FromDAG(g *dag.DAG) Graph { + nodes := g.Nodes() + slices.SortFunc(nodes, func(a, b *dag.Node) int { + if a.ID < b.ID { + return -1 + } + if a.ID > b.ID { + return 1 + } + return 0 + }) + + out := Graph{ + Nodes: make([]Node, len(nodes)), + Edges: make([]Edge, len(g.Edges())), + } + + for i, n := range nodes { + out.Nodes[i] = nodeFromDAG(n) + } + + for i, e := range g.Edges() { + out.Edges[i] = Edge{From: e.From, To: e.To} + } + + return out +} + +// ToDAG converts a Graph to a DAG. +// Returns an error if the structure violates DAG constraints. +// Label is stored in metadata for round-trip fidelity when non-empty. +func ToDAG(gj Graph) (*dag.DAG, error) { + d := dag.New(nil) + + for _, nj := range gj.Nodes { + n := dag.Node{ + ID: nj.ID, + Row: nj.Row, + Meta: copyMeta(nj.Meta), + Kind: stringToDAGKind(nj.Kind), + MasterID: nj.MasterID, + } + if n.Meta == nil { + n.Meta = dag.Metadata{} + } + // Store label in metadata for round-trip fidelity + if nj.Label != "" { + n.Meta[metaLabel] = nj.Label + } + if err := d.AddNode(n); err != nil { + return nil, fmt.Errorf("add node %s: %w", nj.ID, err) + } + } + + for _, ej := range gj.Edges { + if err := d.AddEdge(dag.Edge{From: ej.From, To: ej.To}); err != nil { + return nil, fmt.Errorf("add edge %s→%s: %w", ej.From, ej.To, err) + } + } + + return d, nil +} + +// copyMeta creates a shallow copy of metadata to avoid mutation. +func copyMeta(m map[string]any) map[string]any { + if m == nil { + return nil + } + result := make(map[string]any, len(m)) + for k, v := range m { + result[k] = v + } + return result +} + +// UnmarshalGraph deserializes JSON bytes to a Graph. +func UnmarshalGraph(data []byte) (Graph, error) { + var g Graph + if err := json.Unmarshal(data, &g); err != nil { + return Graph{}, err + } + return g, nil +} + +// ============================================================================= +// Internal Helpers +// ============================================================================= + +// nodeFromDAG converts a dag.Node to a serialization Node. +// This is the single point of conversion for all DAG→Node operations. +// Label is preserved from metadata if previously stored (for round-trip fidelity). +// Brittle is computed from node metadata using feature.IsBrittle(). +func nodeFromDAG(n *dag.Node) Node { + node := Node{ + ID: n.ID, + Row: n.Row, + MasterID: n.MasterID, + Meta: cleanMeta(n.Meta), + Kind: dagKindToString(n.Kind), + Brittle: feature.IsBrittle(n), + } + + // Extract fields from metadata + if n.Meta != nil { + if url, ok := n.Meta[metaRepoURL].(string); ok { + node.URL = url + } + // Restore label for round-trip fidelity + if label, ok := n.Meta[metaLabel].(string); ok { + node.Label = label + } + } + + return node +} + +// cleanMeta returns a copy of metadata without internal keys (e.g., _label). +// Returns nil if the result would be empty. +func cleanMeta(m map[string]any) map[string]any { + if m == nil { + return nil + } + // Check if we have any keys to preserve + hasPublicKeys := false + for k := range m { + if k != metaLabel { + hasPublicKeys = true + break + } + } + if !hasPublicKeys { + return nil + } + // Copy without internal keys + result := make(map[string]any, len(m)) + for k, v := range m { + if k != metaLabel { + result[k] = v + } + } + return result +} + +func dagKindToString(k dag.NodeKind) string { + switch k { + case dag.NodeKindSubdivider: + return KindSubdivider + case dag.NodeKindAuxiliary: + return KindAuxiliary + default: + return "" + } +} + +func stringToDAGKind(s string) dag.NodeKind { + switch s { + case KindSubdivider: + return dag.NodeKindSubdivider + case KindAuxiliary: + return dag.NodeKindAuxiliary + default: + return dag.NodeKindRegular + } +} diff --git a/pkg/httputil/cache.go b/pkg/httputil/cache.go deleted file mode 100644 index 8d93116..0000000 --- a/pkg/httputil/cache.go +++ /dev/null @@ -1,176 +0,0 @@ -package httputil - -import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "os" - "path/filepath" - "time" -) - -// ErrExpired is returned by [Cache.Get] when a cached entry exists but has -// exceeded its time-to-live (TTL). -// -// When you receive ErrExpired, the cached data still exists on disk but is -// considered stale. Callers should fetch fresh data from the source and -// update the cache with [Cache.Set]. -// -// Use errors.Is to check for this error: -// -// ok, err := cache.Get("key", &value) -// if errors.Is(err, httputil.ErrExpired) { -// // Fetch fresh data and update cache -// } -var ErrExpired = errors.New("cache entry expired") - -// Cache provides file-based caching of arbitrary JSON-marshalable data. -// -// Each cache entry is stored as a JSON file in the cache directory, with -// the filename derived from a SHA-256 hash of the cache key. This design -// ensures safe key names (no filesystem special characters) and prevents -// key collisions across different namespaces. -// -// Cache operations are not goroutine-safe. If multiple goroutines access -// the same Cache instance, the caller must synchronize access. However, -// multiple Cache instances (even in different processes) can safely share -// the same directory, as the filesystem provides atomic file operations. -// -// Cache entries have a time-to-live (TTL) based on file modification time. -// A TTL of 0 means entries never expire. -// -// Use [Cache.Namespace] to create scoped views that automatically prefix -// keys, avoiding collisions between different data sources: -// -// pypi := cache.Namespace("pypi:") -// npm := cache.Namespace("npm:") -// pypi.Set("requests", data) // key becomes "pypi:requests" -type Cache struct { - dir string - ttl time.Duration - prefix string -} - -// NewCache creates a Cache that stores entries in dir with the given TTL. -// -// If dir is empty, NewCache uses the default directory ~/.cache/stacktower/. -// The directory is created with mode 0755 if it doesn't exist. If directory -// creation fails (e.g., due to permissions), NewCache returns an error. -// -// Parameters: -// - dir: Cache directory path. Use "" for default (~/.cache/stacktower/). -// - ttl: Time-to-live for cache entries. Use 0 for no expiration. -// -// The returned Cache is ready to use. Directory creation errors are the -// only possible source of failure. -func NewCache(dir string, ttl time.Duration) (*Cache, error) { - if dir == "" { - home, err := os.UserHomeDir() - if err != nil { - return nil, err - } - dir = filepath.Join(home, ".cache", "stacktower") - } - if err := os.MkdirAll(dir, 0o755); err != nil { - return nil, err - } - return &Cache{dir: dir, ttl: ttl, prefix: ""}, nil -} - -// Dir returns the absolute path to the cache directory. -func (c *Cache) Dir() string { return c.dir } - -// TTL returns the time-to-live duration for cache entries. -// A TTL of 0 means cache entries never expire. -func (c *Cache) TTL() time.Duration { return c.ttl } - -// Get retrieves a cached value by key and unmarshals it into v. -// -// Return values indicate three distinct outcomes: -// - (true, nil): Cache hit. The value was found, is fresh, and unmarshaled into v. -// - (false, nil): Cache miss. No entry exists for this key. v is unchanged. -// - (false, ErrExpired): Entry exists but exceeded its TTL. v is unchanged. -// - (false, other error): I/O error, JSON unmarshal error, etc. v may be partially modified. -// -// The key can be any string. Consider namespacing keys to avoid collisions -// (e.g., "pypi:requests", "npm:react"). The key is hashed with SHA-256, -// so long keys are acceptable. -// -// The value v must be a pointer to a type compatible with json.Unmarshal. -// Common types include *string, *[]byte, *map[string]any, and pointers to -// custom structs with JSON tags. -// -// Get does not modify the cache or update modification times; reads are -// non-mutating operations. -func (c *Cache) Get(key string, v any) (bool, error) { - path := c.keyPath(c.prefix + key) - info, err := os.Stat(path) - if os.IsNotExist(err) { - return false, nil - } - if err != nil { - return false, err - } - if c.ttl > 0 && time.Since(info.ModTime()) > c.ttl { - return false, ErrExpired - } - data, err := os.ReadFile(path) - if err != nil { - return false, err - } - return true, json.Unmarshal(data, v) -} - -// Set stores a value in the cache under the given key. -// -// The value v is marshaled to JSON using encoding/json and written to disk. -// If v cannot be marshaled (e.g., contains channels or functions), Set -// returns a json.MarshalError. If the write fails (e.g., disk full, -// permission denied), Set returns the underlying I/O error. -// -// Set overwrites any existing entry for key, resetting its modification time -// to the current time. This effectively refreshes the TTL. -// -// The value v is not modified by Set; marshaling operates on a copy. -func (c *Cache) Set(key string, v any) error { - data, err := json.Marshal(v) - if err != nil { - return err - } - return os.WriteFile(c.keyPath(c.prefix+key), data, 0o644) -} - -// Namespace returns a new Cache that automatically prefixes all keys with prefix. -// -// This creates a scoped view of the cache, useful for avoiding key collisions -// between different data sources or components. The returned Cache shares the -// same underlying directory and TTL as the parent. -// -// Example: -// -// cache, _ := httputil.NewCache("", 24*time.Hour) -// pypiCache := cache.Namespace("pypi:") -// npmCache := cache.Namespace("npm:") -// -// pypiCache.Set("requests", pypiData) // Stored as "pypi:requests" -// npmCache.Set("express", npmData) // Stored as "npm:express" -// -// Namespace calls can be chained to create hierarchical key spaces: -// -// cache.Namespace("python:").Namespace("pypi:") // prefix: "python:pypi:" -// -// The prefix is applied transparently to all Get and Set operations. -// An empty prefix is valid and results in no key transformation. -func (c *Cache) Namespace(prefix string) *Cache { - return &Cache{ - dir: c.dir, - ttl: c.ttl, - prefix: c.prefix + prefix, - } -} - -func (c *Cache) keyPath(key string) string { - h := sha256.Sum256([]byte(key)) - return filepath.Join(c.dir, hex.EncodeToString(h[:])) -} diff --git a/pkg/httputil/cache_test.go b/pkg/httputil/cache_test.go deleted file mode 100644 index a5f6875..0000000 --- a/pkg/httputil/cache_test.go +++ /dev/null @@ -1,212 +0,0 @@ -package httputil - -import ( - "errors" - "os" - "path/filepath" - "testing" - "time" -) - -func TestCache_GetSet(t *testing.T) { - c, _ := NewCache(t.TempDir(), time.Hour) - - tests := []struct { - name string - key string - value any - }{ - {"simple", "key1", map[string]string{"foo": "bar"}}, - {"string", "key2", "test"}, - {"nested", "key3", map[string]any{"a": map[string]int{"b": 1}}}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := c.Set(tt.key, tt.value); err != nil { - t.Fatalf("Set() failed: %v", err) - } - - var result any - switch tt.value.(type) { - case map[string]string: - result = &map[string]string{} - case string: - result = new(string) - case map[string]any: - result = &map[string]any{} - } - - ok, err := c.Get(tt.key, result) - if err != nil { - t.Fatalf("Get() failed: %v", err) - } - if !ok { - t.Fatal("Get() returned false for existing key") - } - }) - } -} - -func TestCache_Miss(t *testing.T) { - c, _ := NewCache(t.TempDir(), time.Hour) - var result string - ok, err := c.Get("missing", &result) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if ok { - t.Error("Get() returned true for missing key") - } -} - -func TestCache_Expiration(t *testing.T) { - c, _ := NewCache(t.TempDir(), 10*time.Millisecond) - - if err := c.Set("key", "value"); err != nil { - t.Fatalf("Set() failed: %v", err) - } - - var res string - ok, err := c.Get("key", &res) - if err != nil || !ok { - t.Fatalf("Get() = %v, %v; want true, nil", ok, err) - } - - time.Sleep(20 * time.Millisecond) - - ok, err = c.Get("key", &res) - if !errors.Is(err, ErrExpired) { - t.Errorf("got error %v, want ErrExpired", err) - } - if ok { - t.Error("Get() returned true for expired key") - } -} - -func TestCache_KeyStability(t *testing.T) { - c, _ := NewCache(t.TempDir(), time.Hour) - p1 := c.keyPath("test") - p2 := c.keyPath("test") - if p1 != p2 { - t.Error("path should be deterministic") - } - p3 := c.keyPath("other") - if p1 == p3 { - t.Error("different keys should produce different paths") - } -} - -func TestNewCache_DefaultDir(t *testing.T) { - home, err := os.UserHomeDir() - if err != nil { - t.Skip("cannot determine home directory") - } - - c, err := NewCache("", time.Hour) - if err != nil { - t.Fatalf("NewCache() failed: %v", err) - } - - want := filepath.Join(home, ".cache", "stacktower") - if c.Dir() != want { - t.Errorf("got Dir = %s, want %s", c.Dir(), want) - } - if c.TTL() != time.Hour { - t.Errorf("got TTL = %v, want 1h", c.TTL()) - } - if _, err := os.Stat(c.Dir()); err != nil { - t.Errorf("directory not created: %v", err) - } -} - -func TestCache_Namespace(t *testing.T) { - c, _ := NewCache(t.TempDir(), time.Hour) - - t.Run("basicNamespacing", func(t *testing.T) { - pypi := c.Namespace("pypi:") - npm := c.Namespace("npm:") - - // Set values in different namespaces - if err := pypi.Set("requests", "pypi-data"); err != nil { - t.Fatalf("pypi.Set() failed: %v", err) - } - if err := npm.Set("requests", "npm-data"); err != nil { - t.Fatalf("npm.Set() failed: %v", err) - } - - // Retrieve from namespaced caches - var pypiVal, npmVal string - ok, err := pypi.Get("requests", &pypiVal) - if !ok || err != nil { - t.Fatalf("pypi.Get() = %v, %v; want true, nil", ok, err) - } - ok, err = npm.Get("requests", &npmVal) - if !ok || err != nil { - t.Fatalf("npm.Get() = %v, %v; want true, nil", ok, err) - } - - if pypiVal != "pypi-data" { - t.Errorf("got pypi value %q, want %q", pypiVal, "pypi-data") - } - if npmVal != "npm-data" { - t.Errorf("got npm value %q, want %q", npmVal, "npm-data") - } - - // Values should not cross-contaminate - _, _ = pypi.Get("requests", &npmVal) - if npmVal != "pypi-data" { - t.Error("namespace isolation violated") - } - }) - - t.Run("chainedNamespacing", func(t *testing.T) { - python := c.Namespace("python:") - pypi := python.Namespace("pypi:") - - if err := pypi.Set("test", "value"); err != nil { - t.Fatalf("Set() failed: %v", err) - } - - var result string - ok, err := pypi.Get("test", &result) - if !ok || err != nil || result != "value" { - t.Errorf("Get() = %v, %v, %q; want true, nil, %q", ok, err, result, "value") - } - - // Should not be accessible without full prefix - found, _ := python.Get("test", &result) - if found { - t.Error("value accessible without full namespace chain") - } - }) - - t.Run("emptyPrefix", func(t *testing.T) { - ns := c.Namespace("") - if err := ns.Set("key", "value"); err != nil { - t.Fatalf("Set() failed: %v", err) - } - - var result string - ok, err := ns.Get("key", &result) - if !ok || err != nil || result != "value" { - t.Errorf("Get() = %v, %v, %q; want true, nil, %q", ok, err, result, "value") - } - - // Should be same as parent cache - ok, err = c.Get("key", &result) - if !ok || err != nil || result != "value" { - t.Error("empty namespace should behave like parent") - } - }) - - t.Run("preservesDirAndTTL", func(t *testing.T) { - ns := c.Namespace("test:") - if ns.Dir() != c.Dir() { - t.Errorf("Dir() = %s, want %s", ns.Dir(), c.Dir()) - } - if ns.TTL() != c.TTL() { - t.Errorf("TTL() = %v, want %v", ns.TTL(), c.TTL()) - } - }) -} diff --git a/pkg/httputil/doc.go b/pkg/httputil/doc.go deleted file mode 100644 index a2aecc6..0000000 --- a/pkg/httputil/doc.go +++ /dev/null @@ -1,52 +0,0 @@ -// Package httputil provides HTTP utilities for package registry clients. -// -// # Overview -// -// This package provides infrastructure used by all registry API clients: -// -// - [Cache]: File-based HTTP response caching -// - [Retry]: Automatic retry with exponential backoff -// -// # Caching -// -// [Cache] stores HTTP responses in the filesystem (~/.cache/stacktower/) -// with configurable TTL. This dramatically speeds up repeated operations -// and reduces load on package registries. -// -// Usage: -// -// cache, err := httputil.NewCache(24 * time.Hour) -// data, ok := cache.Get("pypi:fastapi") // Check cache -// if !ok { -// data = fetchFromAPI() -// cache.Set("pypi:fastapi", data) // Store for later -// } -// -// Cache keys should be namespaced by registry to avoid collisions. -// -// # Retry -// -// [Retry] wraps HTTP requests with automatic retry for transient failures: -// -// - Network errors -// - 5xx server errors -// - 429 rate limit responses -// -// It uses exponential backoff with jitter to avoid thundering herd: -// -// resp, err := httputil.Retry(func() (*http.Response, error) { -// return http.Get(url) -// }) -// -// # Configuration -// -// Default settings are suitable for most use cases: -// -// - Cache directory: ~/.cache/stacktower/ -// - Default TTL: 24 hours -// - Max retries: 3 -// - Base backoff: 1 second -// -// The cache can be cleared via `stacktower cache clear` or by deleting -// the cache directory. -package httputil diff --git a/pkg/httputil/example_test.go b/pkg/httputil/example_test.go deleted file mode 100644 index 3a25911..0000000 --- a/pkg/httputil/example_test.go +++ /dev/null @@ -1,183 +0,0 @@ -package httputil_test - -import ( - "context" - "errors" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/matzehuels/stacktower/pkg/httputil" -) - -func ExampleCache() { - // Create a cache with 24-hour TTL in a temp directory - dir := filepath.Join(os.TempDir(), "stacktower-example") - cache, err := httputil.NewCache(dir, 24*time.Hour) - if err != nil { - fmt.Println("Error:", err) - return - } - - // Store a value - data := map[string]string{"name": "example", "version": "1.0.0"} - if err := cache.Set("mykey", data); err != nil { - fmt.Println("Error:", err) - return - } - - // Retrieve the value - var result map[string]string - if ok, err := cache.Get("mykey", &result); ok && err == nil { - fmt.Println("Name:", result["name"]) - fmt.Println("Version:", result["version"]) - } - - // Clean up - os.RemoveAll(dir) - // Output: - // Name: example - // Version: 1.0.0 -} - -func ExampleCache_miss() { - dir := filepath.Join(os.TempDir(), "stacktower-example-miss") - cache, _ := httputil.NewCache(dir, time.Hour) - defer os.RemoveAll(dir) - - // Try to get a non-existent key - var result string - ok, err := cache.Get("nonexistent", &result) - fmt.Println("Found:", ok) - fmt.Println("Error:", err) - // Output: - // Found: false - // Error: -} - -func ExampleNewCache_defaultDir() { - // Pass empty string to use default directory (~/.cache/stacktower/) - cache, err := httputil.NewCache("", 24*time.Hour) - if err != nil { - fmt.Println("Error:", err) - return - } - fmt.Println("Cache TTL:", cache.TTL()) - // Output: - // Cache TTL: 24h0m0s -} - -func ExampleRetry() { - ctx := context.Background() - attempts := 0 - - // Simulate an operation that fails twice then succeeds - err := httputil.Retry(ctx, 3, 10*time.Millisecond, func() error { - attempts++ - if attempts < 3 { - // Wrap transient errors to enable retry - return &httputil.RetryableError{ - Err: fmt.Errorf("temporary failure (attempt %d)", attempts), - } - } - return nil // Success - }) - - if err != nil { - fmt.Println("Failed:", err) - } else { - fmt.Println("Success after", attempts, "attempts") - } - // Output: - // Success after 3 attempts -} - -func ExampleRetryWithBackoff() { - ctx := context.Background() - - // Fetch data with automatic retry on transient failures - err := httputil.RetryWithBackoff(ctx, func() error { - // Your HTTP request or other operation here - // Return &httputil.RetryableError{...} for transient failures - // Return regular errors for permanent failures - return nil - }) - - if err != nil { - fmt.Println("Error:", err) - } else { - fmt.Println("Success") - } - // Output: - // Success -} - -func ExampleRetryableError() { - ctx := context.Background() - networkErr := errors.New("connection refused") - - err := httputil.Retry(ctx, 2, 10*time.Millisecond, func() error { - // Permanent error - no retry - if false { - return errors.New("invalid request") - } - // Transient error - will retry - return &httputil.RetryableError{Err: networkErr} - }) - - // Check if the underlying error is our network error - if errors.Is(err, networkErr) { - fmt.Println("Failed due to network error") - } - // Output: - // Failed due to network error -} - -func ExampleRetryable() { - ctx := context.Background() - attempts := 0 - - // Using the Retryable helper for cleaner code - err := httputil.RetryWithBackoff(ctx, func() error { - attempts++ - if attempts < 2 { - // Wrap errors concisely with Retryable() - return httputil.Retryable(errors.New("temporary failure")) - } - return nil - }) - - if err == nil { - fmt.Println("Success") - } - // Output: - // Success -} - -func ExampleCache_Namespace() { - dir := filepath.Join(os.TempDir(), "stacktower-namespace-example") - cache, _ := httputil.NewCache(dir, 24*time.Hour) - defer os.RemoveAll(dir) - - // Create namespaced caches for different registries - pypiCache := cache.Namespace("pypi:") - npmCache := cache.Namespace("npm:") - - // Store values in different namespaces - pypiCache.Set("requests", map[string]string{"version": "2.31.0"}) - npmCache.Set("express", map[string]string{"version": "4.18.2"}) - - // Retrieve from appropriate namespace - var pypiData map[string]string - pypiCache.Get("requests", &pypiData) - fmt.Println("PyPI requests:", pypiData["version"]) - - var npmData map[string]string - npmCache.Get("express", &npmData) - fmt.Println("npm express:", npmData["version"]) - - // Output: - // PyPI requests: 2.31.0 - // npm express: 4.18.2 -} diff --git a/pkg/httputil/retry.go b/pkg/httputil/retry.go deleted file mode 100644 index 4b59392..0000000 --- a/pkg/httputil/retry.go +++ /dev/null @@ -1,103 +0,0 @@ -package httputil - -import ( - "context" - "errors" - "time" -) - -// RetryableError wraps an error to indicate it should trigger a retry. -// Use this type to signal transient failures like network timeouts, -// temporary DNS resolution failures, or HTTP 5xx server errors. -// Errors not wrapped in RetryableError are treated as permanent failures -// and cause [Retry] to return immediately without further attempts. -// -// Prefer using the [Retryable] helper function for convenience: -// -// if resp.StatusCode >= 500 { -// return httputil.Retryable(fmt.Errorf("server error: %d", resp.StatusCode)) -// } -// -// RetryableError implements error unwrapping, so errors.Is and errors.As -// work correctly with the wrapped error. -type RetryableError struct{ Err error } - -// Retryable wraps an error as a [RetryableError], signaling to [Retry] -// that this failure should trigger a retry attempt. -// -// This is a convenience helper that avoids verbose struct literal syntax. -// Returns nil if err is nil, allowing safe use in error returns: -// -// if err := doSomething(); err != nil { -// return httputil.Retryable(err) -// } -func Retryable(err error) error { - if err == nil { - return nil - } - return &RetryableError{Err: err} -} - -// Error returns the error message of the wrapped error. -func (e *RetryableError) Error() string { return e.Err.Error() } - -// Unwrap returns the wrapped error, enabling errors.Is and errors.As -// to inspect the underlying cause. -func (e *RetryableError) Unwrap() error { return e.Err } - -// Retry executes fn up to attempts times with exponential backoff. -// -// Only errors wrapped with [RetryableError] trigger a retry; all other errors -// are returned immediately. Between retries, Retry waits for delay, then -// doubles the delay for the next attempt (1s, 2s, 4s, etc.). If ctx is -// cancelled during a retry delay, Retry returns ctx.Err() immediately. -// -// Parameters: -// - ctx: Context for cancellation. If cancelled during backoff, returns ctx.Err(). -// - attempts: Maximum number of attempts (minimum 1). Zero or negative values default to 1. -// - delay: Initial backoff duration. Doubled after each failed attempt. -// - fn: Function to execute. Wrap errors in [RetryableError] to enable retries. -// -// Returns the result of fn on success, the last error if all attempts fail, -// or ctx.Err() if the context is cancelled during backoff. -// -// Retry is safe to call from multiple goroutines. However, fn itself must -// handle any concurrency concerns for the operation it performs. -func Retry(ctx context.Context, attempts int, delay time.Duration, fn func() error) error { - attempts = max(attempts, 1) - var lastErr error - - for i := range attempts { - if err := fn(); err == nil { - return nil - } else if lastErr = err; !isRetryable(err) { - return err - } - - if i < attempts-1 { - select { - case <-ctx.Done(): - return ctx.Err() - case <-time.After(delay): - delay *= 2 - } - } - } - return lastErr -} - -// RetryWithBackoff is a convenience wrapper around [Retry] with sensible defaults. -// -// It performs up to 3 attempts with exponential backoff starting at 1 second: -// attempt 1 (immediate), wait 1s, attempt 2, wait 2s, attempt 3. -// Total maximum wait time is 3 seconds across all retries. -// -// Use this when you need retry logic but don't need custom retry parameters. -// For more control over attempts or delay, call [Retry] directly. -func RetryWithBackoff(ctx context.Context, fn func() error) error { - return Retry(ctx, 3, time.Second, fn) -} - -func isRetryable(err error) bool { - return errors.As(err, new(*RetryableError)) -} diff --git a/pkg/httputil/retry_test.go b/pkg/httputil/retry_test.go deleted file mode 100644 index d9bf096..0000000 --- a/pkg/httputil/retry_test.go +++ /dev/null @@ -1,211 +0,0 @@ -package httputil - -import ( - "context" - "errors" - "testing" - "time" -) - -func TestRetry(t *testing.T) { - retryErr := errors.New("retry me") - - tests := []struct { - name string - maxAttempts int - fn func(*int) func() error - wantAttempts int - wantErr bool - }{ - { - name: "successFirstAttempt", - maxAttempts: 3, - fn: func(attempts *int) func() error { - return func() error { - *attempts++ - return nil - } - }, - wantAttempts: 1, - }, - { - name: "successAfterRetries", - maxAttempts: 3, - fn: func(attempts *int) func() error { - return func() error { - *attempts++ - if *attempts < 3 { - return &RetryableError{Err: retryErr} - } - return nil - } - }, - wantAttempts: 3, - }, - { - name: "maxRetriesExceeded", - maxAttempts: 3, - fn: func(attempts *int) func() error { - return func() error { - *attempts++ - return &RetryableError{Err: retryErr} - } - }, - wantAttempts: 3, - wantErr: true, - }, - { - name: "nonRetryableError", - maxAttempts: 3, - fn: func(attempts *int) func() error { - return func() error { - *attempts++ - return errors.New("permanent") - } - }, - wantAttempts: 1, - wantErr: true, - }, - { - name: "zeroAttemptsDefaultsToOne", - maxAttempts: 0, - fn: func(attempts *int) func() error { - return func() error { - *attempts++ - return nil - } - }, - wantAttempts: 1, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - attempts := 0 - err := Retry(context.Background(), tt.maxAttempts, time.Millisecond, tt.fn(&attempts)) - if (err != nil) != tt.wantErr { - t.Errorf("got error = %v, wantErr %v", err, tt.wantErr) - } - if attempts != tt.wantAttempts { - t.Errorf("got %d attempts, want %d", attempts, tt.wantAttempts) - } - }) - } -} - -func TestRetry_ContextCancellation(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - attempts := 0 - - err := Retry(ctx, 5, time.Millisecond, func() error { - attempts++ - if attempts == 1 { - cancel() - } - return &RetryableError{Err: errors.New("retry")} - }) - - if !errors.Is(err, context.Canceled) { - t.Errorf("got error %v, want context.Canceled", err) - } -} - -func TestRetry_ExponentialBackoff(t *testing.T) { - attempts := 0 - start := time.Now() - - err := Retry(context.Background(), 3, 50*time.Millisecond, func() error { - attempts++ - if attempts < 3 { - return &RetryableError{Err: errors.New("retry")} - } - return nil - }) - - elapsed := time.Since(start) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if attempts != 3 { - t.Errorf("got %d attempts, want 3", attempts) - } - minDelay := 150 * time.Millisecond // 50ms + 100ms - if elapsed < minDelay { - t.Errorf("elapsed %v too short, want >= %v", elapsed, minDelay) - } -} - -func TestRetryWithBackoff(t *testing.T) { - attempts := 0 - err := RetryWithBackoff(context.Background(), func() error { - attempts++ - if attempts < 2 { - return &RetryableError{Err: errors.New("retry")} - } - return nil - }) - - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if attempts != 2 { - t.Errorf("got %d attempts, want 2", attempts) - } -} - -func TestRetryableError(t *testing.T) { - inner := errors.New("inner") - err := &RetryableError{Err: inner} - - if got := err.Error(); got != "inner" { - t.Errorf("got Error() = %q, want %q", got, "inner") - } - if !errors.Is(err, inner) { - t.Error("errors.Is failed to match wrapped error") - } - if got := errors.Unwrap(err); got != inner { - t.Errorf("got Unwrap() = %v, want %v", got, inner) - } -} - -func TestRetryable(t *testing.T) { - t.Run("wrapsError", func(t *testing.T) { - inner := errors.New("test error") - err := Retryable(inner) - - if err == nil { - t.Fatal("expected non-nil error") - } - if !errors.Is(err, inner) { - t.Error("errors.Is failed to match wrapped error") - } - var retryErr *RetryableError - if !errors.As(err, &retryErr) { - t.Error("expected RetryableError type") - } - }) - - t.Run("nilInput", func(t *testing.T) { - err := Retryable(nil) - if err != nil { - t.Errorf("got %v, want nil", err) - } - }) - - t.Run("usedInRetry", func(t *testing.T) { - attempts := 0 - err := Retry(context.Background(), 3, time.Millisecond, func() error { - attempts++ - if attempts < 2 { - return Retryable(errors.New("retry me")) - } - return nil - }) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if attempts != 2 { - t.Errorf("got %d attempts, want 2", attempts) - } - }) -} diff --git a/pkg/integrations/client.go b/pkg/integrations/client.go index 9345670..bf1a847 100644 --- a/pkg/integrations/client.go +++ b/pkg/integrations/client.go @@ -6,8 +6,9 @@ import ( "fmt" "io" "net/http" + "time" - "github.com/matzehuels/stacktower/pkg/httputil" + "github.com/matzehuels/stacktower/pkg/cache" ) // Client provides shared HTTP functionality for all registry API clients. @@ -18,27 +19,36 @@ import ( // // Zero values: Do not use an uninitialized Client; always create via [NewClient]. type Client struct { - http *http.Client - cache *httputil.Cache - headers map[string]string + http *http.Client + cache cache.Cache + keyer cache.Keyer + namespace string // Cache key prefix (e.g., "pypi:", "npm:") + ttl time.Duration // Cache TTL + headers map[string]string } // NewClient creates a Client with the given cache and default headers. // Headers are applied to all requests made through this client. // // Parameters: -// - cache: Cache instance for storing responses (must not be nil). Create with -// [NewCacheWithNamespace] for registry-specific caching. +// - c: Cache for caching HTTP responses. If nil, a NullCache is used (no caching). +// - namespace: Cache key prefix for this client (e.g., "pypi:", "npm:"). +// - ttl: How long to cache responses. // - headers: Default HTTP headers for all requests. Pass nil if no default headers // are needed. Common examples: "Authorization", "User-Agent", "Accept". // // The returned Client is safe for concurrent use by multiple goroutines. -// Panics if cache is nil. -func NewClient(cache *httputil.Cache, headers map[string]string) *Client { +func NewClient(c cache.Cache, namespace string, ttl time.Duration, headers map[string]string) *Client { + if c == nil { + c = cache.NewNullCache() + } return &Client{ - http: NewHTTPClient(), - cache: cache, - headers: headers, + http: NewHTTPClient(), + cache: c, + keyer: cache.NewDefaultKeyer(), + namespace: namespace, + ttl: ttl, + headers: headers, } } @@ -54,11 +64,11 @@ func NewClient(cache *httputil.Cache, headers map[string]string) *Client { // // Behavior: // 1. If refresh=false and cache hit: returns nil immediately with v populated -// 2. If cache miss or refresh=true: calls fetch with automatic retry on [httputil.RetryableError] +// 2. If cache miss or refresh=true: calls fetch with automatic retry on [RetryableError] // 3. On successful fetch: stores result in cache (ignoring cache write errors) // // The fetch function should populate v and return nil on success, or return an error. -// Network errors should be wrapped with [httputil.Retryable] to enable retry. +// Network errors should be wrapped with [Retryable] to enable retry. // // Returns: // - nil on success (v is populated) @@ -68,14 +78,23 @@ func NewClient(cache *httputil.Cache, headers map[string]string) *Client { // This method is safe for concurrent use on the same Client. func (c *Client) Cached(ctx context.Context, key string, refresh bool, v any, fetch func() error) error { if !refresh { - if ok, _ := c.cache.Get(key, v); ok { - return nil + // Try to get from cache + cacheKey := c.keyer.HTTPKey(c.namespace, key) + data, hit, _ := c.cache.Get(ctx, cacheKey) + if hit { + if err := json.Unmarshal(data, v); err == nil { + return nil + } } } - if err := httputil.RetryWithBackoff(ctx, fetch); err != nil { + if err := cache.RetryWithBackoff(ctx, fetch); err != nil { return err } - _ = c.cache.Set(key, v) + // Store in cache (ignore errors) + if data, err := json.Marshal(v); err == nil { + cacheKey := c.keyer.HTTPKey(c.namespace, key) + _ = c.cache.Set(ctx, cacheKey, data, c.ttl) + } return nil } @@ -89,7 +108,7 @@ func (c *Client) Cached(ctx context.Context, key string, refresh bool, v any, fe // // Returns: // - [ErrNotFound] for HTTP 404 responses -// - [ErrNetwork] wrapped with [httputil.RetryableError] for HTTP 5xx responses +// - [ErrNetwork] wrapped with [RetryableError] for HTTP 5xx responses // - [ErrNetwork] for connection failures and timeouts // - json decoding errors if response is not valid JSON // @@ -164,7 +183,7 @@ func (c *Client) doRequest(ctx context.Context, url string, headers map[string]s resp, err := c.http.Do(req) if err != nil { - return nil, httputil.Retryable(fmt.Errorf("%w: %v", ErrNetwork, err)) + return nil, cache.Retryable(fmt.Errorf("%w: %v", ErrNetwork, err)) } if err := checkStatus(resp.StatusCode); err != nil { @@ -180,9 +199,24 @@ func checkStatus(code int) error { return nil case code == http.StatusNotFound: return ErrNotFound + case code == http.StatusTooManyRequests: + return &RateLimitedError{} case code >= 500: - return httputil.Retryable(fmt.Errorf("%w: status %d", ErrNetwork, code)) + return cache.Retryable(fmt.Errorf("%w: status %d", ErrNetwork, code)) default: return fmt.Errorf("%w: status %d", ErrNetwork, code) } } + +// RateLimitedError indicates the API rate limit has been exceeded. +type RateLimitedError struct { + RetryAfter int // Seconds to wait before retrying (0 if unknown) +} + +// Error implements the error interface. +func (e *RateLimitedError) Error() string { + if e.RetryAfter > 0 { + return fmt.Sprintf("rate limited: retry after %d seconds", e.RetryAfter) + } + return "rate limited: too many requests" +} diff --git a/pkg/integrations/client_test.go b/pkg/integrations/client_test.go index 222f4ce..5c7e17a 100644 --- a/pkg/integrations/client_test.go +++ b/pkg/integrations/client_test.go @@ -9,14 +9,15 @@ import ( "testing" "time" - "github.com/matzehuels/stacktower/pkg/httputil" + "github.com/matzehuels/stacktower/pkg/cache" ) func TestNewClient(t *testing.T) { - cache, _ := httputil.NewCache("", time.Hour) - headers := map[string]string{"Authorization": "Bearer token"} + c, _ := cache.NewFileCache(t.TempDir()) + defer c.Close() - client := NewClient(cache, headers) + headers := map[string]string{"Authorization": "Bearer token"} + client := NewClient(c, "test:", time.Hour, headers) if client == nil { t.Fatal("NewClient() returned nil") @@ -24,7 +25,7 @@ func TestNewClient(t *testing.T) { if client.http == nil { t.Error("NewClient() http client is nil") } - if client.cache != cache { + if client.cache != c { t.Error("NewClient() cache not set correctly") } if client.headers["Authorization"] != "Bearer token" { @@ -33,8 +34,10 @@ func TestNewClient(t *testing.T) { } func TestNewClientNilHeaders(t *testing.T) { - cache, _ := httputil.NewCache("", time.Hour) - client := NewClient(cache, nil) + c, _ := cache.NewFileCache(t.TempDir()) + defer c.Close() + + client := NewClient(c, "test:", time.Hour, nil) if client == nil { t.Fatal("NewClient() returned nil") @@ -57,8 +60,10 @@ func TestClientGet(t *testing.T) { })) defer server.Close() - cache, _ := httputil.NewCache("", time.Hour) - client := NewClient(cache, nil) + c, _ := cache.NewFileCache(t.TempDir()) + defer c.Close() + + client := NewClient(c, "test:", time.Hour, nil) client.http = server.Client() var resp response @@ -80,8 +85,10 @@ func TestClientGetWithHeaders(t *testing.T) { })) defer server.Close() - cache, _ := httputil.NewCache("", time.Hour) - client := NewClient(cache, map[string]string{"X-Default": "default"}) + c, _ := cache.NewFileCache(t.TempDir()) + defer c.Close() + + client := NewClient(c, "test:", time.Hour, map[string]string{"X-Default": "default"}) client.http = server.Client() var resp map[string]string @@ -103,8 +110,10 @@ func TestClientGetWithHeadersOverridesDefaults(t *testing.T) { })) defer server.Close() - cache, _ := httputil.NewCache("", time.Hour) - client := NewClient(cache, map[string]string{"X-Override": "default"}) + c, _ := cache.NewFileCache(t.TempDir()) + defer c.Close() + + client := NewClient(c, "test:", time.Hour, map[string]string{"X-Override": "default"}) client.http = server.Client() var resp map[string]string @@ -123,8 +132,10 @@ func TestClientGetText(t *testing.T) { })) defer server.Close() - cache, _ := httputil.NewCache("", time.Hour) - client := NewClient(cache, nil) + c, _ := cache.NewFileCache(t.TempDir()) + defer c.Close() + + client := NewClient(c, "test:", time.Hour, nil) client.http = server.Client() text, err := client.GetText(context.Background(), server.URL) @@ -142,8 +153,10 @@ func TestClientGet404(t *testing.T) { })) defer server.Close() - cache, _ := httputil.NewCache("", time.Hour) - client := NewClient(cache, nil) + c, _ := cache.NewFileCache(t.TempDir()) + defer c.Close() + + client := NewClient(c, "test:", time.Hour, nil) client.http = server.Client() var resp map[string]string @@ -159,8 +172,10 @@ func TestClientGet500(t *testing.T) { })) defer server.Close() - cache, _ := httputil.NewCache("", time.Hour) - client := NewClient(cache, nil) + c, _ := cache.NewFileCache(t.TempDir()) + defer c.Close() + + client := NewClient(c, "test:", time.Hour, nil) client.http = server.Client() var resp map[string]string @@ -169,15 +184,17 @@ func TestClientGet500(t *testing.T) { t.Error("Get() should return error for 500") } - var retryErr *httputil.RetryableError + var retryErr *cache.RetryableError if !errors.As(err, &retryErr) { t.Errorf("Get() error should be RetryableError, got %T", err) } } func TestClientCached(t *testing.T) { - cache, _ := httputil.NewCache("", time.Hour) - client := NewClient(cache, nil) + c, _ := cache.NewFileCache(t.TempDir()) + defer c.Close() + + client := NewClient(c, "test:", time.Hour, nil) fetchCount := 0 type testData struct { @@ -206,8 +223,10 @@ func TestClientCached(t *testing.T) { } func TestClientCachedRefresh(t *testing.T) { - cache, _ := httputil.NewCache("", time.Hour) - client := NewClient(cache, nil) + c, _ := cache.NewFileCache(t.TempDir()) + defer c.Close() + + client := NewClient(c, "test:", time.Hour, nil) fetchCount := 0 var value string @@ -229,8 +248,10 @@ func TestClientCachedRefresh(t *testing.T) { } func TestClientCachedFetchError(t *testing.T) { - cache, _ := httputil.NewCache("", time.Hour) - client := NewClient(cache, nil) + c, _ := cache.NewFileCache(t.TempDir()) + defer c.Close() + + client := NewClient(c, "test:", time.Hour, nil) var value string @@ -314,7 +335,7 @@ func TestCheckStatus(t *testing.T) { t.Errorf("checkStatus() error = %v, want %v", err, tt.wantType) } if tt.isRetryErr { - var retryErr *httputil.RetryableError + var retryErr *cache.RetryableError if !errors.As(err, &retryErr) { t.Errorf("checkStatus() error should be RetryableError, got %T", err) } @@ -406,13 +427,3 @@ func TestNewHTTPClient(t *testing.T) { t.Errorf("Timeout = %v, want %v", client.Timeout, httpTimeout) } } - -func TestNewCache(t *testing.T) { - cache, err := NewCache(time.Hour) - if err != nil { - t.Fatalf("NewCache() error: %v", err) - } - if cache == nil { - t.Error("NewCache() returned nil") - } -} diff --git a/pkg/integrations/common.go b/pkg/integrations/common.go index eac9d07..2e5b97f 100644 --- a/pkg/integrations/common.go +++ b/pkg/integrations/common.go @@ -1,32 +1,27 @@ package integrations import ( - "errors" "net/http" "net/url" "regexp" "strings" "time" - "github.com/matzehuels/stacktower/pkg/httputil" + "github.com/matzehuels/stacktower/pkg/cache" ) // httpTimeout is the default timeout for all HTTP requests to registry APIs. // Individual registries do not override this value. const httpTimeout = 10 * time.Second +// Sentinel errors - re-exported from cache for API consistency. var ( // ErrNotFound is returned when a package or resource doesn't exist in the registry. // This corresponds to HTTP 404 responses. - // Callers should check with errors.Is(err, integrations.ErrNotFound). - // This error is never wrapped with additional context. - ErrNotFound = errors.New("resource not found") + ErrNotFound = cache.ErrNotFound // ErrNetwork is returned for HTTP failures (timeouts, connection errors, 5xx responses). - // This error may be wrapped with [httputil.RetryableError] for 5xx status codes. - // Callers should check with errors.Is(err, integrations.ErrNetwork) for any network issue, - // or errors.As(err, &httputil.RetryableError{}) to detect retryable failures specifically. - ErrNetwork = errors.New("network error") + ErrNetwork = cache.ErrNetwork ) // RepoMetrics holds repository-level data fetched from GitHub or GitLab. @@ -39,6 +34,7 @@ var ( type RepoMetrics struct { RepoURL string `json:"repo_url"` // Canonical repository URL (https://...). Never empty in valid metrics. Owner string `json:"owner"` // Repository owner username. Never empty in valid metrics. + Description string `json:"description,omitempty"` // Repository description from GitHub/GitLab. Empty if not set. Stars int `json:"stars"` // GitHub/GitLab star count. 0 is a valid value for new repositories. SizeKB int `json:"size_kb,omitempty"` // Repository size in kilobytes. 0 means not available or very small. LastCommitAt *time.Time `json:"last_commit_at,omitempty"` // Date of most recent commit. Nil if not available. @@ -69,45 +65,6 @@ func NewHTTPClient() *http.Client { return &http.Client{Timeout: httpTimeout} } -// NewCache creates a file-based cache with the given TTL in the default cache directory. -// See [httputil.NewCache] for details on cache location and behavior. -// -// The ttl parameter must be positive. A ttl of 0 means items never expire (not recommended). -// Negative ttl values are invalid and will be treated as 0. -// -// For registry-specific clients, prefer using [NewCacheWithNamespace] to automatically -// scope cache keys by registry name and prevent collisions. -// -// Returns an error if the cache directory cannot be created or accessed. -// The returned cache is safe for concurrent use by multiple goroutines. -func NewCache(ttl time.Duration) (*httputil.Cache, error) { - return httputil.NewCache("", ttl) -} - -// NewCacheWithNamespace creates a namespaced cache for a specific registry. -// The namespace parameter (e.g., "pypi:", "npm:") is automatically prefixed to all -// cache keys, preventing collisions between different registries. -// -// The namespace should be non-empty and typically ends with a colon. An empty namespace -// is valid but defeats the purpose of this function; use [NewCache] instead. -// -// The ttl parameter must be positive. A ttl of 0 means items never expire (not recommended). -// -// This is the preferred way to create caches for registry clients: -// -// cache, err := integrations.NewCacheWithNamespace("pypi:", 24*time.Hour) -// client := integrations.NewClient(cache, nil) -// -// Returns an error if the cache directory cannot be created or accessed. -// The returned cache is safe for concurrent use by multiple goroutines. -func NewCacheWithNamespace(namespace string, ttl time.Duration) (*httputil.Cache, error) { - cache, err := httputil.NewCache("", ttl) - if err != nil { - return nil, err - } - return cache.Namespace(namespace), nil -} - // NormalizePkgName converts a package name to its canonical form. // Applies lowercase and replaces underscores with hyphens, following PEP 503 // normalization rules used by PyPI and other registries. diff --git a/pkg/integrations/crates/client.go b/pkg/integrations/crates/client.go index 9f861d5..4d14eab 100644 --- a/pkg/integrations/crates/client.go +++ b/pkg/integrations/crates/client.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + "github.com/matzehuels/stacktower/pkg/cache" "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -39,27 +40,22 @@ type Client struct { baseURL string } -// NewClient creates a crates.io client with the specified cache TTL. +// NewClient creates a crates.io client with the given cache backend. // -// The cacheTTL parameter sets how long responses are cached. -// Typical values: 1-24 hours for production, 0 for testing (no cache). +// Parameters: +// - backend: Cache backend for HTTP response caching (use storage.NullBackend{} for no caching) +// - cacheTTL: How long responses are cached (typical: 1-24 hours) // // The client includes a User-Agent header as required by crates.io API policy. -// -// Returns an error if the cache directory cannot be created or accessed. // The returned Client is safe for concurrent use. -func NewClient(cacheTTL time.Duration) (*Client, error) { - cache, err := integrations.NewCacheWithNamespace("crates:", cacheTTL) - if err != nil { - return nil, err - } +func NewClient(backend cache.Cache, cacheTTL time.Duration) *Client { headers := map[string]string{ "User-Agent": "stacktower/1.0 (https://github.com/matzehuels/stacktower)", } return &Client{ - Client: integrations.NewClient(cache, headers), + Client: integrations.NewClient(backend, "crates:", cacheTTL, headers), baseURL: "https://crates.io/api/v1", - }, nil + } } // FetchCrate retrieves metadata for a Rust crate from crates.io. diff --git a/pkg/integrations/crates/client_test.go b/pkg/integrations/crates/client_test.go index 7f0140e..61ca447 100644 --- a/pkg/integrations/crates/client_test.go +++ b/pkg/integrations/crates/client_test.go @@ -8,14 +8,13 @@ import ( "testing" "time" + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/integrations" ) func TestNewClient(t *testing.T) { - c, err := NewClient(time.Hour) - if err != nil { - t.Fatalf("NewClient failed: %v", err) - } + c := NewClient(cache.NewNullCache(), time.Hour) if c.Client == nil { t.Error("expected client to be initialized") } @@ -91,15 +90,11 @@ func TestClient_FetchCrate_NotFound(t *testing.T) { func testClient(t *testing.T, serverURL string) *Client { t.Helper() - cache, err := integrations.NewCache(time.Hour) - if err != nil { - t.Fatal(err) - } headers := map[string]string{ "User-Agent": "stacktower/1.0 (https://github.com/matzehuels/stacktower)", } return &Client{ - Client: integrations.NewClient(cache, headers), + Client: integrations.NewClient(cache.NewNullCache(), "crates:", time.Hour, headers), baseURL: serverURL, } } diff --git a/pkg/integrations/doc.go b/pkg/integrations/doc.go index 37ee1c8..77bd4f6 100644 --- a/pkg/integrations/doc.go +++ b/pkg/integrations/doc.go @@ -30,7 +30,7 @@ // # Shared Infrastructure // // The [Client] type provides shared HTTP functionality used by all registry -// clients, including caching via [httputil.Cache]. +// clients, including HTTP response caching via [cache.Cache]. // // # Adding a New Registry // @@ -51,6 +51,6 @@ // [goproxy]: github.com/matzehuels/stacktower/pkg/integrations/goproxy // [github]: github.com/matzehuels/stacktower/pkg/integrations/github // [gitlab]: github.com/matzehuels/stacktower/pkg/integrations/gitlab -// [httputil.Cache]: github.com/matzehuels/stacktower/pkg/httputil.Cache -// [deps]: github.com/matzehuels/stacktower/pkg/deps +// [cache.Cache]: github.com/matzehuels/stacktower/pkg/cache.Cache +// [deps]: github.com/matzehuels/stacktower/pkg/core/deps package integrations diff --git a/pkg/integrations/example_test.go b/pkg/integrations/example_test.go index 44ef461..84443ae 100644 --- a/pkg/integrations/example_test.go +++ b/pkg/integrations/example_test.go @@ -78,6 +78,6 @@ func Example_errors() { fmt.Println("ErrNotFound:", integrations.ErrNotFound) fmt.Println("ErrNetwork:", integrations.ErrNetwork) // Output: - // ErrNotFound: resource not found + // ErrNotFound: not found // ErrNetwork: network error } diff --git a/pkg/integrations/github/client.go b/pkg/integrations/github/client.go index 9ef24ca..e7f4093 100644 --- a/pkg/integrations/github/client.go +++ b/pkg/integrations/github/client.go @@ -7,6 +7,7 @@ import ( "regexp" "time" + "github.com/matzehuels/stacktower/pkg/cache" "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -23,35 +24,27 @@ type Client struct { // NewClient creates a GitHub API client with optional authentication. // -// The token parameter is a GitHub personal access token for authentication. -// Pass an empty string to use unauthenticated requests. +// Parameters: +// - backend: Cache backend for HTTP response caching (use storage.NullBackend{} for no caching) +// - token: GitHub personal access token (empty string for unauthenticated) +// - cacheTTL: How long responses are cached (typical: 1-24 hours) // // Rate limits: // - Unauthenticated: 60 requests/hour per IP // - Authenticated: 5,000 requests/hour per token // // Authentication is strongly recommended for production use to avoid rate limiting. -// -// The cacheTTL parameter sets how long responses are cached. -// Typical values: 1-24 hours for production, 0 for testing (no cache). -// -// Returns an error if the cache directory cannot be created or accessed. // The returned Client is safe for concurrent use. -func NewClient(token string, cacheTTL time.Duration) (*Client, error) { - cache, err := integrations.NewCacheWithNamespace("github:", cacheTTL) - if err != nil { - return nil, err - } - +func NewClient(backend cache.Cache, token string, cacheTTL time.Duration) *Client { headers := map[string]string{"Accept": "application/vnd.github.v3+json"} if token != "" { headers["Authorization"] = "Bearer " + token } return &Client{ - Client: integrations.NewClient(cache, headers), + Client: integrations.NewClient(backend, "github:", cacheTTL, headers), baseURL: "https://api.github.com", - }, nil + } } // Fetch retrieves repository metrics (stars, maintainers, activity) from GitHub. @@ -97,14 +90,15 @@ func (c *Client) fetchMetrics(ctx context.Context, owner, repo string, m *integr } *m = integrations.RepoMetrics{ - RepoURL: fmt.Sprintf("https://github.com/%s/%s", owner, repo), - Owner: owner, - Stars: data.Stars, - SizeKB: data.Size, - License: data.License.SPDXID, - Language: data.Language, - Topics: data.Topics, - Archived: data.Archived, + RepoURL: fmt.Sprintf("https://github.com/%s/%s", owner, repo), + Owner: owner, + Description: data.Description, + Stars: data.Stars, + SizeKB: data.Size, + License: data.License.SPDXID, + Language: data.Language, + Topics: data.Topics, + Archived: data.Archived, } if data.PushedAt != nil { m.LastCommitAt = data.PushedAt @@ -220,10 +214,11 @@ func ExtractURL(urls map[string]string, homepage string) (owner, repo string, ok } type repoResponse struct { - Stars int `json:"stargazers_count"` - Size int `json:"size"` - PushedAt *time.Time `json:"pushed_at"` - License struct { + Description string `json:"description"` + Stars int `json:"stargazers_count"` + Size int `json:"size"` + PushedAt *time.Time `json:"pushed_at"` + License struct { SPDXID string `json:"spdx_id"` } `json:"license"` Language string `json:"language"` diff --git a/pkg/integrations/github/client_test.go b/pkg/integrations/github/client_test.go index 3ecb1cb..2e991b8 100644 --- a/pkg/integrations/github/client_test.go +++ b/pkg/integrations/github/client_test.go @@ -8,6 +8,8 @@ import ( "testing" "time" + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -95,10 +97,7 @@ func TestExtractURL(t *testing.T) { } func TestNewClient(t *testing.T) { - c, err := NewClient("test-token", time.Hour) - if err != nil { - t.Fatalf("NewClient failed: %v", err) - } + c := NewClient(cache.NewNullCache(), "test-token", time.Hour) if c.Client == nil { t.Error("expected client to be initialized") } @@ -106,16 +105,12 @@ func TestNewClient(t *testing.T) { func testClient(t *testing.T, serverURL, token string) *Client { t.Helper() - cache, err := integrations.NewCache(time.Hour) - if err != nil { - t.Fatal(err) - } headers := map[string]string{"Accept": "application/vnd.github.v3+json"} if token != "" { headers["Authorization"] = "Bearer " + token } return &Client{ - Client: integrations.NewClient(cache, headers), + Client: integrations.NewClient(cache.NewNullCache(), "github:", time.Hour, headers), baseURL: serverURL, } } diff --git a/pkg/integrations/github/content.go b/pkg/integrations/github/content.go new file mode 100644 index 0000000..6d29bae --- /dev/null +++ b/pkg/integrations/github/content.go @@ -0,0 +1,529 @@ +package github + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "sort" + "strings" + "sync" + "time" +) + +// ContentClient provides access to GitHub repository content. +// Use this for fetching files, listing directories, and user operations. +type ContentClient struct { + token string + httpClient *http.Client + baseURL string +} + +// NewContentClient creates a new content client with the given access token. +func NewContentClient(token string) *ContentClient { + return &ContentClient{ + token: token, + httpClient: &http.Client{Timeout: 30 * time.Second}, + baseURL: "https://api.github.com", + } +} + +// FetchUser retrieves the authenticated user's info. +func (c *ContentClient) FetchUser(ctx context.Context) (*User, error) { + req, err := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/user", nil) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + c.setHeaders(req) + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("GitHub API error (%d): %s", resp.StatusCode, string(body)) + } + + var user User + if err := json.NewDecoder(resp.Body).Decode(&user); err != nil { + return nil, fmt.Errorf("decode response: %w", err) + } + + return &user, nil +} + +// FetchUserRepos retrieves all of the authenticated user's repositories. +// This includes private repos if the OAuth token has the 'repo' scope. +// Results are paginated automatically to retrieve all repos. +func (c *ContentClient) FetchUserRepos(ctx context.Context) ([]Repo, error) { + var allRepos []Repo + page := 1 + + for { + url := fmt.Sprintf("%s/user/repos?sort=updated&per_page=100&page=%d", c.baseURL, page) + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + c.setHeaders(req) + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send request: %w", err) + } + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + resp.Body.Close() + return nil, fmt.Errorf("GitHub API error (%d): %s", resp.StatusCode, string(body)) + } + + var repos []Repo + if err := json.NewDecoder(resp.Body).Decode(&repos); err != nil { + resp.Body.Close() + return nil, fmt.Errorf("decode response: %w", err) + } + resp.Body.Close() + + if len(repos) == 0 { + break // No more pages + } + + allRepos = append(allRepos, repos...) + page++ + + // Safety limit to avoid infinite loops + if page > 10 { + break + } + } + + return allRepos, nil +} + +// ListContents lists files and directories in a repository path. +func (c *ContentClient) ListContents(ctx context.Context, owner, repo, path string) ([]ContentItem, error) { + url := fmt.Sprintf("%s/repos/%s/%s/contents/%s", c.baseURL, owner, repo, path) + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + c.setHeaders(req) + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("GitHub API error (%d): %s", resp.StatusCode, string(body)) + } + + var items []apiContentResponse + if err := json.NewDecoder(resp.Body).Decode(&items); err != nil { + return nil, fmt.Errorf("decode response: %w", err) + } + + result := make([]ContentItem, len(items)) + for i, item := range items { + result[i] = ContentItem{ + Name: item.Name, + Path: item.Path, + Type: item.Type, + Size: item.Size, + } + } + + return result, nil +} + +// FetchFile retrieves the content of a file from a repository. +// The content is returned as a string (decoded from base64). +func (c *ContentClient) FetchFile(ctx context.Context, owner, repo, path string) (*FileContent, error) { + url := fmt.Sprintf("%s/repos/%s/%s/contents/%s", c.baseURL, owner, repo, path) + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + c.setHeaders(req) + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("GitHub API error (%d): %s", resp.StatusCode, string(body)) + } + + var fileResp apiContentResponse + if err := json.NewDecoder(resp.Body).Decode(&fileResp); err != nil { + return nil, fmt.Errorf("decode response: %w", err) + } + + // Decode base64 content + content, err := base64.StdEncoding.DecodeString(strings.ReplaceAll(fileResp.Content, "\n", "")) + if err != nil { + return nil, fmt.Errorf("decode content: %w", err) + } + + return &FileContent{ + Path: fileResp.Path, + Size: fileResp.Size, + Content: string(content), + }, nil +} + +// FetchFileRaw retrieves the raw content of a file from a repository. +// This is more efficient for large files as it doesn't use base64 encoding. +func (c *ContentClient) FetchFileRaw(ctx context.Context, owner, repo, path string) (string, error) { + url := fmt.Sprintf("%s/repos/%s/%s/contents/%s", c.baseURL, owner, repo, path) + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return "", fmt.Errorf("create request: %w", err) + } + req.Header.Set("Authorization", "Bearer "+c.token) + req.Header.Set("Accept", "application/vnd.github.v3.raw") // Get raw content + + resp, err := c.httpClient.Do(req) + if err != nil { + return "", fmt.Errorf("send request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return "", fmt.Errorf("GitHub API error (%d): %s", resp.StatusCode, string(body)) + } + + content, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("read content: %w", err) + } + + return string(content), nil +} + +// DetectManifests finds manifest files in a repository's root directory. +// The patterns map filename -> language name (e.g., "go.mod" -> "go"). +// Use deps.SupportedManifests(languages) to get patterns from the deps package. +func (c *ContentClient) DetectManifests(ctx context.Context, owner, repo string, patterns map[string]string) ([]ManifestFile, error) { + items, err := c.ListContents(ctx, owner, repo, "") + if err != nil { + return nil, err + } + + var manifests []ManifestFile + for _, item := range items { + if item.Type == "file" { + if lang, ok := patterns[item.Name]; ok { + manifests = append(manifests, ManifestFile{ + Path: item.Path, + Language: lang, + Name: item.Name, + }) + } + } + } + + return manifests, nil +} + +// setHeaders sets common headers for GitHub API requests. +func (c *ContentClient) setHeaders(req *http.Request) { + req.Header.Set("Accept", "application/vnd.github.v3+json") + if c.token != "" { + req.Header.Set("Authorization", "Bearer "+c.token) + } +} + +// SearchCode searches for code in a repository. +// Query follows GitHub code search syntax: https://docs.github.com/en/search-github/searching-on-github/searching-code +func (c *ContentClient) SearchCode(ctx context.Context, owner, repo, query string) ([]CodeSearchResult, error) { + // Build search query with repo filter + fullQuery := fmt.Sprintf("%s repo:%s/%s", query, owner, repo) + url := fmt.Sprintf("%s/search/code?q=%s&per_page=20", c.baseURL, urlEncode(fullQuery)) + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + c.setHeaders(req) + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("GitHub API error (%d): %s", resp.StatusCode, string(body)) + } + + var searchResp codeSearchResponse + if err := json.NewDecoder(resp.Body).Decode(&searchResp); err != nil { + return nil, fmt.Errorf("decode response: %w", err) + } + + results := make([]CodeSearchResult, len(searchResp.Items)) + for i, item := range searchResp.Items { + results[i] = CodeSearchResult{ + Name: item.Name, + Path: item.Path, + } + // Extract text matches if available + for _, match := range item.TextMatches { + results[i].Matches = append(results[i].Matches, match.Fragment) + } + } + + return results, nil +} + +// GetTree retrieves the full file tree of a repository. +func (c *ContentClient) GetTree(ctx context.Context, owner, repo, branch string) ([]TreeEntry, error) { + if branch == "" { + branch = "HEAD" + } + url := fmt.Sprintf("%s/repos/%s/%s/git/trees/%s?recursive=1", c.baseURL, owner, repo, branch) + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + c.setHeaders(req) + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("GitHub API error (%d): %s", resp.StatusCode, string(body)) + } + + var treeResp treeResponse + if err := json.NewDecoder(resp.Body).Decode(&treeResp); err != nil { + return nil, fmt.Errorf("decode response: %w", err) + } + + entries := make([]TreeEntry, 0, len(treeResp.Tree)) + for _, item := range treeResp.Tree { + entries = append(entries, TreeEntry{ + Path: item.Path, + Type: item.Type, + Size: item.Size, + }) + } + + return entries, nil +} + +// GetRepoInfo retrieves repository metadata. +func (c *ContentClient) GetRepoInfo(ctx context.Context, owner, repo string) (*RepoInfo, error) { + url := fmt.Sprintf("%s/repos/%s/%s", c.baseURL, owner, repo) + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + c.setHeaders(req) + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("GitHub API error (%d): %s", resp.StatusCode, string(body)) + } + + var repoResp apiRepoResponse + if err := json.NewDecoder(resp.Body).Decode(&repoResp); err != nil { + return nil, fmt.Errorf("decode response: %w", err) + } + + return &RepoInfo{ + Name: repoResp.Name, + FullName: repoResp.FullName, + Description: repoResp.Description, + Language: repoResp.Language, + DefaultBranch: repoResp.DefaultBranch, + Stars: repoResp.Stars, + Forks: repoResp.Forks, + OpenIssues: repoResp.OpenIssues, + License: repoResp.License.SPDXID, + Topics: repoResp.Topics, + Archived: repoResp.Archived, + }, nil +} + +func urlEncode(s string) string { + // Simple URL encoding for search queries + replacer := strings.NewReplacer( + " ", "+", + ":", "%3A", + "/", "%2F", + ) + return replacer.Replace(s) +} + +// CodeSearchResult represents a code search match. +type CodeSearchResult struct { + Name string `json:"name"` + Path string `json:"path"` + Matches []string `json:"matches,omitempty"` +} + +// TreeEntry represents a file or directory in the repository tree. +type TreeEntry struct { + Path string `json:"path"` + Type string `json:"type"` // "blob" or "tree" + Size int `json:"size,omitempty"` +} + +// RepoInfo contains repository metadata. +type RepoInfo struct { + Name string `json:"name"` + FullName string `json:"full_name"` + Description string `json:"description"` + Language string `json:"language"` + DefaultBranch string `json:"default_branch"` + Stars int `json:"stars"` + Forks int `json:"forks"` + OpenIssues int `json:"open_issues"` + License string `json:"license"` + Topics []string `json:"topics"` + Archived bool `json:"archived"` +} + +type codeSearchResponse struct { + TotalCount int `json:"total_count"` + Items []struct { + Name string `json:"name"` + Path string `json:"path"` + TextMatches []struct { + Fragment string `json:"fragment"` + } `json:"text_matches"` + } `json:"items"` +} + +type treeResponse struct { + Tree []struct { + Path string `json:"path"` + Type string `json:"type"` + Size int `json:"size"` + } `json:"tree"` + Truncated bool `json:"truncated"` +} + +// DetectLanguageFromManifest determines the language from a manifest filename. +func DetectLanguageFromManifest(path string) string { + name := path + if idx := strings.LastIndex(path, "/"); idx >= 0 { + name = path[idx+1:] + } + + switch name { + case "package.json", "package-lock.json": + return "javascript" + case "requirements.txt", "setup.py", "pyproject.toml", "Pipfile": + return "python" + case "Cargo.toml": + return "rust" + case "go.mod": + return "go" + case "Gemfile": + return "ruby" + case "composer.json": + return "php" + case "pom.xml", "build.gradle": + return "java" + default: + return "" + } +} + +// ScanReposForManifests fetches repos and detects manifests in parallel. +// It returns repos sorted by UpdatedAt (most recent first). +// The manifestPatterns map should be filename -> language (use deps.SupportedManifests). +// Set publicOnly=true to filter out private repos. +func (c *ContentClient) ScanReposForManifests(ctx context.Context, manifestPatterns map[string]string, publicOnly bool) ([]RepoWithManifests, error) { + repos, err := c.FetchUserRepos(ctx) + if err != nil { + return nil, fmt.Errorf("fetch repos: %w", err) + } + + // Sort by most recently updated + sort.Slice(repos, func(i, j int) bool { + return repos[i].UpdatedAt > repos[j].UpdatedAt + }) + + // Filter private repos if requested + if publicOnly { + var filtered []Repo + for _, r := range repos { + if !r.Private { + filtered = append(filtered, r) + } + } + repos = filtered + } + + if len(repos) == 0 { + return nil, nil + } + + // Parallel manifest detection with worker pool + type repoResult struct { + idx int + repo Repo + manifests []ManifestFile + } + + results := make([]repoResult, len(repos)) + var wg sync.WaitGroup + + // Semaphore for concurrency limit (10 parallel requests) + sem := make(chan struct{}, 10) + + for i, r := range repos { + wg.Add(1) + go func(idx int, repo Repo) { + defer wg.Done() + sem <- struct{}{} // Acquire + defer func() { <-sem }() // Release + + parts := strings.SplitN(repo.FullName, "/", 2) + var manifests []ManifestFile + if len(parts) == 2 { + manifests, _ = c.DetectManifests(ctx, parts[0], parts[1], manifestPatterns) + } + results[idx] = repoResult{idx: idx, repo: repo, manifests: manifests} + }(i, r) + } + + wg.Wait() + + // Build final list preserving order + rwm := make([]RepoWithManifests, len(repos)) + for _, r := range results { + rwm[r.idx] = RepoWithManifests{Repo: r.repo, Manifests: r.manifests} + } + + return rwm, nil +} diff --git a/pkg/integrations/github/oauth.go b/pkg/integrations/github/oauth.go new file mode 100644 index 0000000..0dc35c8 --- /dev/null +++ b/pkg/integrations/github/oauth.go @@ -0,0 +1,204 @@ +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "strings" + "time" +) + +// DefaultClientID is the OAuth App Client ID for Stacktower. +// This is public and safe to commit - only the Client Secret must be kept private. +// The Device Flow doesn't require a secret, only the Client ID. +// +// To use your own OAuth App, set GITHUB_CLIENT_ID env var. +const DefaultClientID = "Ov23liyPM58WU6hMeP7E" + +// OAuthClient handles GitHub OAuth operations. +type OAuthClient struct { + config OAuthConfig + httpClient *http.Client +} + +// NewOAuthClient creates a new OAuth client. +func NewOAuthClient(config OAuthConfig) *OAuthClient { + return &OAuthClient{ + config: config, + httpClient: &http.Client{Timeout: 30 * time.Second}, + } +} + +// AuthorizationURL returns the GitHub OAuth authorization URL. +func (c *OAuthClient) AuthorizationURL(state string) string { + params := url.Values{ + "client_id": {c.config.ClientID}, + "redirect_uri": {c.config.RedirectURI}, + "scope": {"read:user user:email repo"}, + "state": {state}, + } + return "https://github.com/login/oauth/authorize?" + params.Encode() +} + +// ExchangeCode exchanges an authorization code for an access token. +func (c *OAuthClient) ExchangeCode(code string) (*OAuthToken, error) { + data := url.Values{ + "client_id": {c.config.ClientID}, + "client_secret": {c.config.ClientSecret}, + "code": {code}, + "redirect_uri": {c.config.RedirectURI}, + } + + req, err := http.NewRequest("POST", "https://github.com/login/oauth/access_token", strings.NewReader(data.Encode())) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Accept", "application/json") + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send request: %w", err) + } + defer resp.Body.Close() + + var result struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + Scope string `json:"scope"` + Error string `json:"error"` + ErrorDesc string `json:"error_description"` + } + + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("decode response: %w", err) + } + + if result.Error != "" { + return nil, fmt.Errorf("%s: %s", result.Error, result.ErrorDesc) + } + + return &OAuthToken{ + AccessToken: result.AccessToken, + TokenType: result.TokenType, + Scope: result.Scope, + }, nil +} + +// DeviceCodeResponse contains the response from requesting a device code. +type DeviceCodeResponse struct { + DeviceCode string `json:"device_code"` + UserCode string `json:"user_code"` + VerificationURI string `json:"verification_uri"` + ExpiresIn int `json:"expires_in"` + Interval int `json:"interval"` +} + +// RequestDeviceCode initiates the device authorization flow. +// The user must visit the VerificationURI and enter the UserCode. +func (c *OAuthClient) RequestDeviceCode(ctx context.Context) (*DeviceCodeResponse, error) { + data := url.Values{ + "client_id": {c.config.ClientID}, + "scope": {"read:user user:email repo"}, + } + + req, err := http.NewRequestWithContext(ctx, "POST", "https://github.com/login/device/code", strings.NewReader(data.Encode())) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Accept", "application/json") + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send request: %w", err) + } + defer resp.Body.Close() + + var result DeviceCodeResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("decode response: %w", err) + } + + return &result, nil +} + +// PollForToken polls GitHub for the access token after user authorization. +// It respects the interval from the device code response. +// Returns the token when authorized, or an error if expired/denied. +func (c *OAuthClient) PollForToken(ctx context.Context, deviceCode string, interval int) (*OAuthToken, error) { + if interval < 5 { + interval = 5 // GitHub minimum interval + } + + ticker := time.NewTicker(time.Duration(interval) * time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-ticker.C: + token, err := c.checkDeviceToken(ctx, deviceCode) + if err != nil { + // Check if it's a "still waiting" error + if strings.Contains(err.Error(), "authorization_pending") { + continue // Keep polling + } + if strings.Contains(err.Error(), "slow_down") { + // Increase interval and continue + ticker.Reset(time.Duration(interval+5) * time.Second) + continue + } + return nil, err // Real error (expired, denied, etc.) + } + return token, nil + } + } +} + +// checkDeviceToken attempts to exchange the device code for a token. +func (c *OAuthClient) checkDeviceToken(ctx context.Context, deviceCode string) (*OAuthToken, error) { + data := url.Values{ + "client_id": {c.config.ClientID}, + "device_code": {deviceCode}, + "grant_type": {"urn:ietf:params:oauth:grant-type:device_code"}, + } + + req, err := http.NewRequestWithContext(ctx, "POST", "https://github.com/login/oauth/access_token", strings.NewReader(data.Encode())) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Accept", "application/json") + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("send request: %w", err) + } + defer resp.Body.Close() + + var result struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + Scope string `json:"scope"` + Error string `json:"error"` + ErrorDesc string `json:"error_description"` + } + + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("decode response: %w", err) + } + + if result.Error != "" { + return nil, fmt.Errorf("%s: %s", result.Error, result.ErrorDesc) + } + + return &OAuthToken{ + AccessToken: result.AccessToken, + TokenType: result.TokenType, + Scope: result.Scope, + }, nil +} diff --git a/pkg/integrations/github/types.go b/pkg/integrations/github/types.go new file mode 100644 index 0000000..bffb003 --- /dev/null +++ b/pkg/integrations/github/types.go @@ -0,0 +1,98 @@ +package github + +import "time" + +// User represents a GitHub user. +type User struct { + ID int64 `json:"id"` + Login string `json:"login"` + Name string `json:"name"` + AvatarURL string `json:"avatar_url"` + Email string `json:"email"` +} + +// Repo represents a GitHub repository. +type Repo struct { + ID int64 `json:"id"` + Name string `json:"name"` + FullName string `json:"full_name"` + Description string `json:"description"` + Private bool `json:"private"` + DefaultBranch string `json:"default_branch"` + Language string `json:"language"` + UpdatedAt string `json:"updated_at"` +} + +// ContentItem represents an item in a repository directory listing. +type ContentItem struct { + Name string `json:"name"` + Path string `json:"path"` + Type string `json:"type"` // "file" or "dir" + Size int `json:"size"` +} + +// FileContent represents the content of a file. +type FileContent struct { + Path string `json:"path"` + Size int `json:"size"` + Content string `json:"content"` +} + +// ManifestFile represents a detected manifest file. +type ManifestFile struct { + Path string `json:"path"` + Language string `json:"language"` + Name string `json:"name"` +} + +// RepoWithManifests holds a repo and its detected manifest files. +type RepoWithManifests struct { + Repo Repo `json:"repo"` + Manifests []ManifestFile `json:"manifests,omitempty"` +} + +// OAuthConfig holds OAuth configuration. +type OAuthConfig struct { + ClientID string + ClientSecret string + RedirectURI string +} + +// OAuthToken represents an OAuth access token response. +type OAuthToken struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + Scope string `json:"scope"` +} + +// apiRepoResponse is the internal GitHub API response structure. +type apiRepoResponse struct { + ID int64 `json:"id"` + Name string `json:"name"` + FullName string `json:"full_name"` + Description string `json:"description"` + Private bool `json:"private"` + DefaultBranch string `json:"default_branch"` + Language string `json:"language"` + UpdatedAt string `json:"updated_at"` + Stars int `json:"stargazers_count"` + Forks int `json:"forks_count"` + OpenIssues int `json:"open_issues_count"` + Size int `json:"size"` + PushedAt *time.Time `json:"pushed_at"` + License struct { + SPDXID string `json:"spdx_id"` + } `json:"license"` + Topics []string `json:"topics"` + Archived bool `json:"archived"` +} + +// apiContentResponse is the internal GitHub API response for file content. +type apiContentResponse struct { + Name string `json:"name"` + Path string `json:"path"` + Type string `json:"type"` + Size int `json:"size"` + Content string `json:"content"` + Encoding string `json:"encoding"` +} diff --git a/pkg/integrations/github/validate.go b/pkg/integrations/github/validate.go new file mode 100644 index 0000000..c2c2211 --- /dev/null +++ b/pkg/integrations/github/validate.go @@ -0,0 +1,59 @@ +package github + +import ( + "errors" + "regexp" + "strings" +) + +// Regex patterns for GitHub resource validation. +var ( + // GitHub usernames/orgs: 1-39 alphanumeric or hyphen, not starting with hyphen + validOwner = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9-]{0,38}$`) + // GitHub repo names: 1-100 alphanumeric, hyphen, underscore, or dot + validRepo = regexp.MustCompile(`^[a-zA-Z0-9._-]{1,100}$`) +) + +// ValidateOwner validates a GitHub username or organization name. +func ValidateOwner(owner string) error { + if owner == "" { + return errors.New("owner is required") + } + if !validOwner.MatchString(owner) { + return errors.New("invalid owner format: must be 1-39 alphanumeric characters or hyphens, cannot start with hyphen") + } + return nil +} + +// ValidateRepo validates a GitHub repository name. +func ValidateRepo(repo string) error { + if repo == "" { + return errors.New("repo is required") + } + if !validRepo.MatchString(repo) { + return errors.New("invalid repo format: must be 1-100 alphanumeric characters, hyphens, underscores, or dots") + } + return nil +} + +// ValidateRepoRef validates both owner and repo parameters. +func ValidateRepoRef(owner, repo string) error { + if err := ValidateOwner(owner); err != nil { + return err + } + return ValidateRepo(repo) +} + +// ParseRepoRef parses an "owner/repo" string and validates both parts. +// Returns owner, repo, and any validation error. +func ParseRepoRef(ref string) (owner, repo string, err error) { + parts := strings.SplitN(ref, "/", 2) + if len(parts) != 2 { + return "", "", errors.New("invalid repo format: use owner/repo") + } + owner, repo = parts[0], parts[1] + if err := ValidateRepoRef(owner, repo); err != nil { + return "", "", err + } + return owner, repo, nil +} diff --git a/pkg/integrations/gitlab/client.go b/pkg/integrations/gitlab/client.go index 6e5493e..85cdb3f 100644 --- a/pkg/integrations/gitlab/client.go +++ b/pkg/integrations/gitlab/client.go @@ -4,6 +4,7 @@ import ( "regexp" "time" + "github.com/matzehuels/stacktower/pkg/cache" "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -22,26 +23,19 @@ type Client struct { // NewClient creates a GitLab API client with optional authentication. // -// The token parameter is a GitLab personal access token for authentication. -// Pass an empty string to use unauthenticated requests (public repositories only). -// -// The cacheTTL parameter sets how long responses are cached. -// Typical values: 1-24 hours for production, 0 for testing (no cache). +// Parameters: +// - backend: Cache backend for HTTP response caching (use storage.NullBackend{} for no caching) +// - token: GitLab personal access token (empty string for unauthenticated) +// - cacheTTL: How long responses are cached (typical: 1-24 hours) // -// Returns an error if the cache directory cannot be created or accessed. // The returned Client is safe for concurrent use. -func NewClient(token string, cacheTTL time.Duration) (*Client, error) { - cache, err := integrations.NewCacheWithNamespace("gitlab:", cacheTTL) - if err != nil { - return nil, err - } - +func NewClient(backend cache.Cache, token string, cacheTTL time.Duration) *Client { var headers map[string]string if token != "" { headers = map[string]string{"PRIVATE-TOKEN": token} } - return &Client{integrations.NewClient(cache, headers)}, nil + return &Client{integrations.NewClient(backend, "gitlab:", cacheTTL, headers)} } // ExtractURL extracts GitLab repository owner and name from package URLs. diff --git a/pkg/integrations/goproxy/client.go b/pkg/integrations/goproxy/client.go index 4baea75..bb407a9 100644 --- a/pkg/integrations/goproxy/client.go +++ b/pkg/integrations/goproxy/client.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/matzehuels/stacktower/pkg/cache" "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -34,22 +35,18 @@ type Client struct { baseURL string } -// NewClient creates a Go module proxy client with the specified cache TTL. +// NewClient creates a Go module proxy client with the given cache backend. // -// The cacheTTL parameter sets how long responses are cached. -// Typical values: 1-24 hours for production, 0 for testing (no cache). +// Parameters: +// - backend: Cache backend for HTTP response caching (use storage.NullBackend{} for no caching) +// - cacheTTL: How long responses are cached (typical: 1-24 hours) // -// Returns an error if the cache directory cannot be created or accessed. // The returned Client is safe for concurrent use. -func NewClient(cacheTTL time.Duration) (*Client, error) { - cache, err := integrations.NewCacheWithNamespace("goproxy:", cacheTTL) - if err != nil { - return nil, err - } +func NewClient(backend cache.Cache, cacheTTL time.Duration) *Client { return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(backend, "goproxy:", cacheTTL, nil), baseURL: "https://proxy.golang.org", - }, nil + } } // FetchModule retrieves metadata for a Go module from the module proxy. diff --git a/pkg/integrations/goproxy/client_test.go b/pkg/integrations/goproxy/client_test.go index 77faaae..db8896c 100644 --- a/pkg/integrations/goproxy/client_test.go +++ b/pkg/integrations/goproxy/client_test.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -140,12 +142,8 @@ func TestClient_FetchModule_NotFound(t *testing.T) { func testClient(t *testing.T, serverURL string) *Client { t.Helper() - cache, err := integrations.NewCache(time.Hour) - if err != nil { - t.Fatal(err) - } return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(cache.NewNullCache(), "goproxy:", time.Hour, nil), baseURL: serverURL, } } diff --git a/pkg/integrations/maven/client.go b/pkg/integrations/maven/client.go index 75035b2..e8deff9 100644 --- a/pkg/integrations/maven/client.go +++ b/pkg/integrations/maven/client.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/matzehuels/stacktower/pkg/cache" "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -27,7 +28,7 @@ type ArtifactInfo struct { Version string // Latest version (e.g., "32.1.3-jre", never empty in valid info) Dependencies []string // Compile-scope dependency coordinates (nil or empty if none or POM fetch failed) Description string // Artifact description from POM (may be empty) - URL string // URL to the POM file on Maven Central (never empty in valid info) + URL string // Project homepage/repository URL from POM (may be empty if not specified in POM) } // Coordinate returns the Maven coordinate string "groupId:artifactId". @@ -45,22 +46,18 @@ type Client struct { baseURL string } -// NewClient creates a Maven Central client with the specified cache TTL. +// NewClient creates a Maven Central client with the given cache backend. // -// The cacheTTL parameter sets how long responses are cached. -// Typical values: 1-24 hours for production, 0 for testing (no cache). +// Parameters: +// - backend: Cache backend for HTTP response caching (use storage.NullBackend{} for no caching) +// - cacheTTL: How long responses are cached (typical: 1-24 hours) // -// Returns an error if the cache directory cannot be created or accessed. // The returned Client is safe for concurrent use. -func NewClient(cacheTTL time.Duration) (*Client, error) { - cache, err := integrations.NewCacheWithNamespace("maven:", cacheTTL) - if err != nil { - return nil, err - } +func NewClient(backend cache.Cache, cacheTTL time.Duration) *Client { return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(backend, "maven:", cacheTTL, nil), baseURL: "https://search.maven.org/solrsearch/select", - }, nil + } } // FetchArtifact retrieves metadata for a Java artifact from Maven Central. @@ -128,30 +125,54 @@ func (c *Client) fetch(ctx context.Context, groupID, artifactID string, info *Ar version = doc.Version } - // Fetch POM to get dependencies - deps, pomURL := c.fetchPOMDeps(ctx, groupID, artifactID, version) + // Fetch POM to get dependencies and project URL + deps, _, projectURL := c.fetchPOMDeps(ctx, groupID, artifactID, version) *info = ArtifactInfo{ GroupID: groupID, ArtifactID: artifactID, Version: version, Dependencies: deps, - URL: pomURL, + URL: projectURL, // Use project URL from POM, not the POM file URL } return nil } -func (c *Client) fetchPOMDeps(ctx context.Context, groupID, artifactID, version string) ([]string, string) { +func (c *Client) fetchPOMDeps(ctx context.Context, groupID, artifactID, version string) ([]string, string, string) { groupPath := strings.ReplaceAll(groupID, ".", "/") pomURL := fmt.Sprintf("https://repo1.maven.org/maven2/%s/%s/%s/%s-%s.pom", groupPath, artifactID, version, artifactID, version) pom, err := c.fetchPOM(ctx, pomURL) if err != nil { - return nil, pomURL + return nil, pomURL, "" } - return extractDeps(pom), pomURL + // Prefer SCM URL (repository) over generic project URL + projectURL := extractProjectURL(pom) + return extractDeps(pom), pomURL, projectURL +} + +// extractProjectURL extracts the best available project URL from a POM. +// Prefers SCM URLs (actual repository) over generic project URLs. +func extractProjectURL(pom *pomProject) string { + // Try SCM URLs first (repository locations) + if pom.SCM != nil { + // Prefer scm.url (browser-viewable) + if pom.SCM.URL != "" { + return integrations.NormalizeRepoURL(pom.SCM.URL) + } + // Fall back to developer connection (git@... or https://...) + if pom.SCM.DeveloperConnection != "" { + return integrations.NormalizeRepoURL(pom.SCM.DeveloperConnection) + } + // Last resort: connection string + if pom.SCM.Connection != "" { + return integrations.NormalizeRepoURL(pom.SCM.Connection) + } + } + // Fall back to project URL (might be project website, not repo) + return pom.URL } func (c *Client) fetchPOM(ctx context.Context, url string) (*pomProject, error) { @@ -232,9 +253,16 @@ type pomProject struct { Name string `xml:"name"` Description string `xml:"description"` URL string `xml:"url"` + SCM *pomSCM `xml:"scm"` Dependencies []pomDependency `xml:"dependencies>dependency"` } +type pomSCM struct { + Connection string `xml:"connection"` + DeveloperConnection string `xml:"developerConnection"` + URL string `xml:"url"` +} + type pomDependency struct { GroupID string `xml:"groupId"` ArtifactID string `xml:"artifactId"` diff --git a/pkg/integrations/maven/client_test.go b/pkg/integrations/maven/client_test.go index 034283a..91d210b 100644 --- a/pkg/integrations/maven/client_test.go +++ b/pkg/integrations/maven/client_test.go @@ -8,6 +8,8 @@ import ( "testing" "time" + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -134,12 +136,8 @@ func TestExtractDeps(t *testing.T) { func testClient(t *testing.T, serverURL string) *Client { t.Helper() - cache, err := integrations.NewCache(time.Hour) - if err != nil { - t.Fatal(err) - } return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(cache.NewNullCache(), "maven:", time.Hour, nil), baseURL: serverURL, } } diff --git a/pkg/integrations/maven/doc.go b/pkg/integrations/maven/doc.go index bc085de..c8443ec 100644 --- a/pkg/integrations/maven/doc.go +++ b/pkg/integrations/maven/doc.go @@ -33,7 +33,7 @@ // - Version: Latest version from Maven Central search // - Dependencies: Compile-scope dependencies from POM // - Description: Project description from POM -// - URL: Link to the POM file +// - URL: Project homepage/repository URL from POM (e.g., GitHub URL) // // # Caching // diff --git a/pkg/integrations/npm/client.go b/pkg/integrations/npm/client.go index 1dddfce..86edfec 100644 --- a/pkg/integrations/npm/client.go +++ b/pkg/integrations/npm/client.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/matzehuels/stacktower/pkg/cache" "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -39,22 +40,18 @@ type Client struct { baseURL string } -// NewClient creates an npm client with the specified cache TTL. +// NewClient creates an npm client with the given cache backend. // -// The cacheTTL parameter sets how long responses are cached. -// Typical values: 1-24 hours for production, 0 for testing (no cache). +// Parameters: +// - backend: Cache backend for HTTP response caching (use storage.NullBackend{} for no caching) +// - cacheTTL: How long responses are cached (typical: 1-24 hours) // -// Returns an error if the cache directory cannot be created or accessed. // The returned Client is safe for concurrent use. -func NewClient(cacheTTL time.Duration) (*Client, error) { - cache, err := integrations.NewCacheWithNamespace("npm:", cacheTTL) - if err != nil { - return nil, err - } +func NewClient(backend cache.Cache, cacheTTL time.Duration) *Client { return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(backend, "npm:", cacheTTL, nil), baseURL: "https://registry.npmjs.org", - }, nil + } } // FetchPackage retrieves metadata for a JavaScript/TypeScript package from npm. diff --git a/pkg/integrations/npm/client_test.go b/pkg/integrations/npm/client_test.go index 941a160..fe06adc 100644 --- a/pkg/integrations/npm/client_test.go +++ b/pkg/integrations/npm/client_test.go @@ -8,14 +8,13 @@ import ( "testing" "time" + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/integrations" ) func TestNewClient(t *testing.T) { - c, err := NewClient(time.Hour) - if err != nil { - t.Fatalf("NewClient failed: %v", err) - } + c := NewClient(cache.NewNullCache(), time.Hour) if c.Client == nil { t.Error("expected client to be initialized") } @@ -136,12 +135,8 @@ func TestExtractField(t *testing.T) { func testClient(t *testing.T, serverURL string) *Client { t.Helper() - cache, err := integrations.NewCache(time.Hour) - if err != nil { - t.Fatal(err) - } return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(cache.NewNullCache(), "npm:", time.Hour, nil), baseURL: serverURL, } } diff --git a/pkg/integrations/packagist/client.go b/pkg/integrations/packagist/client.go index 0c0823a..41e4ab7 100644 --- a/pkg/integrations/packagist/client.go +++ b/pkg/integrations/packagist/client.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/matzehuels/stacktower/pkg/cache" "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -41,22 +42,18 @@ type Client struct { baseURL string } -// NewClient creates a Packagist client with the specified cache TTL. +// NewClient creates a Packagist client with the given cache backend. // -// The cacheTTL parameter sets how long responses are cached. -// Typical values: 1-24 hours for production, 0 for testing (no cache). +// Parameters: +// - backend: Cache backend for HTTP response caching (use storage.NullBackend{} for no caching) +// - cacheTTL: How long responses are cached (typical: 1-24 hours) // -// Returns an error if the cache directory cannot be created or accessed. // The returned Client is safe for concurrent use. -func NewClient(cacheTTL time.Duration) (*Client, error) { - cache, err := integrations.NewCacheWithNamespace("packagist:", cacheTTL) - if err != nil { - return nil, err - } +func NewClient(backend cache.Cache, cacheTTL time.Duration) *Client { return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(backend, "packagist:", cacheTTL, nil), baseURL: "https://repo.packagist.org", - }, nil + } } // FetchPackage retrieves metadata for a PHP package from Packagist. diff --git a/pkg/integrations/packagist/client_test.go b/pkg/integrations/packagist/client_test.go index 4c65825..3d52f2c 100644 --- a/pkg/integrations/packagist/client_test.go +++ b/pkg/integrations/packagist/client_test.go @@ -9,14 +9,13 @@ import ( "testing" "time" + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/integrations" ) func TestNewClient(t *testing.T) { - c, err := NewClient(time.Hour) - if err != nil { - t.Fatalf("NewClient failed: %v", err) - } + c := NewClient(cache.NewNullCache(), time.Hour) if c.Client == nil { t.Error("expected client to be initialized") } @@ -200,12 +199,8 @@ func TestP2Version_UnmarshalJSON(t *testing.T) { func testClient(t *testing.T, serverURL string) *Client { t.Helper() - cache, err := integrations.NewCache(time.Hour) - if err != nil { - t.Fatal(err) - } return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(cache.NewNullCache(), "packagist:", time.Hour, nil), baseURL: serverURL, } } diff --git a/pkg/integrations/pypi/client.go b/pkg/integrations/pypi/client.go index 5d697a7..1b9eb78 100644 --- a/pkg/integrations/pypi/client.go +++ b/pkg/integrations/pypi/client.go @@ -5,8 +5,10 @@ import ( "errors" "fmt" "regexp" + "strings" "time" + "github.com/matzehuels/stacktower/pkg/cache" "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -44,22 +46,18 @@ type Client struct { baseURL string } -// NewClient creates a PyPI client with the specified cache TTL. +// NewClient creates a PyPI client with the given cache backend. // -// The cacheTTL parameter sets how long responses are cached. -// Typical values: 1-24 hours for production, 0 for testing (no cache). +// Parameters: +// - backend: Cache backend for HTTP response caching (use storage.NullBackend{} for no caching) +// - cacheTTL: How long responses are cached (typical: 1-24 hours) // -// Returns an error if the cache directory cannot be created or accessed. // The returned Client is safe for concurrent use. -func NewClient(cacheTTL time.Duration) (*Client, error) { - cache, err := integrations.NewCacheWithNamespace("pypi:", cacheTTL) - if err != nil { - return nil, err - } +func NewClient(backend cache.Cache, cacheTTL time.Duration) *Client { return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(backend, "pypi:", cacheTTL, nil), baseURL: "https://pypi.org/pypi", - }, nil + } } // FetchPackage retrieves metadata for a Python package from PyPI. @@ -112,7 +110,7 @@ func (c *Client) fetch(ctx context.Context, pkg string, info *PackageInfo) error Name: data.Info.Name, Version: data.Info.Version, Summary: data.Info.Summary, - License: data.Info.License, + License: extractLicenseType(data.Info.License, data.Info.Classifiers), Dependencies: extractDeps(data.Info.RequiresDist), ProjectURLs: urls, HomePage: data.Info.HomePage, @@ -148,8 +146,42 @@ type apiInfo struct { Version string `json:"version"` Summary string `json:"summary"` License string `json:"license"` + Classifiers []string `json:"classifiers"` RequiresDist []string `json:"requires_dist"` ProjectURLs map[string]any `json:"project_urls"` HomePage string `json:"home_page"` Author string `json:"author"` } + +// extractLicenseType extracts a short license identifier from PyPI data. +// It prefers the classifier (e.g., "License :: OSI Approved :: MIT License" -> "MIT License") +// and falls back to the license field if it's short enough. +func extractLicenseType(license string, classifiers []string) string { + // First, try to extract from classifiers + for _, c := range classifiers { + if strings.HasPrefix(c, "License :: ") { + parts := strings.Split(c, " :: ") + if len(parts) >= 3 { + // Return the last part, e.g., "MIT License", "BSD-3-Clause" + return parts[len(parts)-1] + } + } + } + + // If license field is short (likely just the type), use it + if license != "" && len(license) < 100 && !strings.Contains(license, "\n") { + return strings.TrimSpace(license) + } + + // Otherwise, try to extract type from the beginning of the license text + if license != "" { + // Common patterns: "MIT License", "BSD 3-Clause License", "Apache License 2.0" + firstLine := strings.Split(license, "\n")[0] + firstLine = strings.TrimSpace(firstLine) + if len(firstLine) < 50 { + return firstLine + } + } + + return "" +} diff --git a/pkg/integrations/pypi/client_test.go b/pkg/integrations/pypi/client_test.go index e9edf9d..a5c936f 100644 --- a/pkg/integrations/pypi/client_test.go +++ b/pkg/integrations/pypi/client_test.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -109,12 +111,8 @@ func TestNormalizePkgName(t *testing.T) { func testClient(t *testing.T, serverURL string) *Client { t.Helper() - cache, err := integrations.NewCache(time.Hour) - if err != nil { - t.Fatal(err) - } return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(cache.NewNullCache(), "pypi:", time.Hour, nil), baseURL: serverURL, } } diff --git a/pkg/integrations/rubygems/client.go b/pkg/integrations/rubygems/client.go index 8f4ec50..b955ca7 100644 --- a/pkg/integrations/rubygems/client.go +++ b/pkg/integrations/rubygems/client.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/matzehuels/stacktower/pkg/cache" "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -39,22 +40,18 @@ type Client struct { baseURL string } -// NewClient creates a RubyGems client with the specified cache TTL. +// NewClient creates a RubyGems client with the given cache backend. // -// The cacheTTL parameter sets how long responses are cached. -// Typical values: 1-24 hours for production, 0 for testing (no cache). +// Parameters: +// - backend: Cache backend for HTTP response caching (use storage.NullBackend{} for no caching) +// - cacheTTL: How long responses are cached (typical: 1-24 hours) // -// Returns an error if the cache directory cannot be created or accessed. // The returned Client is safe for concurrent use. -func NewClient(cacheTTL time.Duration) (*Client, error) { - cache, err := integrations.NewCacheWithNamespace("rubygems:", cacheTTL) - if err != nil { - return nil, err - } +func NewClient(backend cache.Cache, cacheTTL time.Duration) *Client { return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(backend, "rubygems:", cacheTTL, nil), baseURL: "https://rubygems.org/api/v1", - }, nil + } } // FetchGem retrieves metadata for a Ruby gem from RubyGems. diff --git a/pkg/integrations/rubygems/client_test.go b/pkg/integrations/rubygems/client_test.go index 956c826..2dcc5dc 100644 --- a/pkg/integrations/rubygems/client_test.go +++ b/pkg/integrations/rubygems/client_test.go @@ -10,6 +10,8 @@ import ( "testing" "time" + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/integrations" ) @@ -107,12 +109,8 @@ func TestJoinLicenses(t *testing.T) { func testClient(t *testing.T, serverURL string) *Client { t.Helper() - cache, err := integrations.NewCache(time.Hour) - if err != nil { - t.Fatal(err) - } return &Client{ - Client: integrations.NewClient(cache, nil), + Client: integrations.NewClient(cache.NewNullCache(), "rubygems:", time.Hour, nil), baseURL: serverURL, } } diff --git a/pkg/io/doc.go b/pkg/io/doc.go deleted file mode 100644 index 6f9b753..0000000 --- a/pkg/io/doc.go +++ /dev/null @@ -1,97 +0,0 @@ -// Package io provides JSON import and export for directed acyclic graphs (DAGs). -// -// # Overview -// -// This package enables serialization of dependency graphs to and from a simple -// JSON format. The format is designed for: -// -// - Visualization of any directed graph, not just package dependencies -// - Integration with external tools that produce or consume graph data -// - Caching of parsed dependency data for faster re-rendering -// - Round-trip preservation: import, render, export, and re-import identically -// -// # JSON Format -// -// The format has two required top-level arrays: -// -// { -// "nodes": [ -// {"id": "app"}, -// {"id": "lib-a"}, -// {"id": "lib-b"} -// ], -// "edges": [ -// {"from": "app", "to": "lib-a"}, -// {"from": "lib-a", "to": "lib-b"} -// ] -// } -// -// # Node Fields -// -// Required: -// - id: Unique string identifier (also used as the display label) -// -// Optional: -// - row: Pre-assigned layer (computed automatically if omitted) -// - kind: Internal node type ("subdivider" or "auxiliary") -// - meta: Freeform object for package metadata -// -// # Metadata Keys -// -// The meta object can contain any data, but certain keys are recognized by -// render features: -// -// - repo_url: Clickable link for blocks -// - repo_stars: Star count for popups -// - repo_owner: Repository owner for Nebraska ranking -// - repo_maintainers: Maintainer list for Nebraska ranking -// - repo_last_commit: Last commit date for staleness detection -// - repo_archived: Whether the repository is archived -// - summary/description: Displayed in hover popups -// -// # Import -// -// Use [ImportJSON] to read a graph from a file path, or [ReadJSON] to read -// from any io.Reader: -// -// g, err := io.ImportJSON("deps.json") -// if err != nil { -// log.Fatal(err) -// } -// -// Both functions validate the JSON structure and DAG constraints (no cycles, -// no duplicate node IDs). Errors are wrapped with context about which node or -// edge caused the problem. -// -// # Export -// -// Use [ExportJSON] to write a graph to a file, or [WriteJSON] to write to any -// io.Writer: -// -// err := io.ExportJSON(g, "output.json") -// if err != nil { -// log.Fatal(err) -// } -// -// The export includes all node and edge data, including synthetic nodes -// (subdividers, auxiliaries) and their metadata. Row assignments, node kinds, -// and all metadata are preserved. This enables full round-trip fidelity: -// import a graph, transform it, export the result, and re-import identically. -// -// # Concurrency -// -// All functions in this package are safe to call concurrently with other -// readers of the same DAG, but not with concurrent modifications to the DAG. -// The [ReadJSON] and [ImportJSON] functions create independent DAG instances -// that can be used and modified freely after import. -// -// # Layout Export -// -// This package exports the logical graph structure only (nodes, edges, metadata). -// For external tools that need computed layout positions, use the JSON sink in -// [render/tower/sink], which exports the complete [layout.Layout] including -// block coordinates, row orderings, and all render options. -// -// [render/tower/sink]: github.com/matzehuels/stacktower/pkg/render/tower/sink -// [layout.Layout]: github.com/matzehuels/stacktower/pkg/render/tower/layout.Layout -package io diff --git a/pkg/io/export.go b/pkg/io/export.go deleted file mode 100644 index 78a15fc..0000000 --- a/pkg/io/export.go +++ /dev/null @@ -1,115 +0,0 @@ -package io - -import ( - "encoding/json" - "fmt" - "io" - "os" - "slices" - - "github.com/matzehuels/stacktower/pkg/dag" -) - -var kindToString = map[dag.NodeKind]string{ - dag.NodeKindSubdivider: "subdivider", - dag.NodeKindAuxiliary: "auxiliary", -} - -type graph struct { - Nodes []node `json:"nodes"` - Edges []edge `json:"edges"` -} - -type node struct { - ID string `json:"id"` - Row *int `json:"row,omitempty"` - Kind string `json:"kind,omitempty"` - Meta dag.Metadata `json:"meta,omitempty"` -} - -type edge struct { - From string `json:"from"` - To string `json:"to"` -} - -// WriteJSON encodes a DAG as JSON and writes it to w. -// -// The output is a JSON object with "nodes" and "edges" arrays, formatted with -// 2-space indentation. All nodes are written in their original order with: -// - id: always present -// - row: included only if non-zero -// - kind: included only for non-default kinds (subdivider, auxiliary) -// - meta: included if non-empty -// -// Edges are written as {from, to} pairs. -// -// The output can be read back with [ReadJSON] to produce an identical DAG, -// preserving all metadata, node kinds, and assigned row numbers. -// -// WriteJSON returns an error if encoding fails or if writing to w fails. -// It does not validate the DAG structure; malformed graphs will be encoded -// as-is and may fail validation on import. -// -// This function is safe to call concurrently with other readers of g, -// but not with concurrent writes to g. -func WriteJSON(g *dag.DAG, w io.Writer) error { - nodes := g.Nodes() - // Sort nodes by ID for deterministic output - slices.SortFunc(nodes, func(a, b *dag.Node) int { - if a.ID < b.ID { - return -1 - } - if a.ID > b.ID { - return 1 - } - return 0 - }) - - out := graph{ - Nodes: make([]node, len(nodes)), - Edges: make([]edge, len(g.Edges())), - } - - for i, n := range nodes { - nd := node{ID: n.ID, Meta: n.Meta} - if n.Row != 0 { - row := n.Row - nd.Row = &row - } - if s, ok := kindToString[n.Kind]; ok { - nd.Kind = s - } - out.Nodes[i] = nd - } - for i, e := range g.Edges() { - out.Edges[i] = edge{From: e.From, To: e.To} - } - - enc := json.NewEncoder(w) - enc.SetIndent("", " ") - if err := enc.Encode(out); err != nil { - return fmt.Errorf("encode: %w", err) - } - return nil -} - -// ExportJSON writes a DAG to a JSON file at path. -// -// ExportJSON creates (or truncates) the file at path and writes the JSON -// representation of g using [WriteJSON]. The file is created with 0644 -// permissions. -// -// If the file cannot be created, or if writing fails, ExportJSON returns -// an error describing the failure. The error wraps the underlying cause -// with the file path for context. -// -// This function is safe to call concurrently with other readers of g, -// but not with concurrent writes to g. -func ExportJSON(g *dag.DAG, path string) error { - f, err := os.Create(path) - if err != nil { - return fmt.Errorf("create %s: %w", path, err) - } - defer f.Close() - return WriteJSON(g, f) -} diff --git a/pkg/io/export_test.go b/pkg/io/export_test.go deleted file mode 100644 index dd9cf0e..0000000 --- a/pkg/io/export_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package io - -import ( - "bytes" - "encoding/json" - "testing" - - "github.com/matzehuels/stacktower/pkg/dag" -) - -func TestWriteJSON(t *testing.T) { - tests := []struct { - name string - build func() *dag.DAG - wantNodes int - wantEdges int - check func(t *testing.T, g graph) - }{ - { - name: "Empty", - build: func() *dag.DAG { return dag.New(nil) }, - wantNodes: 0, - wantEdges: 0, - }, - { - name: "Simple", - build: func() *dag.DAG { - g := dag.New(nil) - g.AddNode(dag.Node{ID: "a", Meta: dag.Metadata{"version": "1.0"}}) - g.AddNode(dag.Node{ID: "b", Meta: dag.Metadata{"version": "2.0"}}) - g.AddEdge(dag.Edge{From: "a", To: "b"}) - return g - }, - wantNodes: 2, - wantEdges: 1, - }, - { - name: "PreservesMetadata", - build: func() *dag.DAG { - g := dag.New(nil) - g.AddNode(dag.Node{ - ID: "test", - Meta: dag.Metadata{ - "version": "1.0", - "author": "test-author", - }, - }) - return g - }, - wantNodes: 1, - wantEdges: 0, - check: func(t *testing.T, g graph) { - if g.Nodes[0].Meta["version"] != "1.0" { - t.Errorf("version = %v, want 1.0", g.Nodes[0].Meta["version"]) - } - if g.Nodes[0].Meta["author"] != "test-author" { - t.Errorf("author = %v, want test-author", g.Nodes[0].Meta["author"]) - } - }, - }, - { - name: "Diamond", - build: func() *dag.DAG { - g := dag.New(nil) - g.AddNode(dag.Node{ID: "a"}) - g.AddNode(dag.Node{ID: "b"}) - g.AddNode(dag.Node{ID: "c"}) - g.AddNode(dag.Node{ID: "d"}) - g.AddEdge(dag.Edge{From: "a", To: "b"}) - g.AddEdge(dag.Edge{From: "a", To: "c"}) - g.AddEdge(dag.Edge{From: "b", To: "d"}) - g.AddEdge(dag.Edge{From: "c", To: "d"}) - return g - }, - wantNodes: 4, - wantEdges: 4, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := tt.build() - - var buf bytes.Buffer - if err := WriteJSON(g, &buf); err != nil { - t.Fatalf("WriteJSON: %v", err) - } - - var result graph - if err := json.Unmarshal(buf.Bytes(), &result); err != nil { - t.Fatalf("unmarshal: %v", err) - } - - if got := len(result.Nodes); got != tt.wantNodes { - t.Errorf("nodes = %d, want %d", got, tt.wantNodes) - } - if got := len(result.Edges); got != tt.wantEdges { - t.Errorf("edges = %d, want %d", got, tt.wantEdges) - } - - if tt.check != nil { - tt.check(t, result) - } - }) - } -} diff --git a/pkg/io/import.go b/pkg/io/import.go deleted file mode 100644 index 0352a32..0000000 --- a/pkg/io/import.go +++ /dev/null @@ -1,88 +0,0 @@ -package io - -import ( - "encoding/json" - "fmt" - "io" - "os" - - "github.com/matzehuels/stacktower/pkg/dag" -) - -var kindFromString = map[string]dag.NodeKind{ - "subdivider": dag.NodeKindSubdivider, - "auxiliary": dag.NodeKindAuxiliary, -} - -// ReadJSON decodes a JSON graph from r into a DAG. -// -// The input must be a JSON object with "nodes" and "edges" arrays: -// -// { -// "nodes": [{"id": "a"}, {"id": "b"}], -// "edges": [{"from": "a", "to": "b"}] -// } -// -// Each node must have an "id" field. Optional fields: -// - row: integer layer assignment (defaults to 0) -// - kind: "subdivider" or "auxiliary" (defaults to normal node) -// - meta: object with arbitrary key-value pairs -// -// Each edge must have "from" and "to" fields that reference node IDs. -// -// ReadJSON returns an error if: -// - The JSON is malformed or invalid -// - A node has a duplicate ID -// - An edge references an unknown node ID -// - Adding a node or edge violates DAG constraints (e.g., creates a cycle) -// -// Errors are wrapped with context describing which node or edge caused -// the problem. Use errors.Is or errors.As to check for specific DAG errors. -// -// The returned DAG is independent of r and can be modified safely after -// ReadJSON returns. ReadJSON does not close r. -func ReadJSON(r io.Reader) (*dag.DAG, error) { - var data graph - if err := json.NewDecoder(r).Decode(&data); err != nil { - return nil, fmt.Errorf("decode: %w", err) - } - - g := dag.New(nil) - for _, n := range data.Nodes { - nd := dag.Node{ID: n.ID, Meta: n.Meta} - if n.Row != nil { - nd.Row = *n.Row - } - if k, ok := kindFromString[n.Kind]; ok { - nd.Kind = k - } - if err := g.AddNode(nd); err != nil { - return nil, fmt.Errorf("node %s: %w", n.ID, err) - } - } - for _, e := range data.Edges { - if err := g.AddEdge(dag.Edge{From: e.From, To: e.To}); err != nil { - return nil, fmt.Errorf("edge %s->%s: %w", e.From, e.To, err) - } - } - - return g, nil -} - -// ImportJSON reads a JSON file at path and returns the decoded DAG. -// -// ImportJSON opens the file, decodes it using [ReadJSON], and closes the -// file. If the file cannot be opened, or if decoding fails, ImportJSON -// returns an error describing the failure. The error wraps the underlying -// cause with the file path for context. -// -// ImportJSON returns the same validation errors as [ReadJSON] for malformed -// graphs or DAG constraint violations. -func ImportJSON(path string) (*dag.DAG, error) { - f, err := os.Open(path) - if err != nil { - return nil, fmt.Errorf("open %s: %w", path, err) - } - defer f.Close() - return ReadJSON(f) -} diff --git a/pkg/io/import_test.go b/pkg/io/import_test.go deleted file mode 100644 index 81e15a7..0000000 --- a/pkg/io/import_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package io - -import ( - "os" - "path/filepath" - "strings" - "testing" - - "github.com/matzehuels/stacktower/pkg/dag" -) - -func TestReadJSON(t *testing.T) { - tests := []struct { - name string - input string - wantNodes int - wantEdges int - wantErr bool - check func(t *testing.T, g *dag.DAG) - }{ - { - name: "Valid", - input: `{ - "nodes": [ - {"id": "A", "meta": {"version": "1.0"}}, - {"id": "B"} - ], - "edges": [ - {"from": "A", "to": "B"} - ] - }`, - wantNodes: 2, - wantEdges: 1, - check: func(t *testing.T, g *dag.DAG) { - n, ok := g.Node("A") - if !ok { - t.Fatal("node A not found") - } - if n.Meta["version"] != "1.0" { - t.Errorf("version = %v, want 1.0", n.Meta["version"]) - } - }, - }, - { - name: "Empty", - input: `{ - "nodes": [], - "edges": [] - }`, - wantNodes: 0, - wantEdges: 0, - }, - { - name: "Invalid", - input: `{invalid json}`, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := strings.NewReader(tt.input) - g, err := ReadJSON(r) - - if tt.wantErr { - if err == nil { - t.Fatal("expected error, got nil") - } - return - } - - if err != nil { - t.Fatalf("ReadJSON: %v", err) - } - - if got := g.NodeCount(); got != tt.wantNodes { - t.Errorf("nodes = %d, want %d", got, tt.wantNodes) - } - if got := g.EdgeCount(); got != tt.wantEdges { - t.Errorf("edges = %d, want %d", got, tt.wantEdges) - } - - if tt.check != nil { - tt.check(t, g) - } - }) - } -} - -func TestImportJSON(t *testing.T) { - content := `{ - "nodes": [{"id": "A"}], - "edges": [] - }` - - dir := t.TempDir() - path := filepath.Join(dir, "test.json") - - if err := os.WriteFile(path, []byte(content), 0644); err != nil { - t.Fatal(err) - } - - g, err := ImportJSON(path) - if err != nil { - t.Fatalf("ImportJSON: %v", err) - } - - if g.NodeCount() != 1 { - t.Errorf("nodes = %d, want 1", g.NodeCount()) - } -} - -func TestImportJSONNotFound(t *testing.T) { - _, err := ImportJSON("nonexistent.json") - if err == nil { - t.Error("expected error for nonexistent file") - } -} diff --git a/pkg/observability/hooks.go b/pkg/observability/hooks.go new file mode 100644 index 0000000..4b346b9 --- /dev/null +++ b/pkg/observability/hooks.go @@ -0,0 +1,192 @@ +// Package observability provides hooks for metrics, tracing, and logging. +// +// This package enables optional instrumentation without adding hard dependencies +// on specific observability backends. Consumers can register hooks at startup +// to receive events about pipeline execution, cache operations, and API calls. +// +// # Architecture +// +// The package uses a simple hooks pattern: +// - Define hook interfaces for different event categories +// - Provide no-op default implementations +// - Allow registration of custom implementations at startup +// +// This approach: +// - Avoids import cycles (hooks are registered by main, not by libraries) +// - Keeps the core library dependency-free from observability frameworks +// - Allows different backends (OpenTelemetry, Prometheus, DataDog, etc.) +// +// # Usage +// +// Register hooks at application startup: +// +// func main() { +// observability.SetPipelineHooks(&myPipelineHooks{}) +// observability.SetCacheHooks(&myCacheHooks{}) +// // ... run application +// } +// +// Libraries call hooks to emit events: +// +// observability.Pipeline().OnParseStart(ctx, language, pkg) +// // ... do parsing ... +// observability.Pipeline().OnParseComplete(ctx, language, pkg, nodeCount, duration, err) +package observability + +import ( + "context" + "sync" + "time" +) + +// ============================================================================= +// Pipeline Hooks +// ============================================================================= + +// PipelineHooks receives events from the visualization pipeline. +type PipelineHooks interface { + // Parse events + OnParseStart(ctx context.Context, language, pkg string) + OnParseComplete(ctx context.Context, language, pkg string, nodeCount int, duration time.Duration, err error) + + // Layout events + OnLayoutStart(ctx context.Context, vizType string, nodeCount int) + OnLayoutComplete(ctx context.Context, vizType string, duration time.Duration, err error) + + // Render events + OnRenderStart(ctx context.Context, formats []string) + OnRenderComplete(ctx context.Context, formats []string, duration time.Duration, err error) +} + +// ============================================================================= +// Cache Hooks +// ============================================================================= + +// CacheHooks receives events from cache operations. +type CacheHooks interface { + // OnCacheHit records a cache hit. + OnCacheHit(ctx context.Context, keyType string) + + // OnCacheMiss records a cache miss. + OnCacheMiss(ctx context.Context, keyType string) + + // OnCacheSet records a cache write. + OnCacheSet(ctx context.Context, keyType string, size int) +} + +// ============================================================================= +// HTTP Hooks +// ============================================================================= + +// HTTPHooks receives events from HTTP client operations. +type HTTPHooks interface { + // OnRequest records an outgoing HTTP request. + OnRequest(ctx context.Context, method, host, path string) + + // OnResponse records an HTTP response. + OnResponse(ctx context.Context, method, host, path string, statusCode int, duration time.Duration) + + // OnError records an HTTP error (network failure, timeout). + OnError(ctx context.Context, method, host, path string, err error) +} + +// ============================================================================= +// No-op Implementations +// ============================================================================= + +// NoopPipelineHooks is a no-op implementation of PipelineHooks. +type NoopPipelineHooks struct{} + +func (NoopPipelineHooks) OnParseStart(context.Context, string, string) {} +func (NoopPipelineHooks) OnParseComplete(context.Context, string, string, int, time.Duration, error) { +} +func (NoopPipelineHooks) OnLayoutStart(context.Context, string, int) {} +func (NoopPipelineHooks) OnLayoutComplete(context.Context, string, time.Duration, error) {} +func (NoopPipelineHooks) OnRenderStart(context.Context, []string) {} +func (NoopPipelineHooks) OnRenderComplete(context.Context, []string, time.Duration, error) {} + +// NoopCacheHooks is a no-op implementation of CacheHooks. +type NoopCacheHooks struct{} + +func (NoopCacheHooks) OnCacheHit(context.Context, string) {} +func (NoopCacheHooks) OnCacheMiss(context.Context, string) {} +func (NoopCacheHooks) OnCacheSet(context.Context, string, int) {} + +// NoopHTTPHooks is a no-op implementation of HTTPHooks. +type NoopHTTPHooks struct{} + +func (NoopHTTPHooks) OnRequest(context.Context, string, string, string) {} +func (NoopHTTPHooks) OnResponse(context.Context, string, string, string, int, time.Duration) {} +func (NoopHTTPHooks) OnError(context.Context, string, string, string, error) {} + +// ============================================================================= +// Global Hook Registry +// ============================================================================= + +var ( + pipelineHooks PipelineHooks = NoopPipelineHooks{} + cacheHooks CacheHooks = NoopCacheHooks{} + httpHooks HTTPHooks = NoopHTTPHooks{} + hooksMu sync.RWMutex +) + +// SetPipelineHooks registers custom pipeline hooks. +// This should be called once at application startup before any pipeline operations. +func SetPipelineHooks(h PipelineHooks) { + hooksMu.Lock() + defer hooksMu.Unlock() + if h != nil { + pipelineHooks = h + } +} + +// SetCacheHooks registers custom cache hooks. +// This should be called once at application startup before any cache operations. +func SetCacheHooks(h CacheHooks) { + hooksMu.Lock() + defer hooksMu.Unlock() + if h != nil { + cacheHooks = h + } +} + +// SetHTTPHooks registers custom HTTP hooks. +// This should be called once at application startup before any HTTP operations. +func SetHTTPHooks(h HTTPHooks) { + hooksMu.Lock() + defer hooksMu.Unlock() + if h != nil { + httpHooks = h + } +} + +// Pipeline returns the registered pipeline hooks. +func Pipeline() PipelineHooks { + hooksMu.RLock() + defer hooksMu.RUnlock() + return pipelineHooks +} + +// Cache returns the registered cache hooks. +func Cache() CacheHooks { + hooksMu.RLock() + defer hooksMu.RUnlock() + return cacheHooks +} + +// HTTP returns the registered HTTP hooks. +func HTTP() HTTPHooks { + hooksMu.RLock() + defer hooksMu.RUnlock() + return httpHooks +} + +// Reset restores all hooks to their no-op defaults. +// This is primarily useful for testing. +func Reset() { + hooksMu.Lock() + defer hooksMu.Unlock() + pipelineHooks = NoopPipelineHooks{} + cacheHooks = NoopCacheHooks{} + httpHooks = NoopHTTPHooks{} +} diff --git a/pkg/observability/hooks_test.go b/pkg/observability/hooks_test.go new file mode 100644 index 0000000..8630ca3 --- /dev/null +++ b/pkg/observability/hooks_test.go @@ -0,0 +1,94 @@ +package observability + +import ( + "context" + "testing" + "time" +) + +func TestNoopHooksDoNotPanic(t *testing.T) { + ctx := context.Background() + + // Pipeline hooks + p := NoopPipelineHooks{} + p.OnParseStart(ctx, "python", "requests") + p.OnParseComplete(ctx, "python", "requests", 100, time.Second, nil) + p.OnLayoutStart(ctx, "tower", 100) + p.OnLayoutComplete(ctx, "tower", time.Second, nil) + p.OnRenderStart(ctx, []string{"svg"}) + p.OnRenderComplete(ctx, []string{"svg"}, time.Second, nil) + + // Cache hooks + c := NoopCacheHooks{} + c.OnCacheHit(ctx, "graph") + c.OnCacheMiss(ctx, "layout") + c.OnCacheSet(ctx, "artifact", 1024) + + // HTTP hooks + h := NoopHTTPHooks{} + h.OnRequest(ctx, "GET", "pypi.org", "/simple/requests") + h.OnResponse(ctx, "GET", "pypi.org", "/simple/requests", 200, time.Second) + h.OnError(ctx, "GET", "pypi.org", "/simple/requests", nil) +} + +func TestGlobalHooksRegistry(t *testing.T) { + // Reset to known state + Reset() + + // Verify defaults are noop + if _, ok := Pipeline().(NoopPipelineHooks); !ok { + t.Error("Pipeline() should return NoopPipelineHooks by default") + } + if _, ok := Cache().(NoopCacheHooks); !ok { + t.Error("Cache() should return NoopCacheHooks by default") + } + if _, ok := HTTP().(NoopHTTPHooks); !ok { + t.Error("HTTP() should return NoopHTTPHooks by default") + } + + // Set custom hooks + customPipeline := &testPipelineHooks{} + SetPipelineHooks(customPipeline) + if Pipeline() != customPipeline { + t.Error("SetPipelineHooks should set custom hooks") + } + + customCache := &testCacheHooks{} + SetCacheHooks(customCache) + if Cache() != customCache { + t.Error("SetCacheHooks should set custom hooks") + } + + customHTTP := &testHTTPHooks{} + SetHTTPHooks(customHTTP) + if HTTP() != customHTTP { + t.Error("SetHTTPHooks should set custom hooks") + } + + // Reset and verify + Reset() + if _, ok := Pipeline().(NoopPipelineHooks); !ok { + t.Error("Reset() should restore NoopPipelineHooks") + } +} + +func TestSetNilHooksIsIgnored(t *testing.T) { + Reset() + + custom := &testPipelineHooks{} + SetPipelineHooks(custom) + + // Setting nil should be ignored + SetPipelineHooks(nil) + + if Pipeline() != custom { + t.Error("SetPipelineHooks(nil) should be ignored") + } + + Reset() +} + +// Test implementations +type testPipelineHooks struct{ NoopPipelineHooks } +type testCacheHooks struct{ NoopCacheHooks } +type testHTTPHooks struct{ NoopHTTPHooks } diff --git a/pkg/pipeline/layout.go b/pkg/pipeline/layout.go new file mode 100644 index 0000000..778d504 --- /dev/null +++ b/pkg/pipeline/layout.go @@ -0,0 +1,125 @@ +package pipeline + +import ( + "github.com/matzehuels/stacktower/pkg/core/dag" + "github.com/matzehuels/stacktower/pkg/core/render/nodelink" + "github.com/matzehuels/stacktower/pkg/core/render/tower/feature" + "github.com/matzehuels/stacktower/pkg/core/render/tower/layout" + "github.com/matzehuels/stacktower/pkg/core/render/tower/transform" + "github.com/matzehuels/stacktower/pkg/graph" +) + +// ============================================================================= +// Layout Generation +// ============================================================================= + +// GenerateLayout generates a complete layout for any visualization type. +// This is the unified entry point for generating serializable layout data. +// +// Both tower and nodelink layouts include: +// - Graph structure (nodes, edges, rows) +// - Nebraska rankings (maintainer data) +// - Visualization-specific data (blocks for tower, DOT for nodelink) +func GenerateLayout(g *dag.DAG, opts Options) (graph.Layout, error) { + if opts.IsNodelink() { + return generateNodelinkLayout(g, opts) + } + return generateTowerLayout(g, opts) +} + +// ============================================================================= +// Tower +// ============================================================================= + +// generateTowerLayout generates a complete tower layout. +// Computes positions, applies transforms, and includes Nebraska rankings. +// +// Note: Nebraska rankings are ALWAYS computed and stored, regardless of opts.Nebraska. +// The opts.Nebraska flag only controls whether the ranking panel is rendered in the SVG. +func generateTowerLayout(g *dag.DAG, opts Options) (graph.Layout, error) { + // Ensure graph has row assignments + workGraph := g + if g.MaxRow() == 0 && g.EdgeCount() > 0 { + workGraph = g.Clone() + layout.EnsureLayered(workGraph) + } + + // Build layout options + var layoutOpts []layout.Option + if opts.Orderer != nil { + layoutOpts = append(layoutOpts, layout.WithOrderer(opts.Orderer)) + } + + // Compute base layout + l := layout.Build(workGraph, opts.Width, opts.Height, layoutOpts...) + + // Apply transforms + if opts.Merge { + l = transform.MergeSubdividers(l, workGraph) + } + if opts.Randomize { + l = transform.Randomize(l, workGraph, opts.Seed, nil) + } + + // Set metadata + l.Style = opts.Style + l.Seed = opts.Seed + l.Randomize = opts.Randomize + l.Merged = opts.Merge + + // Compute Nebraska rankings + l.Nebraska = feature.RankNebraska(workGraph, 10) + + // Export to serialization format + return l.Export(workGraph) +} + +// ============================================================================= +// Nodelink +// ============================================================================= + +// generateNodelinkLayout generates a complete nodelink layout. +// Includes DOT string for Graphviz and Nebraska rankings. +// +// Note: Nebraska rankings are ALWAYS computed and stored, regardless of opts.Nebraska. +// The opts.Nebraska flag only controls whether the ranking panel is rendered in the SVG. +func generateNodelinkLayout(g *dag.DAG, opts Options) (graph.Layout, error) { + // Generate DOT representation + dot := nodelink.ToDOT(g, nodelink.Options{Detailed: false}) + + // Build base layout + result, err := nodelink.Export(dot, g, nodelink.Options{Detailed: false}, opts.Width, opts.Height, opts.Style) + if err != nil { + return result, err + } + + // Add Nebraska rankings + result.Nebraska = exportNebraska(feature.RankNebraska(g, 10)) + + return result, nil +} + +// ============================================================================= +// Helpers +// ============================================================================= + +// exportNebraska converts internal Nebraska rankings to the serialization format. +func exportNebraska(rankings []feature.NebraskaRanking) []graph.NebraskaRanking { + result := make([]graph.NebraskaRanking, len(rankings)) + for i, r := range rankings { + pkgs := make([]graph.NebraskaPackage, len(r.Packages)) + for j, p := range r.Packages { + pkgs[j] = graph.NebraskaPackage{ + Package: p.Package, + Role: string(p.Role), + URL: p.URL, + } + } + result[i] = graph.NebraskaRanking{ + Maintainer: r.Maintainer, + Score: r.Score, + Packages: pkgs, + } + } + return result +} diff --git a/pkg/pipeline/parse.go b/pkg/pipeline/parse.go new file mode 100644 index 0000000..a30b96b --- /dev/null +++ b/pkg/pipeline/parse.go @@ -0,0 +1,140 @@ +package pipeline + +import ( + "context" + "fmt" + "os" + + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/core/dag" + "github.com/matzehuels/stacktower/pkg/core/deps" + "github.com/matzehuels/stacktower/pkg/core/deps/languages" + "github.com/matzehuels/stacktower/pkg/core/deps/metadata" +) + +// Parse resolves dependencies for a package or manifest. +func Parse(ctx context.Context, c cache.Cache, opts Options) (*dag.DAG, error) { + lang := languages.Find(opts.Language) + if lang == nil { + return nil, fmt.Errorf("unsupported language: %s", opts.Language) + } + + resolveOpts := buildResolveOptions(c, opts) + + var g *dag.DAG + var err error + + if opts.Manifest != "" { + g, err = parseManifest(ctx, c, lang, opts, resolveOpts) + } else { + g, err = resolvePackage(ctx, c, lang, opts.Package, resolveOpts) + } + + if err != nil { + return nil, err + } + + // NOTE: Normalization (adding subdividers) is NOT done here. + // It's applied later during layout via PrepareGraph, which allows + // the raw graph to be stored and reused for different viz types + // (tower needs normalized graph, nodelink needs raw graph). + + return g, nil +} + +// buildResolveOptions creates deps.Options from pipeline options. +func buildResolveOptions(c cache.Cache, opts Options) deps.Options { + resolveOpts := deps.Options{ + MaxDepth: opts.MaxDepth, + MaxNodes: opts.MaxNodes, + Refresh: opts.Refresh, + CacheTTL: deps.DefaultCacheTTL, + } + + // Set up logger callback + if opts.Logger != nil { + resolveOpts.Logger = func(format string, args ...any) { + opts.Logger.Warnf(format, args...) + } + } + + // Set up metadata providers + token := opts.GitHubToken + if token == "" { + token = os.Getenv("GITHUB_TOKEN") + } + if opts.ShouldEnrich() && token != "" { + gh := metadata.NewGitHub(c, token, deps.DefaultCacheTTL) + resolveOpts.MetadataProviders = []deps.MetadataProvider{gh} + } + + return resolveOpts +} + +// resolvePackage resolves dependencies from a package registry. +func resolvePackage(ctx context.Context, c cache.Cache, lang *deps.Language, pkg string, opts deps.Options) (*dag.DAG, error) { + resolver, err := lang.Resolver(c) + if err != nil { + return nil, fmt.Errorf("get resolver: %w", err) + } + + // Normalize package name if the language supports it + if lang.NormalizeName != nil { + pkg = lang.NormalizeName(pkg) + } + + g, err := resolver.Resolve(ctx, pkg, opts) + if err != nil { + return nil, fmt.Errorf("resolve %s: %w", pkg, err) + } + + return g, nil +} + +// parseManifest parses dependencies from a manifest file or content. +func parseManifest(ctx context.Context, c cache.Cache, lang *deps.Language, opts Options, resolveOpts deps.Options) (*dag.DAG, error) { + resolver, err := lang.Resolver(c) + if err != nil { + return nil, fmt.Errorf("get resolver: %w", err) + } + + parser, ok := lang.Manifest(opts.ManifestFilename, resolver) + if !ok { + return nil, fmt.Errorf("no parser for manifest: %s", opts.ManifestFilename) + } + + // If manifest content is provided, write to temp file + var filePath string + if opts.Manifest != "" { + tmpDir, err := os.MkdirTemp("", "stacktower-*") + if err != nil { + return nil, fmt.Errorf("create temp dir: %w", err) + } + defer os.RemoveAll(tmpDir) + + filePath = tmpDir + "/" + opts.ManifestFilename + if err := os.WriteFile(filePath, []byte(opts.Manifest), 0644); err != nil { + return nil, fmt.Errorf("write temp file: %w", err) + } + } else { + filePath = opts.ManifestFilename + } + + result, err := parser.Parse(filePath, resolveOpts) + if err != nil { + return nil, fmt.Errorf("parse manifest: %w", err) + } + + g, ok := result.Graph.(*dag.DAG) + if !ok { + return nil, fmt.Errorf("unexpected graph type: %T", result.Graph) + } + + // Rename __project__ node if RootName is specified + // This allows callers (CLI, API) to set a custom root name + if opts.RootName != "" { + _ = g.RenameNode("__project__", opts.RootName) + } + + return g, nil +} diff --git a/pkg/pipeline/pipeline.go b/pkg/pipeline/pipeline.go new file mode 100644 index 0000000..1fa6906 --- /dev/null +++ b/pkg/pipeline/pipeline.go @@ -0,0 +1,388 @@ +// Package pipeline provides the core visualization pipeline for Stacktower. +// +// This package implements the complete parse → layout → render pipeline that +// can be used by CLI, API, and worker components. By centralizing this logic, +// we ensure consistent behavior across all entry points and avoid code duplication. +// +// # Architecture +// +// The pipeline consists of three stages: +// +// 1. Parse: Resolve dependencies from package registries or manifest files +// 2. Layout: Compute visual positions for the dependency graph +// 3. Render: Generate output in various formats (SVG, PNG, PDF, JSON) +// +// Each stage can be run independently or as part of the complete pipeline. +// +// # Usage +// +// Create a Runner and execute the pipeline: +// +// runner := pipeline.NewRunner(cache, nil, logger) +// opts := pipeline.Options{ +// Language: "python", +// Package: "requests", +// VizType: "tower", +// Formats: []string{"svg"}, +// } +// result, err := runner.Execute(ctx, opts) +// if err != nil { +// log.Fatal(err) +// } +// svg := result.Artifacts["svg"] +// +// Run individual stages: +// +// // Parse only +// g, err := runner.Parse(ctx, parseOpts) +// +// // Layout with existing graph +// layout, err := runner.ComputeLayout(ctx, g, layoutOpts) +// +// // Render with existing layout +// artifacts, err := runner.Render(ctx, layout, g, renderOpts) +package pipeline + +import ( + "fmt" + "io" + "time" + + "github.com/charmbracelet/log" + + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/core/dag" + "github.com/matzehuels/stacktower/pkg/core/render/tower/ordering" + "github.com/matzehuels/stacktower/pkg/graph" +) + +// ============================================================================= +// Default Values - Single Source of Truth for CLI, API, and Worker +// ============================================================================= + +const ( + // DefaultMaxDepth is the maximum dependency traversal depth for the pipeline. + // This is intentionally more conservative than deps.DefaultMaxDepth (50) to + // provide better UX for CLI users and prevent excessively large graphs. + // API users can override this by setting MaxDepth explicitly. + DefaultMaxDepth = 10 + + // DefaultMaxNodes is the maximum number of nodes to fetch. + // This matches deps.DefaultMaxNodes (5000) to maintain consistency. + DefaultMaxNodes = 5000 + + // DefaultWidth is the default frame width in pixels. + DefaultWidth = 800.0 + + // DefaultHeight is the default frame height in pixels. + DefaultHeight = 600.0 + + // DefaultSeed is the default random seed for reproducibility. + DefaultSeed = uint64(42) + + // DefaultOrdering is the default ordering algorithm. + DefaultOrdering = "optimal" +) + +// DefaultVizType is the default visualization type. +const DefaultVizType = graph.VizTypeTower + +// DefaultStyle is the default visual style. +const DefaultStyle = graph.StyleHanddrawn + +// Format constants for output formats. +const ( + FormatSVG = "svg" + FormatPNG = "png" + FormatPDF = "pdf" + FormatJSON = "json" +) + +// ValidFormats is the set of supported output formats. +var ValidFormats = map[string]bool{ + FormatSVG: true, + FormatPNG: true, + FormatPDF: true, + FormatJSON: true, +} + +// ValidStyles is the set of supported visual styles. +var ValidStyles = map[string]bool{ + graph.StyleSimple: true, + graph.StyleHanddrawn: true, +} + +// ValidVizTypes is the set of supported visualization types. +var ValidVizTypes = map[string]bool{ + graph.VizTypeTower: true, + graph.VizTypeNodelink: true, +} + +// ============================================================================= +// Options - Pipeline Configuration +// ============================================================================= + +// Options contains all configuration for the visualization pipeline. +// This struct supports JSON serialization for API requests. +type Options struct { + // Parse options + Language string `json:"language"` + Package string `json:"package,omitempty"` + Manifest string `json:"manifest,omitempty"` + ManifestFilename string `json:"manifest_filename,omitempty"` + Owner string `json:"owner,omitempty"` // GitHub owner (user/org) + Repo string `json:"repo,omitempty"` // GitHub repository name + Ref string `json:"ref,omitempty"` // Git ref (branch/tag) + Path string `json:"path,omitempty"` // Path within repo + RootName string `json:"root_name,omitempty"` // Custom name for root node (replaces __project__) + MaxDepth int `json:"max_depth,omitempty"` + MaxNodes int `json:"max_nodes,omitempty"` + SkipEnrich bool `json:"skip_enrich,omitempty"` // Skip metadata enrichment (default: false = enrich) + Refresh bool `json:"refresh,omitempty"` + + // Layout options + VizType string `json:"viz_type,omitempty"` + Width float64 `json:"width,omitempty"` + Height float64 `json:"height,omitempty"` + Normalize bool `json:"normalize,omitempty"` // Apply graph normalization during layout + Ordering string `json:"ordering,omitempty"` + Merge bool `json:"merge,omitempty"` + Randomize bool `json:"randomize,omitempty"` + Seed uint64 `json:"seed,omitempty"` + + // Render options + Formats []string `json:"formats,omitempty"` + Style string `json:"style,omitempty"` + ShowEdges bool `json:"show_edges,omitempty"` + Nebraska bool `json:"nebraska,omitempty"` // Show Nebraska ranking panel in SVG (data is always computed) + Popups bool `json:"popups,omitempty"` + + // Runtime options (not serialized) + Logger *log.Logger `json:"-"` + GitHubToken string `json:"-"` + Orderer ordering.Orderer `json:"-"` + + // validated tracks whether ValidateAndSetDefaults has been called. + validated bool `json:"-"` +} + +// Result contains the outputs of a pipeline run. +type Result struct { + // Graph is the parsed dependency graph. + Graph *dag.DAG + + // GraphHash is the content hash of the graph. + GraphHash string + + // Layout contains the layout data (positions, nebraska, etc). + Layout graph.Layout + + // Artifacts contains rendered outputs keyed by format. + Artifacts map[string][]byte + + // Stats contains timing and size information. + Stats Stats + + // CacheInfo tracks which stages hit the cache. + CacheInfo CacheInfo +} + +// Stats contains pipeline execution statistics. +type Stats struct { + NodeCount int + EdgeCount int + ParseTime time.Duration + LayoutTime time.Duration + RenderTime time.Duration +} + +// CacheInfo tracks cache hits for each pipeline stage. +type CacheInfo struct { + ParseHit bool // Whether parse result came from cache + LayoutHit bool // Whether layout result came from cache + RenderHit bool // Whether all artifacts came from cache +} + +// ============================================================================= +// Validation Functions +// ============================================================================= + +// ValidateFormat checks that a format is valid. +func ValidateFormat(format string) error { + if !ValidFormats[format] { + return fmt.Errorf("invalid format: %q (must be one of: svg, png, pdf, json)", format) + } + return nil +} + +// ValidateFormats checks that all formats are valid. +func ValidateFormats(formats []string) error { + for _, f := range formats { + if err := ValidateFormat(f); err != nil { + return err + } + } + return nil +} + +// ValidateStyle checks that a style is valid. +func ValidateStyle(style string) error { + if !ValidStyles[style] { + return fmt.Errorf("invalid style: %q (must be one of: simple, handdrawn)", style) + } + return nil +} + +// ValidateVizType checks that a visualization type is valid. +func ValidateVizType(vizType string) error { + if !ValidVizTypes[vizType] { + return fmt.Errorf("invalid viz_type: %q (must be one of: tower, nodelink)", vizType) + } + return nil +} + +// ============================================================================= +// Options Methods +// ============================================================================= + +// ValidateAndSetDefaults checks required fields and applies defaults for the full pipeline. +// This method is idempotent - calling it multiple times has the same effect as calling it once. +func (o *Options) ValidateAndSetDefaults() error { + if o.validated { + return nil + } + if err := o.ValidateForParse(); err != nil { + return err + } + o.SetLayoutDefaults() + o.SetRenderDefaults() + o.validated = true + return nil +} + +// ValidateForParse checks required fields for parsing. +func (o *Options) ValidateForParse() error { + if o.Language == "" { + return fmt.Errorf("language is required") + } + if o.Package == "" && o.Manifest == "" { + return fmt.Errorf("package or manifest is required") + } + if o.Manifest != "" && o.ManifestFilename == "" { + return fmt.Errorf("manifest_filename is required") + } + + // Parse defaults + if o.MaxDepth == 0 { + o.MaxDepth = DefaultMaxDepth + } + if o.MaxNodes == 0 { + o.MaxNodes = DefaultMaxNodes + } + + // Logger default + if o.Logger == nil { + o.Logger = log.NewWithOptions(io.Discard, log.Options{}) + } + + return nil +} + +// SetLayoutDefaults sets default values for layout computation. +func (o *Options) SetLayoutDefaults() { + if o.VizType == "" { + o.VizType = DefaultVizType + } + if o.Width == 0 { + o.Width = DefaultWidth + } + if o.Height == 0 { + o.Height = DefaultHeight + } + if o.Seed == 0 { + o.Seed = DefaultSeed + } + if o.Logger == nil { + o.Logger = log.NewWithOptions(io.Discard, log.Options{}) + } +} + +// ValidateForLayout validates and sets defaults for layout computation. +func (o *Options) ValidateForLayout() error { + o.SetLayoutDefaults() + return ValidateVizType(o.VizType) +} + +// SetRenderDefaults sets default values for rendering. +func (o *Options) SetRenderDefaults() { + if len(o.Formats) == 0 { + o.Formats = []string{FormatSVG} + } + if o.Style == "" { + o.Style = DefaultStyle + } + if o.Logger == nil { + o.Logger = log.NewWithOptions(io.Discard, log.Options{}) + } +} + +// ValidateForRender validates and sets defaults for rendering. +func (o *Options) ValidateForRender() error { + o.SetLayoutDefaults() + o.SetRenderDefaults() + if err := ValidateVizType(o.VizType); err != nil { + return err + } + if err := ValidateFormats(o.Formats); err != nil { + return err + } + return ValidateStyle(o.Style) +} + +// IsTower returns true if this is a tower visualization. +func (o *Options) IsTower() bool { + return o.VizType == "" || o.VizType == graph.VizTypeTower +} + +// IsNodelink returns true if this is a nodelink visualization. +func (o *Options) IsNodelink() bool { + return o.VizType == graph.VizTypeNodelink +} + +// NeedsOptimalOrderer returns true if the ordering algorithm requires the optimal orderer. +// This is true when ordering is "optimal" (the default) or empty. +func (o *Options) NeedsOptimalOrderer() bool { + return o.Ordering == DefaultOrdering || o.Ordering == "" +} + +// ShouldEnrich returns whether metadata enrichment should be performed. +func (o *Options) ShouldEnrich() bool { + return !o.SkipEnrich +} + +// LayoutKeyOpts returns cache key options for layout computation. +func (o *Options) LayoutKeyOpts() cache.LayoutKeyOpts { + return cache.LayoutKeyOpts{ + VizType: o.VizType, + Width: o.Width, + Height: o.Height, + Normalize: o.Normalize, + Ordering: o.Ordering, + Merge: o.Merge, + Randomize: o.Randomize, + Seed: o.Seed, + } +} + +// ArtifactKeyOpts returns cache key options for artifact rendering. +func (o *Options) ArtifactKeyOpts(format string) cache.ArtifactKeyOpts { + return cache.ArtifactKeyOpts{ + Format: format, + Style: o.Style, + ShowEdges: o.ShowEdges, + Popups: o.Popups, + Nebraska: o.Nebraska, + Merge: o.Merge, + Normalize: o.Normalize, + } +} diff --git a/pkg/pipeline/pipeline_test.go b/pkg/pipeline/pipeline_test.go new file mode 100644 index 0000000..3fa8132 --- /dev/null +++ b/pkg/pipeline/pipeline_test.go @@ -0,0 +1,244 @@ +package pipeline + +import ( + "testing" +) + +func TestValidateFormat(t *testing.T) { + tests := []struct { + format string + wantErr bool + }{ + {"svg", false}, + {"png", false}, + {"pdf", false}, + {"json", false}, + {"invalid", true}, + {"SVG", true}, // case-sensitive + {"", true}, + } + + for _, tt := range tests { + err := ValidateFormat(tt.format) + if (err != nil) != tt.wantErr { + t.Errorf("ValidateFormat(%q) error = %v, wantErr %v", tt.format, err, tt.wantErr) + } + } +} + +func TestValidateFormats(t *testing.T) { + if err := ValidateFormats([]string{"svg", "png"}); err != nil { + t.Errorf("Valid formats should pass: %v", err) + } + + if err := ValidateFormats([]string{"svg", "invalid"}); err == nil { + t.Error("Invalid format should fail") + } + + // Empty slice is valid + if err := ValidateFormats(nil); err != nil { + t.Errorf("Empty formats should pass: %v", err) + } +} + +func TestValidateStyle(t *testing.T) { + tests := []struct { + style string + wantErr bool + }{ + {"simple", false}, + {"handdrawn", false}, + {"invalid", true}, + {"", true}, + } + + for _, tt := range tests { + err := ValidateStyle(tt.style) + if (err != nil) != tt.wantErr { + t.Errorf("ValidateStyle(%q) error = %v, wantErr %v", tt.style, err, tt.wantErr) + } + } +} + +func TestValidateVizType(t *testing.T) { + tests := []struct { + vizType string + wantErr bool + }{ + {"tower", false}, + {"nodelink", false}, + {"invalid", true}, + {"", true}, + } + + for _, tt := range tests { + err := ValidateVizType(tt.vizType) + if (err != nil) != tt.wantErr { + t.Errorf("ValidateVizType(%q) error = %v, wantErr %v", tt.vizType, err, tt.wantErr) + } + } +} + +func TestOptionsDefaults(t *testing.T) { + opts := Options{ + Language: "python", + Package: "requests", + } + + if err := opts.ValidateForParse(); err != nil { + t.Errorf("Valid options should pass: %v", err) + } + + // Check defaults were set + if opts.MaxDepth != DefaultMaxDepth { + t.Errorf("MaxDepth should be %d, got %d", DefaultMaxDepth, opts.MaxDepth) + } + if opts.MaxNodes != DefaultMaxNodes { + t.Errorf("MaxNodes should be %d, got %d", DefaultMaxNodes, opts.MaxNodes) + } +} + +func TestOptionsValidateForParse(t *testing.T) { + // Missing language + opts := Options{Package: "requests"} + if err := opts.ValidateForParse(); err == nil { + t.Error("Missing language should fail") + } + + // Missing package and manifest + opts = Options{Language: "python"} + if err := opts.ValidateForParse(); err == nil { + t.Error("Missing package/manifest should fail") + } + + // Manifest without filename + opts = Options{Language: "python", Manifest: "content"} + if err := opts.ValidateForParse(); err == nil { + t.Error("Manifest without filename should fail") + } + + // Valid with manifest + opts = Options{Language: "python", Manifest: "content", ManifestFilename: "poetry.lock"} + if err := opts.ValidateForParse(); err != nil { + t.Errorf("Valid manifest options should pass: %v", err) + } +} + +func TestOptionsIsTower(t *testing.T) { + opts := Options{} + if !opts.IsTower() { + t.Error("Empty VizType should be tower") + } + + opts.VizType = "tower" + if !opts.IsTower() { + t.Error("tower VizType should be tower") + } + + opts.VizType = "nodelink" + if opts.IsTower() { + t.Error("nodelink VizType should not be tower") + } +} + +func TestOptionsIsNodelink(t *testing.T) { + opts := Options{} + if opts.IsNodelink() { + t.Error("Empty VizType should not be nodelink") + } + + opts.VizType = "nodelink" + if !opts.IsNodelink() { + t.Error("nodelink VizType should be nodelink") + } +} + +func TestOptionsShouldEnrich(t *testing.T) { + opts := Options{} + if !opts.ShouldEnrich() { + t.Error("Default should enrich") + } + + opts.SkipEnrich = true + if opts.ShouldEnrich() { + t.Error("SkipEnrich=true should not enrich") + } +} + +func TestOptionsNeedsOptimalOrderer(t *testing.T) { + opts := Options{} + if !opts.NeedsOptimalOrderer() { + t.Error("Empty ordering should need optimal orderer") + } + + opts.Ordering = "optimal" + if !opts.NeedsOptimalOrderer() { + t.Error("optimal ordering should need optimal orderer") + } + + opts.Ordering = "barycentric" + if opts.NeedsOptimalOrderer() { + t.Error("barycentric ordering should not need optimal orderer") + } +} + +func TestOptionsValidateAndSetDefaultsIdempotent(t *testing.T) { + opts := Options{ + Language: "python", + Package: "requests", + } + + // First call + if err := opts.ValidateAndSetDefaults(); err != nil { + t.Fatalf("First validation failed: %v", err) + } + + originalMaxDepth := opts.MaxDepth + originalVizType := opts.VizType + originalStyle := opts.Style + + // Second call should be idempotent + if err := opts.ValidateAndSetDefaults(); err != nil { + t.Fatalf("Second validation failed: %v", err) + } + + if opts.MaxDepth != originalMaxDepth { + t.Error("MaxDepth changed on second call") + } + if opts.VizType != originalVizType { + t.Error("VizType changed on second call") + } + if opts.Style != originalStyle { + t.Error("Style changed on second call") + } +} + +func TestSetLayoutDefaults(t *testing.T) { + opts := Options{} + opts.SetLayoutDefaults() + + if opts.VizType != DefaultVizType { + t.Errorf("VizType should be %s, got %s", DefaultVizType, opts.VizType) + } + if opts.Width != DefaultWidth { + t.Errorf("Width should be %f, got %f", DefaultWidth, opts.Width) + } + if opts.Height != DefaultHeight { + t.Errorf("Height should be %f, got %f", DefaultHeight, opts.Height) + } + if opts.Seed != DefaultSeed { + t.Errorf("Seed should be %d, got %d", DefaultSeed, opts.Seed) + } +} + +func TestSetRenderDefaults(t *testing.T) { + opts := Options{} + opts.SetRenderDefaults() + + if len(opts.Formats) != 1 || opts.Formats[0] != FormatSVG { + t.Errorf("Formats should be [svg], got %v", opts.Formats) + } + if opts.Style != DefaultStyle { + t.Errorf("Style should be %s, got %s", DefaultStyle, opts.Style) + } +} diff --git a/pkg/pipeline/render.go b/pkg/pipeline/render.go new file mode 100644 index 0000000..79a15df --- /dev/null +++ b/pkg/pipeline/render.go @@ -0,0 +1,199 @@ +package pipeline + +import ( + "fmt" + + "github.com/matzehuels/stacktower/pkg/core/dag" + "github.com/matzehuels/stacktower/pkg/core/render/nodelink" + "github.com/matzehuels/stacktower/pkg/core/render/tower/layout" + "github.com/matzehuels/stacktower/pkg/core/render/tower/sink" + "github.com/matzehuels/stacktower/pkg/core/render/tower/styles" + "github.com/matzehuels/stacktower/pkg/core/render/tower/styles/handdrawn" + "github.com/matzehuels/stacktower/pkg/graph" +) + +// Render generates output artifacts in the requested formats. +func Render(l layout.Layout, g *dag.DAG, opts Options) (map[string][]byte, error) { + if opts.IsNodelink() { + // For nodelink, generate DOT graph on-demand + return renderNodelinkFromGraph(g, opts) + } + return renderTower(l, g, opts) +} + +// RenderNodelink generates nodelink outputs from a layout. +// The layout must be a nodelink layout (VizType = "nodelink") with a DOT string. +func RenderNodelink(layout graph.Layout, opts Options) (map[string][]byte, error) { + if layout.DOT == "" { + return nil, fmt.Errorf("nodelink layout missing DOT string") + } + + artifacts := make(map[string][]byte) + + for _, format := range opts.Formats { + var data []byte + var err error + + switch format { + case FormatSVG: + data, err = nodelink.RenderSVG(layout.DOT) + case FormatPNG: + data, err = nodelink.RenderPNG(layout.DOT, 2.0) + case FormatPDF: + data, err = nodelink.RenderPDF(layout.DOT) + case FormatJSON: + data, err = graph.MarshalLayout(layout) + default: + return nil, fmt.Errorf("unsupported nodelink format: %s", format) + } + + if err != nil { + return nil, fmt.Errorf("render %s: %w", format, err) + } + artifacts[format] = data + } + + return artifacts, nil +} + +// renderNodelinkFromGraph generates nodelink outputs directly from a graph. +// This generates the DOT graph on-demand instead of requiring a pre-computed layout. +func renderNodelinkFromGraph(g *dag.DAG, opts Options) (map[string][]byte, error) { + // Generate DOT graph + dot := nodelink.ToDOT(g, nodelink.Options{Detailed: false}) + + // Build layout + layout, err := nodelink.Export(dot, g, nodelink.Options{Detailed: false}, opts.Width, opts.Height, opts.Style) + if err != nil { + return nil, fmt.Errorf("generate nodelink layout: %w", err) + } + + // Render using the layout + return RenderNodelink(layout, opts) +} + +// renderTower generates tower outputs. +func renderTower(l layout.Layout, g *dag.DAG, opts Options) (map[string][]byte, error) { + opts = applyLayoutMetadata(opts, l) + + svgOpts := buildSVGOptions(g, l, opts) + artifacts := make(map[string][]byte) + + for _, format := range opts.Formats { + var data []byte + var err error + + switch format { + case FormatSVG: + data = sink.RenderSVG(l, svgOpts...) + case FormatPNG: + data, err = sink.RenderPNG(l, sink.WithPNGSVGOptions(svgOpts...)) + case FormatPDF: + data, err = sink.RenderPDF(l, sink.WithPDFSVGOptions(svgOpts...)) + case FormatJSON: + var exported graph.Layout + exported, err = l.Export(g) + if err != nil { + return nil, fmt.Errorf("serialize layout: %w", err) + } + data, err = graph.MarshalLayout(exported) + default: + return nil, fmt.Errorf("unsupported tower format: %s", format) + } + + if err != nil { + return nil, fmt.Errorf("render %s: %w", format, err) + } + artifacts[format] = data + } + + return artifacts, nil +} + +// applyLayoutMetadata applies layout metadata to options if not already set. +// This ensures that serialized layouts preserve their original rendering settings. +func applyLayoutMetadata(opts Options, l layout.Layout) Options { + if opts.Style == "" && l.Style != "" { + opts.Style = l.Style + } + if opts.Seed == 0 && l.Seed != 0 { + opts.Seed = l.Seed + } + if !opts.Merge && l.Merged { + opts.Merge = l.Merged + } + return opts +} + +// buildSVGOptions builds SVG rendering options. +// Nebraska rankings are extracted from the layout struct (computed during layout phase). +func buildSVGOptions(g *dag.DAG, l layout.Layout, opts Options) []sink.SVGOption { + var svgOpts []sink.SVGOption + + if g != nil { + svgOpts = append(svgOpts, sink.WithGraph(g)) + } + if opts.ShowEdges { + svgOpts = append(svgOpts, sink.WithEdges()) + } + if opts.Merge { + svgOpts = append(svgOpts, sink.WithMerged()) + } + + // Apply visual style + switch opts.Style { + case graph.StyleHanddrawn: + seed := opts.Seed + if seed == 0 { + seed = 42 + } + svgOpts = append(svgOpts, sink.WithStyle(handdrawn.New(seed))) + case graph.StyleSimple: + svgOpts = append(svgOpts, sink.WithStyle(styles.Simple{})) + } + + // Popups only for handdrawn style (simple doesn't support them yet) + if opts.Style == graph.StyleHanddrawn && opts.Popups && g != nil { + svgOpts = append(svgOpts, sink.WithPopups()) + } + + // Nebraska guy ranking panel - only rendered if opts.Nebraska is true. + // Note: The Nebraska data is ALWAYS computed during layout generation; + // this flag only controls whether the panel is displayed in the SVG. + if opts.Nebraska && len(l.Nebraska) > 0 { + svgOpts = append(svgOpts, sink.WithNebraska(l.Nebraska)) + } + + return svgOpts +} + +// RenderFromLayoutData renders output from serialized layout data. +// This is useful when the layout was computed elsewhere (e.g., cached). +func RenderFromLayoutData(layoutData []byte, g *dag.DAG, opts Options) (map[string][]byte, error) { + // Parse layout + parsed, err := graph.UnmarshalLayout(layoutData) + if err != nil { + return nil, fmt.Errorf("parse layout: %w", err) + } + + // Dispatch based on viz type + return RenderFromLayout(parsed, g, opts) +} + +// RenderFromLayout renders output from a graph.Layout. +// This is the preferred entry point when you have a graph.Layout. +func RenderFromLayout(graphLayout graph.Layout, g *dag.DAG, opts Options) (map[string][]byte, error) { + if graphLayout.IsNodelink() { + opts.VizType = graph.VizTypeNodelink + return RenderNodelink(graphLayout, opts) + } + + // Convert to internal tower layout + l, err := layout.Parse(graphLayout) + if err != nil { + return nil, fmt.Errorf("convert layout: %w", err) + } + + // renderTower applies layout metadata via applyLayoutMetadata + return renderTower(l, g, opts) +} diff --git a/pkg/pipeline/runner.go b/pkg/pipeline/runner.go new file mode 100644 index 0000000..d3334d8 --- /dev/null +++ b/pkg/pipeline/runner.go @@ -0,0 +1,296 @@ +package pipeline + +import ( + "bytes" + "context" + "fmt" + "time" + + "github.com/charmbracelet/log" + + "github.com/matzehuels/stacktower/pkg/cache" + "github.com/matzehuels/stacktower/pkg/core/dag" + dagtransform "github.com/matzehuels/stacktower/pkg/core/dag/transform" + "github.com/matzehuels/stacktower/pkg/graph" +) + +// Runner encapsulates pipeline execution with caching. +// Both CLI and API can use this to avoid duplicating caching logic. +// +// The Runner is stateless except for the cache and logger - it doesn't +// store pipeline results. Multiple goroutines can safely use the same +// Runner with different options. +type Runner struct { + Cache cache.Cache + Keyer cache.Keyer + Logger *log.Logger +} + +// NewRunner creates a runner with the given cache and keyer. +// If keyer is nil, a DefaultKeyer is used. +// If cache is nil, a NullCache is used (caching disabled). +func NewRunner(c cache.Cache, keyer cache.Keyer, logger *log.Logger) *Runner { + if keyer == nil { + keyer = cache.NewDefaultKeyer() + } + if c == nil { + c = cache.NewNullCache() + } + if logger == nil { + logger = log.Default() + } + return &Runner{ + Cache: c, + Keyer: keyer, + Logger: logger, + } +} + +// Execute runs the complete parse → layout → render pipeline with caching. +func (r *Runner) Execute(ctx context.Context, opts Options) (*Result, error) { + if err := opts.ValidateAndSetDefaults(); err != nil { + return nil, fmt.Errorf("invalid options: %w", err) + } + r.applyLogger(&opts) + + result := &Result{ + Artifacts: make(map[string][]byte), + } + + // Stage 1: Parse + parseStart := time.Now() + g, parseHit, err := r.ParseWithCacheInfo(ctx, opts) + if err != nil { + return nil, fmt.Errorf("parse: %w", err) + } + result.Graph = g + result.Stats.ParseTime = time.Since(parseStart) + result.Stats.NodeCount = g.NodeCount() + result.Stats.EdgeCount = g.EdgeCount() + result.CacheInfo.ParseHit = parseHit + + // Compute graph hash for cache keys and API responses + if graphData, err := graph.MarshalGraph(g); err == nil { + result.GraphHash = cache.Hash(graphData) + } + + r.Logger.Info("parsed dependencies", + "nodes", g.NodeCount(), + "edges", g.EdgeCount(), + "duration", result.Stats.ParseTime) + + // Apply normalization if requested (controlled by opts.Normalize) + workGraph := r.PrepareGraph(g, opts) + + // Stage 2: Layout + layoutStart := time.Now() + layout, layoutHit, err := r.GenerateLayoutWithCacheInfo(ctx, workGraph, opts) + if err != nil { + return nil, fmt.Errorf("layout: %w", err) + } + result.Layout = layout + result.Stats.LayoutTime = time.Since(layoutStart) + result.CacheInfo.LayoutHit = layoutHit + + r.Logger.Info("computed layout", + "blocks", len(layout.Blocks), + "duration", result.Stats.LayoutTime) + + // Stage 3: Render + renderStart := time.Now() + artifacts, renderHit, err := r.RenderWithCacheInfo(ctx, layout, workGraph, opts) + if err != nil { + return nil, fmt.Errorf("render: %w", err) + } + result.Artifacts = artifacts + result.Stats.RenderTime = time.Since(renderStart) + result.CacheInfo.RenderHit = renderHit + + r.Logger.Info("rendered outputs", + "formats", opts.Formats, + "duration", result.Stats.RenderTime) + + return result, nil +} + +// ParseWithCacheInfo resolves dependencies with caching and returns cache hit info. +func (r *Runner) ParseWithCacheInfo(ctx context.Context, opts Options) (*dag.DAG, bool, error) { + if err := opts.ValidateForParse(); err != nil { + return nil, false, err + } + r.applyLogger(&opts) + + // Compute cache key + pkgOrManifest := opts.Package + if opts.Manifest != "" { + pkgOrManifest = cache.Hash([]byte(opts.Manifest)) + } + // Include enrichment status in cache key so enriched/non-enriched graphs are cached separately + enriched := opts.ShouldEnrich() && opts.GitHubToken != "" + cacheKey := r.Keyer.GraphKey(opts.Language, pkgOrManifest, cache.GraphKeyOpts{ + MaxDepth: opts.MaxDepth, + MaxNodes: opts.MaxNodes, + Enriched: enriched, + }) + + // Try cache first (unless refresh requested) + if !opts.Refresh { + if data, hit, err := r.Cache.Get(ctx, cacheKey); err == nil && hit { + g, err := graph.ReadGraph(bytes.NewReader(data)) + if err == nil { + return g, true, nil // Cache hit + } + } + } + + // Parse + g, err := Parse(ctx, r.Cache, opts) + if err != nil { + return nil, false, err + } + + // Cache the result + if !opts.Refresh { + if data, err := graph.MarshalGraph(g); err == nil { + _ = r.Cache.Set(ctx, cacheKey, data, cache.TTLGraph) + } + } + + return g, false, nil // Cache miss +} + +// Parse is a convenience wrapper that calls ParseWithCacheInfo and discards the cache hit info. +func (r *Runner) Parse(ctx context.Context, opts Options) (*dag.DAG, error) { + g, _, err := r.ParseWithCacheInfo(ctx, opts) + return g, err +} + +// GenerateLayoutWithCacheInfo generates a layout with caching and returns cache hit info. +func (r *Runner) GenerateLayoutWithCacheInfo(ctx context.Context, g *dag.DAG, opts Options) (graph.Layout, bool, error) { + if err := opts.ValidateForLayout(); err != nil { + return graph.Layout{}, false, err + } + r.applyLogger(&opts) + + // Compute cache key + graphData, _ := graph.MarshalGraph(g) + graphHash := cache.Hash(graphData) + cacheKey := r.Keyer.LayoutKey(graphHash, opts.LayoutKeyOpts()) + + // Try cache first + if data, hit, err := r.Cache.Get(ctx, cacheKey); err == nil && hit { + cached, err := graph.UnmarshalLayout(data) + if err == nil { + return cached, true, nil // Cache hit + } + // If deserialization fails, fall through to recompute + } + + // Generate layout + layout, err := GenerateLayout(g, opts) + if err != nil { + return graph.Layout{}, false, err + } + + // Cache the result + if data, err := graph.MarshalLayout(layout); err == nil { + _ = r.Cache.Set(ctx, cacheKey, data, cache.TTLLayout) + } + + return layout, false, nil // Cache miss +} + +// GenerateLayout is a convenience wrapper that calls GenerateLayoutWithCacheInfo and discards the cache hit info. +func (r *Runner) GenerateLayout(ctx context.Context, g *dag.DAG, opts Options) (graph.Layout, error) { + layout, _, err := r.GenerateLayoutWithCacheInfo(ctx, g, opts) + return layout, err +} + +// RenderWithCacheInfo generates artifacts with caching and returns cache hit info. +func (r *Runner) RenderWithCacheInfo(ctx context.Context, layout graph.Layout, g *dag.DAG, opts Options) (map[string][]byte, bool, error) { + if err := opts.ValidateForRender(); err != nil { + return nil, false, err + } + r.applyLogger(&opts) + + // Compute cache key from layout data + layoutData, err := graph.MarshalLayout(layout) + if err != nil { + return nil, false, fmt.Errorf("serialize layout for cache key: %w", err) + } + cacheKeyHash := cache.Hash(layoutData) + + // Try to get all formats from cache + allCached := true + artifacts := make(map[string][]byte) + + for _, format := range opts.Formats { + cacheKey := r.Keyer.ArtifactKey(cacheKeyHash, opts.ArtifactKeyOpts(format)) + if data, hit, err := r.Cache.Get(ctx, cacheKey); err == nil && hit { + artifacts[format] = data + } else { + allCached = false + break + } + } + + if allCached && len(artifacts) == len(opts.Formats) { + return artifacts, true, nil // All artifacts from cache + } + + // Render all formats + rendered, err := RenderFromLayout(layout, g, opts) + if err != nil { + return nil, false, err + } + + // Cache each format + for format, data := range rendered { + cacheKey := r.Keyer.ArtifactKey(cacheKeyHash, opts.ArtifactKeyOpts(format)) + _ = r.Cache.Set(ctx, cacheKey, data, cache.TTLArtifact) + } + + return rendered, false, nil // Cache miss +} + +// Render is a convenience wrapper that calls RenderWithCacheInfo and discards the cache hit info. +func (r *Runner) Render(ctx context.Context, layout graph.Layout, g *dag.DAG, opts Options) (map[string][]byte, error) { + artifacts, _, err := r.RenderWithCacheInfo(ctx, layout, g, opts) + return artifacts, err +} + +// GenerateNodelinkLayout generates a nodelink layout from a graph. +// +// Deprecated: Use GenerateLayout instead. +func GenerateNodelinkLayout(g *dag.DAG, opts Options) (graph.Layout, error) { + return generateNodelinkLayout(g, opts) +} + +// PrepareGraph applies normalization if opts.Normalize is true. +// Returns the original graph if normalization is disabled. +func (r *Runner) PrepareGraph(g *dag.DAG, opts Options) *dag.DAG { + if opts.Normalize { + workGraph := g.Clone() + dagtransform.Normalize(workGraph) + r.Logger.Debug("normalized graph", + "original_nodes", g.NodeCount(), + "normalized_nodes", workGraph.NodeCount()) + return workGraph + } + return g +} + +// Close releases resources held by the runner (primarily the cache). +func (r *Runner) Close() error { + if r.Cache != nil { + return r.Cache.Close() + } + return nil +} + +// applyLogger sets the runner's logger on options if not already set. +func (r *Runner) applyLogger(opts *Options) { + if opts.Logger == nil { + opts.Logger = r.Logger + } +} diff --git a/pkg/render/nodelink/doc.go b/pkg/render/nodelink/doc.go deleted file mode 100644 index dea6605..0000000 --- a/pkg/render/nodelink/doc.go +++ /dev/null @@ -1,42 +0,0 @@ -// Package nodelink renders dependency graphs as traditional node-link diagrams. -// -// # Overview -// -// This package produces directed graph visualizations using Graphviz, where -// nodes appear as boxes connected by arrows. It's an alternative to the -// tower visualization for cases where a traditional diagram is preferred. -// -// # Usage -// -// Convert a DAG to DOT format, then render to SVG: -// -// dot := nodelink.ToDOT(g, nodelink.Options{Detailed: false}) -// svg, err := nodelink.RenderSVG(dot) -// -// For PDF or PNG output, use the render functions: -// -// pdf, err := nodelink.RenderPDF(dot) -// png, err := nodelink.RenderPNG(dot, 2.0) // 2x scale -// -// # Options -// -// The [Options] struct controls diagram generation: -// -// - Detailed: When true, node labels include all metadata (row, version, etc.) -// -// # DOT Format -// -// The [ToDOT] function produces Graphviz DOT source that can be: -// -// - Rendered directly via [RenderSVG] -// - Saved and processed with external Graphviz tools -// - Customized before rendering -// -// The generated DOT uses top-to-bottom layout (rankdir=TB) with rounded -// box nodes, matching the tower visualization's vertical orientation. -// -// # Dependencies -// -// This package uses [github.com/goccy/go-graphviz] for in-process SVG -// rendering. PDF and PNG conversion requires librsvg (rsvg-convert). -package nodelink diff --git a/pkg/render/tower/feature/brittle.go b/pkg/render/tower/feature/brittle.go deleted file mode 100644 index af7826f..0000000 --- a/pkg/render/tower/feature/brittle.go +++ /dev/null @@ -1,72 +0,0 @@ -package feature - -import ( - "time" - - "github.com/matzehuels/stacktower/pkg/dag" -) - -const ( - abandonedThreshold = 2 * 365 * 24 * time.Hour - staleThreshold = 1 * 365 * 24 * time.Hour - lowStarCount = 100 - minMaintainerCount = 2 -) - -// IsBrittle returns true if a node represents a package that is potentially -// unmaintained or risky to depend on. It checks for archived repositories, -// long periods of inactivity, and low maintainer counts. -func IsBrittle(n *dag.Node) bool { - if n == nil || n.Meta == nil { - return false - } - if archived, _ := n.Meta["repo_archived"].(bool); archived { - return true - } - - lastCommit := ParseDate(n.Meta["repo_last_commit"]) - if lastCommit.IsZero() { - return false - } - - age := time.Since(lastCommit) - if age > abandonedThreshold { - return true - } - if age <= staleThreshold { - return false - } - - maintainers := CountMaintainers(n.Meta["repo_maintainers"]) - stars, _ := n.Meta["repo_stars"].(int) - return maintainers == 1 || stars < lowStarCount || maintainers <= minMaintainerCount -} - -func ParseDate(v any) time.Time { - s, ok := v.(string) - if !ok || s == "" { - return time.Time{} - } - t, _ := time.Parse("2006-01-02", s) - return t -} - -func CountMaintainers(v any) int { - switch v := v.(type) { - case []string: - return len(v) - case []any: - return len(v) - } - return 0 -} - -func AsInt(v any) int { - switch v := v.(type) { - case int: - return v - case float64: - return int(v) - } - return 0 -} diff --git a/pkg/render/tower/sink/json.go b/pkg/render/tower/sink/json.go deleted file mode 100644 index 1103550..0000000 --- a/pkg/render/tower/sink/json.go +++ /dev/null @@ -1,256 +0,0 @@ -package sink - -import ( - "encoding/json" - - "github.com/matzehuels/stacktower/pkg/dag" - "github.com/matzehuels/stacktower/pkg/render/tower/feature" - "github.com/matzehuels/stacktower/pkg/render/tower/layout" -) - -// JSONOption configures JSON rendering via [RenderJSON]. -type JSONOption func(*jsonRenderer) - -type jsonRenderer struct { - graph *dag.DAG - merged bool - randomize bool - seed uint64 - style string - nebraska []feature.NebraskaRanking -} - -// WithJSONGraph attaches the DAG for metadata enrichment (URLs, brittle flags, -// auxiliary/synthetic flags). Without this, blocks will have minimal metadata. -func WithJSONGraph(g *dag.DAG) JSONOption { return func(r *jsonRenderer) { r.graph = g } } - -// WithJSONMerged marks that the layout uses merged subdividers. This ensures the -// JSON correctly represents subdivider relationships. -func WithJSONMerged() JSONOption { return func(r *jsonRenderer) { r.merged = true } } - -// WithJSONRandomize records the randomization seed in the JSON output, enabling -// reproducible re-rendering with the same visual jitter. -func WithJSONRandomize(seed uint64) JSONOption { - return func(r *jsonRenderer) { r.randomize = true; r.seed = seed } -} - -// WithJSONStyle records the style name (e.g., "simple", "handdrawn") in the JSON output -// for documentation or round-trip rendering. -func WithJSONStyle(s string) JSONOption { return func(r *jsonRenderer) { r.style = s } } - -// WithJSONNebraska includes Nebraska ranking data in the JSON output. Rankings should -// come from [feature.RankNebraska]. -func WithJSONNebraska(rankings []feature.NebraskaRanking) JSONOption { - return func(r *jsonRenderer) { r.nebraska = rankings } -} - -type jsonOutput struct { - Width float64 `json:"width"` - Height float64 `json:"height"` - MarginX float64 `json:"margin_x"` - MarginY float64 `json:"margin_y"` - Style string `json:"style,omitempty"` - Seed uint64 `json:"seed,omitempty"` - Randomize bool `json:"randomize,omitempty"` - Merged bool `json:"merged,omitempty"` - Rows map[int][]string `json:"rows,omitempty"` - Blocks []jsonBlock `json:"blocks"` - Edges []jsonEdge `json:"edges,omitempty"` - Nebraska []jsonNebraska `json:"nebraska,omitempty"` -} - -type jsonBlock struct { - ID string `json:"id"` - Label string `json:"label"` - X float64 `json:"x"` - Y float64 `json:"y"` - Width float64 `json:"width"` - Height float64 `json:"height"` - URL string `json:"url,omitempty"` - Brittle bool `json:"brittle,omitempty"` - Auxiliary bool `json:"auxiliary,omitempty"` - Synthetic bool `json:"synthetic,omitempty"` - Meta *jsonMeta `json:"meta,omitempty"` -} - -type jsonMeta struct { - Description string `json:"description,omitempty"` - Stars int `json:"stars,omitempty"` - LastCommit string `json:"last_commit,omitempty"` - LastRelease string `json:"last_release,omitempty"` - Archived bool `json:"archived,omitempty"` -} - -type jsonEdge struct { - From string `json:"from"` - To string `json:"to"` -} - -type jsonNebraska struct { - Maintainer string `json:"maintainer"` - Score float64 `json:"score"` - Packages []jsonNebPackage `json:"packages"` -} - -type jsonNebPackage struct { - Package string `json:"package"` - Role string `json:"role"` // "owner", "lead", or "maintainer" - URL string `json:"url,omitempty"` -} - -// RenderJSON exports the layout and associated metadata as a pretty-printed JSON document. -// This is the primary data interchange format for Stacktower, enabling: -// -// - Integration with external visualization tools -// - Caching computed layouts for fast re-rendering -// - Round-trip rendering (re-import and render identically) -// -// The JSON includes: -// - Block positions and dimensions -// - Row orderings (for reconstructing the layout) -// - Metadata (URLs, stars, dates, auxiliary/synthetic flags) -// - Optional Nebraska rankings -// - Render options (style, seed, merged flag) for reproducibility -// -// RenderJSON returns an error only if JSON marshaling fails (should not happen -// with well-formed layouts). It does not modify l or the DAG, and is safe to call -// concurrently. -func RenderJSON(l layout.Layout, opts ...JSONOption) ([]byte, error) { - r := jsonRenderer{} - for _, opt := range opts { - opt(&r) - } - - out := jsonOutput{ - Width: l.FrameWidth, - Height: l.FrameHeight, - MarginX: l.MarginX, - MarginY: l.MarginY, - Style: r.style, - Seed: r.seed, - Randomize: r.randomize, - Merged: r.merged, - Rows: l.RowOrders, - Blocks: buildJSONBlocks(l, r.graph), - } - - if r.graph != nil { - out.Edges = buildJSONEdges(l, r.graph, r.merged) - } - - if len(r.nebraska) > 0 { - out.Nebraska = buildJSONNebraska(r.nebraska) - } - - return json.MarshalIndent(out, "", " ") -} - -func buildJSONBlocks(l layout.Layout, g *dag.DAG) []jsonBlock { - blocks := make([]jsonBlock, 0, len(l.Blocks)) - for id, b := range l.Blocks { - jb := jsonBlock{ - ID: id, - Label: b.NodeID, - X: b.Left, - Y: b.Bottom, - Width: b.Width(), - Height: b.Height(), - } - if g != nil { - if n, ok := g.Node(id); ok { - jb.Auxiliary = n.IsAuxiliary() - jb.Synthetic = n.IsSynthetic() - if n.Meta != nil { - jb.URL, _ = n.Meta["repo_url"].(string) - jb.Brittle = feature.IsBrittle(n) - jb.Meta = extractJSONMeta(n) - } - } - } - blocks = append(blocks, jb) - } - return blocks -} - -func extractJSONMeta(n *dag.Node) *jsonMeta { - if n.Meta == nil { - return nil - } - m := &jsonMeta{ - Stars: feature.AsInt(n.Meta["repo_stars"]), - } - m.LastCommit, _ = n.Meta["repo_last_commit"].(string) - m.LastRelease, _ = n.Meta["repo_last_release"].(string) - m.Archived, _ = n.Meta["repo_archived"].(bool) - - if desc, ok := n.Meta["description"].(string); ok && desc != "" { - m.Description = desc - } else if summary, ok := n.Meta["summary"].(string); ok && summary != "" { - m.Description = summary - } - - if m.Description == "" && m.Stars == 0 && m.LastCommit == "" && m.LastRelease == "" && !m.Archived { - return nil - } - return m -} - -func buildJSONEdges(l layout.Layout, g *dag.DAG, merged bool) []jsonEdge { - if merged { - return buildJSONMergedEdges(l, g) - } - edges := make([]jsonEdge, 0) - for _, e := range g.Edges() { - if _, ok := l.Blocks[e.From]; !ok { - continue - } - if _, ok := l.Blocks[e.To]; !ok { - continue - } - edges = append(edges, jsonEdge{From: e.From, To: e.To}) - } - return edges -} - -func buildJSONMergedEdges(l layout.Layout, g *dag.DAG) []jsonEdge { - masterOf := func(id string) string { - if n, ok := g.Node(id); ok && n.MasterID != "" { - return n.MasterID - } - return id - } - - type edgeKey struct{ from, to string } - seen := make(map[edgeKey]struct{}) - var edges []jsonEdge - - for _, e := range g.Edges() { - fromMaster, toMaster := masterOf(e.From), masterOf(e.To) - if fromMaster == toMaster { - continue - } - key := edgeKey{fromMaster, toMaster} - if _, exists := seen[key]; exists { - continue - } - seen[key] = struct{}{} - edges = append(edges, jsonEdge{From: fromMaster, To: toMaster}) - } - return edges -} - -func buildJSONNebraska(rankings []feature.NebraskaRanking) []jsonNebraska { - result := make([]jsonNebraska, len(rankings)) - for i, r := range rankings { - pkgs := make([]jsonNebPackage, len(r.Packages)) - for j, p := range r.Packages { - pkgs[j] = jsonNebPackage{Package: p.Package, Role: string(p.Role), URL: p.URL} - } - result[i] = jsonNebraska{ - Maintainer: r.Maintainer, - Score: r.Score, - Packages: pkgs, - } - } - return result -} diff --git a/pkg/render/tower/sink/json_test.go b/pkg/render/tower/sink/json_test.go deleted file mode 100644 index b20826a..0000000 --- a/pkg/render/tower/sink/json_test.go +++ /dev/null @@ -1,373 +0,0 @@ -package sink - -import ( - "encoding/json" - "testing" - - "github.com/matzehuels/stacktower/pkg/dag" - "github.com/matzehuels/stacktower/pkg/render/tower/feature" - "github.com/matzehuels/stacktower/pkg/render/tower/layout" -) - -func TestRenderJSON(t *testing.T) { - l := layout.Layout{ - FrameWidth: 800, - FrameHeight: 600, - MarginX: 40, - MarginY: 30, - Blocks: map[string]layout.Block{ - "pkg-a": {NodeID: "pkg-a", Left: 40, Right: 200, Bottom: 30, Top: 100}, - "pkg-b": {NodeID: "pkg-b", Left: 200, Right: 400, Bottom: 100, Top: 200}, - }, - RowOrders: map[int][]string{ - 0: {"pkg-a"}, - 1: {"pkg-b"}, - }, - } - - data, err := RenderJSON(l) - if err != nil { - t.Fatalf("RenderJSON() error: %v", err) - } - - var out jsonOutput - if err := json.Unmarshal(data, &out); err != nil { - t.Fatalf("json.Unmarshal() error: %v", err) - } - - if out.Width != 800 { - t.Errorf("Width = %v, want 800", out.Width) - } - if out.Height != 600 { - t.Errorf("Height = %v, want 600", out.Height) - } - if out.MarginX != 40 { - t.Errorf("MarginX = %v, want 40", out.MarginX) - } - if out.MarginY != 30 { - t.Errorf("MarginY = %v, want 30", out.MarginY) - } - if len(out.Blocks) != 2 { - t.Errorf("Blocks count = %d, want 2", len(out.Blocks)) - } -} - -func TestRenderJSONWithOptions(t *testing.T) { - l := layout.Layout{ - FrameWidth: 400, - FrameHeight: 300, - Blocks: map[string]layout.Block{}, - } - - data, err := RenderJSON(l, - WithJSONStyle("handdrawn"), - WithJSONMerged(), - WithJSONRandomize(12345), - ) - if err != nil { - t.Fatalf("RenderJSON() error: %v", err) - } - - var out jsonOutput - if err := json.Unmarshal(data, &out); err != nil { - t.Fatalf("json.Unmarshal() error: %v", err) - } - - if out.Style != "handdrawn" { - t.Errorf("Style = %q, want %q", out.Style, "handdrawn") - } - if !out.Merged { - t.Error("Merged should be true") - } - if !out.Randomize { - t.Error("Randomize should be true") - } - if out.Seed != 12345 { - t.Errorf("Seed = %d, want 12345", out.Seed) - } -} - -func TestRenderJSONWithGraph(t *testing.T) { - g := dag.New(nil) - g.AddNode(dag.Node{ID: "a", Row: 0}) - g.AddNode(dag.Node{ID: "b", Row: 1}) - g.AddEdge(dag.Edge{From: "a", To: "b"}) - - l := layout.Layout{ - FrameWidth: 400, - FrameHeight: 300, - Blocks: map[string]layout.Block{ - "a": {NodeID: "a", Left: 0, Right: 100, Bottom: 0, Top: 50}, - "b": {NodeID: "b", Left: 0, Right: 100, Bottom: 50, Top: 100}, - }, - } - - data, err := RenderJSON(l, WithJSONGraph(g)) - if err != nil { - t.Fatalf("RenderJSON() error: %v", err) - } - - var out jsonOutput - if err := json.Unmarshal(data, &out); err != nil { - t.Fatalf("json.Unmarshal() error: %v", err) - } - - if len(out.Edges) != 1 { - t.Errorf("Edges count = %d, want 1", len(out.Edges)) - } - if len(out.Edges) > 0 { - if out.Edges[0].From != "a" || out.Edges[0].To != "b" { - t.Errorf("Edge = {%q -> %q}, want {a -> b}", out.Edges[0].From, out.Edges[0].To) - } - } -} - -func TestRenderJSONWithMergedEdges(t *testing.T) { - g := dag.New(nil) - g.AddNode(dag.Node{ID: "a", Row: 0}) - g.AddNode(dag.Node{ID: "a-sub", Row: 1, MasterID: "a"}) - g.AddNode(dag.Node{ID: "b", Row: 2}) - g.AddEdge(dag.Edge{From: "a", To: "a-sub"}) - g.AddEdge(dag.Edge{From: "a-sub", To: "b"}) - - l := layout.Layout{ - FrameWidth: 400, - FrameHeight: 300, - Blocks: map[string]layout.Block{ - "a": {NodeID: "a", Left: 0, Right: 100, Bottom: 0, Top: 50}, - "a-sub": {NodeID: "a-sub", Left: 0, Right: 100, Bottom: 50, Top: 100}, - "b": {NodeID: "b", Left: 0, Right: 100, Bottom: 100, Top: 150}, - }, - } - - data, err := RenderJSON(l, WithJSONGraph(g), WithJSONMerged()) - if err != nil { - t.Fatalf("RenderJSON() error: %v", err) - } - - var out jsonOutput - if err := json.Unmarshal(data, &out); err != nil { - t.Fatalf("json.Unmarshal() error: %v", err) - } - - // Merged edges should skip self-edges from subdivider nodes - // Edge from a-sub to b should be converted to a->b - found := false - for _, e := range out.Edges { - if e.From == "a" && e.To == "b" { - found = true - } - // Should not have edge from a to a (self-edge via subdivider) - if e.From == e.To { - t.Errorf("Found self-edge: %q -> %q", e.From, e.To) - } - } - if !found && len(out.Edges) > 0 { - t.Logf("Edges: %+v", out.Edges) - } -} - -func TestRenderJSONWithNebraska(t *testing.T) { - rankings := []feature.NebraskaRanking{ - { - Maintainer: "maintainer1", - Score: 10.5, - Packages: []feature.PackageRole{ - {Package: "pkg-a", Role: feature.RoleOwner, URL: "https://github.com/a"}, - {Package: "pkg-b", Role: feature.RoleMaintainer, URL: ""}, - }, - }, - } - - l := layout.Layout{ - FrameWidth: 400, - FrameHeight: 300, - Blocks: map[string]layout.Block{}, - } - - data, err := RenderJSON(l, WithJSONNebraska(rankings)) - if err != nil { - t.Fatalf("RenderJSON() error: %v", err) - } - - var out jsonOutput - if err := json.Unmarshal(data, &out); err != nil { - t.Fatalf("json.Unmarshal() error: %v", err) - } - - if len(out.Nebraska) != 1 { - t.Fatalf("Nebraska count = %d, want 1", len(out.Nebraska)) - } - - neb := out.Nebraska[0] - if neb.Maintainer != "maintainer1" { - t.Errorf("Maintainer = %q, want %q", neb.Maintainer, "maintainer1") - } - if neb.Score != 10.5 { - t.Errorf("Score = %v, want 10.5", neb.Score) - } - if len(neb.Packages) != 2 { - t.Errorf("Packages count = %d, want 2", len(neb.Packages)) - } -} - -func TestRenderJSONWithNodeMeta(t *testing.T) { - g := dag.New(nil) - g.AddNode(dag.Node{ - ID: "pkg", - Row: 0, - Meta: dag.Metadata{ - "description": "A test package", - "repo_url": "https://github.com/test/pkg", - "repo_stars": 1000, - "repo_last_commit": "2024-01-01", - "repo_last_release": "v1.0.0", - "repo_archived": false, - }, - }) - - l := layout.Layout{ - FrameWidth: 400, - FrameHeight: 300, - Blocks: map[string]layout.Block{ - "pkg": {NodeID: "pkg", Left: 0, Right: 100, Bottom: 0, Top: 50}, - }, - } - - data, err := RenderJSON(l, WithJSONGraph(g)) - if err != nil { - t.Fatalf("RenderJSON() error: %v", err) - } - - var out jsonOutput - if err := json.Unmarshal(data, &out); err != nil { - t.Fatalf("json.Unmarshal() error: %v", err) - } - - if len(out.Blocks) != 1 { - t.Fatalf("Blocks count = %d, want 1", len(out.Blocks)) - } - - block := out.Blocks[0] - if block.URL != "https://github.com/test/pkg" { - t.Errorf("URL = %q, want %q", block.URL, "https://github.com/test/pkg") - } - if block.Meta == nil { - t.Fatal("Meta should not be nil") - } - if block.Meta.Description != "A test package" { - t.Errorf("Meta.Description = %q, want %q", block.Meta.Description, "A test package") - } - if block.Meta.Stars != 1000 { - t.Errorf("Meta.Stars = %d, want 1000", block.Meta.Stars) - } -} - -func TestExtractJSONMetaNilMeta(t *testing.T) { - n := &dag.Node{ID: "test", Meta: nil} - m := extractJSONMeta(n) - if m != nil { - t.Error("extractJSONMeta() should return nil for node with nil meta") - } -} - -func TestExtractJSONMetaEmptyMeta(t *testing.T) { - n := &dag.Node{ID: "test", Meta: dag.Metadata{}} - m := extractJSONMeta(n) - if m != nil { - t.Error("extractJSONMeta() should return nil for node with empty meaningful meta") - } -} - -func TestExtractJSONMetaWithSummary(t *testing.T) { - n := &dag.Node{ - ID: "test", - Meta: dag.Metadata{ - "summary": "A summary description", - }, - } - m := extractJSONMeta(n) - if m == nil { - t.Fatal("extractJSONMeta() should not return nil") - } - if m.Description != "A summary description" { - t.Errorf("Description = %q, want %q", m.Description, "A summary description") - } -} - -func TestBuildJSONEdgesFiltersInvalidBlocks(t *testing.T) { - g := dag.New(nil) - g.AddNode(dag.Node{ID: "a", Row: 0}) - g.AddNode(dag.Node{ID: "b", Row: 1}) - g.AddNode(dag.Node{ID: "c", Row: 2}) - g.AddEdge(dag.Edge{From: "a", To: "b"}) - g.AddEdge(dag.Edge{From: "b", To: "c"}) - - // Layout only has "a" and "b", not "c" - l := layout.Layout{ - Blocks: map[string]layout.Block{ - "a": {NodeID: "a"}, - "b": {NodeID: "b"}, - }, - } - - edges := buildJSONEdges(l, g, false) - - // Should only include a->b, not b->c (since c is not in layout) - if len(edges) != 1 { - t.Errorf("edges count = %d, want 1", len(edges)) - } - if len(edges) > 0 && (edges[0].From != "a" || edges[0].To != "b") { - t.Errorf("edge = {%q -> %q}, want {a -> b}", edges[0].From, edges[0].To) - } -} - -func TestWithJSONGraphOption(t *testing.T) { - g := dag.New(nil) - r := &jsonRenderer{} - opt := WithJSONGraph(g) - opt(r) - if r.graph != g { - t.Error("WithJSONGraph should set graph") - } -} - -func TestWithJSONMergedOption(t *testing.T) { - r := &jsonRenderer{} - opt := WithJSONMerged() - opt(r) - if !r.merged { - t.Error("WithJSONMerged should set merged=true") - } -} - -func TestWithJSONRandomizeOption(t *testing.T) { - r := &jsonRenderer{} - opt := WithJSONRandomize(42) - opt(r) - if !r.randomize { - t.Error("WithJSONRandomize should set randomize=true") - } - if r.seed != 42 { - t.Errorf("seed = %d, want 42", r.seed) - } -} - -func TestWithJSONStyleOption(t *testing.T) { - r := &jsonRenderer{} - opt := WithJSONStyle("custom") - opt(r) - if r.style != "custom" { - t.Errorf("style = %q, want %q", r.style, "custom") - } -} - -func TestWithJSONNebraskaOption(t *testing.T) { - rankings := []feature.NebraskaRanking{{Maintainer: "test"}} - r := &jsonRenderer{} - opt := WithJSONNebraska(rankings) - opt(r) - if len(r.nebraska) != 1 { - t.Errorf("nebraska count = %d, want 1", len(r.nebraska)) - } -} diff --git a/pkg/render/tower/sink/svg_nebraska.go b/pkg/render/tower/sink/svg_nebraska.go deleted file mode 100644 index 56a31fc..0000000 --- a/pkg/render/tower/sink/svg_nebraska.go +++ /dev/null @@ -1,126 +0,0 @@ -package sink - -import ( - "bytes" - "fmt" - "strings" - - "github.com/matzehuels/stacktower/pkg/render/tower/feature" - "github.com/matzehuels/stacktower/pkg/render/tower/styles" -) - -const ( - nebraskaPanelLandscape = 280.0 - nebraskaPanelPortrait = 540.0 - nebraskaPanelPadding = 24.0 - nebraskaTitleY = 40.0 - nebraskaUnderlineY = 16.0 - nebraskaEntryStartY = 80.0 - nebraskaEntryHeight = 140.0 - - fontFamily = `'Patrick Hand', 'Comic Sans MS', 'Bradley Hand', 'Segoe Script', sans-serif` -) - -const nebraskaCSS = ` - .maintainer-link text { cursor: pointer; } - .maintainer-link:hover text { text-decoration: underline; }` - -const nebraskaJS = ` - document.querySelectorAll('.maintainer-link').forEach(el => { - el.addEventListener('mouseenter', () => highlight(el.dataset.packages.split(','))); - el.addEventListener('mouseleave', clearHighlight); - }); - document.querySelectorAll('.package-entry').forEach(el => { - el.addEventListener('mouseenter', () => highlight([el.dataset.package])); - el.addEventListener('mouseleave', clearHighlight); - });` - -func calcNebraskaPanelHeight(w, h float64) float64 { - if h > w { - return nebraskaPanelPortrait - } - return nebraskaPanelLandscape -} - -func renderNebraskaPanel(buf *bytes.Buffer, frameWidth, frameHeight float64, rankings []feature.NebraskaRanking) { - panelY := frameHeight + nebraskaPanelPadding - centerX := frameWidth / 2 - - fmt.Fprintf(buf, ` Nebraska Guy Ranking`+"\n", - centerX, panelY+nebraskaTitleY, fontFamily) - fmt.Fprintf(buf, ` `+"\n", - centerX-128, panelY+nebraskaTitleY+nebraskaUnderlineY) - - numEntries := min(len(rankings), 5) - padding := 30.0 - isPortrait := frameHeight > frameWidth - - if isPortrait { - cols := 2 - availableWidth := frameWidth - 2*padding - entryWidth := availableWidth / float64(cols) - - for i := 0; i < numEntries; i++ { - row, col := i/cols, i%cols - var entryX float64 - if row == 2 && numEntries == 5 { - entryX = (frameWidth - entryWidth) / 2 - } else { - entryX = padding + float64(col)*entryWidth - } - entryY := panelY + nebraskaEntryStartY + float64(row)*nebraskaEntryHeight - renderNebraskaEntry(buf, rankings[i], i, entryX, entryY, entryWidth) - } - } else { - availableWidth := frameWidth - 2*padding - entryWidth := availableWidth / float64(numEntries) - entryY := panelY + nebraskaEntryStartY - - for i := 0; i < numEntries; i++ { - entryX := padding + float64(i)*entryWidth - renderNebraskaEntry(buf, rankings[i], i, entryX, entryY, entryWidth) - } - } -} - -const maxDisplayedPackages = 3 - -func renderNebraskaEntry(buf *bytes.Buffer, r feature.NebraskaRanking, idx int, x, y, width float64) { - allPkgIDs := make([]string, len(r.Packages)) - for j, p := range r.Packages { - allPkgIDs[j] = p.Package - } - - displayed := min(len(r.Packages), maxDisplayedPackages) - centerX := x + width/2 - - // Maintainer name with link - fmt.Fprintf(buf, ` `+"\n", - r.Maintainer, styles.EscapeXML(strings.Join(allPkgIDs, ","))) - fmt.Fprintf(buf, ` #%d @%s`+"\n", - centerX, y+20, fontFamily, idx+1, styles.EscapeXML(r.Maintainer)) - buf.WriteString(" \n") - - // Package names - lineY := y + 45 - for j := 0; j < displayed; j++ { - pkg := r.Packages[j].Package - displayPkg := pkg - // Truncate long package names for display - if len(displayPkg) > 25 { - displayPkg = displayPkg[:22] + "..." - } - fmt.Fprintf(buf, ` %s`+"\n", - styles.EscapeXML(pkg), centerX, lineY, fontFamily, styles.EscapeXML(displayPkg)) - lineY += 18 - } - if extra := len(r.Packages) - displayed; extra > 0 { - fmt.Fprintf(buf, ` +%d more`+"\n", - centerX, lineY, fontFamily, extra) - } -} - -func renderNebraskaScript(buf *bytes.Buffer) { - fmt.Fprintf(buf, " \n", nebraskaCSS) - fmt.Fprintf(buf, " \n", nebraskaJS) -} diff --git a/pkg/session/file.go b/pkg/session/file.go new file mode 100644 index 0000000..f2cfc2a --- /dev/null +++ b/pkg/session/file.go @@ -0,0 +1,171 @@ +package session + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "sync" + "time" +) + +// FileStore is a file-based session store for CLI applications. +// Sessions are stored as JSON files in a config directory. +type FileStore struct { + mu sync.RWMutex + baseDir string +} + +// NewFileStore creates a new file-based session store. +// If baseDir is empty, defaults to ~/.config/stacktower/sessions/ +func NewFileStore(baseDir string) (*FileStore, error) { + if baseDir == "" { + home, err := os.UserHomeDir() + if err != nil { + return nil, fmt.Errorf("get home dir: %w", err) + } + baseDir = filepath.Join(home, ".config", "stacktower", "sessions") + } + if err := os.MkdirAll(baseDir, 0700); err != nil { + return nil, fmt.Errorf("create session dir: %w", err) + } + return &FileStore{baseDir: baseDir}, nil +} + +func (s *FileStore) sessionPath(sessionID string) string { + return filepath.Join(s.baseDir, sessionID+".json") +} + +func (s *FileStore) Get(ctx context.Context, sessionID string) (*Session, error) { + s.mu.RLock() + defer s.mu.RUnlock() + + path := s.sessionPath(sessionID) + data, err := os.ReadFile(path) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, fmt.Errorf("read session file: %w", err) + } + + var sess Session + if err := json.Unmarshal(data, &sess); err != nil { + return nil, fmt.Errorf("parse session: %w", err) + } + + if sess.IsExpired() { + os.Remove(path) + return nil, nil + } + return &sess, nil +} + +func (s *FileStore) Set(ctx context.Context, sess *Session) error { + s.mu.Lock() + defer s.mu.Unlock() + + data, err := json.MarshalIndent(sess, "", " ") + if err != nil { + return fmt.Errorf("marshal session: %w", err) + } + + path := s.sessionPath(sess.ID) + if err := os.WriteFile(path, data, 0600); err != nil { + return fmt.Errorf("write session file: %w", err) + } + return nil +} + +func (s *FileStore) Delete(ctx context.Context, sessionID string) error { + s.mu.Lock() + defer s.mu.Unlock() + + path := s.sessionPath(sessionID) + if err := os.Remove(path); err != nil && !os.IsNotExist(err) { + return fmt.Errorf("remove session file: %w", err) + } + return nil +} + +func (s *FileStore) Cleanup(ctx context.Context) error { + s.mu.Lock() + defer s.mu.Unlock() + + entries, err := os.ReadDir(s.baseDir) + if err != nil { + return fmt.Errorf("read session dir: %w", err) + } + + now := time.Now() + for _, entry := range entries { + if entry.IsDir() || filepath.Ext(entry.Name()) != ".json" { + continue + } + path := filepath.Join(s.baseDir, entry.Name()) + data, err := os.ReadFile(path) + if err != nil { + continue + } + var sess Session + if err := json.Unmarshal(data, &sess); err != nil { + continue + } + if now.After(sess.ExpiresAt) { + os.Remove(path) + } + } + return nil +} + +func (s *FileStore) Close() error { return nil } + +// Path returns the base directory for session files. +func (s *FileStore) Path() string { + return s.baseDir +} + +var _ Store = (*FileStore)(nil) + +// ============================================================================= +// CLI convenience wrapper +// ============================================================================= + +const defaultCLISessionID = "github" + +// CLIStore wraps FileStore for simple CLI token storage. +type CLIStore struct { + store *FileStore + sessionID string +} + +// NewCLIStore creates a store for CLI token storage. +func NewCLIStore() (*CLIStore, error) { + store, err := NewFileStore("") + if err != nil { + return nil, err + } + return &CLIStore{store: store, sessionID: defaultCLISessionID}, nil +} + +// GetSession retrieves the CLI session. +func (c *CLIStore) GetSession(ctx context.Context) (*Session, error) { + return c.store.Get(ctx, c.sessionID) +} + +// SaveSession stores the CLI session. +func (c *CLIStore) SaveSession(ctx context.Context, sess *Session) error { + sess.ID = c.sessionID + return c.store.Set(ctx, sess) +} + +// DeleteSession removes the CLI session. +func (c *CLIStore) DeleteSession(ctx context.Context) error { + return c.store.Delete(ctx, c.sessionID) +} + +// Path returns the session file path. +func (c *CLIStore) Path() string { + return c.store.sessionPath(c.sessionID) +} diff --git a/pkg/session/session.go b/pkg/session/session.go new file mode 100644 index 0000000..f64e5ea --- /dev/null +++ b/pkg/session/session.go @@ -0,0 +1,193 @@ +// Package session provides session management for authenticated users. +// +// This package defines interfaces for session storage and OAuth state management, +// with implementations for different backends: +// - memory: In-memory storage for development/testing +// - redis: Redis-backed storage for production multi-instance deployments +// - file: File-based storage for CLI applications +// +// # Architecture +// +// Sessions store user authentication data (access tokens, user info) with +// automatic expiration. The Store interface supports: +// - Get/Set/Delete operations +// - Automatic expiration checking +// - Cleanup of expired sessions +// +// OAuth state tokens provide CSRF protection during the OAuth flow. The +// StateStore interface supports: +// - Token generation with TTL +// - Single-use validation (tokens are deleted after validation) +// +// # Usage +// +// Create a session store: +// +// // Development +// store := memory.NewStore() +// +// // Production +// store, err := redis.NewStore(ctx, redis.Config{ +// Addr: "localhost:6379", +// }) +// +// // CLI +// store, err := file.NewStore("") // Uses ~/.config/stacktower/sessions/ +// +// Manage sessions: +// +// // Create session +// sess, err := session.New(accessToken, user, session.DefaultTTL) +// if err != nil { +// return err +// } +// store.Set(ctx, sess) +// +// // Retrieve session +// sess, err := store.Get(ctx, sessionID) +// if err != nil { +// return err +// } +// if sess == nil || sess.IsExpired() { +// // Session not found or expired +// } +package session + +import ( + "context" + "crypto/rand" + "encoding/base64" + "errors" + "fmt" + "time" + + "github.com/matzehuels/stacktower/pkg/integrations/github" +) + +// Sentinel errors for session operations. +var ( + // ErrNotFound is returned when a session does not exist. + ErrNotFound = errors.New("not found") + + // ErrExpired is returned when a session has exceeded its TTL. + ErrExpired = errors.New("expired") + + // ErrInvalidState is returned when an OAuth state token is invalid or already used. + ErrInvalidState = errors.New("invalid or expired state token") +) + +// Session stores user session data. +type Session struct { + ID string `json:"id"` + AccessToken string `json:"access_token"` + User *github.User `json:"user"` + ExpiresAt time.Time `json:"expires_at"` + CreatedAt time.Time `json:"created_at"` +} + +// IsExpired returns true if the session has expired. +func (s *Session) IsExpired() bool { + return time.Now().After(s.ExpiresAt) +} + +// UserID returns a storage-compatible user identifier. +// Format: "github:{id}" to namespace by auth provider. +// This format is used in cache keys and document ownership. +func (s *Session) UserID() string { + if s == nil || s.User == nil { + return "" + } + return fmt.Sprintf("github:%d", s.User.ID) +} + +// Store is the interface for session storage backends. +type Store interface { + // Get retrieves a session by ID. + // Returns nil, nil if the session doesn't exist. + // Returns nil, ErrExpired if the session exists but has expired. + Get(ctx context.Context, sessionID string) (*Session, error) + + // Set stores a session. + Set(ctx context.Context, session *Session) error + + // Delete removes a session. + Delete(ctx context.Context, sessionID string) error + + // Cleanup removes expired sessions (optional, may be no-op for Redis). + Cleanup(ctx context.Context) error +} + +// StateStore manages OAuth state tokens for CSRF protection. +// State tokens are short-lived (typically 10 minutes) and single-use. +// For multi-instance deployments, use Redis to share state across instances. +type StateStore interface { + // Generate creates a new state token and stores it with the given TTL. + // Returns the generated state token. + Generate(ctx context.Context, ttl time.Duration) (string, error) + + // Validate checks if a state token is valid and removes it (single-use). + // Returns true if the token was valid and not expired. + Validate(ctx context.Context, state string) (bool, error) + + // Cleanup removes expired state tokens (optional, may be no-op for Redis). + Cleanup(ctx context.Context) error +} + +// Default durations. +const ( + // DefaultTTL is the default session duration. + DefaultTTL = 24 * time.Hour + + // DefaultStateTTL is the default OAuth state token duration. + DefaultStateTTL = 10 * time.Minute +) + +// GenerateID creates a cryptographically secure random session ID. +func GenerateID() (string, error) { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + return "", err + } + return base64.URLEncoding.EncodeToString(b), nil +} + +// GenerateState creates a cryptographically secure random state token. +func GenerateState() (string, error) { + return GenerateID() // Same implementation, different semantic meaning +} + +// New creates a new session with the given token and user. +func New(accessToken string, user *github.User, ttl time.Duration) (*Session, error) { + id, err := GenerateID() + if err != nil { + return nil, err + } + + now := time.Now() + return &Session{ + ID: id, + AccessToken: accessToken, + User: user, + ExpiresAt: now.Add(ttl), + CreatedAt: now, + }, nil +} + +// MockLocal creates a mock session for local development without authentication. +// This is used when --no-auth is enabled in standalone mode. +// The mock user has ID "local" and no GitHub access token. +func MockLocal() *Session { + now := time.Now() + return &Session{ + ID: "local-session", + AccessToken: "", // No token - can't make authenticated GitHub API calls + User: &github.User{ + ID: 0, + Login: "local", + Name: "Local User", + AvatarURL: "", + }, + ExpiresAt: now.Add(365 * 24 * time.Hour), // Never expires + CreatedAt: now, + } +} diff --git a/scripts/test_e2e.sh b/scripts/test_cli_e2e.sh similarity index 66% rename from scripts/test_e2e.sh rename to scripts/test_cli_e2e.sh index 159d2fe..08ac674 100755 --- a/scripts/test_e2e.sh +++ b/scripts/test_cli_e2e.sh @@ -3,11 +3,11 @@ set -euo pipefail readonly BIN="./bin/stacktower" readonly EXAMPLES_DIR="./examples" -readonly OUTPUT_DIR="./output" +readonly OUTPUT_DIR="./output/cli-e2e" readonly DEFAULT_MAX_DEPTH=10 readonly DEFAULT_MAX_NODES=200 -readonly REFRESH=${REFRESH:-false} +readonly NO_CACHE=${NO_CACHE:-false} # Render dimensions readonly RENDER_WIDTH=800 @@ -64,17 +64,19 @@ check_prerequisites() { run_parse_tests() { echo "" - echo "--- Parse Registry ---" + echo "--- Parse Registry (without normalization for render tests) ---" mkdir -p "$EXAMPLES_DIR/real" - test_parse python flask - test_parse python openai - test_parse rust serde - test_parse javascript yargs - test_parse ruby rspec - test_parse php symfony/console - test_parse java com.google.guava:guava - test_parse go github.com/spf13/cobra + # Parse WITHOUT normalization so render tests can show the difference + # between --normalize=false and --normalize=true + test_parse_unnormalized python flask + test_parse_unnormalized python openai + test_parse_unnormalized rust serde + test_parse_unnormalized javascript yargs + test_parse_unnormalized ruby rspec + test_parse_unnormalized php symfony/console + test_parse_unnormalized java com.google.guava:guava + test_parse_unnormalized go github.com/spf13/cobra echo "" echo "--- Parse Manifests ---" @@ -108,75 +110,99 @@ render_dag() { local name=$1 local input=$2 local suite=$3 - local dag_dir="$OUTPUT_DIR/$suite/$name" + local dag_dir="$OUTPUT_DIR/render/$suite/$name" echo -n " $name... " - mkdir -p "$dag_dir" + mkdir -p "$dag_dir"/{nodelink,tower} - # Nodelink normalized + # ========================================================================== + # Nodelink visualizations (all formats in single call) + # ========================================================================== + + # Nodelink normalized - all formats at once if ! $BIN render "$input" \ --type nodelink \ - --normalize=true \ - -o "$dag_dir/nodelink.svg" 2>&1 | filter_warnings; then + --normalize \ + --format svg,png,pdf \ + -o "$dag_dir/nodelink/normalized" 2>&1 | filter_warnings; then fail "nodelink render failed" fi - # Nodelink raw + # Nodelink raw (unnormalized) - SVG only if ! $BIN render "$input" \ --type nodelink \ --normalize=false \ - -o "$dag_dir/nodelink_raw.svg" 2>&1 | filter_warnings; then + -o "$dag_dir/nodelink/raw.svg" 2>&1 | filter_warnings; then fail "nodelink raw render failed" fi - # Tower simple + # ========================================================================== + # Tower visualizations (all formats in single call) + # ========================================================================== + + # Tower simple (unmerged) - all formats at once if ! $BIN render "$input" \ --type tower \ - --normalize=true \ + --normalize \ --width "$RENDER_WIDTH" \ --height "$RENDER_HEIGHT" \ --edges \ --style simple \ - -o "$dag_dir/tower_simple.svg" 2>&1 | filter_warnings; then + --merge=false \ + --format svg,png,pdf \ + -o "$dag_dir/tower/simple" 2>&1 | filter_warnings; then fail "tower simple render failed" fi - # Tower simple merged + # Tower merged - SVG only (merged is the default) if ! $BIN render "$input" \ --type tower \ - --normalize=true \ + --normalize \ --width "$RENDER_WIDTH" \ --height "$RENDER_HEIGHT" \ --edges \ --style simple \ - --merge \ - -o "$dag_dir/tower_simple_merged.svg" 2>&1 | filter_warnings; then - fail "tower simple merged render failed" + -o "$dag_dir/tower/merged.svg" 2>&1 | filter_warnings; then + fail "tower merged render failed" fi - # Tower handdrawn + # Tower handdrawn - all formats at once (merged by default) if ! $BIN render "$input" \ --type tower \ - --normalize=true \ + --normalize \ --width "$RENDER_WIDTH" \ --height "$RENDER_HEIGHT" \ --style handdrawn \ --randomize \ - --merge \ - -o "$dag_dir/tower_handdrawn.svg" 2>&1 | filter_warnings; then + --format svg,png,pdf \ + -o "$dag_dir/tower/handdrawn" 2>&1 | filter_warnings; then fail "tower handdrawn render failed" fi - # Validate outputs exist and have expected elements - validate_svg "$dag_dir/nodelink.svg" - validate_svg "$dag_dir/nodelink_raw.svg" - validate_svg "$dag_dir/tower_simple.svg" "rect" - validate_svg "$dag_dir/tower_simple_merged.svg" "rect" - validate_svg "$dag_dir/tower_handdrawn.svg" "path" - - # Create combined view - combine_svgs "$dag_dir" "$name" + # ========================================================================== + # Validation + # ========================================================================== + + # Validate SVG outputs + validate_svg "$dag_dir/nodelink/normalized.svg" + validate_svg "$dag_dir/nodelink/raw.svg" + validate_svg "$dag_dir/tower/simple.svg" "rect" + validate_svg "$dag_dir/tower/merged.svg" "rect" + validate_svg "$dag_dir/tower/handdrawn.svg" "path" + + # Validate PNG outputs exist + validate_file "$dag_dir/nodelink/normalized.png" + validate_file "$dag_dir/tower/simple.png" + validate_file "$dag_dir/tower/handdrawn.png" + + # Validate PDF outputs exist + validate_file "$dag_dir/nodelink/normalized.pdf" + validate_file "$dag_dir/tower/simple.pdf" + validate_file "$dag_dir/tower/handdrawn.pdf" + + # Create combined view (for backward compatibility) + create_combined_svg "$dag_dir" "$name" echo "OK" } @@ -218,17 +244,17 @@ get_svg_content() { } # Create a combined SVG with all variants side by side -combine_svgs() { +create_combined_svg() { local dag_dir=$1 local name=$2 local output="$dag_dir/combined.svg" local files=( - "$dag_dir/nodelink_raw.svg" - "$dag_dir/nodelink.svg" - "$dag_dir/tower_simple.svg" - "$dag_dir/tower_simple_merged.svg" - "$dag_dir/tower_handdrawn.svg" + "$dag_dir/nodelink/raw.svg" + "$dag_dir/nodelink/normalized.svg" + "$dag_dir/tower/simple.svg" + "$dag_dir/tower/merged.svg" + "$dag_dir/tower/handdrawn.svg" ) local labels=("(a) graph" "(b) reduced graph" "(c) stacktower" "(d) merged stacktower" "(e) final stacked tower") @@ -314,12 +340,50 @@ validate_svg() { done } +validate_file() { + local file=$1 + if [[ ! -s "$file" ]]; then + fail "output missing or empty: $file" + fi +} + test_parse() { local lang=$1 local pkg=$2 local depth=${3:-$DEFAULT_MAX_DEPTH} local nodes=${4:-$DEFAULT_MAX_NODES} - local refresh=${REFRESH:-true} + local no_cache=${NO_CACHE:-false} + # Extract basename and replace colons with underscores (colons not allowed in filenames) + local basename="${pkg##*/}" + basename="${basename//:/_}" + local output="$OUTPUT_DIR/parse/registry/${lang}/${basename}.json" + + mkdir -p "$OUTPUT_DIR/parse/registry/${lang}" + + echo -n " $lang/$pkg... " + + local cache_flag="" + if [[ "$no_cache" == "true" ]]; then + cache_flag="--no-cache" + fi + + if ! $BIN parse "$lang" "$pkg" \ + --max-depth "$depth" \ + --max-nodes "$nodes" \ + $cache_flag \ + -o "$output" 2>&1 | filter_warnings; then + fail "parse returned error" + fi + + validate_json "$output" + echo "OK" +} + +test_parse_unnormalized() { + local lang=$1 + local pkg=$2 + local depth=${3:-$DEFAULT_MAX_DEPTH} # Smaller depth for render tests + local nodes=${4:-$DEFAULT_MAX_NODES} # Smaller graph for render tests # Extract basename and replace colons with underscores (colons not allowed in filenames) local basename="${pkg##*/}" basename="${basename//:/_}" @@ -327,11 +391,10 @@ test_parse() { echo -n " $lang/$pkg... " + # Parse produces raw graphs (normalization happens during layout/render) if ! $BIN parse "$lang" "$pkg" \ - --enrich \ --max-depth "$depth" \ --max-nodes "$nodes" \ - --refresh="$refresh" \ -o "$output" 2>&1 | filter_warnings; then fail "parse returned error" fi @@ -346,7 +409,7 @@ test_manifest() { local depth=${3:-$DEFAULT_MAX_DEPTH} local nodes=${4:-$DEFAULT_MAX_NODES} local manifest_path="$EXAMPLES_DIR/manifest/$file" - local output="$OUTPUT_DIR/manifest/${file%.*}.json" + local output="$OUTPUT_DIR/parse/manifest/${lang}/${file%.*}.json" echo -n " $lang/$file... " @@ -354,10 +417,9 @@ test_manifest() { fail "manifest not found: $manifest_path" fi - mkdir -p "$OUTPUT_DIR/manifest" + mkdir -p "$OUTPUT_DIR/parse/manifest/${lang}" if ! $BIN parse "$lang" "$manifest_path" \ - --enrich \ --max-depth "$depth" \ --max-nodes "$nodes" \ -o "$output" 2>&1 | filter_warnings; then