@theventures/caret
Version:
Unofficial Node.js API client for the Caret HTTP API
279 lines (264 loc) • 8.14 kB
JavaScript
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);
}
}