@surf-ai/sdk 0.1.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.
@@ -0,0 +1,798 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/data/client.ts
34
+ var client_exports = {};
35
+ __export(client_exports, {
36
+ get: () => get,
37
+ post: () => post
38
+ });
39
+ function env(surfName, legacyName) {
40
+ return process.env[surfName] || process.env[legacyName];
41
+ }
42
+ function resolveConfig() {
43
+ const proxyBase = env("SURF_SANDBOX_PROXY_BASE", "DATA_PROXY_BASE");
44
+ if (proxyBase) {
45
+ return { baseUrl: proxyBase, headers: {} };
46
+ }
47
+ const gatewayUrl = env("SURF_DEPLOYED_GATEWAY_URL", "GATEWAY_URL");
48
+ const appToken = env("SURF_DEPLOYED_APP_TOKEN", "APP_TOKEN");
49
+ if (gatewayUrl && appToken) {
50
+ return {
51
+ baseUrl: `${gatewayUrl.replace(/\/$/, "")}/gateway/v1`,
52
+ headers: { Authorization: `Bearer ${appToken}` }
53
+ };
54
+ }
55
+ return { baseUrl: DEFAULT_PUBLIC_URL, headers: {} };
56
+ }
57
+ function normalizePath(path2) {
58
+ return String(path2 || "").replace(/^\/+/, "").replace(/^(?:proxy\/)+/, "");
59
+ }
60
+ async function fetchJson(url, init, retries = 1) {
61
+ for (let attempt = 0; attempt <= retries; attempt++) {
62
+ const res = await fetch(url, init);
63
+ if (!res.ok) {
64
+ const text2 = await res.text();
65
+ throw new Error(`API error ${res.status}: ${text2.slice(0, 200)}`);
66
+ }
67
+ const text = await res.text();
68
+ if (text) return JSON.parse(text);
69
+ if (attempt < retries) await new Promise((r) => setTimeout(r, 1e3));
70
+ }
71
+ throw new Error(`Empty response from ${url}`);
72
+ }
73
+ async function get(path2, params) {
74
+ const config = resolveConfig();
75
+ const cleaned = {};
76
+ if (params) {
77
+ for (const [k, v] of Object.entries(params)) {
78
+ if (v != null) cleaned[k] = String(v);
79
+ }
80
+ }
81
+ const qs = Object.keys(cleaned).length ? "?" + new URLSearchParams(cleaned).toString() : "";
82
+ return fetchJson(`${config.baseUrl}/${normalizePath(path2)}${qs}`, {
83
+ headers: config.headers
84
+ });
85
+ }
86
+ async function post(path2, body) {
87
+ const config = resolveConfig();
88
+ return fetchJson(`${config.baseUrl}/${normalizePath(path2)}`, {
89
+ method: "POST",
90
+ headers: { ...config.headers, "Content-Type": "application/json" },
91
+ body: body ? JSON.stringify(body) : void 0
92
+ });
93
+ }
94
+ var DEFAULT_PUBLIC_URL;
95
+ var init_client = __esm({
96
+ "src/data/client.ts"() {
97
+ "use strict";
98
+ DEFAULT_PUBLIC_URL = "https://api.ask.surf/gateway/v1";
99
+ }
100
+ });
101
+
102
+ // src/server/index.ts
103
+ var server_exports = {};
104
+ __export(server_exports, {
105
+ createServer: () => createServer,
106
+ dataApi: () => dataApi
107
+ });
108
+ module.exports = __toCommonJS(server_exports);
109
+
110
+ // src/server/runtime.ts
111
+ var import_express = __toESM(require("express"), 1);
112
+ var import_cors = __toESM(require("cors"), 1);
113
+ var import_fs = __toESM(require("fs"), 1);
114
+ var import_path = __toESM(require("path"), 1);
115
+ var import_http_proxy_middleware = require("http-proxy-middleware");
116
+ var import_croner = require("croner");
117
+ function createServer(options = {}) {
118
+ const port = options.port || parseInt(process.env.PORT || "3001", 10);
119
+ const routesDir = options.routesDir || import_path.default.join(process.cwd(), "routes");
120
+ const cronDir = options.cronDir || process.cwd();
121
+ const enableProxy = options.proxy !== false;
122
+ const app = (0, import_express.default)();
123
+ app.use((0, import_cors.default)());
124
+ if (enableProxy) {
125
+ setupProxy(app, port);
126
+ }
127
+ app.use(import_express.default.json());
128
+ app.get("/api/health", (_req, res) => {
129
+ res.json({ status: "ok" });
130
+ });
131
+ if (import_fs.default.existsSync(routesDir)) {
132
+ for (const file of import_fs.default.readdirSync(routesDir)) {
133
+ if (!file.endsWith(".js") && !file.endsWith(".ts")) continue;
134
+ const name = file.replace(/\.(js|ts)$/, "");
135
+ try {
136
+ const route = require(import_path.default.join(routesDir, file));
137
+ const handler = route.default || route;
138
+ if (typeof handler === "function") {
139
+ app.use(`/api/${name}`, handler);
140
+ console.log(`Route registered: /api/${name}`);
141
+ }
142
+ } catch (err) {
143
+ console.error(`Failed to load route ${file}: ${err.message}`);
144
+ }
145
+ }
146
+ }
147
+ const schemaDir = options.routesDir ? import_path.default.join(options.routesDir, "..", "db") : import_path.default.join(process.cwd(), "db");
148
+ setupSchemaSync(app, schemaDir);
149
+ setupCron(app, cronDir);
150
+ return {
151
+ app,
152
+ port,
153
+ async start() {
154
+ return new Promise((resolve) => {
155
+ app.listen(port, async () => {
156
+ console.log(`Backend listening on port ${port}`);
157
+ await schemaSync.run();
158
+ schemaSync.watch();
159
+ resolve();
160
+ });
161
+ });
162
+ }
163
+ };
164
+ }
165
+ function env2(surfName, legacyName) {
166
+ return process.env[surfName] || process.env[legacyName];
167
+ }
168
+ function setupProxy(app, port) {
169
+ const gatewayUrl = env2("SURF_DEPLOYED_GATEWAY_URL", "GATEWAY_URL");
170
+ const appToken = env2("SURF_DEPLOYED_APP_TOKEN", "APP_TOKEN");
171
+ const proxyBase = env2("SURF_SANDBOX_PROXY_BASE", "DATA_PROXY_BASE");
172
+ const isDeployed = Boolean(gatewayUrl && appToken);
173
+ const bufferResponse = (0, import_http_proxy_middleware.responseInterceptor)(async (buf) => buf);
174
+ if (isDeployed) {
175
+ app.use("/proxy", (0, import_http_proxy_middleware.createProxyMiddleware)({
176
+ target: gatewayUrl,
177
+ changeOrigin: true,
178
+ selfHandleResponse: true,
179
+ pathRewrite: (p) => "/gateway/v1" + p,
180
+ headers: {
181
+ Authorization: `Bearer ${appToken}`,
182
+ "Accept-Encoding": "identity"
183
+ },
184
+ on: { proxyRes: bufferResponse }
185
+ }));
186
+ const loopback = `http://127.0.0.1:${port}/proxy`;
187
+ process.env.SURF_SANDBOX_PROXY_BASE = loopback;
188
+ process.env.DATA_PROXY_BASE = loopback;
189
+ } else if (proxyBase) {
190
+ const target = proxyBase.replace(/\/proxy$/, "");
191
+ app.use((0, import_http_proxy_middleware.createProxyMiddleware)({
192
+ target,
193
+ changeOrigin: true,
194
+ selfHandleResponse: true,
195
+ pathFilter: "/proxy",
196
+ on: {
197
+ proxyReq: (proxyReq, req) => {
198
+ console.log(`[proxy] >> ${req.method} ${req.originalUrl}`);
199
+ },
200
+ proxyRes: (0, import_http_proxy_middleware.responseInterceptor)(async (buf, proxyRes, req) => {
201
+ const status = proxyRes.statusCode;
202
+ const tag = status && status >= 400 ? "ERR" : "OK";
203
+ console.log(`[proxy] << ${status} ${tag} ${req.method} ${req.originalUrl} bytes=${buf.length}`);
204
+ return buf;
205
+ }),
206
+ error: (err, req, res) => {
207
+ console.error(`[proxy] !! ${req.method} ${req.originalUrl} error: ${err.message}`);
208
+ if (!res.headersSent) res.status(502).json({ error: err.message });
209
+ }
210
+ }
211
+ }));
212
+ }
213
+ }
214
+ var schemaSync = { run: async () => {
215
+ }, watch: () => {
216
+ } };
217
+ function setupSchemaSync(app, schemaDir) {
218
+ let syncing = false;
219
+ let schemaReady = false;
220
+ async function doSyncSchema() {
221
+ if (syncing) return;
222
+ syncing = true;
223
+ try {
224
+ const schemaPath = import_path.default.join(schemaDir, "schema.js");
225
+ if (!import_fs.default.existsSync(schemaPath)) return;
226
+ try {
227
+ delete require.cache[require.resolve(schemaPath)];
228
+ } catch {
229
+ }
230
+ let schema;
231
+ try {
232
+ schema = require(schemaPath);
233
+ } catch (err) {
234
+ if (err instanceof SyntaxError) {
235
+ console.log("DB: schema.js has syntax error, waiting for next change...");
236
+ return;
237
+ }
238
+ if (err.message.includes("Cannot find module") || err.message.includes("is not a function")) {
239
+ return;
240
+ }
241
+ throw err;
242
+ }
243
+ let getTableConfig;
244
+ try {
245
+ getTableConfig = require("drizzle-orm/pg-core").getTableConfig;
246
+ } catch {
247
+ return;
248
+ }
249
+ const tables = Object.values(schema).filter(
250
+ (t) => t && typeof t === "object" && /* @__PURE__ */ Symbol.for("drizzle:Name") in t
251
+ );
252
+ if (tables.length === 0) return;
253
+ const { get: dbGet, post: dbPost } = await Promise.resolve().then(() => (init_client(), client_exports));
254
+ await dbPost("db/provision");
255
+ const existing = (await dbGet("db/tables")).map((t) => t.name);
256
+ const missing = tables.filter((t) => !existing.includes(getTableConfig(t).name));
257
+ if (missing.length > 0) {
258
+ const { generateDrizzleJson, generateMigration } = require("drizzle-kit/api");
259
+ const missingSchema = {};
260
+ for (const t of missing) missingSchema[getTableConfig(t).name] = t;
261
+ const sqls = await generateMigration(generateDrizzleJson({}), generateDrizzleJson(missingSchema));
262
+ for (const sql of sqls) {
263
+ for (let attempt = 0; attempt < 2; attempt++) {
264
+ try {
265
+ await dbPost("db/query", { sql, params: [] });
266
+ console.log(`DB: Executed: ${sql.slice(0, 80)}...`);
267
+ break;
268
+ } catch (err) {
269
+ if (attempt === 0) {
270
+ console.warn(`DB: Retrying after: ${err.message}`);
271
+ await new Promise((r) => setTimeout(r, 1500));
272
+ } else {
273
+ console.error(`DB: Failed: ${sql.slice(0, 80)}... \u2014 ${err.message}`);
274
+ }
275
+ }
276
+ }
277
+ }
278
+ }
279
+ const existingTables = tables.filter((t) => existing.includes(getTableConfig(t).name));
280
+ for (const t of existingTables) {
281
+ const cfg = getTableConfig(t);
282
+ try {
283
+ const live = await dbGet("db/table-schema", { table: cfg.name });
284
+ const liveCols = new Set((live.columns || []).map((c) => c.name));
285
+ for (const col of cfg.columns) {
286
+ if (!liveCols.has(col.name)) {
287
+ const colType = col.getSQLType();
288
+ const ddl = `ALTER TABLE "${cfg.name}" ADD COLUMN IF NOT EXISTS "${col.name}" ${colType}`;
289
+ try {
290
+ await dbPost("db/query", { sql: ddl, params: [] });
291
+ console.log(`DB: Added column ${col.name} to ${cfg.name}`);
292
+ } catch (err) {
293
+ console.warn(`DB: Failed to add column ${col.name} to ${cfg.name}: ${err.message}`);
294
+ }
295
+ }
296
+ }
297
+ } catch (err) {
298
+ console.warn(`DB: Column check failed for ${cfg.name}: ${err.message}`);
299
+ }
300
+ }
301
+ } finally {
302
+ syncing = false;
303
+ }
304
+ }
305
+ async function syncWithRetry(retries = 3, delay = 2e3) {
306
+ for (let i = 0; i < retries; i++) {
307
+ try {
308
+ await doSyncSchema();
309
+ return;
310
+ } catch (err) {
311
+ console.error(`DB schema sync attempt ${i + 1}/${retries} failed: ${err.message}`);
312
+ if (i < retries - 1) await new Promise((r) => setTimeout(r, delay * (i + 1)));
313
+ }
314
+ }
315
+ console.error("DB schema sync failed after all retries");
316
+ }
317
+ app.post("/api/__sync-schema", async (_req, res) => {
318
+ try {
319
+ await syncWithRetry(2, 1500);
320
+ res.json({ ok: true });
321
+ } catch (err) {
322
+ res.status(500).json({ ok: false, error: err.message });
323
+ }
324
+ });
325
+ app.use("/api", (req, res, next) => {
326
+ if (schemaReady || req.path === "/health" || req.path === "/__sync-schema" || req.path.startsWith("/cron")) return next();
327
+ res.status(503).json({ error: "Database schema initializing..." });
328
+ });
329
+ schemaSync.run = async () => {
330
+ try {
331
+ await syncWithRetry();
332
+ schemaReady = true;
333
+ console.log("Schema sync complete, API ready");
334
+ } catch {
335
+ schemaReady = true;
336
+ console.warn("Schema sync failed, proceeding anyway");
337
+ }
338
+ };
339
+ schemaSync.watch = () => {
340
+ const schemaPath = import_path.default.join(schemaDir, "schema.js");
341
+ if (!import_fs.default.existsSync(schemaPath)) return;
342
+ let debounce = null;
343
+ import_fs.default.watchFile(schemaPath, { interval: 2e3 }, () => {
344
+ if (debounce) clearTimeout(debounce);
345
+ debounce = setTimeout(async () => {
346
+ console.log("DB: schema.js changed, re-syncing tables...");
347
+ try {
348
+ await syncWithRetry(2, 1500);
349
+ console.log("DB: schema re-sync complete");
350
+ } catch (err) {
351
+ console.error(`DB: schema re-sync failed: ${err.message}`);
352
+ }
353
+ }, 1e3);
354
+ });
355
+ };
356
+ }
357
+ function setupCron(app, cronDir) {
358
+ const cronJobs = /* @__PURE__ */ new Map();
359
+ const cronState = /* @__PURE__ */ new Map();
360
+ let cronTasks = [];
361
+ function loadCronJobs() {
362
+ for (const [, job] of cronJobs) {
363
+ try {
364
+ job.stop();
365
+ } catch (_) {
366
+ }
367
+ }
368
+ cronJobs.clear();
369
+ const cronPath = import_path.default.join(cronDir, "cron.json");
370
+ if (!import_fs.default.existsSync(cronPath)) return;
371
+ let tasks;
372
+ try {
373
+ tasks = JSON.parse(import_fs.default.readFileSync(cronPath, "utf-8"));
374
+ } catch (e) {
375
+ console.error("Failed to parse cron.json:", e.message);
376
+ return;
377
+ }
378
+ if (!Array.isArray(tasks)) {
379
+ console.error("cron.json must be an array");
380
+ return;
381
+ }
382
+ cronTasks = tasks;
383
+ for (const task of tasks) {
384
+ if (!task.enabled) continue;
385
+ const timeoutMs = (task.timeout || 300) * 1e3;
386
+ let handlerMod;
387
+ try {
388
+ handlerMod = require(import_path.default.join(cronDir, task.handler));
389
+ } catch (e) {
390
+ console.error(`Failed to load cron handler ${task.handler}:`, e.message);
391
+ continue;
392
+ }
393
+ if (typeof handlerMod.handler !== "function") {
394
+ console.error(`Cron handler ${task.handler} has no handler function`);
395
+ continue;
396
+ }
397
+ if (!cronState.has(task.id)) {
398
+ cronState.set(task.id, { lastRunAt: null, lastStatus: null, lastError: null });
399
+ }
400
+ const job = new import_croner.Cron(task.schedule, async () => {
401
+ const state = cronState.get(task.id);
402
+ state.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
403
+ try {
404
+ await Promise.race([
405
+ Promise.resolve(handlerMod.handler()),
406
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Cron task timed out")), timeoutMs))
407
+ ]);
408
+ state.lastStatus = "success";
409
+ state.lastError = null;
410
+ console.log(`Cron [${task.id}] ${task.name}: success`);
411
+ } catch (e) {
412
+ state.lastStatus = "error";
413
+ state.lastError = e.message;
414
+ console.error(`Cron [${task.id}] ${task.name}: ${e.message}`);
415
+ }
416
+ });
417
+ cronJobs.set(task.id, job);
418
+ console.log(`Cron registered: [${task.id}] ${task.name} (${task.schedule})`);
419
+ }
420
+ }
421
+ const envFn = (s, l) => process.env[s] || process.env[l];
422
+ app.use("/api/cron", (req, res, next) => {
423
+ const appToken = envFn("SURF_DEPLOYED_APP_TOKEN", "APP_TOKEN");
424
+ if (!appToken) return next();
425
+ const auth = req.headers.authorization;
426
+ if (!auth || auth !== `Bearer ${appToken}`) {
427
+ return res.status(401).json({ error: "Unauthorized" });
428
+ }
429
+ next();
430
+ });
431
+ app.get("/api/cron", (_req, res) => {
432
+ res.json(cronTasks.map((t) => {
433
+ const state = cronState.get(t.id) || { lastRunAt: null, lastStatus: null, lastError: null };
434
+ const job = cronJobs.get(t.id);
435
+ return {
436
+ ...t,
437
+ lastRunAt: state.lastRunAt,
438
+ lastStatus: state.lastStatus,
439
+ lastError: state.lastError,
440
+ nextRun: job ? job.nextRun()?.toISOString() || null : null
441
+ };
442
+ }));
443
+ });
444
+ app.post("/api/cron", (req, res) => {
445
+ const { id, name, schedule, handler, enabled = true, timeout = 300 } = req.body || {};
446
+ if (!id || !name || !schedule || !handler) {
447
+ return res.status(400).json({ error: "Missing required fields: id, name, schedule, handler" });
448
+ }
449
+ try {
450
+ const testJob = new import_croner.Cron(schedule);
451
+ const next1 = testJob.nextRun();
452
+ const next2 = next1 ? testJob.nextRun(next1) : null;
453
+ testJob.stop();
454
+ if (next1 && next2 && next2.getTime() - next1.getTime() < 6e4) {
455
+ return res.status(400).json({ error: "Minimum interval between runs must be at least 1 minute" });
456
+ }
457
+ } catch (err) {
458
+ return res.status(400).json({ error: `Invalid cron expression: ${err.message}` });
459
+ }
460
+ const cronPath = import_path.default.join(cronDir, "cron.json");
461
+ let tasks = [];
462
+ try {
463
+ if (import_fs.default.existsSync(cronPath)) tasks = JSON.parse(import_fs.default.readFileSync(cronPath, "utf8"));
464
+ } catch {
465
+ tasks = [];
466
+ }
467
+ if (tasks.some((t) => t.id === id)) {
468
+ return res.status(409).json({ error: `Task with id "${id}" already exists` });
469
+ }
470
+ const newTask = { id, name, schedule, handler, enabled, timeout };
471
+ tasks.push(newTask);
472
+ import_fs.default.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
473
+ loadCronJobs();
474
+ res.status(201).json(newTask);
475
+ });
476
+ app.patch("/api/cron/:id", (req, res) => {
477
+ const cronPath = import_path.default.join(cronDir, "cron.json");
478
+ let tasks = [];
479
+ try {
480
+ if (import_fs.default.existsSync(cronPath)) tasks = JSON.parse(import_fs.default.readFileSync(cronPath, "utf8"));
481
+ } catch {
482
+ tasks = [];
483
+ }
484
+ const idx = tasks.findIndex((t) => t.id === req.params.id);
485
+ if (idx === -1) return res.status(404).json({ error: "Task not found" });
486
+ const updates = req.body || {};
487
+ if (updates.schedule) {
488
+ try {
489
+ const testJob = new import_croner.Cron(updates.schedule);
490
+ const next1 = testJob.nextRun();
491
+ const next2 = next1 ? testJob.nextRun(next1) : null;
492
+ testJob.stop();
493
+ if (next1 && next2 && next2.getTime() - next1.getTime() < 6e4) {
494
+ return res.status(400).json({ error: "Minimum interval between runs must be at least 1 minute" });
495
+ }
496
+ } catch (err) {
497
+ return res.status(400).json({ error: `Invalid cron expression: ${err.message}` });
498
+ }
499
+ }
500
+ tasks[idx] = { ...tasks[idx], ...updates, id: req.params.id };
501
+ import_fs.default.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
502
+ loadCronJobs();
503
+ res.json(tasks[idx]);
504
+ });
505
+ app.delete("/api/cron/:id", (req, res) => {
506
+ const cronPath = import_path.default.join(cronDir, "cron.json");
507
+ let tasks = [];
508
+ try {
509
+ if (import_fs.default.existsSync(cronPath)) tasks = JSON.parse(import_fs.default.readFileSync(cronPath, "utf8"));
510
+ } catch {
511
+ tasks = [];
512
+ }
513
+ const idx = tasks.findIndex((t) => t.id === req.params.id);
514
+ if (idx === -1) return res.status(404).json({ error: "Task not found" });
515
+ tasks.splice(idx, 1);
516
+ import_fs.default.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
517
+ cronState.delete(req.params.id);
518
+ loadCronJobs();
519
+ res.json({ ok: true });
520
+ });
521
+ app.post("/api/cron/:id/run", async (req, res) => {
522
+ const job = cronJobs.get(req.params.id);
523
+ if (!job) return res.status(404).json({ error: "Task not found or not enabled" });
524
+ try {
525
+ job.trigger();
526
+ res.json({ ok: true, message: `Task ${req.params.id} triggered` });
527
+ } catch (err) {
528
+ res.status(500).json({ error: err.message });
529
+ }
530
+ });
531
+ loadCronJobs();
532
+ }
533
+
534
+ // src/data/data-api.ts
535
+ init_client();
536
+
537
+ // src/data/categories/exchange.ts
538
+ init_client();
539
+ var exchange = {
540
+ /** Get order book bid/ask levels with computed stats: spread, spread percentage, mid-price, and total bid/ask depth. Use `limit` to control the number of price levels (1–100, default 20). Set `type=swap` to query perpetual contract order books instead of spot. */
541
+ depth: (params) => get("exchange/depth", params),
542
+ /** Get historical funding rate records for a perpetual contract. Use `from` to set the start time and `limit` to control result count. For longer history, paginate by using the last returned timestamp as the next `from` value. Note: not all exchanges support historical queries via `from`; some only return recent data regardless. For the latest funding rate snapshot, see `/exchange/perp?fields=funding`. */
543
+ funding_history: (params) => get("exchange/funding-history", params),
544
+ /** Get OHLCV candlestick data with period summary stats (high, low, total volume). Supports 15 intervals from `1m` to `1M`. Use `from` to set the start time and `limit` to control how many candles to return. For longer ranges, paginate by using the last returned candle's timestamp as the next `from` value. Exchange-side limits vary (200–1000 per request). Set `type=swap` to query perpetual contract candles instead of spot. */
545
+ klines: (params) => get("exchange/klines", params),
546
+ /** Get historical long/short ratio for a perpetual contract — ratio value, long account percentage, and short account percentage. Use `interval` (`1h`, `4h`, `1d`) for granularity, `from` for start time, and `limit` for result count. For longer history, paginate by using the last returned timestamp as the next `from` value. Note: not all exchanges support historical queries via `from`; some only return recent data regardless. Just pass the base pair (e.g. `pair=BTC/USDT`). For aggregated cross-exchange long/short ratio, see `/market/futures`. */
547
+ long_short_ratio: (params) => get("exchange/long-short-ratio", params),
548
+ /** List trading pairs available on an exchange. Filter by `type` (`spot`, `swap`, `future`, `option`) or free-text `search`. Returns pair name, base/quote currencies, market type, active status, and default fee rates. Use the returned `pair` values as the `pair` parameter in other exchange endpoints. */
549
+ markets: (params) => get("exchange/markets", params),
550
+ /** Get a combined snapshot of perpetual contract data for a pair. Use `fields` to select which sub-resources to fetch: `funding` (current funding rate, next settlement, mark/index price) and/or `oi` (open interest in contracts and USD). Just pass the base pair (e.g. `pair=BTC/USDT`). The `:USDT` swap suffix is added automatically. */
551
+ perp: (params) => get("exchange/perp", params),
552
+ /** Get the real-time ticker for a trading pair — last price, bid/ask, 24h high/low, 24h volume, and 24h price change. Set `type=swap` to query perpetual contract prices instead of spot. For historical price trends, use `/market/price`. */
553
+ price: (params) => get("exchange/price", params)
554
+ };
555
+
556
+ // src/data/categories/fund.ts
557
+ init_client();
558
+ var fund = {
559
+ /** Get a fund's **profile metadata**: X accounts, team members, recent research, and invested project count. This does NOT return the list of investments — use `/fund/portfolio` for that. Lookup by UUID (`id`) or name (`q`). Returns 404 if not found. */
560
+ detail: (params) => get("fund/detail", params),
561
+ /** List investment rounds for a fund's portfolio, sorted by date (newest first). A project may appear multiple times if the fund participated in multiple rounds. Each entry includes project name, logo, date, raise amount, and lead investor status. Lookup by UUID (`id`) or name (`q`). */
562
+ portfolio: (params) => get("fund/portfolio", params),
563
+ /** List top-ranked funds by metric. Available metrics: `tier` (lower is better), `portfolio_count` (number of invested projects). */
564
+ ranking: (params) => get("fund/ranking", params)
565
+ };
566
+
567
+ // src/data/categories/kalshi.ts
568
+ init_client();
569
+ var kalshi = {
570
+ /** Get Kalshi events with nested markets, optionally filtered by `event_ticker`. Each event includes market count and a list of markets. Data refresh: ~30 minutes */
571
+ events: (params) => get("kalshi/events", params),
572
+ /** Get Kalshi markets, optionally filtered by `market_ticker`. Each market includes price, volume, and status. Data refresh: ~30 minutes */
573
+ markets: (params) => get("kalshi/markets", params),
574
+ /** Get daily open interest history for a Kalshi market filtered by `time_range`. Data refresh: ~30 minutes */
575
+ open_interest: (params) => get("kalshi/open-interest", params),
576
+ /** Get price history for a Kalshi market. Use `interval=1d` for daily OHLC from market reports (~30 min delay), or `interval=latest` for real-time price from trades. Data refresh: ~30 minutes (daily), real-time (latest) */
577
+ prices: (params) => get("kalshi/prices", params),
578
+ /** Get top-ranked Kalshi markets by last day's `notional_volume_usd` or `open_interest`. Filter by `status`. Data refresh: ~30 minutes */
579
+ ranking: (params) => get("kalshi/ranking", params),
580
+ /** Get individual trade records for a Kalshi market. Filter by `taker_side`, `min_contracts`, and date range. Sort by `timestamp` or `num_contracts`. Data refresh: real-time */
581
+ trades: (params) => get("kalshi/trades", params),
582
+ /** Get daily trading volume history for a Kalshi market filtered by `time_range`. Data refresh: ~30 minutes */
583
+ volumes: (params) => get("kalshi/volumes", params)
584
+ };
585
+
586
+ // src/data/categories/market.ts
587
+ init_client();
588
+ var market = {
589
+ /** Get daily ETF flow history for US spot ETFs — net flow (USD), token price, and per-ticker breakdown. Sorted by date descending. `symbol`: `BTC` or `ETH`. */
590
+ etf: (params) => get("market/etf", params),
591
+ /** Get Bitcoin Fear & Greed Index history — index value (0-100), classification label, and BTC price at each data point. Sorted newest-first. Use `from`/`to` to filter by date range. */
592
+ fear_greed: (params) => get("market/fear-greed", params),
593
+ /** Get futures market data across all tracked tokens — open interest, funding rate, long/short ratio, and 24h volume. Sort by `sort_by` (default: volume_24h). */
594
+ futures: (params) => get("market/futures", params),
595
+ /** Get OHLC-style aggregated liquidation data for a token on a specific exchange. Filter by `symbol`, `exchange`, and `interval`. Useful for charting liquidation volume over time. */
596
+ liquidation_chart: (params) => get("market/liquidation-chart", params),
597
+ /** Get liquidation breakdown by exchange — total, long, and short volumes in USD. Filter by `symbol` and `time_range` (`1h`, `4h`, `12h`, `24h`). */
598
+ liquidation_exchange_list: (params) => get("market/liquidation-exchange-list", params),
599
+ /** Get individual large liquidation orders above a USD threshold (`min_amount`, default 10000). Filter by `exchange` and `symbol`. For aggregate totals and long/short breakdown by exchange, use `/market/liquidation/exchange-list`. For historical liquidation charts, use `/market/liquidation/chart`. */
600
+ liquidation_order: (params) => get("market/liquidation-order", params),
601
+ /** Get on-chain indicator time-series for BTC or ETH. Metrics: `nupl`, `sopr`, `mvrv`, `puell-multiple`, `nvm`, `nvt`, `nvt-golden-cross`, `exchange-flows` (inflow/outflow/netflow/reserve). */
602
+ onchain_indicator: (params) => get("market/onchain-indicator", params),
603
+ /** Get options market data — open interest, volume, put/call ratio, and max pain price for a `symbol`. */
604
+ options: (params) => get("market/options", params),
605
+ /** Get historical price data points for a token. Use `time_range` for predefined windows (`1d`, `7d`, `14d`, `30d`, `90d`, `180d`, `365d`, `max`) or `from`/`to` for a custom date range (Unix timestamp or YYYY-MM-DD). Granularity is automatic: 5-min for 1d, hourly for 7-90d, daily for 180d+. */
606
+ price: (params) => get("market/price", params),
607
+ /** Get a technical indicator for a trading pair on a given exchange and interval. Set `from`/`to` for time-series mode, omit for latest value. Use `options` for indicator-specific tuning (e.g. `period:7`, `fast_period:8,slow_period:21,signal_period:5`, `period:10,stddev:1.5`). Indicators: `rsi`, `macd`, `ema`, `sma`, `bbands`, `stoch`, `adx`, `atr`, `cci`, `obv`, `vwap`, `dmi`, `ichimoku`, `supertrend`. */
608
+ price_indicator: (params) => get("market/price-indicator", params),
609
+ /** List tokens ranked by metric. Available metrics: `market_cap`, `top_gainers`, `top_losers`, `volume`. Note: `top_gainers` and `top_losers` rank by 24h price change within the top 250 coins by market cap. For circulating supply, FDV, ATH/ATL, use `/project/detail?fields=token_info`. */
610
+ ranking: (params) => get("market/ranking", params)
611
+ };
612
+
613
+ // src/data/categories/news.ts
614
+ init_client();
615
+ var news = {
616
+ /** Returns the full content of a single news article by its ID (returned as `id` in feed and search results). */
617
+ detail: (params) => get("news/detail", params),
618
+ /** Browse crypto news from major sources. Filter by `source` (enum), `project`, and time range (`from`/`to`). Sort by `recency` (default) or `trending`. Use the detail endpoint with article `id` for full content. */
619
+ feed: (params) => get("news/feed", params)
620
+ };
621
+
622
+ // src/data/categories/onchain.ts
623
+ init_client();
624
+ var onchain = {
625
+ /** List bridge protocols ranked by total USD volume over a time range. */
626
+ bridge_ranking: (params) => get("onchain/bridge-ranking", params),
627
+ /** Get the current gas price for an EVM chain via `eth_gasPrice` JSON-RPC. Returns gas price in both wei (raw) and Gwei (human-readable). **Supported chains:** `ethereum`, `polygon`, `bsc`, `arbitrum`, `optimism`, `base`, `avalanche`, `fantom`, `linea`, `cyber`. */
628
+ gas_price: (params) => get("onchain/gas-price", params),
629
+ /** Execute a structured JSON query on blockchain data. No raw SQL needed — specify source, fields, filters, sort, and pagination. All tables live in the **agent** database. Use `GET /v1/onchain/schema` to discover available tables and their columns. - Source format: `agent.<table_name>` like `agent.ethereum_transactions` or `agent.ethereum_dex_trades` - Max 10,000 rows (default 20), 30s timeout. - **Always filter on block_date** — it is the partition key. Without it, queries scan billions of rows and will timeout. - **Data refresh:** ~24 hours. */
630
+ structured_query: (body) => post("onchain/structured-query", body),
631
+ /** Get table metadata — database, table, column names, types, and comments for all available on-chain databases. */
632
+ schema: () => get("onchain/schema"),
633
+ /** Execute a raw SQL SELECT query against blockchain data. All tables live in the **agent** database. Use `GET /v1/onchain/schema` to discover available tables and their columns before writing queries. */
634
+ sql: (body) => post("onchain/sql", body),
635
+ /** Get transaction details by hash. All numeric fields are hex-encoded — use parseInt(hex, 16) to convert. **Supported chains:** `ethereum`, `polygon`, `bsc`, `arbitrum`, `optimism`, `base`, `avalanche`, `fantom`, `linea`, `cyber`. Returns 404 if the transaction is not found. */
636
+ tx: (params) => get("onchain/tx", params),
637
+ /** List DeFi yield pools ranked by APY or TVL. Returns the latest snapshot. Filter by protocol. */
638
+ yield_ranking: (params) => get("onchain/yield-ranking", params)
639
+ };
640
+
641
+ // src/data/categories/polymarket.ts
642
+ init_client();
643
+ var polymarket = {
644
+ /** Get trade and redemption activity for a Polymarket wallet. Data refresh: ~30 minutes */
645
+ activity: (params) => get("polymarket/activity", params),
646
+ /** Get Polymarket events with nested markets, optionally filtered by `event_slug`. Each event includes aggregated status, volume, and a list of markets with `side_a`/`side_b` outcomes. Data refresh: ~30 minutes */
647
+ events: (params) => get("polymarket/events", params),
648
+ /** Get Polymarket markets, optionally filtered by `market_slug`. Each market includes `side_a` and `side_b` outcomes. Current prices are available via `/polymarket/prices` using the `condition_id`. Data refresh: ~30 minutes */
649
+ markets: (params) => get("polymarket/markets", params),
650
+ /** Get daily open interest history for a Polymarket market. Data refresh: ~30 minutes */
651
+ open_interest: (params) => get("polymarket/open-interest", params),
652
+ /** Get wallet positions on Polymarket markets. Data refresh: ~30 minutes */
653
+ positions: (params) => get("polymarket/positions", params),
654
+ /** Get aggregated price history for a Polymarket market. Use `interval=latest` for the most recent price snapshot. Data refresh: ~30 minutes */
655
+ prices: (params) => get("polymarket/prices", params),
656
+ /** Get top-ranked Polymarket markets by `volume_24h`, `volume_7d`, `open_interest`, or `trade_count`. Filter by `status` and `end_before`. Data refresh: ~30 minutes */
657
+ ranking: (params) => get("polymarket/ranking", params),
658
+ /** Get paginated trade records for a Polymarket market or wallet. Filter by `condition_id` (market) or `address` (wallet), plus `outcome_label`, `min_amount`, and date range. At least one of `condition_id` or `address` is required. Sort by `newest`, `oldest`, or `largest`. Data refresh: ~30 minutes */
659
+ trades: (params) => get("polymarket/trades", params),
660
+ /** Get trading volume and trade count history for a Polymarket market. Data refresh: ~30 minutes */
661
+ volumes: (params) => get("polymarket/volumes", params)
662
+ };
663
+
664
+ // src/data/categories/prediction_market.ts
665
+ init_client();
666
+ var prediction_market = {
667
+ /** Get daily notional volume and open interest aggregated by category across Kalshi and Polymarket. Filter by `source` or `category`. Data refresh: daily */
668
+ category_metrics: (params) => get("prediction-market/category-metrics", params)
669
+ };
670
+
671
+ // src/data/categories/project.ts
672
+ init_client();
673
+ var project = {
674
+ /** Get time-series DeFi metrics for a project. Available metrics: `volume`, `fee`, `fees`, `revenue`, `tvl`, `users`. Lookup by UUID (`id`) or name (`q`). Filter by `chain` and date range (`from`/`to`). Returns 404 if the project is not found. **Note:** this endpoint only returns data for DeFi protocol projects (e.g. `aave`, `uniswap`, `lido`, `makerdao`). Use `q` with a DeFi protocol name. */
675
+ defi_metrics: (params) => get("project/defi-metrics", params),
676
+ /** Get top DeFi projects ranked by a protocol metric. Available metrics: `tvl`, `revenue`, `fees`, `volume`, `users`. */
677
+ defi_ranking: (params) => get("project/defi-ranking", params),
678
+ /** Get multiple project sub-resources in a single request. Use `fields` to select: `overview`, `token_info`, `tokenomics`, `funding`, `team`, `contracts`, `social`, `tge_status`. **Accepts project names directly** via `q` (e.g. `?q=aave`) — no need to call `/search/project` first. Also accepts UUID via `id`. Returns 404 if not found. For DeFi metrics (TVL, fees, revenue, volume, users) and per-chain breakdown, use `/project/defi/metrics`. */
679
+ detail: (params) => get("project/detail", params)
680
+ };
681
+
682
+ // src/data/categories/search.ts
683
+ init_client();
684
+ var search = {
685
+ /** Search and filter airdrop opportunities by keyword, status, reward type, and task type. Returns paginated results with optional task details. */
686
+ airdrop: (params) => get("search/airdrop", params),
687
+ /** Search project events by keyword, optionally filtered by `type`. Valid types: `launch`, `upgrade`, `partnership`, `news`, `airdrop`, `listing`, `twitter`. Lookup by UUID (`id`) or name (`q`). Returns 404 if the project is not found. */
688
+ events: (params) => get("search/events", params),
689
+ /** Search funds by keyword. Returns matching funds with name, tier, type, logo, and top invested projects. */
690
+ fund: (params) => get("search/fund", params),
691
+ /** Search Kalshi events by keyword and/or category. Filter by keyword matching event title, subtitle, or market title; or by category. At least one of `q` or `category` is required. Returns events with nested markets. Data refresh: ~30 minutes */
692
+ kalshi: (params) => get("search/kalshi", params),
693
+ /** Search crypto news articles by keyword. Returns top 10 results ranked by relevance with highlighted matching fragments. */
694
+ news: (params) => get("search/news", params),
695
+ /** Search Polymarket events by keyword, tags, and/or category. Filter by keyword matching market question, event title, or description; by comma-separated tag labels; or by Surf-curated category. At least one of `q`, `tags`, or `category` is required. Returns events with nested markets ranked by volume. Data refresh: ~30 minutes */
696
+ polymarket: (params) => get("search/polymarket", params),
697
+ /** Search crypto projects by keyword. Returns matching projects with name, description, chains, and logo. */
698
+ project: (params) => get("search/project", params),
699
+ /** Search X (Twitter) users by keyword. Returns user profiles with handle, display name, bio, follower count, and avatar. */
700
+ social_people: (params) => get("search/social-people", params),
701
+ /** Search X (Twitter) posts by keyword or `from:handle` syntax. Returns posts with author, content, engagement metrics, and timestamp. To load more results, check `meta.has_more`; if true, pass `meta.next_cursor` as the `cursor` query parameter in the next request. */
702
+ social_posts: (params) => get("search/social-posts", params),
703
+ /** Search wallets by ENS name, address label, or address prefix. Returns matching wallet addresses with entity labels. */
704
+ wallet: (params) => get("search/wallet", params),
705
+ /** Search web pages, articles, and content by keyword. Filter by domain with `site` like `coindesk.com`. Returns titles, URLs, and content snippets. */
706
+ web: (params) => get("search/web", params)
707
+ };
708
+
709
+ // src/data/categories/social.ts
710
+ init_client();
711
+ var social = {
712
+ /** Get a **point-in-time snapshot** of social analytics: sentiment score, follower geo breakdown, and top smart followers. Use `fields` to select: `sentiment`, `follower_geo`, `smart_followers`. Lookup by X account ID (`x_id`) or project name (`q`, e.g. `uniswap`, `solana`). The `q` parameter must be a crypto project name, not a personal Twitter handle. Returns 404 if the project has no linked Twitter account. For sentiment **trends over time**, use `/social/mindshare` instead. */
713
+ detail: (params) => get("social/detail", params),
714
+ /** Get mindshare (social view count) **time-series trend** for a project, aggregated by `interval`. Use this when the user asks about sentiment **trends**, mindshare **over time**, or social momentum changes. `interval` can be `5m`, `1h`, `1d`, or `7d`. Filter by date range with `from`/`to` (Unix seconds). Lookup by name (`q`). For a **point-in-time snapshot** of social analytics (sentiment score, follower geo, smart followers), use `/social/detail` instead. */
715
+ mindshare: (params) => get("social/mindshare", params),
716
+ /** Get top crypto projects ranked by mindshare (social view count), sourced directly from Argus real-time data (refreshed every 5 minutes). Filter by `tag` to scope to a category (e.g. `dex`, `l1`, `meme`). Use `time_range` (`24h`, `48h`, `7d`, `30d`) to control the ranking window. Supports `limit`/`offset` pagination. */
717
+ ranking: (params) => get("social/ranking", params),
718
+ /** Get smart follower count time-series for a project, sorted by date descending. Lookup by X account ID (`x_id`) or project name (`q`). The `q` parameter must be a project name (e.g. `uniswap`, `ethereum`), not a personal X handle — use `x_id` for individual accounts. Returns 404 if the project has no linked X account. */
719
+ smart_followers_history: (params) => get("social/smart-followers-history", params),
720
+ /** Returns replies/comments on a specific tweet. Lookup by `tweet_id`. */
721
+ tweet_replies: (params) => get("social/tweet-replies", params),
722
+ /** Get X (Twitter) posts by numeric post ID strings. Pass up to 100 comma-separated IDs via the `ids` query parameter. */
723
+ tweets: (params) => get("social/tweets", params),
724
+ /** Get an X (Twitter) user profile — display name, follower count, following count, and bio. Lookup by `handle` (without @). */
725
+ user: (params) => get("social/user", params),
726
+ /** Returns a list of followers for the specified handle on X (Twitter). Lookup by `handle` (without @). */
727
+ user_followers: (params) => get("social/user-followers", params),
728
+ /** Returns a list of users that the specified handle follows on X (Twitter). Lookup by `handle` (without @). */
729
+ user_following: (params) => get("social/user-following", params),
730
+ /** Get recent X (Twitter) posts by a specific user, ordered by recency. Lookup by `handle` (without @). Use `filter=original` to exclude retweets. To load more results, check `meta.has_more`; if true, pass `meta.next_cursor` as the `cursor` query parameter in the next request. */
731
+ user_posts: (params) => get("social/user-posts", params),
732
+ /** Returns recent replies by the specified handle on X (Twitter). Lookup by `handle` (without @). */
733
+ user_replies: (params) => get("social/user-replies", params)
734
+ };
735
+
736
+ // src/data/categories/token.ts
737
+ init_client();
738
+ var token = {
739
+ /** Get recent DEX swap events for a token contract address. Covers DEXes like `uniswap`, `sushiswap`, `curve`, and `balancer` on `ethereum` and `base`. Returns trading pair, amounts, USD value, and taker address. Data refresh: ~24 hours · Chain: Ethereum, Base */
740
+ dex_trades: (params) => get("token/dex-trades", params),
741
+ /** Get top token holders for a contract address — wallet address, balance, and percentage. Lookup by `address` and `chain`. Supports EVM chains and Solana. */
742
+ holders: (params) => get("token/holders", params),
743
+ /** Get token unlock time-series — unlock events with amounts and allocation breakdowns. Lookup by project UUID (`id`) or token `symbol`. Filter by date range with `from`/`to`. Defaults to the current calendar month when omitted. Returns 404 if no token found. */
744
+ tokenomics: (params) => get("token/tokenomics", params),
745
+ /** Get recent transfer events **for a specific token** (ERC-20/TRC-20 contract). Pass the **token contract address** in `address` — returns every on-chain transfer of that token regardless of sender/receiver. Each record includes sender, receiver, raw amount, and block timestamp. Use this to analyze a token's on-chain activity (e.g. large movements, distribution patterns). Lookup: `address` (token contract) + `chain`. Sort by `asc` or `desc`. Data refresh: ~24 hours · Chain: Ethereum, Base, TRON (Solana uses a different source with no delay) */
746
+ transfers: (params) => get("token/transfers", params)
747
+ };
748
+
749
+ // src/data/categories/wallet.ts
750
+ init_client();
751
+ var wallet = {
752
+ /** Get multiple wallet sub-resources in a single request. Lookup by `address`. Use `fields` to select: `balance`, `tokens`, `labels`, `nft`. Partial failures return available fields with per-field error info. Returns 422 if `fields` is invalid. */
753
+ detail: (params) => get("wallet/detail", params),
754
+ /** Get full transaction history for a wallet — swaps, transfers, and contract interactions. Lookup by `address`. Filter by `chain` — supports `ethereum`, `polygon`, `bsc`, `arbitrum`, `optimism`, `avalanche`, `fantom`, `base`. */
755
+ history: (params) => get("wallet/history", params),
756
+ /** Get entity labels for multiple wallet addresses. Pass up to 100 comma-separated addresses via the `addresses` query parameter. Returns entity name, type, and labels per address. */
757
+ labels_batch: (params) => get("wallet/labels-batch", params),
758
+ /** Get a time-series of the wallet's total net worth in USD. Returns ~288 data points at 5-minute intervals covering the last 24 hours. Fixed window — no custom time range supported. */
759
+ net_worth: (params) => get("wallet/net-worth", params),
760
+ /** Get all DeFi protocol positions for a wallet — lending, staking, LP, and farming with token breakdowns and USD values. Lookup by `address`. */
761
+ protocols: (params) => get("wallet/protocols", params),
762
+ /** Get recent token transfers **sent or received by a wallet**. Pass the **wallet address** in `address` — returns all ERC-20/SPL token transfers where this wallet is the sender or receiver. Each record includes token contract, counterparty, raw amount, and block timestamp. Use this to audit a wallet's token flow (e.g. inflows, outflows, airdrop receipts). Lookup: `address` (wallet, raw 0x hex or base58 — ENS not supported). Filter by `chain` — supports `ethereum`, `base`, `solana`. Data refresh: ~24 hours · Chain: Ethereum, Base (Solana uses a different source with no delay) */
763
+ transfers: (params) => get("wallet/transfers", params)
764
+ };
765
+
766
+ // src/data/categories/web.ts
767
+ init_client();
768
+ var web = {
769
+ /** Fetch a web page and convert it to clean, LLM-friendly markdown. Use `target_selector` to extract specific page sections and `remove_selector` to strip unwanted elements. Returns 400 if the URL is invalid or unreachable. */
770
+ fetch: (params) => get("web/fetch", params)
771
+ };
772
+
773
+ // src/data/data-api.ts
774
+ var dataApi = {
775
+ /** Escape hatch: raw GET to any endpoint path. */
776
+ get,
777
+ /** Escape hatch: raw POST to any endpoint path. */
778
+ post,
779
+ exchange,
780
+ fund,
781
+ kalshi,
782
+ market,
783
+ news,
784
+ onchain,
785
+ polymarket,
786
+ prediction_market,
787
+ project,
788
+ search,
789
+ social,
790
+ token,
791
+ wallet,
792
+ web
793
+ };
794
+ // Annotate the CommonJS export names for ESM import in node:
795
+ 0 && (module.exports = {
796
+ createServer,
797
+ dataApi
798
+ });