@pear-protocol/core-sdk
Advanced tools
| import { SearchTriggersParams, SearchTriggersResponse } from '@pear-protocol/types'; | ||
| export { ExternalTrigger, ExternalTriggerSource, SearchTriggersParams, SearchTriggersResponse, TriggerOutcome } from '@pear-protocol/types'; | ||
| declare class External { | ||
| triggers: { | ||
| kalshi: { | ||
| marketNames: (tickers: string[]) => Promise<Map<string, string>>; | ||
| search: (params?: SearchTriggersParams) => Promise<SearchTriggersResponse>; | ||
| }; | ||
| polymarket: { | ||
| marketNames: (ids: string[]) => Promise<Map<string, string>>; | ||
| search: (params?: SearchTriggersParams) => Promise<SearchTriggersResponse>; | ||
| }; | ||
| }; | ||
| } | ||
| export { External }; |
| import { searchKalshiTriggers, fetchKalshiMarketNames } from './kalshi'; | ||
| import { searchPolymarketTriggers, fetchPolymarketMarketNames } from './polymarket'; | ||
| class External { | ||
| triggers = { | ||
| kalshi: { | ||
| marketNames: async (tickers) => { | ||
| return fetchKalshiMarketNames(tickers); | ||
| }, | ||
| search: async (params = {}) => { | ||
| return searchKalshiTriggers(params); | ||
| } | ||
| }, | ||
| polymarket: { | ||
| marketNames: async (ids) => { | ||
| return fetchPolymarketMarketNames(ids); | ||
| }, | ||
| search: async (params = {}) => { | ||
| return searchPolymarketTriggers(params); | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| export { External }; |
| import { SearchTriggersParams, SearchTriggersResponse } from '@pear-protocol/types'; | ||
| declare function fetchKalshiMarketNames(tickers: string[]): Promise<Map<string, string>>; | ||
| declare function searchKalshiTriggers(params?: SearchTriggersParams): Promise<SearchTriggersResponse>; | ||
| export { fetchKalshiMarketNames, searchKalshiTriggers }; |
| const BASE_URL = "https://api.elections.kalshi.com"; | ||
| const BATCH_SIZE = 100; | ||
| async function fetchKalshiMarketNames(tickers) { | ||
| const result = /* @__PURE__ */ new Map(); | ||
| for (let i = 0; i < tickers.length; i += BATCH_SIZE) { | ||
| const batch = tickers.slice(i, i + BATCH_SIZE); | ||
| const url = new URL(`${BASE_URL}/trade-api/v2/markets`); | ||
| url.searchParams.set("tickers", batch.join(",")); | ||
| url.searchParams.set("limit", String(BATCH_SIZE)); | ||
| const response = await fetch(url, { method: "GET", headers: { accept: "application/json" } }); | ||
| if (!response.ok) throw new Error(`Kalshi API error: ${response.status} ${response.statusText}`); | ||
| const data = await response.json(); | ||
| for (const m of data.markets ?? []) result.set(m.ticker, m.title); | ||
| } | ||
| return result; | ||
| } | ||
| async function searchKalshiTriggers(params = {}) { | ||
| const url = new URL(`${BASE_URL}/v1/search/series`); | ||
| url.searchParams.set("order_by", "trending"); | ||
| url.searchParams.set("status", "open"); | ||
| url.searchParams.set("page_size", String(params.pageSize ?? 50)); | ||
| if (params.category) url.searchParams.set("category", params.category); | ||
| if (params.search) url.searchParams.set("query", params.search); | ||
| if (params.cursor) url.searchParams.set("cursor", params.cursor); | ||
| const response = await fetch(url, { method: "GET", headers: { accept: "application/json" } }); | ||
| if (!response.ok) return { triggers: [], nextCursor: null }; | ||
| const data = await response.json(); | ||
| return { | ||
| triggers: (data.current_page ?? []).map(mapEventToTrigger), | ||
| nextCursor: data.next_cursor || null | ||
| }; | ||
| } | ||
| function mapEventToTrigger(event) { | ||
| const imageUrl = `https://kalshi-public-docs.s3.amazonaws.com/series-images-webp/${event.event_ticker.split("-")[0]}.webp`; | ||
| if (event.markets.length > 1) { | ||
| return { | ||
| id: event.event_ticker, | ||
| category: "prediction_market", | ||
| source: "kalshi", | ||
| selectable: false, | ||
| oracle: null, | ||
| options: event.markets.map(mapMarketToOutcome), | ||
| type: "multi", | ||
| title: event.event_title, | ||
| imageUrl | ||
| }; | ||
| } | ||
| const market = event.markets[0]; | ||
| return { | ||
| id: market?.ticker ?? event.event_ticker, | ||
| category: "prediction_market", | ||
| source: "kalshi", | ||
| selectable: true, | ||
| oracle: market?.last_price ?? null, | ||
| options: [], | ||
| type: "binary", | ||
| title: event.event_title, | ||
| imageUrl | ||
| }; | ||
| } | ||
| function mapMarketToOutcome(market) { | ||
| return { | ||
| id: market.ticker, | ||
| label: market.yes_subtitle || market.title, | ||
| oracle: market.last_price | ||
| }; | ||
| } | ||
| export { fetchKalshiMarketNames, searchKalshiTriggers }; |
| import { SearchTriggersParams, SearchTriggersResponse } from '@pear-protocol/types'; | ||
| declare function fetchPolymarketMarketNames(ids: string[]): Promise<Map<string, string>>; | ||
| declare function searchPolymarketTriggers(params?: SearchTriggersParams): Promise<SearchTriggersResponse>; | ||
| export { fetchPolymarketMarketNames, searchPolymarketTriggers }; |
| const BASE_URL = "https://gamma-api.polymarket.com"; | ||
| const BATCH_SIZE = 100; | ||
| async function fetchPolymarketMarketNames(ids) { | ||
| const result = /* @__PURE__ */ new Map(); | ||
| for (let i = 0; i < ids.length; i += BATCH_SIZE) { | ||
| const batch = ids.slice(i, i + BATCH_SIZE); | ||
| const url = new URL(`${BASE_URL}/markets`); | ||
| for (const id of batch) url.searchParams.append("id", id); | ||
| url.searchParams.set("limit", String(BATCH_SIZE)); | ||
| const response = await fetch(url, { method: "GET", headers: { accept: "application/json" } }); | ||
| if (!response.ok) throw new Error(`Polymarket API error: ${response.status} ${response.statusText}`); | ||
| const data = await response.json(); | ||
| for (const m of data) result.set(m.id, m.question ?? m.groupItemTitle ?? ""); | ||
| } | ||
| return result; | ||
| } | ||
| async function searchPolymarketTriggers(params = {}) { | ||
| const url = new URL(`${BASE_URL}/events/keyset`); | ||
| url.searchParams.set("closed", "false"); | ||
| url.searchParams.set("limit", String(params.pageSize ?? 50)); | ||
| url.searchParams.set("order", "volume24hr"); | ||
| url.searchParams.set("ascending", "false"); | ||
| if (params.search) url.searchParams.set("title_search", params.search); | ||
| if (params.category) url.searchParams.set("tag_slug", params.category); | ||
| if (params.cursor) url.searchParams.set("after_cursor", params.cursor); | ||
| const response = await fetch(url, { method: "GET", headers: { accept: "application/json" } }); | ||
| if (!response.ok) return { triggers: [], nextCursor: null }; | ||
| const data = await response.json(); | ||
| const events = data.events ?? []; | ||
| return { | ||
| triggers: events.map(mapEventToTrigger).filter((t) => t !== null), | ||
| nextCursor: data.next_cursor ?? null | ||
| }; | ||
| } | ||
| function mapEventToTrigger(event) { | ||
| const markets = (event.markets ?? []).filter((m) => m.active !== false && m.closed !== true); | ||
| const [first] = markets; | ||
| if (!first) return null; | ||
| if (markets.length === 1) { | ||
| return { | ||
| id: first.id, | ||
| category: "prediction_market", | ||
| source: "polymarket", | ||
| selectable: true, | ||
| oracle: parseYesCents(first), | ||
| options: [], | ||
| type: "binary", | ||
| title: first.question ?? event.title ?? first.groupItemTitle ?? first.id, | ||
| imageUrl: first.image ?? first.icon ?? event.image ?? event.icon ?? null | ||
| }; | ||
| } | ||
| return { | ||
| id: event.id ?? event.slug ?? first.id, | ||
| category: "prediction_market", | ||
| source: "polymarket", | ||
| selectable: false, | ||
| oracle: null, | ||
| options: markets.map(mapMarketToOutcome), | ||
| type: "multi", | ||
| title: event.title ?? first.question ?? event.id, | ||
| imageUrl: event.image ?? event.icon ?? first.image ?? first.icon ?? null | ||
| }; | ||
| } | ||
| function mapMarketToOutcome(market) { | ||
| return { | ||
| id: market.id, | ||
| label: market.groupItemTitle ?? market.question ?? market.id, | ||
| oracle: parseYesCents(market) | ||
| }; | ||
| } | ||
| function parseYesCents(market) { | ||
| const outcomes = parseStringArray(market.outcomes); | ||
| const prices = parseStringArray(market.outcomePrices); | ||
| const yesIndex = outcomes.findIndex((o) => o.toLowerCase() === "yes"); | ||
| if (yesIndex < 0) return null; | ||
| const price = Number(prices[yesIndex] ?? ""); | ||
| if (!Number.isFinite(price)) return null; | ||
| return Math.round(price * 1e4) / 100; | ||
| } | ||
| function parseStringArray(value) { | ||
| if (Array.isArray(value)) return value.map(String); | ||
| if (!value) return []; | ||
| try { | ||
| const parsed = JSON.parse(value); | ||
| return Array.isArray(parsed) ? parsed.map(String) : []; | ||
| } catch { | ||
| return []; | ||
| } | ||
| } | ||
| export { fetchPolymarketMarketNames, searchPolymarketTriggers }; |
| export { ExternalTrigger, ExternalTriggerSource, SearchTriggersParams, SearchTriggersResponse, TriggerOutcome } from '@pear-protocol/types'; | ||
| type KalshiMarket = { | ||
| ticker: string; | ||
| title: string; | ||
| }; | ||
| type KalshiMarketsResponse = { | ||
| markets: KalshiMarket[]; | ||
| }; | ||
| type KalshiSearchSeriesMarket = { | ||
| ticker: string; | ||
| yes_subtitle: string; | ||
| last_price: number; | ||
| title: string; | ||
| }; | ||
| type KalshiSearchSeriesEvent = { | ||
| event_ticker: string; | ||
| event_title: string; | ||
| markets: KalshiSearchSeriesMarket[]; | ||
| }; | ||
| type KalshiSearchSeriesResponse = { | ||
| current_page: KalshiSearchSeriesEvent[]; | ||
| next_cursor: string; | ||
| }; | ||
| type PolymarketMarket = { | ||
| id: string; | ||
| question?: string; | ||
| image?: string; | ||
| icon?: string; | ||
| active?: boolean; | ||
| closed?: boolean; | ||
| groupItemTitle?: string; | ||
| outcomes?: string[] | string; | ||
| outcomePrices?: string[] | string; | ||
| }; | ||
| type PolymarketEvent = { | ||
| id: string; | ||
| slug?: string; | ||
| title?: string; | ||
| image?: string; | ||
| icon?: string; | ||
| markets?: PolymarketMarket[]; | ||
| }; | ||
| type PolymarketKeysetEventsResponse = { | ||
| events?: PolymarketEvent[]; | ||
| next_cursor?: string | null; | ||
| }; | ||
| export type { KalshiMarket, KalshiMarketsResponse, KalshiSearchSeriesEvent, KalshiSearchSeriesMarket, KalshiSearchSeriesResponse, PolymarketEvent, PolymarketKeysetEventsResponse, PolymarketMarket }; |
@@ -33,2 +33,8 @@ import { HttpTypes } from '@pear-protocol/types'; | ||
| }>; | ||
| apiKeyMe(headers?: ClientHeaders): Promise<{ | ||
| id: string; | ||
| role: "basic" | "pro" | "admin"; | ||
| scope: "read" | "read_write"; | ||
| authMethod: "api_key"; | ||
| }>; | ||
| refreshSession(headers?: ClientHeaders): Promise<void>; | ||
@@ -35,0 +41,0 @@ refreshToken(body: HttpTypes.RefreshTokenRequest, headers?: ClientHeaders): Promise<{ |
@@ -53,2 +53,9 @@ class Auth { | ||
| } | ||
| // Resolve the identity behind an API key (x-api-key auth). Session-less counterpart to `me`. | ||
| async apiKeyMe(headers) { | ||
| return await this.client.fetch(`/auth/api-key/me`, { | ||
| method: "GET", | ||
| headers | ||
| }); | ||
| } | ||
| async refreshSession(headers) { | ||
@@ -55,0 +62,0 @@ return await this.client.fetch(`/auth/session/refresh`, { |
+48
-10
| class Core { | ||
| client; | ||
| constructor(client) { | ||
| clientId; | ||
| constructor(client, clientId) { | ||
| this.client = client; | ||
| this.clientId = clientId; | ||
| } | ||
| // Auto-attach the configured attribution clientId to a request body unless the | ||
| // caller already set one explicitly. Bound to the exact trade / trigger / schedule / | ||
| // rebalance payloads it serves, so an unrelated body is a compile error. | ||
| withClientId(body) { | ||
| if (!this.clientId) return body; | ||
| return { clientId: this.clientId, ...body }; | ||
| } | ||
| accounts = { | ||
@@ -36,3 +45,3 @@ list: async (query, headers) => { | ||
| return await this.client.fetch(`/trade-accounts/${id}/credentials`, { | ||
| method: "GET", | ||
| method: "POST", | ||
| headers | ||
@@ -48,2 +57,23 @@ }); | ||
| }; | ||
| apiKeys = { | ||
| list: async (headers) => { | ||
| return await this.client.fetch(`/api-keys`, { | ||
| method: "GET", | ||
| headers | ||
| }); | ||
| }, | ||
| create: async (body, headers) => { | ||
| return await this.client.fetch(`/api-keys`, { | ||
| method: "POST", | ||
| headers, | ||
| body | ||
| }); | ||
| }, | ||
| revoke: async (id, headers) => { | ||
| return await this.client.fetch(`/api-keys/${id}`, { | ||
| method: "DELETE", | ||
| headers | ||
| }); | ||
| } | ||
| }; | ||
| executions = { | ||
@@ -120,2 +150,10 @@ list: async (query, headers) => { | ||
| }; | ||
| oracle = { | ||
| btcdom: async (headers) => { | ||
| return await this.client.fetch(`/oracle/btcdom`, { | ||
| method: "GET", | ||
| headers | ||
| }); | ||
| } | ||
| }; | ||
| positions = { | ||
@@ -166,3 +204,3 @@ list: async (query, headers) => { | ||
| headers, | ||
| body | ||
| body: this.withClientId(body) | ||
| }); | ||
@@ -183,3 +221,3 @@ } | ||
| headers, | ||
| body | ||
| body: this.withClientId(body) | ||
| }); | ||
@@ -235,3 +273,3 @@ }, | ||
| headers, | ||
| body | ||
| body: this.withClientId(body) | ||
| }); | ||
@@ -243,3 +281,3 @@ }, | ||
| headers, | ||
| body | ||
| body: this.withClientId(body) | ||
| }); | ||
@@ -251,3 +289,3 @@ }, | ||
| headers, | ||
| body | ||
| body: this.withClientId(body) | ||
| }); | ||
@@ -259,3 +297,3 @@ }, | ||
| headers, | ||
| body | ||
| body: this.withClientId(body) | ||
| }); | ||
@@ -283,3 +321,3 @@ }, | ||
| headers, | ||
| body | ||
| body: this.withClientId(body) | ||
| }); | ||
@@ -291,3 +329,3 @@ }, | ||
| headers, | ||
| body | ||
| body: this.withClientId(body) | ||
| }); | ||
@@ -294,0 +332,0 @@ }, |
+1
-0
| import { AuthMode, Logger, RequestMiddleware, ResponseMiddleware, FetchInput, FetchArgs } from './types.js'; | ||
| import '@pear-protocol/types'; | ||
@@ -3,0 +4,0 @@ declare class HttpClient { |
+4
-2
| import { Admin } from './admin/index.js'; | ||
| import { Auth } from './auth/index.js'; | ||
| import { Core } from './core/index.js'; | ||
| import { External } from './external/index.js'; | ||
| import { SDKConfig } from './types.js'; | ||
| export { HttpTypes } from '@pear-protocol/types'; | ||
| export { ExternalTrigger, ExternalTriggerSource, HttpTypes, SearchTriggersParams, SearchTriggersResponse, TriggerOutcome } from '@pear-protocol/types'; | ||
| export * from '@pear-protocol/utils'; | ||
@@ -13,5 +14,6 @@ import './http.js'; | ||
| core: Core; | ||
| external: External; | ||
| constructor(config: SDKConfig); | ||
| } | ||
| export { PearSDK as default }; | ||
| export { External, PearSDK as default }; |
+24
-0
| import { Admin } from './admin'; | ||
| import { Auth } from './auth'; | ||
| import { Core } from './core'; | ||
| import { External } from './external'; | ||
| export { External } from './external'; | ||
| import { HttpClient } from './http'; | ||
@@ -12,2 +14,3 @@ import { FetchError } from './utils'; | ||
| core; | ||
| external; | ||
| constructor(config) { | ||
@@ -26,2 +29,3 @@ const authMode = config.auth?.type ?? "session"; | ||
| this.core = new Core(client); | ||
| this.external = new External(); | ||
| if (authMode === "session") { | ||
@@ -78,2 +82,22 @@ let refreshInFlight = null; | ||
| } | ||
| if (authMode === "apikey" && config.auth?.key) { | ||
| const getKey = config.auth.key; | ||
| client.useRequest(async (request, next) => { | ||
| const key = getKey(); | ||
| if (key) { | ||
| request.headers.set("x-api-key", key); | ||
| } | ||
| return next(); | ||
| }); | ||
| if (config.onAuthError) { | ||
| const onAuthError = config.onAuthError; | ||
| client.useResponse(async (response) => { | ||
| if (response.status === 401) { | ||
| onAuthError(); | ||
| throw new FetchError("Unauthorized. API key may be expired or revoked.", "Unauthorized", 401); | ||
| } | ||
| return response; | ||
| }); | ||
| } | ||
| } | ||
| if (config.getTradeAccountId) { | ||
@@ -80,0 +104,0 @@ const getTradeAccountId = config.getTradeAccountId; |
+30
-7
@@ -1,2 +0,4 @@ | ||
| type AuthMode = 'session' | 'bearer'; | ||
| import { HttpTypes } from '@pear-protocol/types'; | ||
| type AuthMode = 'session' | 'bearer' | 'apikey'; | ||
| type SDKConfig = { | ||
@@ -19,4 +21,9 @@ /** | ||
| * Token set on every request via `Authorization` header. Provide | ||
| * `token` getter to supply the API key or JWT. No client-side refresh. | ||
| * If 401, caller handles re-auth. Used by headless / API-key clients. | ||
| * `token` getter to supply the JWT. No client-side refresh. | ||
| * If 401, caller handles re-auth. Used by headless JWT clients. | ||
| * | ||
| * Apikey: | ||
| * API key sent on every request via the `x-api-key` header. Provide | ||
| * `key` getter. No cookies, no client-side refresh. If 401, caller | ||
| * handles re-auth. Used by programmatic API-key clients. | ||
| */ | ||
@@ -26,7 +33,13 @@ auth?: { | ||
| /** | ||
| * Token getter for bearer mode (e.g. () => apiKey). Ignored in session mode. | ||
| * Token getter for bearer mode (e.g. () => jwt). Ignored in other modes. | ||
| * | ||
| * Example: () => localStorage.getItem('accessToken') | ||
| */ | ||
| token?: () => string | null; | ||
| /** | ||
| * API key getter for apikey mode. Ignored in other modes. | ||
| * | ||
| * Example: () => localStorage.getItem('apiKey') | ||
| */ | ||
| token?: () => string | null; | ||
| key?: () => string | null; | ||
| }; | ||
@@ -40,4 +53,13 @@ /** | ||
| /** | ||
| * Called when server returns 401 (bearer mode only). | ||
| * Default attribution client code (client_ids.code). When set, it is | ||
| * auto-added to the body of trade / trigger / schedule / rebalance requests | ||
| * unless the call already specifies its own `clientId`. `null`/omitted → | ||
| * nothing is added. | ||
| * | ||
| * Example: 'AGENTPEAR' | ||
| */ | ||
| clientId?: string | null; | ||
| /** | ||
| * Called when server returns 401 (bearer / apikey mode). | ||
| * | ||
| * Example: () => { | ||
@@ -95,3 +117,4 @@ * console.error('Unauthorized. Token may be expired or revoked.'); | ||
| type ResponseMiddleware = (response: Response, retry: () => Promise<Response>, request: Request) => Promise<Response>; | ||
| type ClientIdBody = HttpTypes.RebalanceManually | HttpTypes.CreateSchedule | HttpTypes.CreateTradeOpen | HttpTypes.CreateTradeAdjust | HttpTypes.CreateTradeClose | HttpTypes.CreateTradeCloseAll | HttpTypes.CreateTriggerOpen | HttpTypes.CreateTriggerClose; | ||
| export type { AuthMode, ClientHeaders, FetchArgs, FetchInput, FetchParams, Logger, RequestMiddleware, ResponseMiddleware, SDKConfig }; | ||
| export type { AuthMode, ClientHeaders, ClientIdBody, FetchArgs, FetchInput, FetchParams, Logger, RequestMiddleware, ResponseMiddleware, SDKConfig }; |
+1
-0
| import { FetchArgs, AuthMode } from './types.js'; | ||
| import '@pear-protocol/types'; | ||
@@ -3,0 +4,0 @@ declare function getBaseUrl(passedBaseUrl: string): string; |
+3
-3
| { | ||
| "name": "@pear-protocol/core-sdk", | ||
| "version": "0.1.0", | ||
| "version": "1.0.0", | ||
| "description": "Pear Protocol Core SDK", | ||
@@ -28,4 +28,4 @@ "private": false, | ||
| "dependencies": { | ||
| "@pear-protocol/types": "^0.1.0", | ||
| "@pear-protocol/utils": "^0.0.22", | ||
| "@pear-protocol/types": "^1.0.0", | ||
| "@pear-protocol/utils": "^0.0.23", | ||
| "qs": "^6.14.0" | ||
@@ -32,0 +32,0 @@ }, |
Sorry, the diff of this file is too big to display
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
159759
16.3%23
53.33%4252
16.14%2
-33.33%2
100%85
11.84%+ Added
+ Added
- Removed
- Removed
Updated
Updated