roe-typescript 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +189 -0
  3. package/dist/api/agents.d.ts +87 -0
  4. package/dist/api/agents.js +263 -0
  5. package/dist/auth.d.ts +6 -0
  6. package/dist/auth.js +11 -0
  7. package/dist/client.d.ts +13 -0
  8. package/dist/client.js +18 -0
  9. package/dist/config.d.ts +17 -0
  10. package/dist/config.js +43 -0
  11. package/dist/exceptions.d.ts +25 -0
  12. package/dist/exceptions.js +73 -0
  13. package/dist/index.d.ts +10 -0
  14. package/dist/index.js +9 -0
  15. package/dist/integration_test.js +686 -0
  16. package/dist/models/agent.d.ts +78 -0
  17. package/dist/models/agent.js +40 -0
  18. package/dist/models/file.d.ts +30 -0
  19. package/dist/models/file.js +85 -0
  20. package/dist/models/job.d.ts +37 -0
  21. package/dist/models/job.js +133 -0
  22. package/dist/models/responses.d.ts +71 -0
  23. package/dist/models/responses.js +36 -0
  24. package/dist/models/user.d.ts +6 -0
  25. package/dist/models/user.js +1 -0
  26. package/dist/src/api/agents.js +269 -0
  27. package/dist/src/auth.js +15 -0
  28. package/dist/src/client.js +22 -0
  29. package/dist/src/config.js +47 -0
  30. package/dist/src/exceptions.js +86 -0
  31. package/dist/src/models/agent.js +45 -0
  32. package/dist/src/models/file.js +92 -0
  33. package/dist/src/models/job.js +138 -0
  34. package/dist/src/models/responses.js +42 -0
  35. package/dist/src/models/user.js +2 -0
  36. package/dist/src/utils/fileDetection.js +46 -0
  37. package/dist/src/utils/httpClient.js +236 -0
  38. package/dist/src/utils/pagination.js +18 -0
  39. package/dist/utils/fileDetection.d.ts +11 -0
  40. package/dist/utils/fileDetection.js +38 -0
  41. package/dist/utils/httpClient.d.ts +30 -0
  42. package/dist/utils/httpClient.js +229 -0
  43. package/dist/utils/pagination.d.ts +3 -0
  44. package/dist/utils/pagination.js +14 -0
  45. package/package.json +36 -0
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JobBatch = exports.Job = void 0;
4
+ const responses_1 = require("./responses");
5
+ class Job {
6
+ constructor(opts) {
7
+ this.agentsApi = opts.agentsApi;
8
+ this.jobId = opts.jobId;
9
+ this.timeoutSeconds = opts.timeoutSeconds ?? 7200;
10
+ if (this.timeoutSeconds <= 0) {
11
+ throw new Error(`timeoutSeconds must be positive, got ${this.timeoutSeconds}`);
12
+ }
13
+ }
14
+ get id() {
15
+ return this.jobId;
16
+ }
17
+ async wait(params) {
18
+ const intervalSeconds = params?.intervalSeconds ?? 5;
19
+ const timeoutSeconds = params?.timeoutSeconds ?? this.timeoutSeconds;
20
+ if (timeoutSeconds <= 0)
21
+ throw new Error(`timeoutSeconds must be positive, got ${timeoutSeconds}`);
22
+ const start = Date.now();
23
+ while (true) {
24
+ const status = await this.retrieveStatus();
25
+ const code = status.status;
26
+ if (code === responses_1.JobStatus.SUCCESS || code === responses_1.JobStatus.CACHED) {
27
+ return this.retrieveResult();
28
+ }
29
+ if (code === responses_1.JobStatus.FAILURE || code === responses_1.JobStatus.CANCELLED) {
30
+ throw new Error(`Job ${this.jobId} failed with status ${code}${status.error_message ? `: ${status.error_message}` : ""}`);
31
+ }
32
+ if (Date.now() - start > timeoutSeconds * 1000) {
33
+ throw new Error(`Job ${this.jobId} did not complete within ${timeoutSeconds} seconds`);
34
+ }
35
+ await new Promise((r) => setTimeout(r, intervalSeconds * 1000));
36
+ }
37
+ }
38
+ retrieveStatus() {
39
+ return this.agentsApi.jobs.retrieveStatus(this.jobId);
40
+ }
41
+ retrieveResult() {
42
+ return this.agentsApi.jobs.retrieveResult(this.jobId);
43
+ }
44
+ }
45
+ exports.Job = Job;
46
+ class JobBatch {
47
+ constructor(opts) {
48
+ this.completed = {};
49
+ this.statusCache = {};
50
+ this.agentsApi = opts.agentsApi;
51
+ this.jobIds = opts.jobIds;
52
+ this.timeoutSeconds = opts.timeoutSeconds ?? 7200;
53
+ if (this.timeoutSeconds <= 0) {
54
+ throw new Error(`timeoutSeconds must be positive, got ${this.timeoutSeconds}`);
55
+ }
56
+ }
57
+ get jobs() {
58
+ return this.jobIds.map((id) => new Job({ agentsApi: this.agentsApi, jobId: id, timeoutSeconds: this.timeoutSeconds }));
59
+ }
60
+ async wait(params) {
61
+ const intervalSeconds = params?.intervalSeconds ?? 5;
62
+ const timeoutSeconds = params?.timeoutSeconds ?? this.timeoutSeconds;
63
+ if (timeoutSeconds <= 0)
64
+ throw new Error(`timeoutSeconds must be positive, got ${timeoutSeconds}`);
65
+ const start = Date.now();
66
+ while (Object.keys(this.completed).length < this.jobIds.length) {
67
+ const pending = this.jobIds.filter((id) => !this.completed[id]);
68
+ if (!pending.length)
69
+ break;
70
+ const statusBatch = await this.agentsApi.jobs.retrieveStatusMany(pending);
71
+ const failures = statusBatch.filter((s) => s.status === responses_1.JobStatus.FAILURE || s.status === responses_1.JobStatus.CANCELLED);
72
+ if (failures.length) {
73
+ const detail = failures.map((f) => `${f.id}:${f.status ?? "unknown"}`).join(', ');
74
+ throw new Error(`Jobs failed or cancelled: ${detail}`);
75
+ }
76
+ const completedIds = [];
77
+ for (const status of statusBatch) {
78
+ const code = status.status;
79
+ if (code === responses_1.JobStatus.SUCCESS || code === responses_1.JobStatus.CACHED) {
80
+ completedIds.push(status.id);
81
+ }
82
+ if (code !== undefined && code !== null) {
83
+ this.statusCache[status.id] = code;
84
+ }
85
+ }
86
+ if (completedIds.length) {
87
+ const resultBatch = await this.agentsApi.jobs.retrieveResultMany(completedIds);
88
+ for (const res of resultBatch) {
89
+ if (!res.agent_id || !res.agent_version_id) {
90
+ const id = res.id ?? 'unknown';
91
+ throw new Error(`Job ${id} missing agent identifiers`);
92
+ }
93
+ // Use corrected_outputs as fallback if result is null/undefined
94
+ const outputs = res.result ?? res.corrected_outputs;
95
+ if (outputs == null) {
96
+ // Both result and corrected_outputs are null - this may indicate an error or incomplete job
97
+ throw new Error(`Job ${res.id} returned null or undefined result`);
98
+ }
99
+ if (!Array.isArray(outputs)) {
100
+ // Result exists but is not an array - unexpected format
101
+ throw new Error(`Job ${res.id} returned unexpected result format: ${typeof outputs}`);
102
+ }
103
+ this.completed[res.id] = {
104
+ agent_id: res.agent_id,
105
+ agent_version_id: res.agent_version_id,
106
+ inputs: res.inputs ?? [],
107
+ input_tokens: res.input_tokens,
108
+ output_tokens: res.output_tokens,
109
+ outputs,
110
+ };
111
+ }
112
+ }
113
+ if (Object.keys(this.completed).length < this.jobIds.length) {
114
+ if (Date.now() - start > timeoutSeconds * 1000) {
115
+ const remaining = this.jobIds.filter((id) => !this.completed[id]);
116
+ throw new Error(`Jobs ${remaining.join(", ")} did not complete within ${timeoutSeconds} seconds`);
117
+ }
118
+ await new Promise((r) => setTimeout(r, intervalSeconds * 1000));
119
+ }
120
+ }
121
+ return this.jobIds.map((id) => this.completed[id]);
122
+ }
123
+ async retrieveStatus() {
124
+ const statusMap = { ...this.statusCache };
125
+ const toQuery = this.jobIds.filter((id) => statusMap[id] === undefined);
126
+ if (toQuery.length) {
127
+ const statuses = await this.agentsApi.jobs.retrieveStatusMany(toQuery);
128
+ for (const s of statuses) {
129
+ if (s.status !== undefined && s.status !== null) {
130
+ statusMap[s.id] = s.status;
131
+ this.statusCache[s.id] = s.status;
132
+ }
133
+ }
134
+ }
135
+ return statusMap;
136
+ }
137
+ }
138
+ exports.JobBatch = JobBatch;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JobStatus = void 0;
4
+ exports.referenceFromUrl = referenceFromUrl;
5
+ exports.extractReferencesFromOutputs = extractReferencesFromOutputs;
6
+ exports.getJobReferences = getJobReferences;
7
+ const uuid_1 = require("uuid");
8
+ var JobStatus;
9
+ (function (JobStatus) {
10
+ JobStatus[JobStatus["PENDING"] = 0] = "PENDING";
11
+ JobStatus[JobStatus["STARTED"] = 1] = "STARTED";
12
+ JobStatus[JobStatus["RETRY"] = 2] = "RETRY";
13
+ JobStatus[JobStatus["SUCCESS"] = 3] = "SUCCESS";
14
+ JobStatus[JobStatus["FAILURE"] = 4] = "FAILURE";
15
+ JobStatus[JobStatus["CANCELLED"] = 5] = "CANCELLED";
16
+ JobStatus[JobStatus["CACHED"] = 6] = "CACHED";
17
+ })(JobStatus || (exports.JobStatus = JobStatus = {}));
18
+ function referenceFromUrl(url) {
19
+ const resource_id = url.split("/references/").at(-1)?.replace(/\/+$/, "") ?? (0, uuid_1.v4)();
20
+ return { url, resource_id };
21
+ }
22
+ function extractReferencesFromOutputs(outputs) {
23
+ const references = [];
24
+ for (const output of outputs) {
25
+ try {
26
+ const parsed = JSON.parse(output.value);
27
+ const refs = Array.isArray(parsed?.references) ? parsed.references : [];
28
+ for (const ref of refs) {
29
+ if (typeof ref === "string" && ref.includes("/references/")) {
30
+ references.push(referenceFromUrl(ref));
31
+ }
32
+ }
33
+ }
34
+ catch {
35
+ // ignore unparsable outputs
36
+ }
37
+ }
38
+ return references;
39
+ }
40
+ function getJobReferences(result) {
41
+ return extractReferencesFromOutputs(result.outputs ?? []);
42
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isUuidString = isUuidString;
7
+ exports.isFilePath = isFilePath;
8
+ exports.isFilePathSync = isFilePathSync;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const uuidRegex = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;
11
+ function isUuidString(value) {
12
+ return typeof value === "string" && uuidRegex.test(value);
13
+ }
14
+ /**
15
+ * Asynchronously checks if a value is a valid file path.
16
+ * Uses async fs operations to avoid blocking the event loop.
17
+ */
18
+ async function isFilePath(value) {
19
+ if (typeof value !== "string")
20
+ return false;
21
+ if (isUuidString(value))
22
+ return false;
23
+ try {
24
+ const stat = await fs_1.default.promises.stat(value);
25
+ return stat.isFile();
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ /**
32
+ * Synchronous version of isFilePath for backward compatibility.
33
+ * @deprecated Use isFilePath (async) instead to avoid blocking the event loop.
34
+ */
35
+ function isFilePathSync(value) {
36
+ if (typeof value !== "string")
37
+ return false;
38
+ if (isUuidString(value))
39
+ return false;
40
+ try {
41
+ return fs_1.default.existsSync(value) && fs_1.default.statSync(value).isFile();
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
@@ -0,0 +1,236 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RoeHTTPClient = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const form_data_1 = __importDefault(require("form-data"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const http_1 = __importDefault(require("http"));
11
+ const https_1 = __importDefault(require("https"));
12
+ const file_1 = require("../models/file");
13
+ const exceptions_1 = require("../exceptions");
14
+ const fileDetection_1 = require("./fileDetection");
15
+ // Keep-alive agents for connection reuse (improves performance under load)
16
+ const httpAgent = new http_1.default.Agent({ keepAlive: true });
17
+ const httpsAgent = new https_1.default.Agent({ keepAlive: true });
18
+ function sleep(ms) {
19
+ return new Promise((resolve) => setTimeout(resolve, ms));
20
+ }
21
+ class RoeHTTPClient {
22
+ constructor(config, auth) {
23
+ this.config = config;
24
+ this.auth = auth;
25
+ this.maxRetries = config.maxRetries;
26
+ this.client = axios_1.default.create({
27
+ baseURL: config.baseUrl,
28
+ timeout: config.timeoutMs,
29
+ headers: auth.getHeaders(),
30
+ httpAgent,
31
+ httpsAgent,
32
+ });
33
+ }
34
+ close() {
35
+ // no-op; included for parity
36
+ }
37
+ async execute(fn) {
38
+ let attempt = 0;
39
+ let lastError;
40
+ while (attempt <= this.maxRetries) {
41
+ try {
42
+ const res = await fn();
43
+ return this.handleResponse(res);
44
+ }
45
+ catch (err) {
46
+ lastError = err;
47
+ const status = axios_1.default.isAxiosError(err) ? err.response?.status : undefined;
48
+ const isNetworkError = axios_1.default.isAxiosError(err) && !err.response;
49
+ const retriableStatus = status !== undefined &&
50
+ (status >= 500 || status === 429 || status === 408);
51
+ const shouldRetry = (isNetworkError || retriableStatus) && attempt < this.maxRetries;
52
+ if (!shouldRetry) {
53
+ if (axios_1.default.isAxiosError(err) && err.response) {
54
+ const ExceptionClass = (0, exceptions_1.getExceptionForStatusCode)(err.response.status);
55
+ const data = err.response.data;
56
+ // Use improved error message extraction that handles multiple formats
57
+ const message = (0, exceptions_1.extractErrorMessage)(data, err.response.status);
58
+ throw new ExceptionClass(message, err.response.status, data ?? null);
59
+ }
60
+ throw err;
61
+ }
62
+ const backoffMs = Math.min(1000 * 2 ** attempt, 10000);
63
+ await sleep(backoffMs);
64
+ }
65
+ attempt += 1;
66
+ }
67
+ if (lastError)
68
+ throw lastError;
69
+ throw new Error("Request failed");
70
+ }
71
+ handleResponse(response) {
72
+ if (response.status >= 200 && response.status < 300) {
73
+ return response.data;
74
+ }
75
+ const ExceptionClass = (0, exceptions_1.getExceptionForStatusCode)(response.status);
76
+ const data = response.data;
77
+ // Use improved error message extraction that handles multiple formats
78
+ const message = (0, exceptions_1.extractErrorMessage)(data, response.status);
79
+ throw new ExceptionClass(message, response.status, data ?? null);
80
+ }
81
+ get(url, params) {
82
+ return this.execute(() => this.client.get(url, { params }));
83
+ }
84
+ async getBytes(url, params) {
85
+ const data = await this.execute(() => this.client.get(url, { params, responseType: "arraybuffer" }));
86
+ // Normalize ArrayBuffer/Buffer to Buffer (axios returns ArrayBuffer in browsers)
87
+ return Buffer.isBuffer(data) ? data : Buffer.from(data);
88
+ }
89
+ async post(options) {
90
+ const { url, json, formData, files, params } = options;
91
+ const config = { params };
92
+ if (json !== undefined) {
93
+ return this.execute(() => this.client.post(url, json, config));
94
+ }
95
+ // Check if we have files that need retry-safe handling
96
+ const hasFiles = files && Object.keys(files).length > 0;
97
+ if (!hasFiles) {
98
+ // No files - build FormData once (safe for retries)
99
+ const fd = new form_data_1.default();
100
+ if (formData) {
101
+ for (const [k, v] of Object.entries(formData)) {
102
+ if (v === undefined || v === null)
103
+ continue;
104
+ fd.append(k, String(v));
105
+ }
106
+ }
107
+ config.headers = { ...(config.headers || {}), ...fd.getHeaders() };
108
+ return this.execute(() => this.client.post(url, fd, config));
109
+ }
110
+ // With files - need to rebuild FormData on each retry attempt
111
+ // because file streams can only be read once
112
+ return this.executeWithFormData(url, formData ?? {}, files, config);
113
+ }
114
+ /**
115
+ * Execute a POST request with FormData that contains files.
116
+ * Rebuilds FormData on each retry attempt to handle stream consumption.
117
+ */
118
+ async executeWithFormData(url, formData, files, config) {
119
+ // Pre-resolve file paths to avoid async in retry loop
120
+ const resolvedFiles = [];
121
+ for (const [k, v] of Object.entries(files)) {
122
+ if (v instanceof file_1.FileUpload) {
123
+ resolvedFiles.push({ key: k, type: "fileUpload", value: v });
124
+ }
125
+ else if (typeof v === "string" && await (0, fileDetection_1.isFilePath)(v)) {
126
+ resolvedFiles.push({ key: k, type: "path", value: v });
127
+ }
128
+ else if (typeof v === "string") {
129
+ resolvedFiles.push({ key: k, type: "string", value: v });
130
+ }
131
+ else {
132
+ // Raw streams cannot be safely retried - warn but allow first attempt
133
+ resolvedFiles.push({ key: k, type: "stream", value: v });
134
+ }
135
+ }
136
+ let attempt = 0;
137
+ let lastError;
138
+ while (attempt <= this.maxRetries) {
139
+ try {
140
+ // Build fresh FormData for each attempt
141
+ const fd = new form_data_1.default();
142
+ for (const [k, v] of Object.entries(formData)) {
143
+ if (v === undefined || v === null)
144
+ continue;
145
+ fd.append(k, String(v));
146
+ }
147
+ for (const file of resolvedFiles) {
148
+ if (file.type === "fileUpload") {
149
+ const fu = file.value;
150
+ fd.append(file.key, fu.open(), {
151
+ filename: fu.effectiveFilename,
152
+ contentType: fu.effectiveMimeType,
153
+ });
154
+ }
155
+ else if (file.type === "path") {
156
+ fd.append(file.key, fs_1.default.createReadStream(file.value));
157
+ }
158
+ else if (file.type === "string") {
159
+ fd.append(file.key, file.value);
160
+ }
161
+ else {
162
+ // Stream - can only be used on first attempt
163
+ if (attempt > 0) {
164
+ throw new Error(`Cannot retry request with consumed stream for field "${file.key}". ` +
165
+ `Use FileUpload or file path instead for retry-safe uploads.`);
166
+ }
167
+ fd.append(file.key, file.value);
168
+ }
169
+ }
170
+ const reqConfig = { ...config, headers: { ...(config.headers || {}), ...fd.getHeaders() } };
171
+ const res = await this.client.post(url, fd, reqConfig);
172
+ return this.handleResponse(res);
173
+ }
174
+ catch (err) {
175
+ lastError = err;
176
+ const status = axios_1.default.isAxiosError(err) ? err.response?.status : undefined;
177
+ const isNetworkError = axios_1.default.isAxiosError(err) && !err.response;
178
+ const retriableStatus = status !== undefined &&
179
+ (status >= 500 || status === 429 || status === 408);
180
+ const shouldRetry = (isNetworkError || retriableStatus) && attempt < this.maxRetries;
181
+ if (!shouldRetry) {
182
+ if (axios_1.default.isAxiosError(err) && err.response) {
183
+ const ExceptionClass = (0, exceptions_1.getExceptionForStatusCode)(err.response.status);
184
+ const data = err.response.data;
185
+ const message = (0, exceptions_1.extractErrorMessage)(data, err.response.status);
186
+ throw new ExceptionClass(message, err.response.status, data ?? null);
187
+ }
188
+ throw err;
189
+ }
190
+ const backoffMs = Math.min(1000 * 2 ** attempt, 10000);
191
+ await sleep(backoffMs);
192
+ }
193
+ attempt += 1;
194
+ }
195
+ if (lastError)
196
+ throw lastError;
197
+ throw new Error("Request failed");
198
+ }
199
+ put(url, json, params) {
200
+ return this.execute(() => this.client.put(url, json ?? {}, { params }));
201
+ }
202
+ delete(url, params) {
203
+ return this.execute(() => this.client.delete(url, { params }));
204
+ }
205
+ async postWithDynamicInputs(url, inputs, params) {
206
+ const formData = {};
207
+ const files = {};
208
+ // Process inputs and detect file paths asynchronously
209
+ for (const [key, value] of Object.entries(inputs)) {
210
+ if (value instanceof file_1.FileUpload) {
211
+ files[key] = value;
212
+ }
213
+ else if (value && typeof value === "object" && typeof value.pipe === "function") {
214
+ // More robust stream detection: check if it's an object with a pipe function
215
+ files[key] = value;
216
+ }
217
+ else if (typeof value === "string") {
218
+ if ((0, fileDetection_1.isUuidString)(value)) {
219
+ formData[key] = value;
220
+ }
221
+ else if (await (0, fileDetection_1.isFilePath)(value)) {
222
+ // Use async file detection to avoid blocking the event loop
223
+ files[key] = value;
224
+ }
225
+ else {
226
+ formData[key] = value;
227
+ }
228
+ }
229
+ else if (value !== undefined && value !== null) {
230
+ formData[key] = value;
231
+ }
232
+ }
233
+ return this.post({ url, formData, files, params });
234
+ }
235
+ }
236
+ exports.RoeHTTPClient = RoeHTTPClient;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PaginationHelper = void 0;
4
+ class PaginationHelper {
5
+ static buildQueryParams(organizationId, page, pageSize, extra = {}) {
6
+ const params = { organization_id: organizationId };
7
+ if (page !== undefined)
8
+ params.page = String(page);
9
+ if (pageSize !== undefined)
10
+ params.page_size = String(pageSize);
11
+ for (const [k, v] of Object.entries(extra)) {
12
+ if (v !== undefined && v !== null)
13
+ params[k] = String(v);
14
+ }
15
+ return params;
16
+ }
17
+ }
18
+ exports.PaginationHelper = PaginationHelper;
@@ -0,0 +1,11 @@
1
+ export declare function isUuidString(value: unknown): value is string;
2
+ /**
3
+ * Asynchronously checks if a value is a valid file path.
4
+ * Uses async fs operations to avoid blocking the event loop.
5
+ */
6
+ export declare function isFilePath(value: unknown): Promise<boolean>;
7
+ /**
8
+ * Synchronous version of isFilePath for backward compatibility.
9
+ * @deprecated Use isFilePath (async) instead to avoid blocking the event loop.
10
+ */
11
+ export declare function isFilePathSync(value: unknown): value is string;
@@ -0,0 +1,38 @@
1
+ import fs from "fs";
2
+ const uuidRegex = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;
3
+ export function isUuidString(value) {
4
+ return typeof value === "string" && uuidRegex.test(value);
5
+ }
6
+ /**
7
+ * Asynchronously checks if a value is a valid file path.
8
+ * Uses async fs operations to avoid blocking the event loop.
9
+ */
10
+ export async function isFilePath(value) {
11
+ if (typeof value !== "string")
12
+ return false;
13
+ if (isUuidString(value))
14
+ return false;
15
+ try {
16
+ const stat = await fs.promises.stat(value);
17
+ return stat.isFile();
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ }
23
+ /**
24
+ * Synchronous version of isFilePath for backward compatibility.
25
+ * @deprecated Use isFilePath (async) instead to avoid blocking the event loop.
26
+ */
27
+ export function isFilePathSync(value) {
28
+ if (typeof value !== "string")
29
+ return false;
30
+ if (isUuidString(value))
31
+ return false;
32
+ try {
33
+ return fs.existsSync(value) && fs.statSync(value).isFile();
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
@@ -0,0 +1,30 @@
1
+ import { FileUpload } from "../models/file";
2
+ import { RoeAuth } from "../auth";
3
+ import { RoeConfig } from "../config";
4
+ export declare class RoeHTTPClient {
5
+ private readonly config;
6
+ private readonly auth;
7
+ private readonly client;
8
+ private readonly maxRetries;
9
+ constructor(config: RoeConfig, auth: RoeAuth);
10
+ close(): void;
11
+ private execute;
12
+ private handleResponse;
13
+ get<T>(url: string, params?: Record<string, unknown>): Promise<T>;
14
+ getBytes(url: string, params?: Record<string, unknown>): Promise<Buffer>;
15
+ post<T>(options: {
16
+ url: string;
17
+ json?: Record<string, unknown> | unknown[];
18
+ formData?: Record<string, unknown>;
19
+ files?: Record<string, FileUpload | NodeJS.ReadableStream | string>;
20
+ params?: Record<string, unknown>;
21
+ }): Promise<T>;
22
+ /**
23
+ * Execute a POST request with FormData that contains files.
24
+ * Rebuilds FormData on each retry attempt to handle stream consumption.
25
+ */
26
+ private executeWithFormData;
27
+ put<T>(url: string, json?: Record<string, unknown>, params?: Record<string, unknown>): Promise<T>;
28
+ delete(url: string, params?: Record<string, unknown>): Promise<void>;
29
+ postWithDynamicInputs<T>(url: string, inputs: Record<string, unknown>, params?: Record<string, unknown>): Promise<T>;
30
+ }