@0xinsider/sdk 0.19.0 → 0.21.0

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
@@ -223,80 +223,78 @@ access lapses and returns `credential_status` plus
223
223
  paid data routes still return `402 subscription_required` until access is
224
224
  renewed.
225
225
 
226
- For `listPreGameSides`, require every row's `category_skill` object and
227
- compare `meta.category_skill_base_payload_hash` with
228
- `meta.category_skill_enriched_base_payload_hash` before consuming category
229
- evidence. The evidence is Polymarket-only, forward-observed, and explicitly
230
- partial; `degraded` or mismatched hashes must not be treated as a measured edge.
231
-
232
- Rows carry `side`, `ranked_at`, `backing_score` and `side_share`. The older
233
- `piled_side`, `signal_created_at`, `conviction_score` and `smart_score` keys
234
- carry the same values and stay on the wire. `listSportsEdgeSignals` and
235
- `listSportsEdgeObservations` remain as deprecated aliases on the deprecated
236
- `/api/v1/sports-edge-*` paths, which answer with `Deprecation` and successor
237
- `Link` headers.
238
-
239
- ### Observation-only pre-game side cohorts
240
-
241
- `listPreGameSideObservations` exposes the measured `wider_holder`, the
242
- overlapping `emerging_pile` projection, and provider-confirmed `in_play`
243
- cohorts without changing the funded pre-game sides route. The response
244
- includes `snapshot_as_of` and a
245
- required snapshot-wide operational/unknown-completeness `degraded` verdict plus
246
- a per-sport terminal-reason `funnel`; every row is tagged
247
- `observation_only: true`. Do not route these rows to an order executor.
226
+ ### Edge Hunter (Max only)
248
227
 
249
- ```ts
250
- const tennis = await client.listPreGameSideObservations({
251
- cohort: "wider_holder",
252
- category: "Tennis",
253
- limit: 20,
254
- });
228
+ Read the same experimental board as [Edge Hunter](https://0xinsider.com/sports/edge):
255
229
 
256
- console.log(tennis.degraded, tennis.data, tennis.funnel.sports);
257
- const previousEtag = tennis.meta.etag;
230
+ ```ts
231
+ const board = await client.edgeHunter.get({ horizon_hours: 48 });
232
+ if (board.object === "edge_hunter") {
233
+ console.log(board.data.total, board.data.rows, board.data.as_of);
234
+ }
258
235
  ```
259
236
 
260
- Treat `degraded: true` as partial evidence even when a row or the funnel's
261
- single terminal reason does not independently expose every contributing
262
- operational or unknown-completeness failure. `capacity_limited` is different:
263
- it records intentional bounded provider-work admission, remains accountable in
264
- the funnel, and does not by itself set `degraded: true`.
265
-
266
- All category and all-sports cache scopes share one global observation
267
- provider-work admission. Healthy `wider_holder` requests may reuse a snapshot
268
- for about 180 seconds. `emerging_pile` projects `wider_holder` rows with finite
269
- `sharp_pct` in the `[0.75, 0.85)` band, `holder_scan_complete: true`, and a
270
- kickoff after the first-page projection cutoff. It overlaps the `wider_holder`
271
- source and is not arrival history or an independent denominator. `in_play`
272
- never serves a cached observation snapshot older than about 30 seconds and
273
- fails closed when the provider live-board snapshot is stale or unavailable.
274
-
275
- The SDK copies a successful response's weak semantic `ETag` header into
276
- `meta.etag`; it is client metadata, not an additional field in the server's
277
- JSON body. The validator covers the stable page payload, including the stable
278
- page position in `next_cursor`, but excludes request-specific `meta` and, for
279
- `emerging_pile`, the opaque projection cutoff inside that cursor.
280
-
281
- For a conditional request, use `listPreGameSideObservationsConditional()` so a
282
- `304` returns the typed `{ object: "not_modified", data: null, meta }` result
283
- while a `200` retains the full observation response type:
237
+ The board covers all sports and esports, uses a fixed B grade floor, and returns
238
+ at most 30 ranked rows. `total` counts rows before that cap. `horizon_hours`
239
+ defaults to 48 and is clamped to 1..48. The default board accepts only `view` and `horizon_hours` and has no ETag or pagination.
240
+
241
+ Every row includes provider participants, both outcome quotes, and backed-side
242
+ analytics. Current prices are decimal strings with their own `basis`, `ts`,
243
+ and optional `validated_at`; compare those clocks independently from `as_of`.
244
+ Null means unavailable. `ranking_source: "db_only"` and
245
+ `directional_source: "degraded"` identify weaker evidence. This board is
246
+ analytics, not an instruction to place an order.
247
+
248
+ A Pro credential throws `MaxPlanRequiredError` (403 `forbidden`,
249
+ `reason: max_plan_required`); every other Pro route keeps working with the
250
+ same key. All four previous sports-edge and pre-game-side paths are retired
251
+ and return 410 `endpoint_retired`. Their list and conditional-read methods
252
+ were removed in SDK 0.20.0. Migrate to `client.edgeHunter.get()`.
253
+ SDK 0.21.0 preserves the default board and adds the complete advanced contracts:
284
254
 
285
255
  ```ts
286
- import { isApiNotModifiedResponse } from "@0xinsider/sdk";
287
-
288
- if (previousEtag) {
289
- const conditional = await client.listPreGameSideObservationsConditional(
290
- { cohort: "wider_holder", category: "Tennis", limit: 20 },
291
- {
292
- headers: { "If-None-Match": previousEtag },
293
- },
294
- );
256
+ const signals = await client.edgeHunter.get({ view: "signals", category: "Soccer", min_grade: "A", limit: 20 });
257
+ if (signals.object === "list") console.log(signals.view, signals.data, signals.next_cursor);
295
258
 
296
- if (isApiNotModifiedResponse(conditional)) {
297
- console.log("snapshot unchanged");
298
- }
299
- }
259
+ const observations = await client.edgeHunter.observations({ cohort: "in_play", limit: 20 });
260
+ if (observations.object === "list") console.log(observations.snapshot_as_of, observations.degraded, observations.funnel);
261
+
262
+ const unchanged = await client.edgeHunter.getConditional(
263
+ { view: "observations", cohort: "in_play" },
264
+ { headers: { "If-None-Match": observations.meta.etag ?? "" } },
265
+ );
266
+ if (unchanged.object === "not_modified") console.log("Unchanged snapshot");
267
+ ```
268
+
269
+ `view: "signals"` returns full ranked records, nullable `token_id`, snapshot clocks,
270
+ holder/directional analytics, category evidence and provenance. It accepts `category`,
271
+ `min_grade` (S/A/B, default B), `horizon_hours` (default 12, clamp 1..48), `limit`
272
+ (default 20, clamp 1..100) and `cursor`. Grade floor requires a holder at that grade or better;
273
+ it does not remove other holders. `signals()` is a convenience for the same mode.
274
+
275
+ `view: "observations"` requires `cohort` (`wider_holder`, `in_play`, `emerging_pile`)
276
+ and accepts `category`, `limit` and `cursor`. Records preserve verified token IDs,
277
+ midpoint `backed_price` marks, observation/holder clocks, provider/directional status,
278
+ and completeness. The envelope retains `snapshot_as_of`, `degraded` and all per-sport
279
+ funnel diagnostics. Price marks are not wallet entry costs; volume is shares.
280
+ In-play reuse is capped at 30 seconds and fails closed without fresh source evidence.
281
+
282
+ Literal view queries have distinct result types, including on empty pages. Advanced
283
+ responses add `view: "signals" | "observations"` beside `object: "list"`. Both advanced
284
+ views support ETag/304 and private revalidation. They read warmed generations only;
285
+ missing/stale readiness returns 503 `read_model_warming` with `Retry-After: 5`
286
+ without running holder/provider computation in the request. `getConditional()`
287
+ accepts advanced views only. Parameters for another view return 400, and a refreshed
288
+ snapshot expires its cursors. Start a new cursor walk when migrating from retired URLs.
289
+ The shared `list()`, `paginate()`, `paginatePages()` and `collect()` helpers accept
290
+ Edge Hunter only with an explicit advanced view and preserve the corresponding record
291
+ and envelope types:
292
+
293
+ ```ts
294
+ for await (const page of paginatePages(client, "getEdgeHunter", {
295
+ query: { view: "observations", cohort: "wider_holder", limit: 100 },
296
+ maxPages: 2,
297
+ })) console.log(page.view, page.funnel, page.data);
300
298
  ```
301
299
 
302
300
  ### Beyond the JSON envelope
@@ -397,7 +395,7 @@ for await (const trade of paginate(client, "listWhaleTrades", {
397
395
  }
398
396
  ```
399
397
 
400
- The loop ends when a page says `has_more: false`, whatever its `next_cursor` says. A page that says `has_more: true` with no usable `next_cursor`, or one whose `next_cursor` repeats a cursor the walk already requested, is a broken response rather than the end of the collection: the walk throws `PaginationError` (`reason` `missing_cursor` or `repeated_cursor`) before yielding that page and before any duplicate request, with the received `page` on the error so its data is not lost. The last 1,024 cursors are remembered for the repeat check (`CURSOR_HISTORY_LIMIT`), which keeps a long walk's memory flat. A list response whose `has_more` is not a boolean or whose `next_cursor` is not a string throws `InvalidResponseError`.
398
+ The loop ends when a page says `has_more: false`, whatever its `next_cursor` says. A page that says `has_more: true` with no usable `next_cursor`, or one whose `next_cursor` repeats a cursor the walk already requested, is a broken response rather than the end of the collection: the walk throws `PaginationError` (`reason` `missing_cursor` or `repeated_cursor`) before yielding that page and before any duplicate request, with the received `page` on the error so its data is not lost. The last 1,024 cursors are remembered for the repeat check (`CURSOR_HISTORY_LIMIT`), which keeps a long walk's memory flat. A list response whose `has_more` is not a boolean or whose `next_cursor` is neither a string, null nor omitted throws `InvalidResponseError`. A null terminal cursor is preserved for Edge Hunter observations.
401
399
 
402
400
  `maxPages` caps fetches and is checked before any request: a positive integer or `Infinity`; `0`, a negative, fractional or `NaN` value throws `RangeError`. Reaching it is your choice, not exhaustion. Pass a `progress` object to tell the two apart and keep the continuation:
403
401