// Compile-time proof that `SomniaMarketsClient` still matches the concept modules // it delegates to. // // THE PROBLEM THIS SOLVES. `SomniaMarketsClient` is a hand-written interface // mirroring ~105 delegations, and its doc comments are what TypeDoc publishes — // so it cannot simply be derived away. But hand-mirroring means a concept // function's signature can drift from the interface that fronts it, and nothing // notices: `createClient` satisfies the INTERFACE, and the interface is the thing // that drifted. CONVENTIONS.md's "Definition of done" documents the resulting tax // (three files to touch for one new read). // // So: keep the interface (and its docs), and assert the pairing HERE. A signature // that drifts becomes a `tsc` error instead of a silently-wrong published type. // // WHY THE ARITIES DIFFER. Every indexer read takes the client's config as trailing // arguments the client injects — `Fills.getFills(pool, opts, url)` fronted by // `client.getFills(pool, opts)`. `WithoutConfig` strips exactly that tail, so the // two shapes can be compared directly. // // This module exports nothing at runtime; it exists for its type errors. import type * as Activity from "./activity.js"; import type * as Balances from "./balances.js"; import type * as BinaryPortfolio from "./binary/portfolio.js"; import type * as BinarySettlement from "./binary/settlement.js"; import type * as Candles from "./candles.js"; import type * as Fees from "./fees.js"; import type * as Fills from "./fills.js"; import type * as Markets from "./markets.js"; import type * as Orders from "./orders.js"; import type * as PerpHistory from "./perp/history.js"; import type * as PerpState from "./perp/state.js"; import type * as PerpPortfolio from "./perp/portfolio.js"; import type * as Pools from "./pools.js"; import type * as Router from "./router.js"; import type * as SpotPortfolio from "./spot/portfolio.js"; import type * as SpotStops from "./spot/stops.js"; import type * as SyncStatus from "./syncStatus.js"; import type { SomniaMarketsClient } from "./somniaMarketsClient.js"; /** * Drops the trailing indexer-config arguments the client injects. * * **Details** * * Matches the three real shapes in this package by arity, LONGEST TAIL FIRST: * `(…args, url, headers)`, `(…args, url)`, and `(url)` alone. A function whose * tail does not look like config is returned unchanged, so a mismatch shows up * as a normal signature error rather than being silently rewritten. * * **Gotchas** * * Do NOT collapse the three patterns into one with a trailing optional * (`[...infer H, string, (Record | undefined)?]`): TypeScript * cannot match a variadic tuple whose last element is optional against a * fixed-arity signature, so that form silently strips NOTHING and every * assertion below passes vacuously — the file keeps compiling while checking * nothing. Verify any change here by checking `Parameters<>` arity, not just * assignability; assignability alone is what hid the bug. */ type WithoutConfig = F extends (...a: [...infer H, string, Record | undefined]) => infer R ? (...a: H) => R : F extends (...a: [...infer H, string, Record]) => infer R ? (...a: H) => R : F extends (...a: [...infer H, string]) => infer R ? (...a: H) => R : F; /** `true` when `Actual` is assignable to `Expected` (parameter/return compatible). */ type Matches = Actual extends Expected ? true : false; /** Compile error unless `T` is `true`. The assertion mechanism. */ type Assert = T; // --------------------------------------------------------------------------- // The pairings. Each line reads: "the client's method must accept what the // concept function accepts, minus the injected config". Add a line when you add // a read; the compiler then holds the pair together. // --------------------------------------------------------------------------- /* eslint-disable @typescript-eslint/no-unused-vars -- these types ARE the test */ // Markets type _listMarkets = Assert, SomniaMarketsClient["listMarkets"]>>; type _getMarket = Assert, SomniaMarketsClient["getMarket"]>>; type _countMarkets = Assert, SomniaMarketsClient["countMarkets"]>>; type _countMarketsBounded = Assert< Matches, SomniaMarketsClient["countMarketsBounded"]> >; type _countBinaryMarketsBounded = Assert< Matches, SomniaMarketsClient["countBinaryMarketsBounded"]> >; type _listBinaryMarkets = Assert< Matches, SomniaMarketsClient["listBinaryMarkets"]> >; type _listSpotMarkets = Assert< Matches, SomniaMarketsClient["listSpotMarkets"]> >; type _listPerpMarkets = Assert< Matches, SomniaMarketsClient["listPerpMarkets"]> >; type _getMarketFees = Assert< Matches, SomniaMarketsClient["getMarketFees"]> >; // Orders + fills + candles type _getOpenOrders = Assert, SomniaMarketsClient["getOpenOrders"]>>; type _getOrders = Assert, SomniaMarketsClient["getOrders"]>>; type _countOrders = Assert, SomniaMarketsClient["countOrders"]>>; type _listSweepableOrders = Assert< Matches, SomniaMarketsClient["listSweepableOrders"]> >; type _getMarketActivity = Assert< Matches, SomniaMarketsClient["getMarketActivity"]> >; type _getTransactionActivity = Assert< Matches, SomniaMarketsClient["getTransactionActivity"]> >; // The three block reads are deliberately NOT paired. Their concept functions // take an owner-injected chain read (`BlockTimestampResolver`) that // `WithoutConfig` cannot strip, because the client resolves the block's // timestamp itself rather than asking the caller for it — see createClient. The // shapes therefore differ by design, and asserting equality here would only // force the anchor back into the public signature. type _getLatestActiveBlock = Assert< Matches, SomniaMarketsClient["getLatestActiveBlock"]> >; type _getFills = Assert, SomniaMarketsClient["getFills"]>>; type _getTradeContext = Assert< Matches, SomniaMarketsClient["getTradeContext"]> >; type _getUserFills = Assert, SomniaMarketsClient["getUserFills"]>>; type _getUserFillsPage = Assert< Matches< ( account: Parameters[0], options?: Parameters[1], ) => ReturnType, SomniaMarketsClient["getUserFillsPage"] > >; type _countUserFills = Assert< Matches, SomniaMarketsClient["countUserFills"]> >; type _getCandles = Assert, SomniaMarketsClient["getCandles"]>>; // Portfolios (one per kind — the kind directories, proving the split holds) type _getPortfolio = Assert< Matches, SomniaMarketsClient["getPortfolio"]> >; type _getSpotPortfolio = Assert< Matches, SomniaMarketsClient["getSpotPortfolio"]> >; type _getPerpPortfolio = Assert< Matches, SomniaMarketsClient["getPerpPortfolio"]> >; type _listPerpOrderHistory = Assert< Matches, SomniaMarketsClient["listPerpOrderHistory"]> >; type _getOutcomeBalances = Assert< Matches, SomniaMarketsClient["getOutcomeBalances"]> >; // Fees + router + pools + sync type _listProtocolFees = Assert< Matches, SomniaMarketsClient["listProtocolFees"]> >; type _listBuilderFees = Assert< Matches, SomniaMarketsClient["listBuilderFees"]> >; type _getRouterActions = Assert< Matches, SomniaMarketsClient["getRouterActions"]> >; type _getPool = Assert, SomniaMarketsClient["getPool"]>>; type _getPoolBindings = Assert< Matches, SomniaMarketsClient["getPoolBindings"]> >; type _getSyncStatus = Assert< Matches, SomniaMarketsClient["getSyncStatus"]> >; // Kind-specific reads type _getSpotStopOrders = Assert< Matches, SomniaMarketsClient["getSpotStopOrders"]> >; type _getMarketResolution = Assert< Matches, SomniaMarketsClient["getMarketResolution"]> >; type _getVaultPayoutFallbacks = Assert< Matches, SomniaMarketsClient["getVaultPayoutFallbacks"]> >; type _getFundingPayments = Assert< Matches, SomniaMarketsClient["getFundingPayments"]> >; type _getLiquidations = Assert< Matches, SomniaMarketsClient["getLiquidations"]> >; type _listPerpPositions = Assert< Matches, SomniaMarketsClient["listPerpPositions"]> >; /* eslint-enable @typescript-eslint/no-unused-vars */ // Chain reads take a `PublicClient` rather than a url, so they are not // WithoutConfig-shaped; the client injects the client instead. Asserting those // would need a second stripper for a different tail, which buys little: the // delegation itself (`Balances.getErc20Balance(token, account, client)`) is // already checked by the interface's return type at the call site. export type ChainReadNote = typeof Balances.getErc20Balance;