UNPKG

@theventures/caret

Version:

Unofficial Node.js API client for the Caret HTTP API

279 lines (264 loc) 8.14 kB
var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __hasOwnProp = Object.prototype.hasOwnProperty; var __moduleCache = /* @__PURE__ */ new WeakMap; var __toCommonJS = (from) => { var entry = __moduleCache.get(from), desc; if (entry) return entry; entry = __defProp({}, "__esModule", { value: true }); if (from && typeof from === "object" || typeof from === "function") __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable })); __moduleCache.set(from, entry); return entry; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true, configurable: true, set: (newValue) => all[name] = () => newValue }); }; // src/index.ts var exports_src = {}; __export(exports_src, { UnprocessableEntityError: () => UnprocessableEntityError, RateLimitError: () => RateLimitError, PermissionDeniedError: () => PermissionDeniedError, Notes: () => Notes, NotFoundError: () => NotFoundError, InternalServerError: () => InternalServerError, ConflictError: () => ConflictError, CaretError: () => CaretError, CaretAPIError: () => CaretAPIError, Caret: () => Caret, BadRequestError: () => BadRequestError, AuthenticationError: () => AuthenticationError }); module.exports = __toCommonJS(exports_src); // src/core/errors.ts class CaretError extends Error { constructor(message) { super(message); this.name = "CaretError"; } } class CaretAPIError extends CaretError { status; headers; error; requestId; constructor(status, error, message, headers = {}) { super(message); this.name = "CaretAPIError"; this.status = status; this.error = error; this.headers = headers; this.requestId = headers["x-request-id"] || headers["X-Request-ID"]; } static generate(status, error, message, headers = {}) { const ErrorClass = CaretAPIError.getErrorClass(status); return new ErrorClass(status, error, message, headers); } static getErrorClass(status) { switch (status) { case 400: return BadRequestError; case 401: return AuthenticationError; case 403: return PermissionDeniedError; case 404: return NotFoundError; case 409: return ConflictError; case 422: return UnprocessableEntityError; case 429: return RateLimitError; case 500: return InternalServerError; default: return CaretAPIError; } } } class BadRequestError extends CaretAPIError { constructor(status, error, message, headers = {}) { super(status, error, message, headers); this.name = "BadRequestError"; } } class AuthenticationError extends CaretAPIError { constructor(status, error, message, headers = {}) { super(status, error, message, headers); this.name = "AuthenticationError"; } } class PermissionDeniedError extends CaretAPIError { constructor(status, error, message, headers = {}) { super(status, error, message, headers); this.name = "PermissionDeniedError"; } } class NotFoundError extends CaretAPIError { constructor(status, error, message, headers = {}) { super(status, error, message, headers); this.name = "NotFoundError"; } } class ConflictError extends CaretAPIError { constructor(status, error, message, headers = {}) { super(status, error, message, headers); this.name = "ConflictError"; } } class UnprocessableEntityError extends CaretAPIError { constructor(status, error, message, headers = {}) { super(status, error, message, headers); this.name = "UnprocessableEntityError"; } } class RateLimitError extends CaretAPIError { constructor(status, error, message, headers = {}) { super(status, error, message, headers); this.name = "RateLimitError"; } } class InternalServerError extends CaretAPIError { constructor(status, error, message, headers = {}) { super(status, error, message, headers); this.name = "InternalServerError"; } } // src/core/resource.ts class APIResource { _client; constructor(client) { this._client = client; } } // src/resources/notes.ts class Notes extends APIResource { async list(params = {}) { return this._client.get("/notes", { params }); } async get(id) { try { const response = await this._client.get(`/notes/${id}`); return response.note; } catch (error) { if (error instanceof NotFoundError) { return null; } throw error; } } async update(id, params) { const response = await this._client.patch(`/notes/${id}`, { body: params }); return response.note; } } // src/client.ts class Caret { apiKey; baseURL; timeout; maxRetries; notes; constructor(options = {}) { this.apiKey = options.apiKey ?? process.env.CARET_API_KEY ?? ""; this.baseURL = options.baseURL ?? "https://api.caret.so/v1"; this.timeout = options.timeout ?? 30000; this.maxRetries = options.maxRetries ?? 3; if (!this.apiKey) { throw new Error("API key is required. Set CARET_API_KEY environment variable or pass apiKey option."); } this.notes = new Notes(this); } async request(method, path, options = {}) { const cleanPath = path.startsWith("/") ? path.slice(1) : path; const url = new URL(cleanPath, this.baseURL.endsWith("/") ? this.baseURL : `${this.baseURL}/`); if (options.params) { Object.entries(options.params).forEach(([key, value]) => { if (value !== undefined && value !== null) { url.searchParams.append(key, String(value)); } }); } const headers = { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json", ...options.headers }; const requestOptions = { method, headers, signal: AbortSignal.timeout(this.timeout) }; if (options.body && (method === "POST" || method === "PATCH" || method === "PUT")) { requestOptions.body = JSON.stringify(options.body); } let lastError; for (let attempt = 0;attempt <= this.maxRetries; attempt++) { try { const response = await fetch(url.toString(), requestOptions); const responseHeaders = {}; response.headers.forEach((value, key) => { responseHeaders[key] = value; }); if (!response.ok) { let errorData; try { errorData = await response.json(); } catch { errorData = { message: response.statusText }; } throw CaretAPIError.generate(response.status, errorData, errorData.message || `HTTP ${response.status}: ${response.statusText}`, responseHeaders); } return await response.json(); } catch (error) { lastError = error; if (error instanceof CaretAPIError) { if (error.status === 429 && attempt < this.maxRetries) { const retryAfter = error.headers["retry-after"] || error.headers["Retry-After"]; const delay = retryAfter ? parseInt(retryAfter) * 1000 : 2 ** attempt * 1000; await new Promise((resolve) => setTimeout(resolve, delay)); continue; } throw error; } if (attempt < this.maxRetries) { const delay = 2 ** attempt * 1000; await new Promise((resolve) => setTimeout(resolve, delay)); } } } throw lastError || new Error("Request failed after all retries"); } get(path, options) { return this.request("GET", path, options); } post(path, options) { return this.request("POST", path, options); } patch(path, options) { return this.request("PATCH", path, options); } put(path, options) { return this.request("PUT", path, options); } delete(path, options) { return this.request("DELETE", path, options); } }