@0xinsider/mcp 1.2.3 → 2.0.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 +93 -1
- package/dist/catalog.generated.d.ts +4 -0
- package/dist/catalog.generated.d.ts.map +1 -1
- package/dist/catalog.generated.js +5 -0
- package/dist/catalog.generated.js.map +1 -1
- package/dist/cli-lib.d.ts.map +1 -1
- package/dist/cli-lib.js +9 -5
- package/dist/cli-lib.js.map +1 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/product-cli/errors.d.ts +7 -0
- package/dist/product-cli/errors.d.ts.map +1 -0
- package/dist/product-cli/errors.js +13 -0
- package/dist/product-cli/errors.js.map +1 -0
- package/dist/product-cli/index.d.ts +4 -0
- package/dist/product-cli/index.d.ts.map +1 -0
- package/dist/product-cli/index.js +372 -0
- package/dist/product-cli/index.js.map +1 -0
- package/dist/product-cli/oauth.d.ts +10 -0
- package/dist/product-cli/oauth.d.ts.map +1 -0
- package/dist/product-cli/oauth.js +245 -0
- package/dist/product-cli/oauth.js.map +1 -0
- package/dist/product-cli/storage.d.ts +20 -0
- package/dist/product-cli/storage.d.ts.map +1 -0
- package/dist/product-cli/storage.js +149 -0
- package/dist/product-cli/storage.js.map +1 -0
- package/dist/sdk/client.d.ts +765 -0
- package/dist/sdk/client.d.ts.map +1 -0
- package/dist/sdk/client.js +974 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/sdk/errors.d.ts +313 -0
- package/dist/sdk/errors.d.ts.map +1 -0
- package/dist/sdk/errors.js +521 -0
- package/dist/sdk/errors.js.map +1 -0
- package/dist/sdk/index.d.ts +26 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +25 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/pagination.d.ts +76 -0
- package/dist/sdk/pagination.d.ts.map +1 -0
- package/dist/sdk/pagination.js +106 -0
- package/dist/sdk/pagination.js.map +1 -0
- package/dist/sdk/schema.d.ts +2372 -0
- package/dist/sdk/schema.d.ts.map +1 -0
- package/dist/sdk/schema.js +7 -0
- package/dist/sdk/schema.js.map +1 -0
- package/dist/sdk/stream.d.ts +182 -0
- package/dist/sdk/stream.d.ts.map +1 -0
- package/dist/sdk/stream.js +391 -0
- package/dist/sdk/stream.js.map +1 -0
- package/dist/sdk/webhooks.d.ts +163 -0
- package/dist/sdk/webhooks.d.ts.map +1 -0
- package/dist/sdk/webhooks.js +122 -0
- package/dist/sdk/webhooks.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/get-trending-wallets.js +4 -4
- package/dist/tools/get-trending-wallets.js.map +1 -1
- package/dist/tools/read-parity.d.ts +1 -0
- package/dist/tools/read-parity.d.ts.map +1 -1
- package/dist/tools/read-parity.js +27 -0
- package/dist/tools/read-parity.js.map +1 -1
- package/package.json +6 -3
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE consumer for `GET /api/v1/stream`.
|
|
3
|
+
*
|
|
4
|
+
* The endpoint forwards the platform's live feed envelopes as Server-Sent
|
|
5
|
+
* Events. Source of truth: `web/public/api/v1/openapi.json` -> paths./api/v1/stream.
|
|
6
|
+
*
|
|
7
|
+
* Wire format (per the spec's response description):
|
|
8
|
+
* - Each data frame is `id: <seq>\ndata: <json-envelope>\n\n`, where the JSON
|
|
9
|
+
* envelope is `{ seq, published_at, type, ...event-specific }`. The SSE id
|
|
10
|
+
* equals the envelope `seq`.
|
|
11
|
+
* - A resync marker is `event: resync\nid: <seq>\ndata: <json>\n\n`, where the
|
|
12
|
+
* JSON is `{ type: "resync", completeness, from_sequence, to_sequence }`.
|
|
13
|
+
* It means the requested resume point is outside the retained window or a
|
|
14
|
+
* live sequence gap was observed; treat it as "refetch current state".
|
|
15
|
+
* - Idle connections emit `: keep-alive` comment lines (ignored).
|
|
16
|
+
*
|
|
17
|
+
* Resume is via the `Last-Event-ID` header set to the last `seq` you processed;
|
|
18
|
+
* the spec also accepts `last_event_id` / `seq` query fallbacks. This consumer
|
|
19
|
+
* sends the header and auto-tracks the last seen seq so a caller-driven
|
|
20
|
+
* reconnect resumes from the right place.
|
|
21
|
+
*
|
|
22
|
+
* Filters (per-connection, subscribe-time): `event` (comma-separated frame
|
|
23
|
+
* types), `condition_id`, and `min_grade` (S|A|B|C|D|F).
|
|
24
|
+
*/
|
|
25
|
+
import { errorFromResponse, OxinsiderApiError } from "./errors.js";
|
|
26
|
+
/**
|
|
27
|
+
* Open the stream and async-iterate its frames. Yields a discriminated
|
|
28
|
+
* `StreamEvent`: `kind: "event"` carries the `FeedEnvelope`, `kind: "resync"`
|
|
29
|
+
* carries the `ResyncMarker`.
|
|
30
|
+
*
|
|
31
|
+
* Pass `options.cursor` (a `{ seq?: number }` object) to have the last
|
|
32
|
+
* delivered `seq` written back as frames arrive; reconnect with
|
|
33
|
+
* `lastEventId: cursor.seq` to resume after a transport drop.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* for await (const frame of streamFeed(client, { event: ["WhaleTradesInserted"], min_grade: "S" })) {
|
|
37
|
+
* if (frame.kind === "event") console.log(frame.envelope.type, frame.seq);
|
|
38
|
+
* }
|
|
39
|
+
*/
|
|
40
|
+
export async function* streamFeed(client, options = {}) {
|
|
41
|
+
const apiKey = client.getApiKey();
|
|
42
|
+
if (!apiKey) {
|
|
43
|
+
throw new Error("getStream requires an API key (oxi_sk_*)");
|
|
44
|
+
}
|
|
45
|
+
const url = buildStreamUrl(client, options);
|
|
46
|
+
const headers = new Headers({ accept: "text/event-stream" });
|
|
47
|
+
headers.set("authorization", `Bearer ${apiKey}`);
|
|
48
|
+
if (options.lastEventId !== undefined) {
|
|
49
|
+
headers.set("last-event-id", String(options.lastEventId));
|
|
50
|
+
}
|
|
51
|
+
const fetchImpl = client.getFetch();
|
|
52
|
+
const response = await fetchImpl(url, {
|
|
53
|
+
method: "GET",
|
|
54
|
+
headers,
|
|
55
|
+
signal: options.signal,
|
|
56
|
+
});
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
const body = await safeText(response);
|
|
59
|
+
throw errorFromResponse(response.status, tryParse(body), retryAfterSeconds(response));
|
|
60
|
+
}
|
|
61
|
+
if (!response.body) {
|
|
62
|
+
throw new Error("Stream response has no readable body");
|
|
63
|
+
}
|
|
64
|
+
const reader = response.body.getReader();
|
|
65
|
+
const decoder = new TextDecoder();
|
|
66
|
+
let buffer = "";
|
|
67
|
+
try {
|
|
68
|
+
for (;;) {
|
|
69
|
+
const { value, done } = await reader.read();
|
|
70
|
+
if (done)
|
|
71
|
+
break;
|
|
72
|
+
buffer += decoder.decode(value, { stream: true });
|
|
73
|
+
// SSE frames are separated by a blank line. Handle CRLF and LF.
|
|
74
|
+
let sepIndex = nextFrameBoundary(buffer);
|
|
75
|
+
while (sepIndex.index !== -1) {
|
|
76
|
+
const rawFrame = buffer.slice(0, sepIndex.index);
|
|
77
|
+
buffer = buffer.slice(sepIndex.index + sepIndex.length);
|
|
78
|
+
const parsed = parseSseFrame(rawFrame);
|
|
79
|
+
if (parsed) {
|
|
80
|
+
if (parsed.event === "resync") {
|
|
81
|
+
const marker = (tryParse(parsed.data) ?? {});
|
|
82
|
+
options.onResync?.(marker);
|
|
83
|
+
yield { kind: "resync", seq: parsed.id ?? null, marker };
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
const envelope = tryParse(parsed.data);
|
|
87
|
+
if (envelope && typeof envelope === "object") {
|
|
88
|
+
const seq = typeof envelope.seq === "number"
|
|
89
|
+
? envelope.seq
|
|
90
|
+
: (parsed.id ?? NaN);
|
|
91
|
+
if (Number.isFinite(seq) && options.cursor) {
|
|
92
|
+
options.cursor.seq = seq;
|
|
93
|
+
}
|
|
94
|
+
yield { kind: "event", seq, envelope };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
sepIndex = nextFrameBoundary(buffer);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
// `releaseLock()` detaches the reader but leaves the body -- and the
|
|
104
|
+
// underlying HTTP connection -- open. Any early exit from the consumer
|
|
105
|
+
// loop (`break`, `return`, or a throw from a handler) runs this block via
|
|
106
|
+
// the generator's `return()`, so without `cancel()` every early exit leaks
|
|
107
|
+
// one open connection to the SSE endpoint. The documented reconnect loop
|
|
108
|
+
// makes that one leak per reconnect, until the process runs out of sockets
|
|
109
|
+
// or the server's per-user SSE lease cap rejects the user's own
|
|
110
|
+
// reconnects. `cancel()` releases the lock as part of cancelling, and can
|
|
111
|
+
// reject on an already-errored stream, so the rejection is swallowed to
|
|
112
|
+
// keep the `finally` non-throwing (#9682).
|
|
113
|
+
await reader.cancel().catch(() => undefined);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Default for `ResilientStreamOptions.maxReconnects`. */
|
|
117
|
+
export const DEFAULT_MAX_STREAM_RECONNECTS = 10;
|
|
118
|
+
const STREAM_RECONNECT_BASE_MS = 1_000;
|
|
119
|
+
const STREAM_RECONNECT_MAX_MS = 30_000;
|
|
120
|
+
const STREAM_RECONNECT_JITTER_MS = 250;
|
|
121
|
+
/**
|
|
122
|
+
* `streamFeedResilient` gave up after `maxReconnects` consecutive failed
|
|
123
|
+
* connections. `lastSeq` is the seq to resume after (pass it as `lastEventId`
|
|
124
|
+
* later); `cause` is the last connection's failure.
|
|
125
|
+
*/
|
|
126
|
+
export class StreamReconnectsExhaustedError extends Error {
|
|
127
|
+
lastSeq;
|
|
128
|
+
attempts;
|
|
129
|
+
constructor(attempts, lastSeq, cause) {
|
|
130
|
+
super(`0xinsider stream gave up after ${String(attempts)} consecutive reconnect attempts`, { cause });
|
|
131
|
+
this.name = "StreamReconnectsExhaustedError";
|
|
132
|
+
this.attempts = attempts;
|
|
133
|
+
this.lastSeq = lastSeq;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* A 4xx other than 429 will fail the same way on every reconnect: a bad filter
|
|
138
|
+
* (400), a key that is invalid (401), lapsed (402), refused (403), or locked
|
|
139
|
+
* (423). Reconnecting would only hammer the API with a credential a person
|
|
140
|
+
* has to fix.
|
|
141
|
+
*/
|
|
142
|
+
function isPermanentStreamError(error) {
|
|
143
|
+
return (error instanceof OxinsiderApiError &&
|
|
144
|
+
error.status >= 400 &&
|
|
145
|
+
error.status < 500 &&
|
|
146
|
+
error.status !== 429);
|
|
147
|
+
}
|
|
148
|
+
function streamReconnectDelayMs(attempt, cause) {
|
|
149
|
+
const retryAfter = cause instanceof OxinsiderApiError &&
|
|
150
|
+
"retryAfterSeconds" in cause &&
|
|
151
|
+
typeof cause.retryAfterSeconds === "number"
|
|
152
|
+
? cause.retryAfterSeconds
|
|
153
|
+
: null;
|
|
154
|
+
if (retryAfter !== null) {
|
|
155
|
+
return Math.max(0, retryAfter * 1000) + Math.random() * STREAM_RECONNECT_JITTER_MS;
|
|
156
|
+
}
|
|
157
|
+
const ceiling = Math.min(STREAM_RECONNECT_MAX_MS, STREAM_RECONNECT_BASE_MS * 2 ** (attempt - 1));
|
|
158
|
+
return ceiling / 2 + Math.random() * (ceiling / 2);
|
|
159
|
+
}
|
|
160
|
+
/** Resolve `true` after `ms`, or `false` the moment `signal` aborts. */
|
|
161
|
+
function waitUnlessAborted(ms, signal) {
|
|
162
|
+
return new Promise((resolve) => {
|
|
163
|
+
if (signal?.aborted) {
|
|
164
|
+
resolve(false);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const onAbort = () => {
|
|
168
|
+
clearTimeout(timer);
|
|
169
|
+
resolve(false);
|
|
170
|
+
};
|
|
171
|
+
const timer = setTimeout(() => {
|
|
172
|
+
signal?.removeEventListener("abort", onAbort);
|
|
173
|
+
resolve(true);
|
|
174
|
+
}, ms);
|
|
175
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* `streamFeed` with the reconnect-and-resume loop built in (#14286).
|
|
180
|
+
*
|
|
181
|
+
* - A clean server close, a network error, a 429 or a 5xx reconnects,
|
|
182
|
+
* resuming after the last delivered seq (`Last-Event-ID`). Before any frame
|
|
183
|
+
* arrives it resumes from `lastEventId`, or attaches live when that is unset.
|
|
184
|
+
* - The wait is `Retry-After` plus jitter when the refusal carries one,
|
|
185
|
+
* otherwise a jittered backoff from 1 s capped at 30 s.
|
|
186
|
+
* - Any other 4xx (400, 401, 402, 403, 423) is thrown at once: it cannot heal
|
|
187
|
+
* on its own.
|
|
188
|
+
* - After `maxReconnects` consecutive connections that delivered nothing, it
|
|
189
|
+
* throws `StreamReconnectsExhaustedError` carrying `lastSeq`.
|
|
190
|
+
* - `resync` markers are yielded unchanged, and `onResync` still fires. The
|
|
191
|
+
* marker's `id` becomes the resume cursor, as the server intends, so a
|
|
192
|
+
* reconnect after one does not request the aged window again.
|
|
193
|
+
* - Aborting `signal` ends the iterator without throwing, during a connection
|
|
194
|
+
* or a backoff. Each connection's body reader is cancelled when it ends, so
|
|
195
|
+
* reconnects do not accumulate sockets.
|
|
196
|
+
* - No request deadline applies, as with `streamFeed`.
|
|
197
|
+
*
|
|
198
|
+
* `options.cursor`, when passed, tracks the last delivered seq across every
|
|
199
|
+
* connection.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* const controller = new AbortController();
|
|
203
|
+
* for await (const frame of streamFeedResilient(client, {
|
|
204
|
+
* event: ["WhaleTradesInserted"],
|
|
205
|
+
* signal: controller.signal,
|
|
206
|
+
* onReconnect: (attempt, lastSeq, cause) => console.warn("reconnecting", attempt, lastSeq, cause),
|
|
207
|
+
* })) {
|
|
208
|
+
* if (frame.kind === "resync") await refetchState();
|
|
209
|
+
* else handle(frame.envelope);
|
|
210
|
+
* }
|
|
211
|
+
*/
|
|
212
|
+
export async function* streamFeedResilient(client, options = {}) {
|
|
213
|
+
const { maxReconnects = DEFAULT_MAX_STREAM_RECONNECTS, onReconnect, ...streamOptions } = options;
|
|
214
|
+
if (!Number.isInteger(maxReconnects) || maxReconnects < 0) {
|
|
215
|
+
throw new Error(`maxReconnects must be a non-negative integer, got ${String(maxReconnects)}`);
|
|
216
|
+
}
|
|
217
|
+
// The one configuration error `streamFeed` throws before any request; it
|
|
218
|
+
// would otherwise count as a transport failure and burn every reconnect.
|
|
219
|
+
if (!client.getApiKey()) {
|
|
220
|
+
throw new Error("streamFeedResilient requires an API key (oxi_sk_*)");
|
|
221
|
+
}
|
|
222
|
+
const signal = streamOptions.signal;
|
|
223
|
+
const cursor = streamOptions.cursor ?? {};
|
|
224
|
+
const initialLastEventId = streamOptions.lastEventId;
|
|
225
|
+
let attempt = 0;
|
|
226
|
+
for (;;) {
|
|
227
|
+
if (signal?.aborted)
|
|
228
|
+
return;
|
|
229
|
+
let cause;
|
|
230
|
+
try {
|
|
231
|
+
for await (const frame of streamFeed(client, {
|
|
232
|
+
...streamOptions,
|
|
233
|
+
cursor,
|
|
234
|
+
lastEventId: cursor.seq ?? initialLastEventId,
|
|
235
|
+
})) {
|
|
236
|
+
attempt = 0;
|
|
237
|
+
if (frame.kind === "resync" && frame.seq !== null) {
|
|
238
|
+
cursor.seq = frame.seq;
|
|
239
|
+
}
|
|
240
|
+
yield frame;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
if (signal?.aborted)
|
|
245
|
+
return;
|
|
246
|
+
if (isPermanentStreamError(error))
|
|
247
|
+
throw error;
|
|
248
|
+
cause = error;
|
|
249
|
+
}
|
|
250
|
+
if (signal?.aborted)
|
|
251
|
+
return;
|
|
252
|
+
attempt += 1;
|
|
253
|
+
const lastSeq = cursor.seq ?? toSeq(initialLastEventId);
|
|
254
|
+
if (attempt > maxReconnects) {
|
|
255
|
+
throw new StreamReconnectsExhaustedError(attempt - 1, lastSeq, cause);
|
|
256
|
+
}
|
|
257
|
+
onReconnect?.(attempt, lastSeq, cause);
|
|
258
|
+
if (!(await waitUnlessAborted(streamReconnectDelayMs(attempt, cause), signal))) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function toSeq(value) {
|
|
264
|
+
if (value === undefined)
|
|
265
|
+
return undefined;
|
|
266
|
+
const seq = Number(value);
|
|
267
|
+
return Number.isFinite(seq) ? seq : undefined;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Callback-style stream consumer for environments where an async iterator is
|
|
271
|
+
* awkward. Returns a promise that resolves when the stream ends (or rejects on
|
|
272
|
+
* error / abort). Pass a `signal` to stop it.
|
|
273
|
+
*/
|
|
274
|
+
export async function consumeStream(client, handlers, options = {}) {
|
|
275
|
+
for await (const frame of streamFeed(client, options)) {
|
|
276
|
+
if (frame.kind === "event") {
|
|
277
|
+
await handlers.onEvent?.(frame.envelope, frame.seq);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
await handlers.onResync?.(frame.marker, frame.seq);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function buildStreamUrl(client, options) {
|
|
285
|
+
const url = new URL("api/v1/stream", `${client.getBaseUrl()}/`);
|
|
286
|
+
if (options.event && options.event.length > 0) {
|
|
287
|
+
url.searchParams.set("event", options.event.join(","));
|
|
288
|
+
}
|
|
289
|
+
if (options.condition_id) {
|
|
290
|
+
url.searchParams.set("condition_id", options.condition_id);
|
|
291
|
+
}
|
|
292
|
+
if (options.min_grade) {
|
|
293
|
+
url.searchParams.set("min_grade", options.min_grade);
|
|
294
|
+
}
|
|
295
|
+
return url.toString();
|
|
296
|
+
}
|
|
297
|
+
/** Parse one SSE frame (without the trailing blank line). */
|
|
298
|
+
export function parseSseFrame(raw) {
|
|
299
|
+
const lines = raw.split(/\r?\n/);
|
|
300
|
+
let event;
|
|
301
|
+
let id;
|
|
302
|
+
const dataParts = [];
|
|
303
|
+
let sawField = false;
|
|
304
|
+
for (const line of lines) {
|
|
305
|
+
if (line === "" || line.startsWith(":")) {
|
|
306
|
+
// Blank or comment line (e.g. ": keep-alive"): ignore.
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const colon = line.indexOf(":");
|
|
310
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
311
|
+
let value = colon === -1 ? "" : line.slice(colon + 1);
|
|
312
|
+
if (value.startsWith(" "))
|
|
313
|
+
value = value.slice(1);
|
|
314
|
+
switch (field) {
|
|
315
|
+
case "event":
|
|
316
|
+
event = value;
|
|
317
|
+
sawField = true;
|
|
318
|
+
break;
|
|
319
|
+
case "id": {
|
|
320
|
+
const n = Number(value);
|
|
321
|
+
if (Number.isFinite(n))
|
|
322
|
+
id = n;
|
|
323
|
+
sawField = true;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
case "data":
|
|
327
|
+
dataParts.push(value);
|
|
328
|
+
sawField = true;
|
|
329
|
+
break;
|
|
330
|
+
default:
|
|
331
|
+
// Unknown SSE field; ignore per the spec.
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (!sawField && dataParts.length === 0) {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
return { event, id, data: dataParts.join("\n") };
|
|
339
|
+
}
|
|
340
|
+
/** Find the next SSE frame boundary (blank line); handles `\n\n` and `\r\n\r\n`. */
|
|
341
|
+
function nextFrameBoundary(buffer) {
|
|
342
|
+
// Three terminator shapes, not two (#9682). A producer that ends FIELD lines
|
|
343
|
+
// with CRLF but the blank separator line with a bare LF yields "\r\n\n",
|
|
344
|
+
// which the old two-way scan cut at the "\n\n" -- one byte late, leaving a
|
|
345
|
+
// trailing "\r" on the last field. `parseSseFrame` splits on /\r?\n/ and so
|
|
346
|
+
// cannot strip a "\r" from a line with no newline after it, which turned
|
|
347
|
+
// `event: resync` into `event: "resync\r"` and made the exact `=== "resync"`
|
|
348
|
+
// routing check fail: the resume marker the cursor protocol depends on was
|
|
349
|
+
// silently delivered as a malformed envelope. The current Axum backend emits
|
|
350
|
+
// LF only, so this hardens a latent path rather than fixing a live break.
|
|
351
|
+
const candidates = [
|
|
352
|
+
{ index: buffer.indexOf("\r\n\r\n"), length: 4 },
|
|
353
|
+
{ index: buffer.indexOf("\r\n\n"), length: 3 },
|
|
354
|
+
{ index: buffer.indexOf("\n\n"), length: 2 },
|
|
355
|
+
].filter((candidate) => candidate.index !== -1);
|
|
356
|
+
if (candidates.length === 0)
|
|
357
|
+
return { index: -1, length: 0 };
|
|
358
|
+
// Earliest boundary wins; on a tie the LONGEST terminator wins, because
|
|
359
|
+
// "\r\n\r\n" and "\r\n\n" both also match "\n\n" at a later index and a
|
|
360
|
+
// short match would leave terminator bytes in the next frame.
|
|
361
|
+
return candidates.reduce((best, candidate) => candidate.index < best.index ||
|
|
362
|
+
(candidate.index === best.index && candidate.length > best.length)
|
|
363
|
+
? candidate
|
|
364
|
+
: best);
|
|
365
|
+
}
|
|
366
|
+
function tryParse(text) {
|
|
367
|
+
if (text === null || text === "")
|
|
368
|
+
return null;
|
|
369
|
+
try {
|
|
370
|
+
return JSON.parse(text);
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async function safeText(response) {
|
|
377
|
+
try {
|
|
378
|
+
return await response.text();
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
return "";
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function retryAfterSeconds(response) {
|
|
385
|
+
const header = response.headers.get("retry-after");
|
|
386
|
+
if (!header)
|
|
387
|
+
return null;
|
|
388
|
+
const seconds = Number(header);
|
|
389
|
+
return Number.isFinite(seconds) ? seconds : null;
|
|
390
|
+
}
|
|
391
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../../sdk/src/stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook signature verification and typed delivery payloads.
|
|
3
|
+
*
|
|
4
|
+
* The backend signs every webhook delivery exactly like this (source of truth:
|
|
5
|
+
* `backend/src/api_v1/handlers/webhooks/endpoint_security.rs::sign_webhook_payload`):
|
|
6
|
+
*
|
|
7
|
+
* signature = "v1=" + hex( HMAC_SHA256( signing_secret, `${timestamp}.${raw_body}` ) )
|
|
8
|
+
*
|
|
9
|
+
* It is sent on two headers:
|
|
10
|
+
* - `x-0xinsider-timestamp: <unix-seconds>`
|
|
11
|
+
* - `x-0xinsider-signature: v1=<hex>` (may be a comma-separated list)
|
|
12
|
+
*
|
|
13
|
+
* Verification, the receiver side. The backend only signs; the published
|
|
14
|
+
* recipe is the delivery-signing paragraph on POST /api/v1/webhooks in
|
|
15
|
+
* `web/public/api/v1/openapi.json`:
|
|
16
|
+
* 1. Reject if `|now - timestamp| > 300` seconds (replay tolerance).
|
|
17
|
+
* 2. Recompute the expected `v1=<hex>` over `${timestamp}.${rawBody}`.
|
|
18
|
+
* 3. Constant-time compare against each candidate in the header.
|
|
19
|
+
*
|
|
20
|
+
* `rawBody` MUST be the exact bytes received: verify before JSON parsing, since
|
|
21
|
+
* re-serializing changes whitespace/key order and breaks the HMAC.
|
|
22
|
+
*
|
|
23
|
+
* ENDPOINT ACTIVATION (`verifyWebhook`, `POST /api/v1/webhooks/{id}/verify`):
|
|
24
|
+
* the one-time `verification.token` is necessary but not sufficient. That call
|
|
25
|
+
* POSTs a challenge to your webhook url and activates the endpoint only if you
|
|
26
|
+
* answer 2xx within 10 seconds. The challenge body is
|
|
27
|
+
* `{ "type": "webhook.verification", "token": "<verification_token>",
|
|
28
|
+
* "webhook_id": <id> }`, it carries `x-0xinsider-event-type:
|
|
29
|
+
* webhook.verification`, and it is signed with the endpoint's `signing_secret`
|
|
30
|
+
* exactly like a delivery -- so `verifySignature` below validates it unchanged.
|
|
31
|
+
* The challenge follows no redirects and never reads your response body. A
|
|
32
|
+
* non-2xx answer, no answer inside 10 seconds, or a url that does not resolve to
|
|
33
|
+
* a publicly routable address returns 422 and leaves the endpoint
|
|
34
|
+
* `pending_verification`.
|
|
35
|
+
*/
|
|
36
|
+
/** Default replay tolerance; the published contract is 300 seconds. */
|
|
37
|
+
export declare const SIGNATURE_TOLERANCE_SECONDS = 300;
|
|
38
|
+
/** Header carrying the `v1=<hex>` signature. */
|
|
39
|
+
export declare const SIGNATURE_HEADER = "x-0xinsider-signature";
|
|
40
|
+
/** Header carrying the unix-seconds signing timestamp. */
|
|
41
|
+
export declare const TIMESTAMP_HEADER = "x-0xinsider-timestamp";
|
|
42
|
+
export interface VerifySignatureInput {
|
|
43
|
+
/** The endpoint's signing secret (returned on create / rotate-secret). */
|
|
44
|
+
secret: string;
|
|
45
|
+
/** The `x-0xinsider-timestamp` value (unix seconds, number or string). */
|
|
46
|
+
timestamp: number | string;
|
|
47
|
+
/** The exact raw request body bytes/string, pre-JSON-parse. */
|
|
48
|
+
body: string | Uint8Array;
|
|
49
|
+
/** The `x-0xinsider-signature` header value (`v1=<hex>`, possibly a list). */
|
|
50
|
+
signature: string;
|
|
51
|
+
/** Replay tolerance in seconds; defaults to 300. */
|
|
52
|
+
toleranceSeconds?: number;
|
|
53
|
+
/** Reference "now" in unix seconds; defaults to `Date.now()/1000`. */
|
|
54
|
+
nowSeconds?: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Recompute the canonical signature for a payload. Returns `v1=<hex>`.
|
|
58
|
+
* Exposed so callers can sign in tests; matches the backend byte-for-byte.
|
|
59
|
+
*/
|
|
60
|
+
export declare function computeSignature(secret: string, timestamp: number | string, body: string | Uint8Array): string;
|
|
61
|
+
/**
|
|
62
|
+
* Verify a webhook signature. Returns `true` only when the timestamp is within
|
|
63
|
+
* tolerance AND a candidate signature constant-time-matches the recomputed one.
|
|
64
|
+
* Never throws on a bad signature; throws only on a missing/empty secret.
|
|
65
|
+
*/
|
|
66
|
+
export declare function verifySignature(input: VerifySignatureInput): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Webhook event types. Source of truth:
|
|
69
|
+
* `web/public/api/v1/openapi.json` -> components.schemas.WebhookEventType.enum.
|
|
70
|
+
* `whale_trades_inserted`, `wallet_grade_changed`, `insider_radar_flag_raised`,
|
|
71
|
+
* and `smart_money_flow_detected` are Pro-only: they deliver only to API keys
|
|
72
|
+
* on an active Pro subscription.
|
|
73
|
+
*/
|
|
74
|
+
export declare const WEBHOOK_EVENT_TYPES: readonly ["whale_trades_inserted", "live_sports_updated", "whale_trader_synced", "large_positions_updated", "wallet_grade_changed", "insider_radar_flag_raised", "smart_money_flow_detected"];
|
|
75
|
+
export type WebhookEventType = (typeof WEBHOOK_EVENT_TYPES)[number];
|
|
76
|
+
/**
|
|
77
|
+
* Common delivery envelope every webhook POST body carries. Source of truth:
|
|
78
|
+
* the `enqueue_webhook_event` call sites in the backend
|
|
79
|
+
* (`{ id, type, created_at, data }`).
|
|
80
|
+
*/
|
|
81
|
+
export interface WebhookEventEnvelope<TType extends WebhookEventType = WebhookEventType, TData = unknown> {
|
|
82
|
+
/** Stable event id; identical across retries of the same logical event. */
|
|
83
|
+
id: string;
|
|
84
|
+
type: TType;
|
|
85
|
+
/** ISO-8601 timestamp of when the event was enqueued. */
|
|
86
|
+
created_at: string;
|
|
87
|
+
data: TData;
|
|
88
|
+
}
|
|
89
|
+
/** `whale_trades_inserted` data (Pro-only): a batch of new whale trades was ingested. */
|
|
90
|
+
export interface WhaleTradesInsertedData {
|
|
91
|
+
count: number;
|
|
92
|
+
}
|
|
93
|
+
/** `whale_trader_synced` data: a tracked trader finished a sync pass. */
|
|
94
|
+
export interface WhaleTraderSyncedData {
|
|
95
|
+
trader_id: number;
|
|
96
|
+
wallet: string;
|
|
97
|
+
}
|
|
98
|
+
/** `large_positions_updated` data. */
|
|
99
|
+
export interface LargePositionsUpdatedData {
|
|
100
|
+
count: number;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* `wallet_grade_changed` data (Pro-only): a tracked wallet's grade moved.
|
|
104
|
+
* Source: `backend/crates/sync/src/periodic/heavy_pipeline.rs`.
|
|
105
|
+
*/
|
|
106
|
+
export interface WalletGradeChangedData {
|
|
107
|
+
wallet: string;
|
|
108
|
+
trader_id: number;
|
|
109
|
+
old_grade: string;
|
|
110
|
+
new_grade: string;
|
|
111
|
+
/** Direction of the grade move, e.g. "up" / "down". */
|
|
112
|
+
direction: string;
|
|
113
|
+
skill_index: number;
|
|
114
|
+
final_score: number;
|
|
115
|
+
date: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* `insider_radar_flag_raised` data (Pro-only): a suspicion threshold was
|
|
119
|
+
* crossed on a fresh fill. Source: `backend/src/polymarket_rtds_ingest/flushing.rs`.
|
|
120
|
+
*/
|
|
121
|
+
export interface InsiderRadarFlagRaisedData {
|
|
122
|
+
trade_id: string;
|
|
123
|
+
wallet: string;
|
|
124
|
+
trader_id: number;
|
|
125
|
+
condition_id: string;
|
|
126
|
+
suspicion_score: number;
|
|
127
|
+
track: string;
|
|
128
|
+
/** Outcome side the flagged fill took. */
|
|
129
|
+
side: string;
|
|
130
|
+
size: number;
|
|
131
|
+
price: number;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* `smart_money_flow_detected` payload (Pro-only). Fires when a scheduled
|
|
135
|
+
* scanner detects ranked-trader net flow crossing a threshold on a market.
|
|
136
|
+
*/
|
|
137
|
+
export interface SmartMoneyFlowDetectedData {
|
|
138
|
+
condition_id: string;
|
|
139
|
+
/** Signed net YES/NO exposure from ranked traders. */
|
|
140
|
+
net_flow_usd: number;
|
|
141
|
+
abs_net_flow_usd: number;
|
|
142
|
+
/** Side the net flow leans: "yes" or "no". */
|
|
143
|
+
dominant_side: string;
|
|
144
|
+
/** Minimum trader grade included: "S", "A", "B", "C", "D", or "F". */
|
|
145
|
+
grade_floor: string;
|
|
146
|
+
whale_trade_count: number;
|
|
147
|
+
/** Detection window, e.g. "24h". */
|
|
148
|
+
window: string;
|
|
149
|
+
}
|
|
150
|
+
export type WhaleTradesInsertedEvent = WebhookEventEnvelope<"whale_trades_inserted", WhaleTradesInsertedData>;
|
|
151
|
+
export type WhaleTraderSyncedEvent = WebhookEventEnvelope<"whale_trader_synced", WhaleTraderSyncedData>;
|
|
152
|
+
export type LargePositionsUpdatedEvent = WebhookEventEnvelope<"large_positions_updated", LargePositionsUpdatedData>;
|
|
153
|
+
export type WalletGradeChangedEvent = WebhookEventEnvelope<"wallet_grade_changed", WalletGradeChangedData>;
|
|
154
|
+
export type InsiderRadarFlagRaisedEvent = WebhookEventEnvelope<"insider_radar_flag_raised", InsiderRadarFlagRaisedData>;
|
|
155
|
+
export type SmartMoneyFlowDetectedEvent = WebhookEventEnvelope<"smart_money_flow_detected", SmartMoneyFlowDetectedData>;
|
|
156
|
+
/** Discriminated union over every typed webhook delivery payload. */
|
|
157
|
+
export type WebhookEvent = WhaleTradesInsertedEvent | WhaleTraderSyncedEvent | LargePositionsUpdatedEvent | WalletGradeChangedEvent | InsiderRadarFlagRaisedEvent | SmartMoneyFlowDetectedEvent | WebhookEventEnvelope<"live_sports_updated", unknown>;
|
|
158
|
+
/**
|
|
159
|
+
* Parse a verified raw body into the typed delivery envelope. Call only AFTER
|
|
160
|
+
* `verifySignature` passes (parsing first would let an unverified payload in).
|
|
161
|
+
*/
|
|
162
|
+
export declare function parseWebhookEvent(body: string): WebhookEvent;
|
|
163
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../../sdk/src/webhooks.ts"],"names":[],"mappings":"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"}
|