UNPKG

@lume-ai/typescript-sdk

Version:

Lume SDK for Typescript to automate data mappings with AI. Learn more at docs.lume.ai

800 lines (789 loc) 25.6 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; 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); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/index.ts var src_exports = {}; __export(src_exports, { FlowClass: () => Flow, Lume: () => Lume, RunClass: () => Run, Status: () => Status }); module.exports = __toCommonJS(src_exports); // src/models/status.ts var Status = /* @__PURE__ */ ((Status2) => { Status2["SUCCEEDED"] = "SUCCEEDED"; Status2["RUNNING"] = "RUNNING"; Status2["INCOMPLETE"] = "INCOMPLETE"; Status2["FAILED"] = "FAILED"; Status2["CREATED"] = "CREATED"; Status2["PENDING"] = "PENDING"; Status2["CRASHED"] = "CRASHED"; return Status2; })(Status || {}); // src/services/ApiClient.ts var import_axios = __toESM(require("axios")); var import_qs = __toESM(require("qs")); // src/utils/retryUtils.ts function retryWithBackoff(operation, retries = 5, delay = 1e3, factor = 2) { return __async(this, null, function* () { var _a; try { return yield operation(); } catch (error) { const retryableErrorCodes = [408, 429, 502, 503, 504]; const retryableNetworkErrors = ["ECONNRESET", "ETIMEDOUT", "ESOCKETTIMEDOUT", "ECONNABORTED"]; const shouldRetry = retries > 0 && // HTTP status codes (error.response && retryableErrorCodes.includes(error.response.status) || // Axios error codes error.code && retryableNetworkErrors.includes(error.code) || // Other timeout indicators error.code === "ERR_NETWORK"); if (!shouldRetry) throw error; const errorCode = ((_a = error.response) == null ? void 0 : _a.status) || error.code || "NETWORK_ERROR"; console.warn(`Request failed with ${errorCode} - retrying in ${delay}ms (${retries} retries left)...`); yield new Promise((resolve) => setTimeout(resolve, delay)); return retryWithBackoff(operation, retries - 1, delay * factor, factor); } }); } // src/services/ApiClient.ts var ApiClient = class { constructor(apiKey, baseURL) { this.handleRequest = (config) => { return config; }; this.handleResponse = (response) => { return response; }; this.handleError = (error) => { var _a, _b, _c, _d; if (error.code) { switch (error.code) { case "ERR_NETWORK": return Promise.reject({ code: -1, message: "Network error - please check your internet connection" }); case "ERR_INVALID_URL": return Promise.reject({ code: -2, message: `Invalid API URL configuration: ${((_a = error.config) == null ? void 0 : _a.baseURL) || "no URL provided"}` }); case "ECONNREFUSED": return Promise.reject({ code: -3, message: "Connection refused - server may be down or URL is incorrect" }); } } if (error.response) { const code = error.response.status; const message = ((_b = error.response.data) == null ? void 0 : _b.detail) || ((_c = error.response.data) == null ? void 0 : _c.message) || error.message || "An unknown error occurred"; const error_id = ((_d = error.response.data) == null ? void 0 : _d.error_id) || null; return Promise.reject({ code, message, error_id }); } if (error.request) { return Promise.reject({ code: -4, message: `No response received from server: ${error.message}` }); } return Promise.reject({ code: -5, message: `Request failed: ${error.message || "Unknown error occurred"}`, detail: error }); }; this.httpClient = import_axios.default.create({ baseURL, headers: { "Content-Type": "application/json", "lume-api-key": apiKey }, paramsSerializer: (params) => { const queryParams = __spreadValues({}, params); return import_qs.default.stringify(queryParams, { arrayFormat: "repeat", encode: false }); } }); this.initializeInterceptors(); } initializeInterceptors() { this.httpClient.interceptors.request.use( this.handleRequest, this.handleError ); this.httpClient.interceptors.response.use( this.handleResponse, this.handleError ); } // Public methods to be used in other classes get(url, config, baseURLOverride) { return __async(this, null, function* () { const finalConfig = __spreadValues({}, config); if (baseURLOverride) { finalConfig.baseURL = baseURLOverride; } return retryWithBackoff(() => this.httpClient.get(url, finalConfig).then((res) => res.data)); }); } post(url, data, config, baseURLOverride) { return __async(this, null, function* () { const finalConfig = __spreadValues({}, config); if (baseURLOverride) { finalConfig.baseURL = baseURLOverride; } return retryWithBackoff(() => this.httpClient.post(url, data, finalConfig).then((res) => res.data)); }); } patch(url, data, config, baseURLOverride) { return __async(this, null, function* () { const finalConfig = __spreadValues({}, config); if (baseURLOverride) { finalConfig.baseURL = baseURLOverride; } return retryWithBackoff(() => this.httpClient.patch(url, data, finalConfig).then((res) => res.data)); }); } delete(url, config, baseURLOverride) { return __async(this, null, function* () { const finalConfig = __spreadValues({}, config); if (baseURLOverride) { finalConfig.baseURL = baseURLOverride; } return retryWithBackoff(() => this.httpClient.delete(url, finalConfig).then((res) => res.data)); }); } }; // src/models/LumeError.ts var LumeSDKError = class _LumeSDKError extends Error { constructor(message, statusCode, detail, error_id) { super(message); this.name = "LumeSDKError"; this.statusCode = statusCode; this.detail = detail; this.error_id = error_id; Object.setPrototypeOf(this, _LumeSDKError.prototype); } }; var FlowError = class _FlowError extends LumeSDKError { constructor(err, message, flow_id) { var _a, _b, _c, _d; const statusCode = (_a = err.code) != null ? _a : 500; const detail = (_c = (_b = err.message) != null ? _b : message) != null ? _c : "An unknown error occurred"; const error_id = (_d = err.error_id) != null ? _d : void 0; let detailMessage = detail; if (Array.isArray(detail)) { detailMessage = detail[0].msg; } super(detailMessage, statusCode, detail, error_id); this.name = "FlowError"; this.flow_id = flow_id; Object.setPrototypeOf(this, _FlowError.prototype); } }; var RunError = class _RunError extends LumeSDKError { constructor(err, message, run_id, flow_id) { var _a, _b, _c, _d; const statusCode = (_a = err.code) != null ? _a : 500; const detail = (_c = (_b = err.message) != null ? _b : message) != null ? _c : "An unknown error occurred"; const error_id = (_d = err.error_id) != null ? _d : void 0; let detailMessage = detail; if (Array.isArray(detail)) { detailMessage = detail[0].msg; } super(detailMessage, statusCode, detail, error_id); this.name = "RunError"; this.run_id = run_id; this.flow_id = flow_id; Object.setPrototypeOf(this, _RunError.prototype); } }; // src/services/SchemaTransformer.ts var SchemaTransformer = class { constructor(apiClient, id, type, status, created_at, updated_at, user_id, input, output, target_fields, flow_id, name, edit) { Object.defineProperty(this, "apiClient", { value: apiClient, enumerable: false, writable: true, configurable: true }); Object.defineProperty(this, "id", { value: id, enumerable: false, writable: true, configurable: true }); this.type = type; this.status = status; this.created_at = created_at; this.updated_at = updated_at; this.user_id = user_id; this.input = input; this.output = output; this.target_fields = target_fields; this.edit = edit || void 0; this.flow_id = flow_id; this.name = name; } /** * Refreshes the schema transformer details with the latest data from the API, * including the output (mapped_data, errors, etc.). */ get() { return __async(this, arguments, function* (options = {}, revalidate) { const { page = 1, size = 50 } = options; const headers = revalidate ? { "Cache-Control": "no-cache, no-store, must-revalidate", Pragma: "no-cache", Expires: "0" } : {}; let updatedData; try { updatedData = yield this.apiClient.get( `/schema_transformers/${this.id}`, { params: { page, size, validation: true, target_fields: false }, headers } ); } catch (err) { throw new RunError(err, `Failed to refresh schema transformer run${this.id}`, this.id, this.flow_id); } this.type = updatedData.type; this.status = updatedData.status; this.created_at = updatedData.created_at; this.updated_at = updatedData.updated_at; this.user_id = updatedData.user_id; this.input = updatedData.input; this.output = updatedData.output; this.target_fields = updatedData.target_fields; this.edit = updatedData.edit; this.flow_id = updatedData.flow_id; this.name = updatedData.name; }); } /** * Returns the raw data model, if needed internally for debugging. */ getValues() { return __async(this, null, function* () { return { id: this.id, type: this.type, status: this.status, created_at: this.created_at, updated_at: this.updated_at, user_id: this.user_id, flow_id: this.flow_id, edit: this.edit, input: this.input, output: this.output, target_fields: this.target_fields }; }); } }; // src/services/Run.ts var Run = class { constructor(apiClient, runData, flow_id, run_id) { var _a; Object.defineProperty(this, "apiClient", { value: apiClient, enumerable: false, writable: true, configurable: true }); Object.defineProperty(this, "flow_id", { value: flow_id, enumerable: false, writable: true, configurable: true }); this.id = run_id != null ? run_id : runData.id; this.user_id = runData.user_id; this.type = runData.type; this.flow_id = flow_id; this.status = runData.status; this.created_at = runData.created_at; this.updated_at = runData.updated_at; this.metadata = (_a = runData.metadata) != null ? _a : {}; this.steps = runData.steps; } /** * Polls until the run is complete (status = SUCCEEDED/FAILED/CRASHED). * If the run ends with a fail status, we throw a RunError with details. */ waitForCompletion(pollIntervalMs = 1e3) { return __async(this, null, function* () { while (this.status === "CREATED" /* CREATED */ || this.status === "PENDING" /* PENDING */ || this.status === "RUNNING" /* RUNNING */) { yield new Promise((resolve) => setTimeout(resolve, pollIntervalMs)); yield this.get(); } }); } /** * Refresh the Run instance with the latest data from the API. * (Your existing code uses the same endpoint to fetch flow data with run_id param.) */ get() { return __async(this, null, function* () { var _a; let response; try { response = yield this.apiClient.get(`/flows/${this.flow_id}`, { params: { run_id: this.id } }); } catch (err) { console.log("ERROR", err); throw new RunError(err, `Failed to refresh run ${this.id}`, this.id, this.flow_id); } this.user_id = response.user_id; this.type = response.type; this.status = response.status; this.created_at = response.created_at; this.updated_at = response.updated_at; this.metadata = (_a = response.metadata) != null ? _a : {}; this.steps = response.steps; }); } /** * Private: returns a typed representation of this run. * Possibly used internally for debugging or logging. */ getValues() { return { id: this.id, type: this.type, status: this.status, created_at: this.created_at, updated_at: this.updated_at, user_id: this.user_id, flow_id: this.flow_id, metadata: this.metadata, steps: this.steps, runId: this.runId, file_name: this.file_name }; } /** * (Internal Use Only) * Called by Flow.getRunResults() to retrieve the "schema_transform" step's final output. * We do not expose "SchemaTransformer" to the user directly. */ _getTransformationOutput(page = 1, size = 50) { return __async(this, null, function* () { var _a, _b; if (!this.steps || this.steps.length < 1) { return null; } const schemaTransformStep = this.steps[this.steps.length - 2]; if (!schemaTransformStep || schemaTransformStep.type !== "schema_transform") { return null; } const transformer = new SchemaTransformer( this.apiClient, schemaTransformStep.id, schemaTransformStep.type, schemaTransformStep.status, this.created_at, this.updated_at, this.user_id, {}, {}, [], this.flow_id, (_b = (_a = this.metadata) == null ? void 0 : _a.name) != null ? _b : "" ); yield transformer.get({ page, size }); return transformer.output; }); } }; // src/services/Flow.ts var Flow = class { constructor(apiClient, data) { Object.defineProperty(this, "apiClient", { value: apiClient, enumerable: false, writable: true, configurable: true }); this.id = data.id; this.user_id = data.user_id; this.version = data.version; this.tags = data.tags.map((tag) => `${tag.key}:${tag.value}`); this.description = data.description; this.steps = data.steps; this.name = data.name; this.status = data.status; this.created_at = data.created_at; this.updated_at = data.updated_at; } /** * Refreshes the flow's data from the API. */ get() { return __async(this, null, function* () { let flowData; try { flowData = yield this.apiClient.get(`/flows/${this.id}`); } catch (err) { throw new FlowError(err, `Failed to refresh flow with ID ${this.id}`, this.id); } this.version = flowData.version; this.tags = flowData.tags.map((tag) => `${tag.key}:${tag.value}`); this.description = flowData.description; this.steps = flowData.steps; this.name = flowData.name; this.status = flowData.status; this.created_at = flowData.created_at; this.updated_at = flowData.updated_at; }); } /** * Creates a new run of this flow with provided data. * If wait=true, this method polls until the run is complete. */ createRun(data, wait = false) { return __async(this, null, function* () { let newRunData; try { newRunData = yield this.apiClient.post( `/flows/${this.id}/runs`, data ); } catch (err) { throw new FlowError(err, `Failed to create run for flow ${this.id}`, this.id); } const run = new Run(this.apiClient, newRunData, this.id); if (wait) { yield run.waitForCompletion(); } return run; }); } /** * Fetches a specific run by run ID from this flow. */ getRun(run_id) { return __async(this, null, function* () { let response; try { response = yield this.apiClient.get(`/flows/${this.id}`, { params: { run_id } }); } catch (err) { throw new RunError(err, `Failed to get run ${run_id} for flow ${this.id}`, run_id, this.id); } return new Run(this.apiClient, response, this.id, run_id); }); } /** * Fetches all runs for this flow. */ getRuns(page = 1, size = 50) { return __async(this, null, function* () { let response; try { response = yield this.apiClient.get( `/flows/${this.id}/runs`, { params: { page, size } } ); } catch (err) { throw new FlowError(err, `Failed to get runs for flow ${this.id}`, this.id); } return { items: response.items.map((runData) => new Run(this.apiClient, runData, this.id)), total: response.total, page, size, pages: response.pages }; }); } /** * Searches for runs by name, tags_filter, and version_id */ searchRuns(searchDto, page = 1, size = 50) { return __async(this, null, function* () { let response; try { response = yield this.apiClient.post(`/flows/${this.id}/runs/search`, searchDto, { params: { page, size } }); } catch (err) { throw new FlowError(err, `Failed to search runs for flow ${this.id}`, this.id); } return { items: response.items.map((runData) => new Run(this.apiClient, runData, this.id)), total: response.total, page, size, pages: response.pages }; }); } /** * High-level method: processes new data through this flow, * waits for the run to complete, and returns the final mapped results. */ process(sourceData, page = 1, size = 50) { return __async(this, null, function* () { const run = yield this.createRun({ source_data: sourceData }, true); return this.getRunResults(run, page, size); }); } /** * Gets results from a specific run with pagination. * Internally calls run.getSchemaTransformerOutput, * which is where the real step logic is hidden. */ getRunResults(run, page = 1, size = 50) { return __async(this, null, function* () { const output = yield run._getTransformationOutput(page, size); return (output == null ? void 0 : output.mapped_data) || { items: [], total: 0 }; }); } /** * A new method that directly fetches run results by run ID. * Useful in concurrency scenarios: you always fetch the exact run you want. */ getRunResultsById(runId, page = 1, size = 50) { return __async(this, null, function* () { const run = yield this.getRun(runId); return this.getRunResults(run, page, size); }); } /** * Gets the results from the most recent successful run, * or null if none exist. */ getLatestRunResults(page = 1, size = 50) { return __async(this, null, function* () { const runs = yield this.getRuns(); if ((runs == null ? void 0 : runs.total) === 0) return null; const latestSuccessfulRun = runs.items.find( (r) => r.status === "SUCCEEDED" /* SUCCEEDED */ ); if (!latestSuccessfulRun) return null; const run = yield this.getRun(latestSuccessfulRun.id); if (!run) return null; return this.getRunResults(run, page, size); }); } }; // src/services/FlowService.ts var throat = require("throat"); var FlowService = class { constructor(apiClient, concurrencyLimit = 0) { this.apiClient = apiClient; this.concurrencyLimit = concurrencyLimit; this.requestQueue = concurrencyLimit > 0 ? throat(concurrencyLimit) : null; } /** * Creates a new flow, THEN executes the initial run using runData. * If `wait` is true, it waits for that initial run to finish. * * Some users prefer a single call so that no flow is created without data. */ createAndRunFlow(flowData, runData, wait = false) { return __async(this, null, function* () { const executor = () => __async(this, null, function* () { let flowObj; try { flowObj = yield this.apiClient.post("/flows", flowData); } catch (err) { throw new FlowError(err, "Failed to create flow."); } const flow = new Flow(this.apiClient, flowObj); yield flow.createRun(runData, wait); return flow; }); if (this.requestQueue) { return this.requestQueue(executor); } return executor(); }); } /** * Retrieves a flow by ID. */ getFlow(id) { return __async(this, null, function* () { if (!id) throw new Error("Flow ID is required"); let flowData; try { flowData = yield this.apiClient.get(`/flows/${id}`); } catch (err) { throw new FlowError(err, `Failed to retrieve flow ID: ${id}`, id); } return new Flow(this.apiClient, flowData); }); } /** * Creates a new flow without an initial run (less common), * but you can still do `flow.createRun(...)` after. */ createFlow(data) { return __async(this, null, function* () { let flowData; try { flowData = yield this.apiClient.post("/flows", data); } catch (err) { throw new FlowError(err, "Failed to create flow."); } return new Flow(this.apiClient, flowData); }); } /** * Retrieves all flows. */ getFlows(page = 1, size = 50) { return __async(this, null, function* () { let response; try { response = yield this.apiClient.get("/flows", { params: { page, size } }); } catch (err) { throw new FlowError(err, "Failed to retrieve flows."); } return { items: response.items.map((flow) => new Flow(this.apiClient, flow)), total: response.total, page, size, pages: response.pages }; }); } /** * Searches for flows by name or tags_filter. */ searchFlows(searchDto, page = 1, size = 50) { return __async(this, null, function* () { let response; try { response = yield this.apiClient.post("/flows/search", searchDto, { params: { page, size } }); } catch (err) { throw new FlowError(err, "Failed to search flows."); } return { items: response.items.map((flow) => new Flow(this.apiClient, flow)), total: response.total, page, size, pages: response.pages }; }); } }; // src/index.ts var PROD_ENDPOINT = "https://api.lume-terminus.com"; var Lume = class { /** * Initializes the Lume SDK. * @param apiKey - Your Lume API authentication key * @param baseURL - Optional custom API endpoint (defaults to production) */ constructor(apiKey, baseURL = PROD_ENDPOINT) { this.apiClient = new ApiClient(apiKey, baseURL); this.flowService = new FlowService(this.apiClient); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { FlowClass, Lume, RunClass, Status }); //# sourceMappingURL=index.js.map