UNPKG

@datanest-earth/nodejs-client

Version:
878 lines (866 loc) 34 kB
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 };