exa-js
Version:
Exa SDK for Node.js and the browser
1,527 lines (1,511 loc) • 55.2 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
CreateEnrichmentParametersFormat: () => CreateEnrichmentParametersFormat,
CreateImportParametersFormat: () => CreateImportParametersFormat,
CreateWebsetParametersImportSource: () => CreateWebsetParametersImportSource,
CreateWebsetParametersSearchExcludeSource: () => CreateWebsetParametersSearchExcludeSource,
CreateWebsetSearchParametersExcludeSource: () => CreateWebsetSearchParametersExcludeSource,
CreateWebsetSearchParametersScopeSource: () => CreateWebsetSearchParametersScopeSource,
EventType: () => EventType,
EventsClient: () => EventsClient,
Exa: () => Exa2,
ExaError: () => ExaError,
HttpStatusCode: () => HttpStatusCode,
ImportFailedReason: () => ImportFailedReason,
ImportFormat: () => ImportFormat,
ImportObject: () => ImportObject,
ImportStatus: () => ImportStatus,
ImportsClient: () => ImportsClient,
MonitorObject: () => MonitorObject,
MonitorRunObject: () => MonitorRunObject,
MonitorRunStatus: () => MonitorRunStatus,
MonitorRunType: () => MonitorRunType,
MonitorStatus: () => MonitorStatus,
PreviewWebsetResponseEnrichmentsFormat: () => PreviewWebsetResponseEnrichmentsFormat,
ResearchClient: () => ResearchClient,
ScopeSourceType: () => ScopeSourceType,
UpdateMonitorStatus: () => UpdateMonitorStatus,
WebhookStatus: () => WebhookStatus,
WebsetEnrichmentFormat: () => WebsetEnrichmentFormat,
WebsetEnrichmentStatus: () => WebsetEnrichmentStatus,
WebsetEnrichmentsClient: () => WebsetEnrichmentsClient,
WebsetItemEvaluationSatisfied: () => WebsetItemEvaluationSatisfied,
WebsetItemSource: () => WebsetItemSource,
WebsetItemsClient: () => WebsetItemsClient,
WebsetMonitorsClient: () => WebsetMonitorsClient,
WebsetSearchBehavior: () => WebsetSearchBehavior,
WebsetSearchCanceledReason: () => WebsetSearchCanceledReason,
WebsetSearchStatus: () => WebsetSearchStatus,
WebsetSearchesClient: () => WebsetSearchesClient,
WebsetStatus: () => WebsetStatus,
WebsetWebhooksClient: () => WebsetWebhooksClient,
WebsetsClient: () => WebsetsClient,
default: () => index_default
});
module.exports = __toCommonJS(index_exports);
var import_cross_fetch = __toESM(require("cross-fetch"));
// src/errors.ts
var HttpStatusCode = /* @__PURE__ */ ((HttpStatusCode2) => {
HttpStatusCode2[HttpStatusCode2["BadRequest"] = 400] = "BadRequest";
HttpStatusCode2[HttpStatusCode2["NotFound"] = 404] = "NotFound";
HttpStatusCode2[HttpStatusCode2["Unauthorized"] = 401] = "Unauthorized";
HttpStatusCode2[HttpStatusCode2["Forbidden"] = 403] = "Forbidden";
HttpStatusCode2[HttpStatusCode2["TooManyRequests"] = 429] = "TooManyRequests";
HttpStatusCode2[HttpStatusCode2["RequestTimeout"] = 408] = "RequestTimeout";
HttpStatusCode2[HttpStatusCode2["InternalServerError"] = 500] = "InternalServerError";
HttpStatusCode2[HttpStatusCode2["ServiceUnavailable"] = 503] = "ServiceUnavailable";
return HttpStatusCode2;
})(HttpStatusCode || {});
var ExaError = class extends Error {
/**
* Create a new ExaError
* @param message Error message
* @param statusCode HTTP status code
* @param timestamp ISO timestamp from API
* @param path Path that caused the error
*/
constructor(message, statusCode, timestamp, path) {
super(message);
this.name = "ExaError";
this.statusCode = statusCode;
this.timestamp = timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
this.path = path;
}
};
// src/websets/base.ts
var WebsetsBaseClient = class {
/**
* Initialize a new Websets base client
* @param client The Exa client instance
*/
constructor(client) {
this.client = client;
}
/**
* Make a request to the Websets API
* @param endpoint The endpoint path
* @param method The HTTP method
* @param data Optional request body data
* @param params Optional query parameters
* @returns The response JSON
* @throws ExaError with API error details if the request fails
*/
async request(endpoint, method = "POST", data, params) {
return this.client.request(`/websets${endpoint}`, method, data, params);
}
/**
* Helper to build pagination parameters
* @param pagination The pagination parameters
* @returns QueryParams object with pagination parameters
*/
buildPaginationParams(pagination) {
const params = {};
if (!pagination) return params;
if (pagination.cursor) params.cursor = pagination.cursor;
if (pagination.limit) params.limit = pagination.limit;
return params;
}
};
// src/websets/enrichments.ts
var WebsetEnrichmentsClient = class extends WebsetsBaseClient {
/**
* Create an Enrichment for a Webset
* @param websetId The ID of the Webset
* @param params The enrichment parameters
* @returns The created Webset Enrichment
*/
async create(websetId, params) {
return this.request(
`/v0/websets/${websetId}/enrichments`,
"POST",
params
);
}
/**
* Get an Enrichment by ID
* @param websetId The ID of the Webset
* @param id The ID of the Enrichment
* @returns The Webset Enrichment
*/
async get(websetId, id) {
return this.request(
`/v0/websets/${websetId}/enrichments/${id}`,
"GET"
);
}
/**
* Delete an Enrichment
* @param websetId The ID of the Webset
* @param id The ID of the Enrichment
* @returns The deleted Webset Enrichment
*/
async delete(websetId, id) {
return this.request(
`/v0/websets/${websetId}/enrichments/${id}`,
"DELETE"
);
}
/**
* Cancel a running Enrichment
* @param websetId The ID of the Webset
* @param id The ID of the Enrichment
* @returns The canceled Webset Enrichment
*/
async cancel(websetId, id) {
return this.request(
`/v0/websets/${websetId}/enrichments/${id}/cancel`,
"POST"
);
}
};
// src/websets/events.ts
var EventsClient = class extends WebsetsBaseClient {
/**
* Initialize a new Events client
* @param client The Exa client instance
*/
constructor(client) {
super(client);
}
/**
* List all Events
* @param options Optional filtering and pagination options
* @returns The list of Events
*/
async list(options) {
const params = {
cursor: options?.cursor,
limit: options?.limit,
types: options?.types
};
return this.request(
"/v0/events",
"GET",
void 0,
params
);
}
/**
* Get an Event by ID
* @param id The ID of the Event
* @returns The Event
*/
async get(id) {
return this.request(`/v0/events/${id}`, "GET");
}
};
// src/websets/openapi.ts
var CreateEnrichmentParametersFormat = /* @__PURE__ */ ((CreateEnrichmentParametersFormat2) => {
CreateEnrichmentParametersFormat2["text"] = "text";
CreateEnrichmentParametersFormat2["date"] = "date";
CreateEnrichmentParametersFormat2["number"] = "number";
CreateEnrichmentParametersFormat2["options"] = "options";
CreateEnrichmentParametersFormat2["email"] = "email";
CreateEnrichmentParametersFormat2["phone"] = "phone";
return CreateEnrichmentParametersFormat2;
})(CreateEnrichmentParametersFormat || {});
var CreateImportParametersFormat = /* @__PURE__ */ ((CreateImportParametersFormat2) => {
CreateImportParametersFormat2["csv"] = "csv";
return CreateImportParametersFormat2;
})(CreateImportParametersFormat || {});
var CreateWebsetParametersImportSource = /* @__PURE__ */ ((CreateWebsetParametersImportSource2) => {
CreateWebsetParametersImportSource2["import"] = "import";
CreateWebsetParametersImportSource2["webset"] = "webset";
return CreateWebsetParametersImportSource2;
})(CreateWebsetParametersImportSource || {});
var CreateWebsetParametersSearchExcludeSource = /* @__PURE__ */ ((CreateWebsetParametersSearchExcludeSource2) => {
CreateWebsetParametersSearchExcludeSource2["import"] = "import";
CreateWebsetParametersSearchExcludeSource2["webset"] = "webset";
return CreateWebsetParametersSearchExcludeSource2;
})(CreateWebsetParametersSearchExcludeSource || {});
var ScopeSourceType = /* @__PURE__ */ ((ScopeSourceType2) => {
ScopeSourceType2["import"] = "import";
ScopeSourceType2["webset"] = "webset";
return ScopeSourceType2;
})(ScopeSourceType || {});
var CreateWebsetSearchParametersExcludeSource = /* @__PURE__ */ ((CreateWebsetSearchParametersExcludeSource2) => {
CreateWebsetSearchParametersExcludeSource2["import"] = "import";
CreateWebsetSearchParametersExcludeSource2["webset"] = "webset";
return CreateWebsetSearchParametersExcludeSource2;
})(CreateWebsetSearchParametersExcludeSource || {});
var CreateWebsetSearchParametersScopeSource = /* @__PURE__ */ ((CreateWebsetSearchParametersScopeSource2) => {
CreateWebsetSearchParametersScopeSource2["import"] = "import";
CreateWebsetSearchParametersScopeSource2["webset"] = "webset";
return CreateWebsetSearchParametersScopeSource2;
})(CreateWebsetSearchParametersScopeSource || {});
var EventType = /* @__PURE__ */ ((EventType2) => {
EventType2["webset_created"] = "webset.created";
EventType2["webset_deleted"] = "webset.deleted";
EventType2["webset_paused"] = "webset.paused";
EventType2["webset_idle"] = "webset.idle";
EventType2["webset_search_created"] = "webset.search.created";
EventType2["webset_search_canceled"] = "webset.search.canceled";
EventType2["webset_search_completed"] = "webset.search.completed";
EventType2["webset_search_updated"] = "webset.search.updated";
EventType2["import_created"] = "import.created";
EventType2["import_completed"] = "import.completed";
EventType2["import_processing"] = "import.processing";
EventType2["webset_item_created"] = "webset.item.created";
EventType2["webset_item_enriched"] = "webset.item.enriched";
EventType2["webset_export_created"] = "webset.export.created";
EventType2["webset_export_completed"] = "webset.export.completed";
return EventType2;
})(EventType || {});
var ImportFailedReason = /* @__PURE__ */ ((ImportFailedReason2) => {
ImportFailedReason2["invalid_format"] = "invalid_format";
ImportFailedReason2["invalid_file_content"] = "invalid_file_content";
ImportFailedReason2["missing_identifier"] = "missing_identifier";
return ImportFailedReason2;
})(ImportFailedReason || {});
var ImportFormat = /* @__PURE__ */ ((ImportFormat2) => {
ImportFormat2["csv"] = "csv";
ImportFormat2["webset"] = "webset";
return ImportFormat2;
})(ImportFormat || {});
var ImportObject = /* @__PURE__ */ ((ImportObject2) => {
ImportObject2["import"] = "import";
return ImportObject2;
})(ImportObject || {});
var ImportStatus = /* @__PURE__ */ ((ImportStatus2) => {
ImportStatus2["pending"] = "pending";
ImportStatus2["processing"] = "processing";
ImportStatus2["completed"] = "completed";
ImportStatus2["failed"] = "failed";
return ImportStatus2;
})(ImportStatus || {});
var MonitorObject = /* @__PURE__ */ ((MonitorObject2) => {
MonitorObject2["monitor"] = "monitor";
return MonitorObject2;
})(MonitorObject || {});
var MonitorStatus = /* @__PURE__ */ ((MonitorStatus2) => {
MonitorStatus2["enabled"] = "enabled";
MonitorStatus2["disabled"] = "disabled";
return MonitorStatus2;
})(MonitorStatus || {});
var MonitorRunObject = /* @__PURE__ */ ((MonitorRunObject2) => {
MonitorRunObject2["monitor_run"] = "monitor_run";
return MonitorRunObject2;
})(MonitorRunObject || {});
var MonitorRunStatus = /* @__PURE__ */ ((MonitorRunStatus2) => {
MonitorRunStatus2["created"] = "created";
MonitorRunStatus2["running"] = "running";
MonitorRunStatus2["completed"] = "completed";
MonitorRunStatus2["canceled"] = "canceled";
MonitorRunStatus2["failed"] = "failed";
return MonitorRunStatus2;
})(MonitorRunStatus || {});
var MonitorRunType = /* @__PURE__ */ ((MonitorRunType2) => {
MonitorRunType2["search"] = "search";
MonitorRunType2["refresh"] = "refresh";
return MonitorRunType2;
})(MonitorRunType || {});
var PreviewWebsetResponseEnrichmentsFormat = /* @__PURE__ */ ((PreviewWebsetResponseEnrichmentsFormat2) => {
PreviewWebsetResponseEnrichmentsFormat2["text"] = "text";
PreviewWebsetResponseEnrichmentsFormat2["date"] = "date";
PreviewWebsetResponseEnrichmentsFormat2["number"] = "number";
PreviewWebsetResponseEnrichmentsFormat2["options"] = "options";
PreviewWebsetResponseEnrichmentsFormat2["email"] = "email";
PreviewWebsetResponseEnrichmentsFormat2["phone"] = "phone";
return PreviewWebsetResponseEnrichmentsFormat2;
})(PreviewWebsetResponseEnrichmentsFormat || {});
var UpdateMonitorStatus = /* @__PURE__ */ ((UpdateMonitorStatus2) => {
UpdateMonitorStatus2["enabled"] = "enabled";
UpdateMonitorStatus2["disabled"] = "disabled";
return UpdateMonitorStatus2;
})(UpdateMonitorStatus || {});
var WebhookStatus = /* @__PURE__ */ ((WebhookStatus2) => {
WebhookStatus2["active"] = "active";
WebhookStatus2["inactive"] = "inactive";
return WebhookStatus2;
})(WebhookStatus || {});
var WebsetStatus = /* @__PURE__ */ ((WebsetStatus2) => {
WebsetStatus2["idle"] = "idle";
WebsetStatus2["pending"] = "pending";
WebsetStatus2["running"] = "running";
WebsetStatus2["paused"] = "paused";
return WebsetStatus2;
})(WebsetStatus || {});
var WebsetEnrichmentStatus = /* @__PURE__ */ ((WebsetEnrichmentStatus2) => {
WebsetEnrichmentStatus2["pending"] = "pending";
WebsetEnrichmentStatus2["canceled"] = "canceled";
WebsetEnrichmentStatus2["completed"] = "completed";
return WebsetEnrichmentStatus2;
})(WebsetEnrichmentStatus || {});
var WebsetEnrichmentFormat = /* @__PURE__ */ ((WebsetEnrichmentFormat2) => {
WebsetEnrichmentFormat2["text"] = "text";
WebsetEnrichmentFormat2["date"] = "date";
WebsetEnrichmentFormat2["number"] = "number";
WebsetEnrichmentFormat2["options"] = "options";
WebsetEnrichmentFormat2["email"] = "email";
WebsetEnrichmentFormat2["phone"] = "phone";
return WebsetEnrichmentFormat2;
})(WebsetEnrichmentFormat || {});
var WebsetItemSource = /* @__PURE__ */ ((WebsetItemSource2) => {
WebsetItemSource2["search"] = "search";
WebsetItemSource2["import"] = "import";
return WebsetItemSource2;
})(WebsetItemSource || {});
var WebsetItemEvaluationSatisfied = /* @__PURE__ */ ((WebsetItemEvaluationSatisfied2) => {
WebsetItemEvaluationSatisfied2["yes"] = "yes";
WebsetItemEvaluationSatisfied2["no"] = "no";
WebsetItemEvaluationSatisfied2["unclear"] = "unclear";
return WebsetItemEvaluationSatisfied2;
})(WebsetItemEvaluationSatisfied || {});
var WebsetSearchStatus = /* @__PURE__ */ ((WebsetSearchStatus2) => {
WebsetSearchStatus2["created"] = "created";
WebsetSearchStatus2["pending"] = "pending";
WebsetSearchStatus2["running"] = "running";
WebsetSearchStatus2["completed"] = "completed";
WebsetSearchStatus2["canceled"] = "canceled";
return WebsetSearchStatus2;
})(WebsetSearchStatus || {});
var WebsetSearchBehavior = /* @__PURE__ */ ((WebsetSearchBehavior2) => {
WebsetSearchBehavior2["override"] = "override";
WebsetSearchBehavior2["append"] = "append";
return WebsetSearchBehavior2;
})(WebsetSearchBehavior || {});
var WebsetSearchCanceledReason = /* @__PURE__ */ ((WebsetSearchCanceledReason2) => {
WebsetSearchCanceledReason2["webset_deleted"] = "webset_deleted";
WebsetSearchCanceledReason2["webset_canceled"] = "webset_canceled";
return WebsetSearchCanceledReason2;
})(WebsetSearchCanceledReason || {});
// src/websets/imports.ts
var ImportsClient = class extends WebsetsBaseClient {
async create(params, csv) {
if (csv === void 0) {
return this.request(
"/v0/imports",
"POST",
params
);
}
let csvBuffer;
if (typeof csv === "string") {
csvBuffer = Buffer.from(csv, "utf8");
} else if (Buffer.isBuffer(csv)) {
csvBuffer = csv;
} else {
throw new ExaError(
"Invalid CSV data input. Must be string or Buffer",
400 /* BadRequest */
);
}
const sizeInBytes = csvBuffer.length;
const sizeInMB = Math.max(1, Math.ceil(sizeInBytes / (1024 * 1024)));
const csvText = csvBuffer.toString("utf8");
const lines = csvText.split("\n").filter((line) => line.trim().length > 0);
const recordCount = Math.max(0, lines.length - 1);
if (sizeInMB > 50) {
throw new ExaError(
`CSV file too large: ${sizeInMB}MB. Maximum size is 50MB.`,
400 /* BadRequest */
);
}
if (recordCount === 0) {
throw new ExaError(
"CSV file appears to have no data records (only header or empty)",
400 /* BadRequest */
);
}
const createParams = {
title: params.title,
format: "csv" /* csv */,
entity: params.entity,
size: sizeInBytes,
count: recordCount,
metadata: params.metadata,
csv: params.csv
};
const importResponse = await this.request(
"/v0/imports",
"POST",
createParams
);
try {
const uploadResponse = await fetch(importResponse.uploadUrl, {
method: "PUT",
body: csvBuffer
});
if (!uploadResponse.ok) {
const errorText = await uploadResponse.text();
throw new ExaError(
`Upload failed: ${uploadResponse.status} ${uploadResponse.statusText}. ${errorText}`,
400 /* BadRequest */
);
}
} catch (error) {
if (error instanceof ExaError) {
throw error;
}
throw new ExaError(
`Failed to upload CSV data: ${error.message}`,
400 /* BadRequest */
);
}
return importResponse;
}
/**
* Get an Import by ID
* @param id The ID of the Import
* @returns The Import
*/
async get(id) {
return this.request(`/v0/imports/${id}`, "GET");
}
/**
* List all Imports
* @param options Pagination options
* @returns The list of Imports
*/
async list(options) {
const params = this.buildPaginationParams(options);
return this.request(
"/v0/imports",
"GET",
void 0,
params
);
}
/**
* Update an Import
* @param id The ID of the Import
* @param params The import update parameters
* @returns The updated Import
*/
async update(id, params) {
return this.request(`/v0/imports/${id}`, "PATCH", params);
}
/**
* Delete an Import
* @param id The ID of the Import
* @returns The deleted Import
*/
async delete(id) {
return this.request(`/v0/imports/${id}`, "DELETE");
}
/**
* Wait until an Import is completed or failed
* @param id The ID of the Import
* @param options Configuration options for timeout and polling
* @returns The Import once it reaches a final state (completed or failed)
* @throws Error if the Import does not complete within the timeout or fails
*/
async waitUntilCompleted(id, options) {
const timeout = options?.timeout ?? 30 * 60 * 1e3;
const pollInterval = options?.pollInterval ?? 2e3;
const onPoll = options?.onPoll;
const startTime = Date.now();
while (true) {
const importItem = await this.get(id);
if (onPoll) {
onPoll(importItem.status);
}
if (importItem.status === "completed" /* completed */) {
return importItem;
}
if (importItem.status === "failed" /* failed */) {
throw new ExaError(
`Import ${id} failed: ${importItem.failedMessage || "Unknown error"}`,
400 /* BadRequest */
);
}
if (Date.now() - startTime > timeout) {
throw new ExaError(
`Import ${id} did not complete within ${timeout}ms. Current status: ${importItem.status}`,
408 /* RequestTimeout */
);
}
await new Promise((resolve) => setTimeout(resolve, pollInterval));
}
}
};
// src/websets/items.ts
var WebsetItemsClient = class extends WebsetsBaseClient {
/**
* List all Items for a Webset
* @param websetId The ID of the Webset
* @param params - Optional pagination and filtering parameters
* @returns A promise that resolves with the list of Items
*/
list(websetId, params) {
const queryParams = {
...this.buildPaginationParams(params),
sourceId: params?.sourceId
};
return this.request(
`/v0/websets/${websetId}/items`,
"GET",
void 0,
queryParams
);
}
/**
* Iterate through all Items in a Webset, handling pagination automatically
* @param websetId The ID of the Webset
* @param options Pagination options
* @returns Async generator of Webset Items
*/
async *listAll(websetId, options) {
let cursor = void 0;
const pageOptions = options ? { ...options } : {};
while (true) {
pageOptions.cursor = cursor;
const response = await this.list(websetId, pageOptions);
for (const item of response.data) {
yield item;
}
if (!response.hasMore || !response.nextCursor) {
break;
}
cursor = response.nextCursor;
}
}
/**
* Collect all items from a Webset into an array
* @param websetId The ID of the Webset
* @param options Pagination options
* @returns Promise resolving to an array of all Webset Items
*/
async getAll(websetId, options) {
const items = [];
for await (const item of this.listAll(websetId, options)) {
items.push(item);
}
return items;
}
/**
* Get an Item by ID
* @param websetId The ID of the Webset
* @param id The ID of the Item
* @returns The Webset Item
*/
async get(websetId, id) {
return this.request(
`/v0/websets/${websetId}/items/${id}`,
"GET"
);
}
/**
* Delete an Item
* @param websetId The ID of the Webset
* @param id The ID of the Item
* @returns The deleted Webset Item
*/
async delete(websetId, id) {
return this.request(
`/v0/websets/${websetId}/items/${id}`,
"DELETE"
);
}
};
// src/websets/monitors.ts
var WebsetMonitorRunsClient = class extends WebsetsBaseClient {
/**
* List all runs for a Monitor
* @param monitorId The ID of the Monitor
* @param options Pagination options
* @returns The list of Monitor runs
*/
async list(monitorId, options) {
const params = this.buildPaginationParams(options);
return this.request(
`/v0/monitors/${monitorId}/runs`,
"GET",
void 0,
params
);
}
/**
* Get a specific Monitor run
* @param monitorId The ID of the Monitor
* @param runId The ID of the Monitor run
* @returns The Monitor run
*/
async get(monitorId, runId) {
return this.request(
`/v0/monitors/${monitorId}/runs/${runId}`,
"GET"
);
}
};
var WebsetMonitorsClient = class extends WebsetsBaseClient {
constructor(client) {
super(client);
this.runs = new WebsetMonitorRunsClient(client);
}
/**
* Create a Monitor
* @param params The monitor parameters
* @returns The created Monitor
*/
async create(params) {
return this.request("/v0/monitors", "POST", params);
}
/**
* Get a Monitor by ID
* @param id The ID of the Monitor
* @returns The Monitor
*/
async get(id) {
return this.request(`/v0/monitors/${id}`, "GET");
}
/**
* List all Monitors
* @param options Pagination and filtering options
* @returns The list of Monitors
*/
async list(options) {
const params = {
cursor: options?.cursor,
limit: options?.limit,
websetId: options?.websetId
};
return this.request(
"/v0/monitors",
"GET",
void 0,
params
);
}
/**
* Update a Monitor
* @param id The ID of the Monitor
* @param params The monitor update parameters (status, metadata)
* @returns The updated Monitor
*/
async update(id, params) {
return this.request(`/v0/monitors/${id}`, "PATCH", params);
}
/**
* Delete a Monitor
* @param id The ID of the Monitor
* @returns The deleted Monitor
*/
async delete(id) {
return this.request(`/v0/monitors/${id}`, "DELETE");
}
};
// src/websets/searches.ts
var WebsetSearchesClient = class extends WebsetsBaseClient {
/**
* Create a new Search for the Webset
* @param websetId The ID of the Webset
* @param params The search parameters
* @returns The created Webset Search
*/
async create(websetId, params) {
return this.request(
`/v0/websets/${websetId}/searches`,
"POST",
params
);
}
/**
* Get a Search by ID
* @param websetId The ID of the Webset
* @param id The ID of the Search
* @returns The Webset Search
*/
async get(websetId, id) {
return this.request(
`/v0/websets/${websetId}/searches/${id}`,
"GET"
);
}
/**
* Cancel a running Search
* @param websetId The ID of the Webset
* @param id The ID of the Search
* @returns The canceled Webset Search
*/
async cancel(websetId, id) {
return this.request(
`/v0/websets/${websetId}/searches/${id}/cancel`,
"POST"
);
}
};
// src/websets/webhooks.ts
var WebsetWebhooksClient = class extends WebsetsBaseClient {
/**
* Create a Webhook
* @param params The webhook parameters
* @returns The created Webhook
*/
async create(params) {
return this.request("/v0/webhooks", "POST", params);
}
/**
* Get a Webhook by ID
* @param id The ID of the Webhook
* @returns The Webhook
*/
async get(id) {
return this.request(`/v0/webhooks/${id}`, "GET");
}
/**
* List all Webhooks
* @param options Pagination options
* @returns The list of Webhooks
*/
async list(options) {
const params = this.buildPaginationParams(options);
return this.request(
"/v0/webhooks",
"GET",
void 0,
params
);
}
/**
* Iterate through all Webhooks, handling pagination automatically
* @param options Pagination options
* @returns Async generator of Webhooks
*/
async *listAll(options) {
let cursor = void 0;
const pageOptions = options ? { ...options } : {};
while (true) {
pageOptions.cursor = cursor;
const response = await this.list(pageOptions);
for (const webhook of response.data) {
yield webhook;
}
if (!response.hasMore || !response.nextCursor) {
break;
}
cursor = response.nextCursor;
}
}
/**
* Collect all Webhooks into an array
* @param options Pagination options
* @returns Promise resolving to an array of all Webhooks
*/
async getAll(options) {
const webhooks = [];
for await (const webhook of this.listAll(options)) {
webhooks.push(webhook);
}
return webhooks;
}
/**
* Update a Webhook
* @param id The ID of the Webhook
* @param params The webhook update parameters (events, metadata, url)
* @returns The updated Webhook
*/
async update(id, params) {
return this.request(`/v0/webhooks/${id}`, "PATCH", params);
}
/**
* Delete a Webhook
* @param id The ID of the Webhook
* @returns The deleted Webhook
*/
async delete(id) {
return this.request(`/v0/webhooks/${id}`, "DELETE");
}
/**
* List all attempts for a Webhook
* @param id The ID of the Webhook
* @param options Pagination and filtering options
* @returns The list of Webhook attempts
*/
async listAttempts(id, options) {
const params = {
cursor: options?.cursor,
limit: options?.limit,
eventType: options?.eventType,
successful: options?.successful
};
return this.request(
`/v0/webhooks/${id}/attempts`,
"GET",
void 0,
params
);
}
/**
* Iterate through all attempts for a Webhook, handling pagination automatically
* @param id The ID of the Webhook
* @param options Pagination and filtering options
* @returns Async generator of Webhook attempts
*/
async *listAllAttempts(id, options) {
let cursor = void 0;
const pageOptions = options ? { ...options } : {};
while (true) {
pageOptions.cursor = cursor;
const response = await this.listAttempts(id, pageOptions);
for (const attempt of response.data) {
yield attempt;
}
if (!response.hasMore || !response.nextCursor) {
break;
}
cursor = response.nextCursor;
}
}
/**
* Collect all attempts for a Webhook into an array
* @param id The ID of the Webhook
* @param options Pagination and filtering options
* @returns Promise resolving to an array of all Webhook attempts
*/
async getAllAttempts(id, options) {
const attempts = [];
for await (const attempt of this.listAllAttempts(id, options)) {
attempts.push(attempt);
}
return attempts;
}
};
// src/websets/client.ts
var WebsetsClient = class extends WebsetsBaseClient {
/**
* Initialize a new Websets client
* @param client The Exa client instance
*/
constructor(client) {
super(client);
this.events = new EventsClient(client);
this.imports = new ImportsClient(client);
this.items = new WebsetItemsClient(client);
this.searches = new WebsetSearchesClient(client);
this.enrichments = new WebsetEnrichmentsClient(client);
this.monitors = new WebsetMonitorsClient(client);
this.webhooks = new WebsetWebhooksClient(client);
}
/**
* Create a new Webset
* @param params The Webset creation parameters
* @returns The created Webset
*/
async create(params) {
return this.request("/v0/websets", "POST", params);
}
/**
* Preview a webset
* @param params The preview parameters
* @returns The preview response showing how the query will be decomposed
*/
async preview(params) {
return this.request("/v0/websets/preview", "POST", params);
}
/**
* Get a Webset by ID
* @param id The ID of the Webset
* @param expand Optional array of relations to expand
* @returns The Webset
*/
async get(id, expand) {
const params = {};
if (expand) {
params.expand = expand;
}
return this.request(
`/v0/websets/${id}`,
"GET",
void 0,
params
);
}
/**
* List all Websets
* @param options Pagination options (filtering by status is not supported by API)
* @returns The list of Websets
*/
async list(options) {
const params = this.buildPaginationParams(options);
return this.request(
"/v0/websets",
"GET",
void 0,
params
);
}
/**
* Iterate through all Websets, handling pagination automatically
* @param options Pagination options
* @returns Async generator of Websets
*/
async *listAll(options) {
let cursor = void 0;
const pageOptions = options ? { ...options } : {};
while (true) {
pageOptions.cursor = cursor;
const response = await this.list(pageOptions);
for (const webset of response.data) {
yield webset;
}
if (!response.hasMore || !response.nextCursor) {
break;
}
cursor = response.nextCursor;
}
}
/**
* Collect all Websets into an array
* @param options Pagination options
* @returns Promise resolving to an array of all Websets
*/
async getAll(options) {
const websets = [];
for await (const webset of this.listAll(options)) {
websets.push(webset);
}
return websets;
}
/**
* Update a Webset
* @param id The ID of the Webset
* @param params The Webset update parameters
* @returns The updated Webset
*/
async update(id, params) {
return this.request(`/v0/websets/${id}`, "POST", params);
}
/**
* Delete a Webset
* @param id The ID of the Webset
* @returns The deleted Webset
*/
async delete(id) {
return this.request(`/v0/websets/${id}`, "DELETE");
}
/**
* Cancel a running Webset
* @param id The ID or external ID of the Webset
* @returns The canceled Webset (as returned by the API)
*/
async cancel(id) {
return this.request(`/v0/websets/${id}/cancel`, "POST");
}
/**
* Wait until a Webset is idle
* @param id The ID of the Webset
* @param options Configuration options for timeout and polling
* @returns The Webset once it becomes idle
* @throws Error if the Webset does not become idle within the timeout
*/
async waitUntilIdle(id, options) {
let timeout;
let pollInterval = 1e3;
let onPoll;
if (typeof options === "number") {
timeout = options;
} else if (options) {
timeout = options.timeout;
pollInterval = options.pollInterval || 1e3;
onPoll = options.onPoll;
}
const startTime = Date.now();
while (true) {
const webset = await this.get(id);
if (onPoll) {
onPoll(webset.status);
}
if (webset.status === "idle" /* idle */) {
return webset;
}
if (timeout && Date.now() - startTime > timeout) {
throw new ExaError(
`Webset ${id} did not reach idle state within ${timeout}ms. Current status: ${webset.status}`,
408 /* RequestTimeout */
);
}
await new Promise((resolve) => setTimeout(resolve, pollInterval));
}
}
};
// src/zod-utils.ts
var import_zod = require("zod");
var import_zod_to_json_schema = require("zod-to-json-schema");
function isZodSchema(obj) {
return obj instanceof import_zod.ZodType;
}
function zodToJsonSchema(schema) {
return (0, import_zod_to_json_schema.zodToJsonSchema)(schema, {
$refStrategy: "none"
});
}
// src/research/base.ts
var ResearchBaseClient = class {
/**
* Initialize a new Research base client
* @param client The Exa client instance
*/
constructor(client) {
this.client = client;
}
/**
* Make a request to the Research API (prefixes all paths with `/research`).
* @param endpoint The endpoint path, beginning with a slash (e.g. "/tasks").
* @param method The HTTP method. Defaults to "POST".
* @param data Optional request body
* @param params Optional query parameters
* @returns The parsed JSON response
*/
async request(endpoint, method = "POST", data, params) {
return this.client.request(
`/research/v0${endpoint}`,
method,
data,
params
);
}
/**
* Make a request to the Research API (prefixes all paths with `/research`).
* @param endpoint The endpoint path, beginning with a slash (e.g. "/tasks").
* @param method The HTTP method. Defaults to "POST".
* @param data Optional request body
* @param params Optional query parameters
* @returns The parsed JSON response
*/
async rawRequest(endpoint, method = "POST", data, params) {
return this.client.rawRequest(
`/research/v0${endpoint}`,
method,
data,
params
);
}
/**
* Helper to build pagination parameters.
* @param pagination The pagination parameters
* @returns QueryParams object with pagination parameters
*/
buildPaginationParams(pagination) {
const params = {};
if (!pagination) return params;
if (pagination.cursor) params.cursor = pagination.cursor;
if (pagination.limit) params.limit = pagination.limit;
return params;
}
};
// src/research/client.ts
var ResearchClient = class extends ResearchBaseClient {
constructor(client) {
super(client);
}
async createTask(params) {
const { instructions, model, output } = params;
let schema = output?.schema;
if (schema && isZodSchema(schema)) {
schema = zodToJsonSchema(schema);
}
const payload = {
instructions,
model: model ?? "exa-research",
output: output ? {
schema,
inferSchema: output.inferSchema ?? true
} : { inferSchema: true }
};
return this.request("/tasks", "POST", payload);
}
getTask(id, options) {
if (options?.stream) {
const promise = async () => {
const resp = await this.rawRequest(`/tasks/${id}?stream=true`, "GET");
if (!resp.body) {
throw new Error("No response body for SSE stream");
}
const reader = resp.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
function processPart(part) {
const lines = part.split("\n");
let data = lines.slice(1).join("\n");
if (data.startsWith("data:")) {
data = data.slice(5).trimStart();
}
try {
return JSON.parse(data);
} catch (e) {
return null;
}
}
async function* streamEvents() {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let parts = buffer.split("\n\n");
buffer = parts.pop() ?? "";
for (const part of parts) {
const processed = processPart(part);
if (processed) {
yield processed;
}
}
}
if (buffer.trim()) {
const processed = processPart(buffer.trim());
if (processed) {
yield processed;
}
}
}
return streamEvents();
};
return promise();
} else {
return this.request(`/tasks/${id}`, "GET");
}
}
/**
* @deprecated This method is deprecated and may be removed in a future release.
* @see getTask(id, {stream: true})
* Poll a research task until completion or failure.
* Polls every 1 second with a maximum timeout of 10 minutes.
* Resilient to up to 10 consecutive polling failures.
*/
async pollTask(id) {
const pollingInterval = 1e3;
const maxPollingTime = 10 * 60 * 1e3;
const maxConsecutiveFailures = 10;
const startTime = Date.now();
let consecutiveFailures = 0;
while (true) {
try {
const task = await this.request(`/tasks/${id}`, "GET");
consecutiveFailures = 0;
if (task.status === "completed" || task.status === "failed") {
return task;
}
} catch (err) {
consecutiveFailures += 1;
if (consecutiveFailures >= maxConsecutiveFailures) {
throw new Error(
`Polling failed ${maxConsecutiveFailures} times in a row for task ${id}: ${err}`
);
}
}
if (Date.now() - startTime > maxPollingTime) {
throw new Error(
`Polling timeout: Task ${id} did not complete within 10 minutes`
);
}
await new Promise((resolve) => setTimeout(resolve, pollingInterval));
}
}
/**
* List research tasks
* @param options Pagination options
* @returns The paginated list of research tasks
*/
async listTasks(options) {
const params = this.buildPaginationParams(options);
return this.request(
"/tasks",
"GET",
void 0,
params
);
}
};
// src/index.ts
var fetchImpl = typeof global !== "undefined" && global.fetch ? global.fetch : import_cross_fetch.default;
var HeadersImpl = typeof global !== "undefined" && global.Headers ? global.Headers : import_cross_fetch.Headers;
var Exa2 = class {
/**
* Helper method to separate out the contents-specific options from the rest.
*/
extractContentsOptions(options) {
const {
text,
highlights,
summary,
subpages,
subpageTarget,
extras,
livecrawl,
livecrawlTimeout,
context,
...rest
} = options;
const contentsOptions = {};
if (text === void 0 && summary === void 0 && highlights === void 0 && extras === void 0) {
contentsOptions.text = true;
}
if (text !== void 0) contentsOptions.text = text;
if (summary !== void 0) {
if (typeof summary === "object" && summary !== null && "schema" in summary && summary.schema && isZodSchema(summary.schema)) {
contentsOptions.summary = {
...summary,
schema: zodToJsonSchema(summary.schema)
};
} else {
contentsOptions.summary = summary;
}
}
if (highlights !== void 0) contentsOptions.highlights = highlights;
if (subpages !== void 0) contentsOptions.subpages = subpages;
if (subpageTarget !== void 0)
contentsOptions.subpageTarget = subpageTarget;
if (extras !== void 0) contentsOptions.extras = extras;
if (livecrawl !== void 0) contentsOptions.livecrawl = livecrawl;
if (livecrawlTimeout !== void 0)
contentsOptions.livecrawlTimeout = livecrawlTimeout;
if (context !== void 0) contentsOptions.context = context;
return {
contentsOptions,
restOptions: rest
};
}
/**
* Constructs the Exa API client.
* @param {string} apiKey - The API key for authentication.
* @param {string} [baseURL] - The base URL of the Exa API.
*/
constructor(apiKey, baseURL = "https://api.exa.ai") {
this.baseURL = baseURL;
if (!apiKey) {
apiKey = process.env.EXASEARCH_API_KEY;
if (!apiKey) {
throw new ExaError(
"API key must be provided as an argument or as an environment variable (EXASEARCH_API_KEY)",
401 /* Unauthorized */
);
}
}
this.headers = new HeadersImpl({
"x-api-key": apiKey,
"Content-Type": "application/json",
"User-Agent": "exa-node 1.4.0"
});
this.websets = new WebsetsClient(this);
this.research = new ResearchClient(this);
}
/**
* Makes a request to the Exa API.
* @param {string} endpoint - The API endpoint to call.
* @param {string} method - The HTTP method to use.
* @param {any} [body] - The request body for POST requests.
* @param {Record<string, any>} [params] - The query parameters.
* @returns {Promise<any>} The response from the API.
* @throws {ExaError} When any API request fails with structured error information
*/
async request(endpoint, method, body, params) {
let url = this.baseURL + endpoint;
if (params && Object.keys(params).length > 0) {
const searchParams = new URLSearchParams();
for (const [key, value] of Object.entries(params)) {
if (Array.isArray(value)) {
for (const item of value) {
searchParams.append(key, item);
}
} else if (value !== void 0) {
searchParams.append(key, String(value));
}
}
url += `?${searchParams.toString()}`;
}
const response = await fetchImpl(url, {
method,
headers: this.headers,
body: body ? JSON.stringify(body) : void 0
});
if (!response.ok) {
const errorData = await response.json();
if (!errorData.statusCode) {
errorData.statusCode = response.status;
}
if (!errorData.timestamp) {
errorData.timestamp = (/* @__PURE__ */ new Date()).toISOString();
}
if (!errorData.path) {
errorData.path = endpoint;
}
let message = errorData.error || "Unknown error";
if (errorData.message) {
message += (message.length > 0 ? ". " : "") + errorData.message;
}
throw new ExaError(
message,
response.status,
errorData.timestamp,
errorData.path
);
}
const contentType = response.headers.get("content-type") || "";
if (contentType.includes("text/event-stream")) {
return await this.parseSSEStream(response);
}
return await response.json();
}
async rawRequest(endpoint, method = "POST", body, queryParams) {
let url = this.baseURL + endpoint;
if (queryParams) {
const searchParams = new URLSearchParams();
for (const [key, value] of Object.entries(queryParams)) {
if (Array.isArray(value)) {
for (const item of value) {
searchParams.append(key, String(item));
}
} else if (value !== void 0) {
searchParams.append(key, String(value));
}
}
url += `?${searchParams.toString()}`;
}
const response = await fetchImpl(url, {
method,
headers: this.headers,
body: body ? JSON.stringify(body) : void 0
});
return response;
}
/**
* Performs a search with an Exa prompt-engineered query.
*
* @param {string} query - The query string.
* @param {RegularSearchOptions} [options] - Additional search options
* @returns {Promise<SearchResponse<{}>>} A list of relevant search results.
*/
async search(query, options) {
return await this.request("/search", "POST", { query, ...options });
}
/**
* Performs a search with an Exa prompt-engineered query and returns the contents of the documents.
*
* @param {string} query - The query string.
* @param {RegularSearchOptions & T} [options] - Additional search + contents options
* @returns {Promise<SearchResponse<T>>} A list of relevant search results with requested contents.
*/
async searchAndContents(query, options) {
const { contentsOptions, restOptions } = options === void 0 ? { contentsOptions: { text: true }, restOptions: {} } : this.extractContentsOptions(options);
return await this.request("/search", "POST", {
query,
contents: contentsOptions,
...restOptions
});
}
/**
* Finds similar links to the provided URL.
* @param {string} url - The URL for which to find similar links.
* @param {FindSimilarOptions} [options] - Additional options for finding similar links.
* @returns {Promise<SearchResponse<{}>>} A list of similar search results.
*/
async findSimilar(url, options) {
return await this.request("/findSimilar", "POST", { url, ...options });
}
/**
* Finds similar links to the provided URL and returns the contents of the documents.
* @param {string} url - The URL for which to find similar links.
* @param {FindSimilarOptions & T} [options] - Additional options for finding similar links + contents.
* @returns {Promise<SearchResponse<T>>} A list of similar search results, including requested contents.
*/
async findSimilarAndContents(url, options) {
const { contentsOptions, restOptions } = options === void 0 ? { contentsOptions: { text: true }, restOptions: {} } : this.extractContentsOptions(options);
return await this.request("/findSimilar", "POST", {
url,
contents: contentsOptions,
...restOptions
});
}
/**
* Retrieves contents of documents based on URLs.
* @param {string | string[] | SearchResult[]} urls - A URL or array of URLs, or an array of SearchResult objects.
* @param {ContentsOptions} [options] - Additional options for retrieving document contents.
* @returns {Promise<SearchResponse<T>>} A list of document contents for the requested URLs.
*/
async getContents(urls, options) {
if (!urls || Array.isArray(urls) && urls.length === 0) {
throw new ExaError(
"Must provide at least one URL",
400 /* BadRequest */
);
}
let requestUrls;
if (typeof urls === "string") {
requestUrls = [urls];
} else if (typeof urls[0] === "string") {
requestUrls = urls;
} else {
requestUrls = urls.map((result) => result.url);
}
const payload = {
urls: requestUrls,
...options
};
return await this.request("/contents", "POST", payload);
}
async answer(query, options) {
if (options?.stream) {
throw new ExaError(
"For streaming responses, please use streamAnswer() instead:\n\nfor await (const chunk of exa.streamAnswer(query)) {\n // Handle chunks\n}",
400 /* BadRequest */
);
}
let outputSchema = options?.outputSchema;
if (outputSchema && isZodSchema(outputSchema)) {
outputSchema = zodToJsonSchema(outputSchema);
}
const requestBody = {
query,
stream: false,
text: options?.text ?? false,
model: options?.model ?? "exa",
systemPrompt: options?.systemPrompt,
outputSchema
};
return await this.request("/answer", "POST", requestBody);
}
async *streamAnswer(query, options) {
let outputSchema = options?.outputSchema;
if (outputSchema && isZodSchema(outputSchema)) {
outputSchema = zodToJsonSchema(outputSchema);
}
const body = {
query,
text: options?.text ?? false,
stream: true,
model: options?.model ?? "exa",
systemPrompt: options?.systemPrompt,
outputSchema
};
const response = await fetchImpl(this.baseURL + "/answer", {
method: "POST",
headers: this.headers,
body: JSON.stringify(body)
});
if (!response.ok) {
const message = await response.text();
throw new ExaError(message, response.status, (/* @__PURE__ */ new Date()).toISOString());
}
const reader = response.body?.getReader();
if (!reader) {
throw new ExaError(
"No response body available for streaming.",