@datanest-earth/nodejs-client
Version:
Datanest API Client to easily create signed requests
878 lines (866 loc) • 34 kB
JavaScript
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/index.ts
import { createHmac as createHmac2 } from "node:crypto";
// src/projects.ts
var projects_exports = {};
__export(projects_exports, {
ProjectType: () => ProjectType,
archiveProject: () => archiveProject,
createProject: () => createProject,
getProject: () => getProject,
listProjects: () => listProjects,
patchProject: () => patchProject,
restoreProject: () => restoreProject,
waitForProjectWorkflow: () => waitForProjectWorkflow
});
var ProjectType = /* @__PURE__ */ ((ProjectType2) => {
ProjectType2[ProjectType2["PROJECT_TYPE_ENVIRO"] = 0] = "PROJECT_TYPE_ENVIRO";
ProjectType2[ProjectType2["PROJECT_TYPE_STANDARD"] = 1] = "PROJECT_TYPE_STANDARD";
return ProjectType2;
})(ProjectType || {});
async function listProjects(client, page = 1, archived = false, filters) {
const response = await client.get("v1/projects", { archived, page, ...filters });
const data = await response.json();
return data;
}
async function getProject(client, projectUuid) {
const response = await client.get("v1/projects/" + projectUuid);
const data = await response.json();
return data;
}
async function createProject(client, projectData) {
const response = await client.post("v1/projects", projectData);
if (response.status !== 201) {
throw new DatanestResponseError(`Failed to create project: ${response.status}`, response.status, await response.json());
}
const data = await response.json();
return data;
}
async function waitForProjectWorkflow(client, projectUuid, timeout = 45e3) {
let projectData = void 0;
const start = Date.now();
while (projectData?.workflow_importing_at !== null) {
await new Promise((resolve) => setTimeout(resolve, 2e3));
projectData = (await getProject(client, projectUuid)).project;
if (Date.now() - start > timeout) {
throw new Error("Timeout waiting for project workflow to complete");
}
}
return projectData;
}
async function patchProject(client, projectUuid, projectData) {
const response = await client.patch("v1/projects/" + projectUuid, projectData);
if (response.status !== 200) {
throw new DatanestResponseError(`Failed to create project: ${response.status}`, response.status, await response.json());
}
const data = await response.json();
return data;
}
async function archiveProject(client, projectUuid, options) {
const response = await client.delete("v1/projects/" + projectUuid + "/archive", options);
if (response.status !== 200) {
throw new DatanestResponseError(`Failed to archive project: ${response.status}`, response.status, await response.json());
}
return true;
}
async function restoreProject(client, projectUuid) {
await client.post("v1/projects/" + projectUuid + "/restore");
return true;
}
// src/workflows.ts
var workflows_exports = {};
__export(workflows_exports, {
assignProjectWorkflowAppUser: () => assignProjectWorkflowAppUser,
getCompanyCustomRoles: () => getCompanyCustomRoles,
getCompanyWorkflow: () => getCompanyWorkflow,
getCompanyWorkflows: () => getCompanyWorkflows,
getLatestDraftWorkflowFromList: () => getLatestDraftWorkflowFromList,
getLatestPublishedWorkflowFromList: () => getLatestPublishedWorkflowFromList,
getLatestWorkflowFromList: () => getLatestWorkflowFromList,
isDraftWorkflow: () => isDraftWorkflow,
isPublishedWorkflow: () => isPublishedWorkflow,
unassignProjectWorkflowAppUser: () => unassignProjectWorkflowAppUser
});
async function getCompanyWorkflows(client, filters) {
const response = await client.get("v1/company-workflows", filters);
return await response.json();
}
async function getCompanyWorkflow(client, workflowId) {
const response = await client.get("v1/company-workflows/" + workflowId);
return await response.json();
}
async function getCompanyCustomRoles(client) {
const response = await client.get("v1/company-custom-roles");
return await response.json();
}
async function assignProjectWorkflowAppUser(client, projectUuid, userUuidOrEmail, shareGroupOrWorkflowAppId, customRoleId) {
const response = await client.post("v1/projects/" + projectUuid + "/teams/workflow-apps/" + shareGroupOrWorkflowAppId + "/" + userUuidOrEmail, {
custom_role_id: customRoleId
});
return await response.json();
}
async function unassignProjectWorkflowAppUser(client, projectUuid, userUuidOrEmail, workflowAppId) {
const response = await client.delete("v1/projects/" + projectUuid + "/teams/workflow-apps/" + workflowAppId + "/" + userUuidOrEmail);
return await response.json();
}
function getLatestWorkflowFromList(workflows) {
return workflows.reduce((max, workflow) => {
if (workflow.revision > max.revision) {
return workflow;
}
return max;
}, workflows[0]);
}
function getLatestDraftWorkflowFromList(workflows) {
return workflows.reduce((max, workflow) => {
if (workflow.published_at !== null) {
return max;
}
if (workflow.revision > max.revision) {
return workflow;
}
return max;
}, workflows[0]);
}
function getLatestPublishedWorkflowFromList(workflows) {
return workflows.reduce((max, workflow) => {
if (workflow.published_at === null) {
return max;
}
if (workflow.revision > max.revision) {
return workflow;
}
return max;
}, workflows[0]);
}
function isDraftWorkflow(workflow) {
return workflow.published_at === null;
}
function isPublishedWorkflow(workflow) {
return workflow.published_at !== null;
}
// src/enviro.ts
var enviro_exports = {};
__export(enviro_exports, {
getAllEnviroChemicals: () => getAllEnviroChemicals,
getAllEnviroMatrices: () => getAllEnviroMatrices,
getCompanyChemicalProfiles: () => getCompanyChemicalProfiles,
getProjectMatrices: () => getProjectMatrices,
getProjectSampleChemicalResults: () => getProjectSampleChemicalResults,
getProjectSampleLocations: () => getProjectSampleLocations,
getProjectSamples: () => getProjectSamples,
getProjectScenarioGuidelines: () => getProjectScenarioGuidelines,
getProjectScenarioStandards: () => getProjectScenarioStandards,
getProjectScenarios: () => getProjectScenarios
});
async function getAllEnviroMatrices(client) {
const response = await client.get("v1/enviro/matrices");
return await response.json();
}
async function getAllEnviroChemicals(client, page = 1, filters) {
const response = await client.get("v1/enviro/chemicals", {
page,
...filters
});
return await response.json();
}
async function getCompanyChemicalProfiles(client) {
const response = await client.get("v1/enviro/chemicals/alias-profiles");
return await response.json();
}
async function getProjectMatrices(client, projectUuid) {
const response = await client.get("v1/projects/" + projectUuid + "/enviro/matrices");
return await response.json();
}
async function getProjectScenarios(client, projectUuid) {
const response = await client.get("v1/projects/" + projectUuid + "/enviro/scenarios");
return await response.json();
}
async function getProjectScenarioStandards(client, projectUuid, scenarioId, filters) {
const response = await client.get("v1/projects/" + projectUuid + "/enviro/scenarios/" + scenarioId + "/standards", filters);
return await response.json();
}
async function getProjectScenarioGuidelines(client, projectUuid, scenarioId, filters) {
const response = await client.get("v1/projects/" + projectUuid + "/enviro/scenarios/" + scenarioId + "/guidelines", filters);
return await response.json();
}
async function getProjectSampleChemicalResults(client, projectUuid, filters) {
const response = await client.get("v1/projects/" + projectUuid + "/enviro/samples/chemical-results", filters);
return await response.json();
}
async function getProjectSampleLocations(client, projectUuid, filters) {
const response = await client.get("v1/projects/" + projectUuid + "/enviro/samples/locations", filters);
return await response.json();
}
async function getProjectSamples(client, projectUuid, filters) {
const response = await client.get("v1/projects/" + projectUuid + "/enviro/samples", filters);
return await response.json();
}
// src/gather.ts
var gather_exports = {};
__export(gather_exports, {
FieldTypeNames: () => FieldTypeNames,
FieldTypes: () => FieldTypes,
createGatherItem: () => createGatherItem,
deleteApp: () => deleteApp,
deleteItem: () => deleteItem,
getAppSchema: () => getAppSchema,
getProjectItemDetails: () => getProjectItemDetails,
importAppGroup: () => importAppGroup,
importAppSchemaFromJson: () => importAppSchemaFromJson,
listProjectAppItems: () => listProjectAppItems,
listProjectApps: () => listProjectApps,
listProjectItems: () => listProjectItems,
listSharedAppGroups: () => listSharedAppGroups,
shareAppsFromProject: () => shareAppsFromProject,
unshareAppGroup: () => unshareAppGroup,
updateGatherItem: () => updateGatherItem,
updateShareGroup: () => updateShareGroup
});
var FieldTypes = {
TEXT: 1,
NUMBER: 2,
DATE: 3,
DROPDOWN: 4,
CHECKBOX: 5,
MEDIA: 6,
DEPTH: 7,
REFERENCE: 8,
SIGNATURE: 9,
CAPTION: 10,
EXPRESSION: 11,
DOCUMENT: 12,
DUPLICATE: 13,
TRIPLICATE: 14,
DRAWING: 15,
LITHOLOGY: 16,
ADDRESS: 17,
USER: 18,
LAB_ID: 19,
COPY_DATA_LINK: 20,
AI_PROMPT: 21,
FIELD_SPLITTER: 22
};
var FieldTypeNames = {
[FieldTypes.TEXT]: "Text",
[FieldTypes.NUMBER]: "Number",
[FieldTypes.DATE]: "Date",
[FieldTypes.DROPDOWN]: "Choice / Dropdown",
[FieldTypes.CHECKBOX]: "Checkbox",
[FieldTypes.MEDIA]: "Media",
[FieldTypes.DEPTH]: "Depth",
[FieldTypes.REFERENCE]: "Link another App's Item",
[FieldTypes.SIGNATURE]: "Signature",
[FieldTypes.CAPTION]: "Caption",
[FieldTypes.EXPRESSION]: "Expression",
[FieldTypes.DOCUMENT]: "Document",
[FieldTypes.DUPLICATE]: "Duplicate",
[FieldTypes.TRIPLICATE]: "Triplicate",
[FieldTypes.DRAWING]: "Drawing",
[FieldTypes.LITHOLOGY]: "Lithology",
[FieldTypes.ADDRESS]: "Address",
[FieldTypes.USER]: "User",
[FieldTypes.LAB_ID]: "Lab ID",
[FieldTypes.COPY_DATA_LINK]: "Copy Data Link",
[FieldTypes.AI_PROMPT]: "AI Prompt",
[FieldTypes.FIELD_SPLITTER]: "Field Splitter"
};
async function listProjectApps(client, projectUuid) {
const response = await client.get("v1/projects/" + projectUuid + "/apps");
const data = await response.json();
return data;
}
async function listProjectItems(client, projectUuid, page = 1, filters = {}) {
const response = await client.get("v1/projects/" + projectUuid + "/items", { page, ...filters });
const data = await response.json();
return data;
}
async function getProjectItemDetails(client, projectUuid, itemId) {
const response = await client.get("v1/projects/" + projectUuid + "/items/" + itemId);
const data = await response.json();
return data;
}
async function listProjectAppItems(client, projectUuid, appUuid, page = 1, filters) {
const response = await client.get("v1/projects/" + projectUuid + "/apps/" + appUuid + "/items", { page, ...filters });
const data = await response.json();
return data;
}
async function getAppSchema(client, appUuid) {
const response = await client.get("v1/apps/" + appUuid + "/schema");
const data = await response.json();
return data;
}
async function deleteApp(client, projectUuid, appUuid) {
const response = await client.delete("v1/projects/" + projectUuid + "/apps/" + appUuid);
if (response.status !== 204) {
throw new Error("Failed to delete app, unexpected status: " + response.status);
}
}
async function importAppSchemaFromJson(client, projectUuid, appsJson, options) {
if (typeof appsJson === "string") {
appsJson = JSON.parse(appsJson);
}
if (options?.asDuplicates) {
appsJson.apps = appsJson.apps.map((app) => {
delete app.id;
delete app.uuid;
return app;
});
}
const response = await client.post("v1/projects/" + projectUuid + "/apps/schema", appsJson);
const data = await response.json();
return data;
}
async function listSharedAppGroups(client, page = 1, filter = "all", filters) {
const response = await client.get("v1/apps/share-groups", { page, filter, ...filters });
const data = await response.json();
return data;
}
async function importAppGroup(client, projectUuid, shareGroup) {
if (typeof shareGroup !== "string") {
throw new Error("Share group must be a string");
}
const response = await client.post(`v1/projects/${projectUuid}/share-groups/import`, {
share_group: shareGroup
});
return await response.json();
}
async function shareAppsFromProject(client, projectUuid, shareGroupDetails) {
const response = await client.post(`v1/projects/${projectUuid}/share-groups`, shareGroupDetails);
return await response.json();
}
async function updateShareGroup(client, projectUuid, shareGroup, shareGroupDetails) {
const response = await client.post(`v1/projects/${projectUuid}/share-groups/${shareGroup}`, shareGroupDetails);
return await response.json();
}
async function unshareAppGroup(client, projectUuid, shareGroup) {
await client.delete(`v1/projects/${projectUuid}/share-groups/${shareGroup}`);
}
async function createGatherItem(client, projectUuid, appUuid, data) {
data.app_uuid = appUuid;
const response = await client.post("v1/projects/" + projectUuid + "/items", data);
const responseData = await response.json();
return responseData;
}
async function updateGatherItem(client, projectUuid, itemId, data) {
const response = await client.patch("v1/projects/" + projectUuid + "/items/" + itemId, data);
const responseData = await response.json();
return responseData;
}
async function deleteItem(client, projectUuid, itemId) {
const response = await client.delete("v1/projects/" + projectUuid + "/items/" + itemId);
await response.json();
return true;
}
// src/integrations.ts
var integrations_exports = {};
__export(integrations_exports, {
IntegrationKey: () => IntegrationKey,
removeIntegration: () => removeIntegration,
setupIntegration: () => setupIntegration
});
var IntegrationKey = /* @__PURE__ */ ((IntegrationKey2) => {
IntegrationKey2["BORE_DM"] = "boredm";
return IntegrationKey2;
})(IntegrationKey || {});
async function setupIntegration(client, integrationKey, integrationSetupData) {
const response = await client.post("v1/integrations/" + integrationKey, integrationSetupData);
if (response.status !== 200) {
throw new DatanestResponseError(`Failed to setup integration: ${response.status}`, response.status, await response.json());
}
const data = await response.json();
return data;
}
async function removeIntegration(client, integrationKey) {
await client.delete("v1/integrations/" + integrationKey);
return true;
}
// src/teams.ts
var teams_exports = {};
__export(teams_exports, {
addExternalUserToProject: () => addExternalUserToProject,
addProjectTeamMember: () => addProjectTeamMember,
assignProjectWorkflowUser: () => assignProjectWorkflowAppUser,
getProjectTeam: () => getProjectTeam,
removeExternalUserToProject: () => removeExternalUserToProject,
removeProjectTeamMember: () => removeProjectTeamMember,
unassignProjectWorkflowUser: () => unassignProjectWorkflowAppUser,
updateProjectMemberRole: () => updateProjectMemberRole
});
async function getProjectTeam(client, projectUuid) {
const response = await client.get("v1/projects/" + projectUuid + "/teams");
const data = await response.json();
return data;
}
async function addProjectTeamMember(client, projectUuid, userUuid, customRoleId) {
const response = await client.post("v1/projects/" + projectUuid + "/teams/members/" + userUuid, {
custom_role_id: customRoleId
});
const data = await response.json();
return data.user;
}
async function removeProjectTeamMember(client, projectUuid, userUuid) {
const response = await client.delete("v1/projects/" + projectUuid + "/teams/members/" + userUuid);
const data = await response.json();
return data.user;
}
async function addExternalUserToProject(client, projectUuid, userData) {
const response = await client.post("v1/projects/" + projectUuid + "/teams/external-users", userData);
const data = await response.json();
return data.user;
}
async function removeExternalUserToProject(client, projectUuid, userUuidOrEmail) {
const response = await client.delete("v1/projects/" + projectUuid + "/teams/external-users/" + userUuidOrEmail);
const data = await response.json();
return data.user;
}
async function updateProjectMemberRole(client, projectUuid, userUuid, customRoleId) {
const response = await client.patch("v1/projects/" + projectUuid + "/teams/members/" + userUuid + "/custom-role", {
custom_role_id: customRoleId
});
const data = await response.json();
return data.user;
}
// src/users.ts
var users_exports = {};
__export(users_exports, {
deleteCompanyUser: () => deleteCompanyUser,
getCompanyExternalUserProjects: () => getCompanyExternalUserProjects,
getCompanyExternalUsers: () => getCompanyExternalUsers,
getCompanyUsers: () => getCompanyUsers,
inviteCompanyUser: () => inviteCompanyUser,
patchCompanyUser: () => patchCompanyUser,
purgeCompanyExternalUser: () => purgeCompanyExternalUser
});
async function getCompanyUsers(client, params) {
const response = await client.get("v1/users", params);
const data = await response.json();
return data;
}
async function inviteCompanyUser(client, userData) {
const response = await client.post("v1/users", userData);
const data = await response.json();
return data.user;
}
async function patchCompanyUser(client, userUuid, userData) {
const response = await client.patch("v1/users/" + userUuid, userData);
const data = await response.json();
return data.user;
}
async function deleteCompanyUser(client, userUuid) {
const response = await client.delete("v1/users/" + userUuid);
const data = await response.json();
return data;
}
async function getCompanyExternalUsers(client, params) {
const response = await client.get("v1/company/external-users", params);
return await response.json();
}
async function getCompanyExternalUserProjects(client, externalUserUuid, params) {
const response = await client.get("v1/company/external-users/" + externalUserUuid + "/projects", params);
return await response.json();
}
async function purgeCompanyExternalUser(client, externalUserUuid) {
const response = await client.delete("v1/company/external-users/" + externalUserUuid);
return await response.json();
}
// src/files.ts
var files_exports = {};
__export(files_exports, {
ReviewStatus: () => ReviewStatus,
VirusStatus: () => VirusStatus,
acceptFile: () => acceptFile,
createNewFileUploadUrl: () => createNewFileUploadUrl,
deleteFile: () => deleteFile,
getProjectFile: () => getProjectFile,
getProjectFileHistory: () => getProjectFileHistory,
getProjectFileVersionUrl: () => getProjectFileVersionUrl,
getProjectFiles: () => getProjectFiles,
getRecentNotifications: () => getRecentNotifications,
uploadFile: () => uploadFile
});
var VirusStatus = /* @__PURE__ */ ((VirusStatus2) => {
VirusStatus2[VirusStatus2["PENDING"] = 0] = "PENDING";
VirusStatus2[VirusStatus2["PASSED"] = 1] = "PASSED";
VirusStatus2[VirusStatus2["FAILED"] = 2] = "FAILED";
VirusStatus2[VirusStatus2["UNABLE"] = 3] = "UNABLE";
VirusStatus2[VirusStatus2["SKIP"] = 4] = "SKIP";
return VirusStatus2;
})(VirusStatus || {});
var ReviewStatus = /* @__PURE__ */ ((ReviewStatus2) => {
ReviewStatus2[ReviewStatus2["WIP"] = 0] = "WIP";
ReviewStatus2[ReviewStatus2["AWAITING_REVIEW"] = 1] = "AWAITING_REVIEW";
ReviewStatus2[ReviewStatus2["REVIEW_PASSED"] = 2] = "REVIEW_PASSED";
ReviewStatus2[ReviewStatus2["REVIEW_FAILED"] = 3] = "REVIEW_FAILED";
ReviewStatus2[ReviewStatus2["AWAITING_FORMATTING"] = 4] = "AWAITING_FORMATTING";
ReviewStatus2[ReviewStatus2["FORMATTING_FAILED"] = 5] = "FORMATTING_FAILED";
ReviewStatus2[ReviewStatus2["READY"] = 6] = "READY";
ReviewStatus2[ReviewStatus2["SENT"] = 7] = "SENT";
return ReviewStatus2;
})(ReviewStatus || {});
async function getProjectFiles(client, projectUuid, page = 1, options) {
const response = await client.get("v1/projects/" + projectUuid + "/files", { page, ...options });
const data = await response.json();
return data;
}
async function getProjectFile(client, projectUuid, fileUuid) {
const response = await client.get("v1/projects/" + projectUuid + "/files/" + fileUuid);
const data = await response.json();
return data;
}
async function getProjectFileHistory(client, projectUuid, fileUuid) {
const response = await client.get("v1/projects/" + projectUuid + "/files/" + fileUuid + "/history");
const data = await response.json();
return data;
}
async function getProjectFileVersionUrl(client, projectUuid, fileUuid, version) {
const response = await client.get("v1/projects/" + projectUuid + "/files/" + fileUuid + "/history/" + version + "/temporary-url");
const data = await response.json();
return data;
}
async function uploadFile(client, projectUuid, path, name, file, options) {
const uploadUrl = await createNewFileUploadUrl(client, projectUuid, path, name, options);
const response = await fetch(uploadUrl.upload_put_url, {
method: "PUT",
headers: uploadUrl.upload_put_headers,
body: file
});
if (!response.ok) {
throw new Error("Failed to upload file: " + response.status + " " + await response.text());
}
return await acceptFile(client, projectUuid, uploadUrl.uuid);
}
async function createNewFileUploadUrl(client, projectUuid, path, name, options) {
const response = await client.post("v1/projects/" + projectUuid + "/files/upload-url", {
path,
name,
create_notification: options?.create_notification
});
const data = await response.json();
return data;
}
async function acceptFile(client, projectUuid, fileUuid) {
const response = await client.post("v1/projects/" + projectUuid + "/files/" + fileUuid + "/accept-upload");
const data = await response.json();
return data;
}
async function deleteFile(client, projectUuid, fileUuid) {
await client.delete("v1/projects/" + projectUuid + "/files/" + fileUuid);
}
async function getRecentNotifications(client, projectUuid, page = 1, filters) {
const response = await client.get("v1/projects/" + projectUuid + "/notifications", { page, ...filters });
const data = await response.json();
return data;
}
// src/webhook.ts
var webhook_exports = {};
__export(webhook_exports, {
WebhookAuthorizationStatus: () => WebhookAuthorizationStatus,
authenticateWebhook: () => authenticateWebhook,
formatAuthorizationStatus: () => formatAuthorizationStatus
});
import { createHmac } from "node:crypto";
var WebhookAuthorizationStatus = /* @__PURE__ */ ((WebhookAuthorizationStatus2) => {
WebhookAuthorizationStatus2[WebhookAuthorizationStatus2["Success"] = 0] = "Success";
WebhookAuthorizationStatus2[WebhookAuthorizationStatus2["TimestampError"] = 1] = "TimestampError";
WebhookAuthorizationStatus2[WebhookAuthorizationStatus2["SignatureError"] = 2] = "SignatureError";
WebhookAuthorizationStatus2[WebhookAuthorizationStatus2["MissingTimestamp"] = 3] = "MissingTimestamp";
WebhookAuthorizationStatus2[WebhookAuthorizationStatus2["MissingSignature"] = 4] = "MissingSignature";
WebhookAuthorizationStatus2[WebhookAuthorizationStatus2["InvalidApiKey"] = 5] = "InvalidApiKey";
WebhookAuthorizationStatus2[WebhookAuthorizationStatus2["MissingApiKey"] = 6] = "MissingApiKey";
return WebhookAuthorizationStatus2;
})(WebhookAuthorizationStatus || {});
function formatAuthorizationStatus(status) {
return {
[0 /* Success */]: "Success",
[1 /* TimestampError */]: "Timestamp Error",
[2 /* SignatureError */]: "Signature Error",
[5 /* InvalidApiKey */]: "Invalid API Key",
[3 /* MissingTimestamp */]: "Missing Timestamp",
[4 /* MissingSignature */]: "Missing Signature",
[6 /* MissingApiKey */]: "Missing API Key"
}[status];
}
async function authenticateWebhook(request, requestBody, apiKey, secretKey, disableTimestampCheck = false) {
const signature = request.headers.get("X-Signature");
const timestamp = request.headers.get("X-Timestamp");
const apiKeyFromRequest = request.headers.get("X-Api-Key");
if (apiKeyFromRequest !== apiKey) {
return 5 /* InvalidApiKey */;
}
const currentTimestamp = Math.floor(Date.now() / 1e3);
if (!signature) {
return 4 /* MissingSignature */;
}
if (!timestamp) {
return 3 /* MissingTimestamp */;
}
if (!disableTimestampCheck && Math.abs(currentTimestamp - parseInt(timestamp)) > 60) {
return 1 /* TimestampError */;
}
const content = `${request.method}:${request.url}:${requestBody ? requestBody + ":" : ""}${timestamp}`;
const hmac = createHmac("sha256", secretKey);
const hash = hmac.update(content).digest("hex");
return hash === signature ? 0 /* Success */ : 2 /* SignatureError */;
}
// src/index.ts
var user = users_exports;
var DatanestClient3 = class _DatanestClient {
/**
* Create a new Datanest API client
* Note: You can use environment variables instead of using
* the constructor params `apiKey` and `apiSecret`
* ENV:
* - `DATANEST_API_KEY`
* - `DATANEST_API_SECRET`
* - `DATANEST_API_BASE_URL` (optional) Default: https://app.datanest.earth/api
*/
constructor(apiKey, apiSecret) {
this.clientId = null;
this.logErrors = true;
this.logTrace = false;
this.apiKey = apiKey || process.env.DATANEST_API_KEY || "";
this.apiSecret = apiSecret || process.env.DATANEST_API_SECRET || "";
this.baseUrl = process.env.DATANEST_API_BASE_URL || "https://app.datanest.earth/api";
this.baseUrl = this.baseUrl.replace(/\/$/, "");
if (!this.baseUrl.endsWith("/api")) {
throw new Error('Invalid base URL. Must end with "/api"');
}
if (this.apiKey === "" || this.apiSecret === "") {
throw new Error("API key and secret are required.");
}
}
static {
// Static rate limiter properties (shared across all instances)
this.rateLimitMax = 120 / 8;
}
static {
// Divide by 8 to allow for shorter max delays
this.rateLimitIntervalMs = 6e4 / 8;
}
static {
this.requestTimestamps = [];
}
static disableRateLimit() {
_DatanestClient.rateLimitMax = 1e8;
_DatanestClient.rateLimitIntervalMs = 0;
}
/** Datanest accepts up to 60 requests per minute, default limit is less for typical use */
static setRateLimit(maxRequests, intervalMs = 6e4) {
_DatanestClient.rateLimitMax = maxRequests;
_DatanestClient.rateLimitIntervalMs = intervalMs;
}
static async checkRateLimit(logWarning = false) {
while (_DatanestClient.requestTimestamps.length >= _DatanestClient.rateLimitMax) {
const now = Date.now();
_DatanestClient.requestTimestamps = _DatanestClient.requestTimestamps.filter((timestamp) => now - timestamp < _DatanestClient.rateLimitIntervalMs);
const waitTime = _DatanestClient.rateLimitIntervalMs - (now - _DatanestClient.requestTimestamps[0]);
if (logWarning) {
console.debug("Waiting for rate limit window to expire", waitTime);
}
await new Promise((resolve) => setTimeout(resolve, waitTime));
}
_DatanestClient.requestTimestamps.push(Date.now());
}
setLogErrors(logErrors) {
this.logErrors = logErrors;
}
setLogTrace(logTrace) {
this.logTrace = logTrace;
}
signRequest(url, requestOptions) {
const hmac = createHmac2("sha256", this.apiSecret);
const timestamp = Date.now() / 1e3;
if (!requestOptions.headers) {
requestOptions.headers = {};
}
const headers = requestOptions.headers;
headers["X-Timestamp"] = timestamp.toFixed(0);
const content = `${requestOptions.method}:${url}:${requestOptions.body ? requestOptions.body + ":" : ""}${timestamp.toFixed(0)}`;
headers["X-Signature"] = hmac.update(content).digest("hex");
}
/**
* Send a request to the Datanest API
* @param method
* @param path
* @param params
* @param fetchOptions
* @throws DatanestResponseError Request HTTP server or validation error
* @returns Fetch response with readable stream.
*/
async sendRequest(method, path, params, fetchOptions) {
if (_DatanestClient.rateLimitIntervalMs > 0 && _DatanestClient.rateLimitMax !== Infinity) {
await _DatanestClient.checkRateLimit(this.logErrors);
}
method = method.toUpperCase();
path = path.replace(/^\//, "");
if (path.startsWith("api/")) {
throw new Error('Invalid endpoint, must not start with "api/"');
}
const headers = {
...fetchOptions?.headers ?? {},
"Accept": "application/json",
"Content-Type": "application/json",
"X-API-Key": this.apiKey
};
if (this.clientId) {
headers["X-Client-ID"] = this.clientId;
}
const options = {
redirect: "error",
mode: method !== "DELETE" && method !== "PATCH" ? "no-cors" : void 0,
...fetchOptions,
method,
headers
};
let url = `${this.baseUrl}/${path}`;
if (params) {
if (method === "GET" || method === "DELETE") {
const queryParams = new URLSearchParams(params);
if (queryParams.size) {
queryParams.sort();
url += `?${queryParams.toString().replace(/\+/g, "%20")}`;
}
} else {
options.body = JSON.stringify(params);
}
}
this.signRequest(url, options);
const response = await fetch(url, options);
if (response.status > 299) {
const error = new DatanestResponseError(`Datanest API Failed: ${path}: ${response.status}`, response.status, await response.json());
if (this.logErrors) {
console.error(error.message, error.data);
} else {
this.traceRequest(method, url, params, options, response);
}
throw error;
}
this.traceRequest(method, url, params, options, response);
return response;
}
traceRequest(method, url, params, options, response) {
if (!this.logTrace) {
return;
}
const sanitizedOptions = options ? structuredClone(options) : void 0;
if (sanitizedOptions?.headers) {
sanitizedOptions.headers = {
...sanitizedOptions.headers,
"X-API-Key": "(REDACTED)",
"X-Signature": "(REDACTED)"
};
}
if (method === "GET" || method === "DELETE") {
console.log(method, url, sanitizedOptions, response?.status, response?.statusText);
return;
}
console.log(method, url, params, sanitizedOptions, response?.status, response?.statusText);
}
/**
* Send a GET request to the Datanest API
* @param path e.g. `v1/projects`
* @param params Query parameters
* @param fetchOptions
* @throws DatanestResponseError Request HTTP server or validation error
* @returns Fetch response with readable stream.
*/
async get(path, params, fetchOptions) {
return await this.sendRequest("GET", path, params, fetchOptions);
}
/**
* Send a POST request to the Datanest API
* @param path e.g. `v1/projects`
* @param params Will be converted to JSON in request body
* @param fetchOptions
* @throws DatanestResponseError Request HTTP server or validation error
* @returns Fetch response with readable stream.
*/
async post(path, params, fetchOptions) {
return await this.sendRequest("POST", path, params, fetchOptions);
}
/**
* Send a PATCH request to the Datanest API
* @param path e.g. `v1/projects/{uuid}`
* @param params Will be converted to JSON in request body
* @param fetchOptions
* @throws DatanestResponseError Request HTTP server or validation error
* @returns Fetch response with readable stream.
*/
async patch(path, params, fetchOptions) {
return await this.sendRequest("PATCH", path, params, fetchOptions);
}
/**
* Send a PUT request to the Datanest API
* @param path e.g. `v1/projects/{uuid}`
* @param params Will be converted to JSON in request body
* @param fetchOptions
* @throws DatanestResponseError Request HTTP server or validation error
* @returns Fetch response with readable stream.
*/
async put(path, params, fetchOptions) {
return await this.sendRequest("PUT", path, params, fetchOptions);
}
/**
* Send a DELETE request to the Datanest API
* @param path e.g. `v1/projects/{uuid}`
* @param params Query parameters
* @param fetchOptions
* @throws DatanestResponseError Request HTTP server or validation error
* @returns Fetch response with readable stream.
*/
async delete(path, params, fetchOptions) {
return await this.sendRequest("DELETE", path, params, fetchOptions);
}
/**
* Set the base URL for the Datanest API
* @param baseUrl e.g. `https://app.datanest.earth/api`
*/
setBaseUrl(baseUrl) {
this.baseUrl = baseUrl;
if (!this.baseUrl.endsWith("/api")) {
throw new Error('Invalid base URL. Must end with "/api"');
}
}
/**
* Set your client ID for the Datanest API
* This will append the `X-Client-ID` header to all requests
* @param clientId Your application identifier, this can assist Datanest for debugging assistance
*/
setClientId(clientId) {
this.clientId = clientId;
}
removeClientId() {
this.clientId = null;
}
};
var DatanestResponseError = class extends Error {
constructor(message, status, data) {
let msg = message;
if (typeof data.message === "string") {
msg += `: ${data.message}`;
}
super(msg);
this.status = status;
this.data = data;
}
};
export {
DatanestClient3 as DatanestClient,
DatanestResponseError,
DatanestClient3 as default,
enviro_exports as enviro,
files_exports as files,
gather_exports as gather,
integrations_exports as integrations,
projects_exports as projects,
teams_exports as teams,
user,
users_exports as users,
webhook_exports as webhook,
workflows_exports as workflows
};