@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
JavaScript
// 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