@structbuild/sdk 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,8 +7,6 @@ TypeScript SDK for prediction market data via [api.struct.to](https://api.struct
7
7
  ```bash
8
8
  npm install @structbuild/sdk
9
9
  # or
10
- pnpm add @structbuild/sdk
11
- # or
12
10
  bun add @structbuild/sdk
13
11
  ```
14
12
 
@@ -73,11 +71,11 @@ const trades = await client.trader.getTraderTrades({ address: "0x..." });
73
71
  const profile = await client.trader.getTraderProfile({ address: "0x..." });
74
72
  const profiles = await client.trader.getTraderProfilesBatch({ addresses: "0x...,0x..." });
75
73
  const pnl = await client.trader.getTraderPnl({ address: "0x..." });
76
- const pnlByMarket = await client.trader.getTraderMarketPnl({ address: "0x..." });
77
- const pnlByEvent = await client.trader.getTraderEventPnl({ address: "0x..." });
74
+ const marketPnl = await client.trader.getTraderMarketPnl({ address: "0x..." });
75
+ const eventPnl = await client.trader.getTraderEventPnl({ address: "0x..." });
76
+ const outcomePnl = await client.trader.getTraderOutcomePnl({ address: "0x..." });
78
77
  const pnlCandles = await client.trader.getTraderPnlCandles({ address: "0x..." });
79
- const calendar = await client.trader.getTraderPnlCalendar({ address: "0x..." });
80
- const positionPnl = await client.trader.getTraderOutcomePnl({ address: "0x..." });
78
+ const pnlCalendar = await client.trader.getTraderPnlCalendar({ address: "0x..." });
81
79
  const volumeChart = await client.trader.getTraderVolumeChart({ address: "0x..." });
82
80
  const leaderboard = await client.trader.getGlobalPnl();
83
81
  ```
@@ -91,16 +89,20 @@ const history = await client.holders.getMarketHoldersHistory({ condition_id: "0x
91
89
  const posHistory = await client.holders.getPositionHoldersHistory({ positionId: "123" });
92
90
  ```
93
91
 
94
- ### Series
92
+ ### Order Book
95
93
 
96
94
  ```typescript
97
- const series = await client.series.getSeriesList();
98
- const outcomes = await client.series.getSeriesOutcomes({ series_slug: "my-series" });
95
+ const orderBook = await client.orderBook.getOrderBook({ asset_id: "0x..." });
96
+ const history = await client.orderBook.getOrderBookHistory();
97
+ const marketBook = await client.orderBook.getMarketOrderBook();
98
+ const spreads = await client.orderBook.getSpreadHistory();
99
99
  ```
100
100
 
101
- ### Assets, Search, Tags, Bonds
101
+ ### Series, Search, Tags, Assets, Bonds
102
102
 
103
103
  ```typescript
104
+ const series = await client.series.getSeriesList();
105
+ const outcomes = await client.series.getSeriesOutcomes({ series_slug: "my-series" });
104
106
  const assetHistory = await client.assets.getAssetHistory({ symbol: "BTC", variant: "1d" });
105
107
  const results = await client.search.search({ query: "election" });
106
108
  const tags = await client.tags.getTags();
@@ -147,7 +149,7 @@ Individual schemas are also exported: `OrderFilledTrade`, `RedemptionTrade`, `Me
147
149
 
148
150
  ### Webhooks
149
151
 
150
- Manage webhook subscriptions for real-time event notifications. Webhook endpoints are platform-level (not venue-scoped).
152
+ Manage webhook subscriptions for real-time event notifications:
151
153
 
152
154
  ```typescript
153
155
  const webhooks = await client.webhooks.list();
@@ -159,32 +161,129 @@ const webhook = await client.webhooks.create({
159
161
  min_usd_value: 100,
160
162
  },
161
163
  });
162
- const detail = await client.webhooks.getWebhook({ webhookId: webhook.data.id });
163
- await client.webhooks.update({ webhookId: webhook.data.id, events: ["first_trade"] });
164
164
  await client.webhooks.test({ webhookId: webhook.data.id });
165
165
  await client.webhooks.rotateSecret({ webhookId: webhook.data.id });
166
166
  await client.webhooks.deleteWebhook({ webhookId: webhook.data.id });
167
167
  const events = await client.webhooks.listEvents();
168
168
  ```
169
169
 
170
- #### Webhook Payload Types
170
+ ## WebSocket API
171
171
 
172
- The SDK exports typed payload schemas for building webhook receivers:
172
+ Real-time streaming via room-based subscriptions with fully typed filters, responses, and events.
173
173
 
174
174
  ```typescript
175
- import type {
176
- FirstTradePayload,
177
- ProbabilitySpikePayload,
178
- GlobalPnlPayload,
179
- VolumeMilestonePayload,
180
- } from "@structbuild/sdk";
181
-
182
- function handleWebhook(payload: FirstTradePayload) {
183
- console.log(payload.trader, payload.price, payload.side);
184
- }
175
+ import { StructWebSocket } from "@structbuild/sdk";
176
+
177
+ const ws = new StructWebSocket({ apiKey: "your-api-key" });
178
+ await ws.connect();
179
+ ```
180
+
181
+ ### Subscribing to rooms
182
+
183
+ Each room has typed filters and a typed subscribe response:
184
+
185
+ ```typescript
186
+ const res = await ws.subscribe("polymarket_trades", {
187
+ condition_ids: ["0xabc123"],
188
+ });
189
+
190
+ await ws.subscribe("polymarket_order_book", {
191
+ asset_ids: ["0xabc123"],
192
+ });
193
+
194
+ // Some rooms have optional filters
195
+ await ws.subscribe("polymarket_asset_prices");
196
+ await ws.subscribe("polymarket_clob_rewards", { subscribe_all: true });
197
+ ```
198
+
199
+ ### Listening for events
200
+
201
+ ```typescript
202
+ ws.on("trade_stream_update", (event) => {
203
+ event.condition_id;
204
+ event.price;
205
+ event.size;
206
+ event.side;
207
+ });
208
+
209
+ ws.on("order_book_update", (event) => {
210
+ event.asset_id;
211
+ event.bids;
212
+ event.asks;
213
+ });
214
+
215
+ ws.on("clob_rewards_update", (event) => {
216
+ event.event_type; // "added" | "removed" | "updated"
217
+ event.condition_id;
218
+ event.reward;
219
+ });
220
+ ```
221
+
222
+ ### Alerts
223
+
224
+ Alerts use a separate client with per-event typed filters and payloads:
225
+
226
+ ```typescript
227
+ import { StructAlertsWebSocket } from "@structbuild/sdk";
228
+
229
+ const alerts = new StructAlertsWebSocket({ apiKey: "your-api-key" });
230
+ await alerts.connect();
231
+
232
+ await alerts.subscribe("trader_whale_trade", {
233
+ wallet_addresses: ["0xd91..."],
234
+ min_usd_value: 10000,
235
+ });
236
+
237
+ await alerts.subscribe("probability_spike", {
238
+ spike_direction: "up",
239
+ min_probability_change_pct: 5,
240
+ });
241
+
242
+ alerts.on("trader_whale_trade", (payload) => {
243
+ payload.data.trader;
244
+ payload.data.amount_usd;
245
+ });
246
+
247
+ alerts.on("probability_spike", (payload) => {
248
+ payload.data.spike_direction;
249
+ payload.data.spike_pct;
250
+ });
185
251
  ```
186
252
 
187
- Available payload types: `FirstTradePayload`, `GlobalPnlPayload`, `MarketPnlPayload`, `EventPnlPayload`, `ConditionMetricsPayload`, `EventMetricsPayload`, `PositionMetricsPayload`, `VolumeMilestonePayload`, `EventVolumeMilestonePayload`, `PositionVolumeMilestonePayload`, `ProbabilitySpikePayload`.
253
+ ### Available rooms
254
+
255
+ | Room | Filters | Event |
256
+ |------|---------|-------|
257
+ | `polymarket_trades` | `condition_ids?`, `market_slugs?`, `event_slugs?`, `position_ids?`, `traders?`, `trade_types?`, `status?`, `subscribe_all?` | `trade_stream_update` |
258
+ | `polymarket_asset_prices` | `asset_symbols?` | `asset_price_tick`, `asset_price_window_update` |
259
+ | `polymarket_asset_window_updates` | `asset_symbols?`, `timeframes?` | `asset_window_update` |
260
+ | `polymarket_market_metrics` | `condition_ids` | `market_metrics_update` |
261
+ | `polymarket_event_metrics` | `event_slugs` | `event_metrics_update` |
262
+ | `polymarket_position_metrics` | `position_ids` | `position_metrics_update` |
263
+ | `polymarket_trader_pnl` | `traders` | `trader_global_pnl_update`, `trader_market_pnl_update`, `trader_event_pnl_update` |
264
+ | `polymarket_trader_positions` | `traders` | `trader_position_update` |
265
+ | `polymarket_accounts` | `wallets`, `include_usdce?`, `include_matic?` | `accounts_update`, `usdce_update`, `matic_update` |
266
+ | `polymarket_order_book` | `condition_ids?`, `position_ids?` | `order_book_update` |
267
+ | `polymarket_clob_rewards` | `condition_ids?`, `subscribe_all?` | `clob_rewards_update` |
268
+
269
+ ### Lifecycle events
270
+
271
+ ```typescript
272
+ ws.on("connected", () => {});
273
+ ws.on("disconnected", ({ code, reason }) => {});
274
+ ws.on("reconnecting", ({ attempt }) => {});
275
+ ws.on("reconnect_failed", (err) => {});
276
+ ws.on("auth_failed", (err) => {});
277
+ ws.on("warning", (warning) => {});
278
+ ws.on("error", (err) => {});
279
+ ```
280
+
281
+ ### Cleanup
282
+
283
+ ```typescript
284
+ ws.unsubscribe("polymarket_trades");
285
+ ws.disconnect();
286
+ ```
188
287
 
189
288
  ## JWT Auth
190
289
 
@@ -208,6 +307,15 @@ const ws = new StructWebSocket({
208
307
 
209
308
  The `pk_jwt_*` key is safe to hardcode in frontend bundles — it is useless without a valid JWT from your configured auth provider.
210
309
 
310
+ If your JWT can rotate while a socket stays alive, prefer `getJwt` so reconnects always rebuild the URL with a fresh token:
311
+
312
+ ```typescript
313
+ const ws = new StructWebSocket({
314
+ apiKey: "pk_jwt_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
315
+ getJwt: () => userAccessToken,
316
+ });
317
+ ```
318
+
211
319
  ## Pagination
212
320
 
213
321
  Use the `paginate` helper to iterate through all results:
@@ -257,6 +365,32 @@ const client = new StructClient({
257
365
  });
258
366
  ```
259
367
 
368
+ ## Testing
369
+
370
+ WebSocket unit tests stay in the default fast suite:
371
+
372
+ ```bash
373
+ bun test
374
+ bun run typecheck
375
+ ```
376
+
377
+ The live websocket soak test is opt-in and env-gated. It first calls the REST API to fetch a recent market, event, position, and trader, then subscribes across the main websocket rooms and keeps the socket alive for 5 minutes by default.
378
+
379
+ ```bash
380
+ STRUCT_RUN_WS_LIVE_TESTS=1 \
381
+ STRUCT_API_KEY=your-api-key \
382
+ bun run test:ws:live
383
+ ```
384
+
385
+ To run longer than 5 minutes, set `STRUCT_WS_SOAK_DURATION_MS`:
386
+
387
+ ```bash
388
+ STRUCT_RUN_WS_LIVE_TESTS=1 \
389
+ STRUCT_API_KEY=your-api-key \
390
+ STRUCT_WS_SOAK_DURATION_MS=420000 \
391
+ bun run test:ws:live
392
+ ```
393
+
260
394
  ## License
261
395
 
262
396
  MIT