@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
JavaScript
"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