defmodule DpExchangeGemini.MixProject do use Mix.Project # SEED, not a release. CI increments the last segment of whatever it finds here, so # `0.1.0` first publishes as `0.1.1`. Hand-editing this to `0.2.0` is how a breaking # change is signalled. The bump script matches the attribute assignment below by its # exact literal form — do not reformat it, and do not repeat that form anywhere else # in this file, comments included, or the script will rewrite the wrong line. @version "0.2.30" @source_url "https://github.com/DistortionPoint/dp-exchange-gemini" def project do [ app: :dp_exchange_gemini, version: @version, elixir: "~> 1.18", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, deps: deps(), aliases: aliases(), dialyzer: dialyzer(), preferred_cli_env: preferred_cli_env(), test_coverage: test_coverage(), # Hex.pm name: "DpExchangeGemini", description: "EXPERIMENTAL — Gemini venue package for the DpExchange family. Market data, " <> "trading and streaming behind the shared DpExchange.Core.Venue facade.", package: package(), source_url: @source_url, docs: docs(), usage_rules: usage_rules() ] end # No `mod:` — a library does not start itself. A consumer supervises this venue # through `child_spec/1` and decides restart strategy, shutdown order and naming. A # consumer that has not asked for Gemini must not find a socket open. def application do [extra_applications: [:logger]] end defp elixirc_paths(:test), do: ["lib", "test/support"] defp elixirc_paths(_env), do: ["lib"] defp deps do [ # The contract. Three-part pin: while Core is 0.x a minor bump may break us, and # that is the signal it is meant to send. `0.1.53` is the floor because it is where # `Types.OrderBookDelta` shipped — `WsDecode.to_order_book_delta/2` decodes `depth` # and `depthFast` frames straight into it, and `Socket` forwards that value without # building a book here at all (see `socket.ex`'s moduledoc). A lower resolution has # no `OrderBookDelta` module to alias; `~> 0.1.48` previously allowed one and only # compiled here because CI always resolves the newest allowed version. # `0.2.6` is the floor now, and unlike the history below it is a HARD one: `Feed` # calls `Core.Fanout.max_queue_len!/2` in `init/1` and `Core.Fanout.deliver/4` on # every payload, and neither existed before 0.2.6. Against a lower Core this package # does not merely misbehave, it fails to compile — which is the good outcome, and the # reason the floor is stated rather than left to `script/check_dependency_floor.sh` to # discover. The older floor history is kept above because its lesson is the one that # keeps applying: a floor is only correct once it has been RESOLVED and compiled # against, never once it has been reasoned about. # # `0.3.1` is a MINOR bump, and the signal is deliberate: Core 0.3.0 deleted # `Core.DataProvider` and `Core.FeedBehaviour`, two contracts with zero implementers. # Nothing in this package referenced either, so there is no code change here — the # floor moves because a pin of `~> 0.2.8` would not resolve 0.3.x, which is the pin # doing its job rather than a problem to route around. Resolved and compiled against # before this line was written, per the lesson recorded below: a floor is only correct # once it has been RESOLVED, never once it has been reasoned about. # {:dp_exchange_core, "~> 0.3.3"}, # This venue's own transport. Core ships no transport library at any strength — # a venue that speaks WebSocket ships what it needs to speak it. {:websockex, "~> 0.4"}, {:jason, "~> 1.4"}, {:decimal, "~> 2.0"}, # Dev/Test {:usage_rules, "~> 1.2", only: :dev}, {:ex_doc, "~> 0.34", only: :dev, runtime: false}, {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}, {:sobelow, "~> 0.13", only: [:dev, :test], runtime: false}, # Exercises the REST pipeline through Req's test seam, so tier-1 tests reach no # network. Never ships. {:plug, "~> 1.16", only: :test} ] end defp test_coverage, do: [threshold: 90, ignore_modules: []] defp aliases do [quality: ["format --check-formatted", "credo --strict", "dialyzer", "sobelow --config"]] end defp dialyzer do [plt_add_apps: [:mix, :ex_unit], plt_file: {:no_warn, "priv/plts/dialyzer.plt"}] end defp preferred_cli_env, do: [quality: :test] defp package do [ licenses: ["MIT"], links: %{"GitHub" => @source_url}, maintainers: ["bcatherall"], # `files:` belongs HERE, not at the project level. Hex reads `package[:files]`; a # `files:` in `project/0` is silently ignored and Hex ships its own defaults — # which puts `priv/plts/dialyzer.plt` in the tarball and leaves out anything you # meant to add. Nothing warns. Inspect `mix hex.build` before every publish. # # `priv/` is absent because the only thing in it is the dialyzer PLT. # `config/` is absent: it governs this package's own dev and test, never a # consumer's. files: [ "lib", "mix.exs", ".formatter.exs", "README.md", "LICENSE", "CHANGELOG.md", "AGENTS.md", "usage-rules.md", "docs/reference" ] ] end defp docs do [ main: "DpExchange.Gemini", extras: ["README.md", "CHANGELOG.md", "usage-rules.md"], source_ref: "v#{@version}" ] end defp usage_rules, do: [file: "AGENTS.md", usage_rules: [:usage_rules]] end