UNPKG

@riverfl0w/dune-client

Version:

A TypeScript client for querying the Dune API, designed to simplify the integration of Dune's powerful analytics into your projects.

293 lines (282 loc) 8.74 kB
// src/DuneError.ts var DuneError = class extends Error { }; // src/schemas/ErrorResponse.ts import { z } from "zod"; var ErrorResponse = z.object({ error: z.string() }); var ErrorResponse_default = ErrorResponse; // src/clients/BaseClient.ts var MAX_RATE_LIMIT_DELAY = 6e4; var BaseClient = class { constructor(apiKey) { this.apiKey = apiKey; } base = "https://api.dune.com/api"; async call({ path, searchParams, schema, delay = 0, ...options }) { if (searchParams) { const search = searchParams.toString(); if (search.length > 0) { path += `?${search}`; } } const response = await fetch(`${this.base}${path}`, { ...options, headers: { ...options.headers, "x-dune-api-key": this.apiKey } }); const data = await response.json(); const hasError = await ErrorResponse_default.safeParseAsync(data); if (hasError.success) { if (hasError.data.error.match(/too many requests/)) { await new Promise((resolve) => setTimeout(resolve, delay)); if (delay < MAX_RATE_LIMIT_DELAY) { const newDelay = (delay + Math.floor(Math.random() * 1e3)) * 2; return this.call({ path, searchParams, schema, delay: newDelay, ...options }); } } throw new DuneError(hasError.data.error); } return await schema.parseAsync(data); } }; // src/schemas/CancelExecutionResponse.ts import { z as z2 } from "zod"; var CancelExecutionResponse = z2.object({ success: z2.boolean() }); var CancelExecutionResponse_default = CancelExecutionResponse; // src/schemas/ExecutionResultsResponse.ts import { z as z4 } from "zod"; // src/schemas/ResultMetadata.ts import { z as z3 } from "zod"; var ResultMetadata = z3.object({ column_names: z3.array(z3.string()), result_set_bytes: z3.number().int().nonnegative(), total_row_count: z3.number().int().nonnegative(), datapoint_count: z3.number().int().nonnegative(), pending_time_millis: z3.number().int().nonnegative(), execution_time_millis: z3.number().int().nonnegative() }); var ResultMetadata_default = ResultMetadata; // src/schemas/ExecutionResultsResponse.ts var ExecutionResultsResponse = z4.object({ execution_id: z4.string(), query_id: z4.number(), is_execution_finished: z4.boolean() }).and( z4.discriminatedUnion("state", [ z4.object({ state: z4.literal("QUERY_STATE_FAILED"), submitted_at: z4.string().datetime(), expires_at: z4.string().datetime(), execution_started_at: z4.string().datetime(), execution_ended_at: z4.string().datetime(), error: z4.object({ type: z4.string(), message: z4.string(), metadata: z4.record(z4.unknown()) }) }), z4.object({ state: z4.literal("QUERY_STATE_COMPLETED"), submitted_at: z4.string().datetime(), expires_at: z4.string().datetime(), execution_started_at: z4.string().datetime(), execution_ended_at: z4.string().datetime(), result: z4.object({ rows: z4.array(z4.record(z4.unknown())), metadata: ResultMetadata_default }) }), z4.object({ state: z4.literal("QUERY_STATE_CANCELLED"), submitted_at: z4.string().datetime(), expires_at: z4.string().datetime(), cancelled_at: z4.string().datetime() }), z4.object({ state: z4.literal("QUERY_STATE_EXPIRED") // TODO: Add more fields }) ]) ); var ExecutionResultsResponse_default = ExecutionResultsResponse; // src/schemas/ExecutionStatusResponse.ts import { z as z5 } from "zod"; var ExecutionStatusResponse = z5.object({ execution_id: z5.string(), query_id: z5.number(), is_execution_finished: z5.boolean() }).and( z5.discriminatedUnion("state", [ z5.object({ state: z5.literal("QUERY_STATE_PENDING"), queue_position: z5.number().int().nonnegative().optional(), submitted_at: z5.string().datetime() }), z5.object({ state: z5.literal("QUERY_STATE_EXECUTING"), submitted_at: z5.string().datetime(), execution_started_at: z5.string().datetime() }), z5.object({ state: z5.literal("QUERY_STATE_FAILED"), submitted_at: z5.string().datetime(), expires_at: z5.string().datetime(), execution_started_at: z5.string().datetime(), execution_ended_at: z5.string().datetime() }), z5.object({ state: z5.literal("QUERY_STATE_COMPLETED"), submitted_at: z5.string().datetime(), expires_at: z5.string().datetime(), execution_started_at: z5.string().datetime(), execution_ended_at: z5.string().datetime(), result_metadata: ResultMetadata_default }), z5.object({ state: z5.literal("QUERY_STATE_CANCELLED"), submitted_at: z5.string().datetime(), expires_at: z5.string().datetime(), cancelled_at: z5.string().datetime() }), z5.object({ state: z5.literal("QUERY_STATE_EXPIRED") // TODO: Add more fields }) ]) ); var ExecutionStatusResponse_default = ExecutionStatusResponse; // src/clients/ExecutionClient.ts var ExecutionClient = class extends BaseClient { /** * Get the status of an execution. * @see https://dune.com/docs/api/api-reference/get-results/execution-status/ */ status({ execution_id }) { return this.call({ method: "GET", path: `/v1/execution/${execution_id}/status`, schema: ExecutionStatusResponse_default }); } /** * Cancel an execution. * @see https://dune.com/docs/api/api-reference/execute-queries/cancel-execution/ */ cancel({ execution_id }) { return this.call({ method: "POST", path: `/v1/execution/${execution_id}/cancel`, schema: CancelExecutionResponse_default }); } /** * Get the results of an execution. * @see https://dune.com/docs/api/api-reference/get-results/execution-status/ */ results({ execution_id }) { return this.call({ method: "GET", path: `/v1/execution/${execution_id}/results`, schema: ExecutionResultsResponse_default }); } }; // src/schemas/ExecuteQueryResponse.ts import { z as z6 } from "zod"; var ExecuteQueryResponse = z6.object({ execution_id: z6.string() }).and( z6.discriminatedUnion("state", [ z6.object({ state: z6.literal("QUERY_STATE_PENDING") }), z6.object({ state: z6.literal("QUERY_STATE_EXECUTING") }) ]) ); var ExecuteQueryResponse_default = ExecuteQueryResponse; // src/clients/QueryClient.ts var QueryClient = class extends BaseClient { /** * Execute am existing Dune query. * @see https://dune.com/docs/api/api-reference/execute-queries/execute-query-id/ */ execute({ query_id, ...options }) { return this.call({ method: "POST", path: `/v1/query/${query_id}/execute`, schema: ExecuteQueryResponse_default, body: JSON.stringify(options) }); } /** * Get the latedst results of a Dune query. This method does NOT execute the query. * @see https://dune.com/docs/api/api-reference/get-results/latest-results/ */ results({ query_id, ignore_max_datapoints_per_request }) { const searchParams = new URLSearchParams(); if (ignore_max_datapoints_per_request) { searchParams.set("ignore_max_datapoints_per_request", "true"); } return this.call({ method: "GET", path: `/v1/query/${query_id}/results`, searchParams, schema: ExecutionResultsResponse_default }); } }; // src/DuneClient.ts var DuneClient = class { execution; query; constructor(apiKey) { this.execution = new ExecutionClient(apiKey); this.query = new QueryClient(apiKey); } /** * Convenience method to refresh the results of a query. * It will wait for the query to finish and return the results. * @param param0 * @returns */ async refresh({ cooldown = 500, ...args }) { const { execution_id } = await this.query.execute(args); for (; ; ) { const { state } = await this.execution.status({ execution_id }); if (state === "QUERY_STATE_PENDING" || state === "QUERY_STATE_EXECUTING") { await new Promise((resolve) => setTimeout(resolve, cooldown)); continue; } break; } return await this.execution.results({ execution_id }); } }; export { BaseClient, CancelExecutionResponse_default as CancelQueryResponse, DuneClient, DuneError, ErrorResponse_default as ErrorResponse, ExecuteQueryResponse_default as ExecuteQueryResponse, ExecutionClient, ExecutionResultsResponse_default as ExecutionResultResponse, ExecutionStatusResponse_default as ExecutionStatusResponse, QueryClient, ResultMetadata_default as ResultMetadata }; //# sourceMappingURL=index.js.map