@fluxbase/sdk 0.0.1-rc.48 → 0.0.1-rc.51
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.
- package/dist/index.cjs +1820 -283
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1593 -226
- package/dist/index.d.ts +1593 -226
- package/dist/index.js +1818 -284
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -155,6 +155,12 @@ var FluxbaseFetch = class {
|
|
|
155
155
|
async getWithHeaders(path, options = {}) {
|
|
156
156
|
return this.requestWithHeaders(path, { ...options, method: "GET" });
|
|
157
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* POST request that returns response with headers (for POST-based queries with count)
|
|
160
|
+
*/
|
|
161
|
+
async postWithHeaders(path, body, options = {}) {
|
|
162
|
+
return this.requestWithHeaders(path, { ...options, method: "POST", body });
|
|
163
|
+
}
|
|
158
164
|
/**
|
|
159
165
|
* Make an HTTP request and return response with headers
|
|
160
166
|
*/
|
|
@@ -262,6 +268,44 @@ var FluxbaseFetch = class {
|
|
|
262
268
|
});
|
|
263
269
|
return response.headers;
|
|
264
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* GET request that returns response as Blob (for file downloads)
|
|
273
|
+
*/
|
|
274
|
+
async getBlob(path, options = {}) {
|
|
275
|
+
const url = `${this.baseUrl}${path}`;
|
|
276
|
+
const headers = { ...this.defaultHeaders, ...options.headers };
|
|
277
|
+
delete headers["Content-Type"];
|
|
278
|
+
const controller = new AbortController();
|
|
279
|
+
const timeoutId = setTimeout(() => controller.abort(), options.timeout ?? this.timeout);
|
|
280
|
+
if (this.debug) {
|
|
281
|
+
console.log(`[Fluxbase SDK] GET (blob) ${url}`);
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
const response = await fetch(url, {
|
|
285
|
+
method: "GET",
|
|
286
|
+
headers,
|
|
287
|
+
signal: controller.signal
|
|
288
|
+
});
|
|
289
|
+
clearTimeout(timeoutId);
|
|
290
|
+
if (!response.ok) {
|
|
291
|
+
const error = new Error(response.statusText);
|
|
292
|
+
error.status = response.status;
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
return await response.blob();
|
|
296
|
+
} catch (err) {
|
|
297
|
+
clearTimeout(timeoutId);
|
|
298
|
+
if (err instanceof Error) {
|
|
299
|
+
if (err.name === "AbortError") {
|
|
300
|
+
const timeoutError = new Error("Request timeout");
|
|
301
|
+
timeoutError.status = 408;
|
|
302
|
+
throw timeoutError;
|
|
303
|
+
}
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
306
|
+
throw new Error("Unknown error occurred");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
265
309
|
};
|
|
266
310
|
|
|
267
311
|
// src/utils/error-handling.ts
|
|
@@ -1112,8 +1156,10 @@ var RealtimeChannel = class {
|
|
|
1112
1156
|
this.callbacks = /* @__PURE__ */ new Map();
|
|
1113
1157
|
this.presenceCallbacks = /* @__PURE__ */ new Map();
|
|
1114
1158
|
this.broadcastCallbacks = /* @__PURE__ */ new Map();
|
|
1159
|
+
this.executionLogCallbacks = /* @__PURE__ */ new Set();
|
|
1115
1160
|
this.subscriptionConfig = null;
|
|
1116
1161
|
this.subscriptionId = null;
|
|
1162
|
+
this.executionLogConfig = null;
|
|
1117
1163
|
this._presenceState = {};
|
|
1118
1164
|
this.myPresenceKey = null;
|
|
1119
1165
|
this.reconnectAttempts = 0;
|
|
@@ -1162,6 +1208,11 @@ var RealtimeChannel = class {
|
|
|
1162
1208
|
this.presenceCallbacks.set(config.event, /* @__PURE__ */ new Set());
|
|
1163
1209
|
}
|
|
1164
1210
|
this.presenceCallbacks.get(config.event).add(actualCallback);
|
|
1211
|
+
} else if (event === "execution_log" && typeof configOrCallback !== "function") {
|
|
1212
|
+
const config = configOrCallback;
|
|
1213
|
+
this.executionLogConfig = config;
|
|
1214
|
+
const actualCallback = callback;
|
|
1215
|
+
this.executionLogCallbacks.add(actualCallback);
|
|
1165
1216
|
} else {
|
|
1166
1217
|
const actualEvent = event;
|
|
1167
1218
|
const actualCallback = configOrCallback;
|
|
@@ -1442,14 +1493,26 @@ var RealtimeChannel = class {
|
|
|
1442
1493
|
this.ws.onopen = () => {
|
|
1443
1494
|
console.log("[Fluxbase Realtime] Connected");
|
|
1444
1495
|
this.reconnectAttempts = 0;
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1496
|
+
if (this.executionLogConfig) {
|
|
1497
|
+
const logSubscribeMessage = {
|
|
1498
|
+
type: "subscribe_logs",
|
|
1499
|
+
channel: this.channelName,
|
|
1500
|
+
config: {
|
|
1501
|
+
execution_id: this.executionLogConfig.execution_id,
|
|
1502
|
+
type: this.executionLogConfig.type || "function"
|
|
1503
|
+
}
|
|
1504
|
+
};
|
|
1505
|
+
this.sendMessage(logSubscribeMessage);
|
|
1506
|
+
} else {
|
|
1507
|
+
const subscribeMessage = {
|
|
1508
|
+
type: "subscribe",
|
|
1509
|
+
channel: this.channelName
|
|
1510
|
+
};
|
|
1511
|
+
if (this.subscriptionConfig) {
|
|
1512
|
+
subscribeMessage.config = this.subscriptionConfig;
|
|
1513
|
+
}
|
|
1514
|
+
this.sendMessage(subscribeMessage);
|
|
1451
1515
|
}
|
|
1452
|
-
this.sendMessage(subscribeMessage);
|
|
1453
1516
|
this.startHeartbeat();
|
|
1454
1517
|
};
|
|
1455
1518
|
this.ws.onmessage = (event) => {
|
|
@@ -1559,8 +1622,25 @@ var RealtimeChannel = class {
|
|
|
1559
1622
|
this.handlePostgresChanges(message.payload);
|
|
1560
1623
|
}
|
|
1561
1624
|
break;
|
|
1625
|
+
case "execution_log":
|
|
1626
|
+
if (message.payload) {
|
|
1627
|
+
this.handleExecutionLog(message.payload);
|
|
1628
|
+
}
|
|
1629
|
+
break;
|
|
1562
1630
|
}
|
|
1563
1631
|
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Internal: Handle execution log message
|
|
1634
|
+
*/
|
|
1635
|
+
handleExecutionLog(log) {
|
|
1636
|
+
this.executionLogCallbacks.forEach((callback) => {
|
|
1637
|
+
try {
|
|
1638
|
+
callback(log);
|
|
1639
|
+
} catch (err) {
|
|
1640
|
+
console.error("[Fluxbase Realtime] Error in execution log callback:", err);
|
|
1641
|
+
}
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1564
1644
|
/**
|
|
1565
1645
|
* Internal: Handle broadcast message
|
|
1566
1646
|
*/
|
|
@@ -1804,6 +1884,98 @@ var FluxbaseRealtime = class {
|
|
|
1804
1884
|
channel.updateToken(token);
|
|
1805
1885
|
});
|
|
1806
1886
|
}
|
|
1887
|
+
/**
|
|
1888
|
+
* Create an execution log subscription channel
|
|
1889
|
+
*
|
|
1890
|
+
* This provides a cleaner API for subscribing to execution logs
|
|
1891
|
+
* (functions, jobs, or RPC procedures).
|
|
1892
|
+
*
|
|
1893
|
+
* @param executionId - The execution ID to subscribe to
|
|
1894
|
+
* @param type - The type of execution ('function', 'job', 'rpc')
|
|
1895
|
+
* @returns ExecutionLogsChannel instance with fluent API
|
|
1896
|
+
*
|
|
1897
|
+
* @example
|
|
1898
|
+
* ```typescript
|
|
1899
|
+
* const channel = client.realtime.executionLogs('exec-123', 'function')
|
|
1900
|
+
* .onLog((log) => {
|
|
1901
|
+
* console.log(`[${log.level}] ${log.message}`)
|
|
1902
|
+
* })
|
|
1903
|
+
* .subscribe()
|
|
1904
|
+
* ```
|
|
1905
|
+
*/
|
|
1906
|
+
executionLogs(executionId, type = "function") {
|
|
1907
|
+
return new ExecutionLogsChannel(this.url, executionId, type, this.token, this.tokenRefreshCallback);
|
|
1908
|
+
}
|
|
1909
|
+
};
|
|
1910
|
+
var ExecutionLogsChannel = class {
|
|
1911
|
+
constructor(url, executionId, type, token, tokenRefreshCallback) {
|
|
1912
|
+
this.logCallbacks = [];
|
|
1913
|
+
this.executionId = executionId;
|
|
1914
|
+
this.executionType = type;
|
|
1915
|
+
const channelName = `execution:${executionId}`;
|
|
1916
|
+
this.channel = new RealtimeChannel(url, channelName, token);
|
|
1917
|
+
if (tokenRefreshCallback) {
|
|
1918
|
+
this.channel.setTokenRefreshCallback(tokenRefreshCallback);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
/**
|
|
1922
|
+
* Register a callback for log events
|
|
1923
|
+
*
|
|
1924
|
+
* @param callback - Function to call when log entries are received
|
|
1925
|
+
* @returns This channel for chaining
|
|
1926
|
+
*
|
|
1927
|
+
* @example
|
|
1928
|
+
* ```typescript
|
|
1929
|
+
* channel.onLog((log) => {
|
|
1930
|
+
* console.log(`[${log.level}] Line ${log.line_number}: ${log.message}`)
|
|
1931
|
+
* })
|
|
1932
|
+
* ```
|
|
1933
|
+
*/
|
|
1934
|
+
onLog(callback) {
|
|
1935
|
+
this.logCallbacks.push(callback);
|
|
1936
|
+
return this;
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Subscribe to execution logs
|
|
1940
|
+
*
|
|
1941
|
+
* @param callback - Optional status callback
|
|
1942
|
+
* @returns Promise that resolves when subscribed
|
|
1943
|
+
*
|
|
1944
|
+
* @example
|
|
1945
|
+
* ```typescript
|
|
1946
|
+
* await channel.subscribe()
|
|
1947
|
+
* ```
|
|
1948
|
+
*/
|
|
1949
|
+
subscribe(callback) {
|
|
1950
|
+
this.channel.on(
|
|
1951
|
+
"execution_log",
|
|
1952
|
+
{ execution_id: this.executionId, type: this.executionType },
|
|
1953
|
+
(log) => {
|
|
1954
|
+
this.logCallbacks.forEach((cb) => {
|
|
1955
|
+
try {
|
|
1956
|
+
cb(log);
|
|
1957
|
+
} catch (err) {
|
|
1958
|
+
console.error("[Fluxbase ExecutionLogs] Error in log callback:", err);
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
}
|
|
1962
|
+
);
|
|
1963
|
+
this.channel.subscribe(callback);
|
|
1964
|
+
return this;
|
|
1965
|
+
}
|
|
1966
|
+
/**
|
|
1967
|
+
* Unsubscribe from execution logs
|
|
1968
|
+
*
|
|
1969
|
+
* @returns Promise resolving to status
|
|
1970
|
+
*
|
|
1971
|
+
* @example
|
|
1972
|
+
* ```typescript
|
|
1973
|
+
* await channel.unsubscribe()
|
|
1974
|
+
* ```
|
|
1975
|
+
*/
|
|
1976
|
+
async unsubscribe() {
|
|
1977
|
+
return this.channel.unsubscribe();
|
|
1978
|
+
}
|
|
1807
1979
|
};
|
|
1808
1980
|
|
|
1809
1981
|
// src/storage.ts
|
|
@@ -2266,6 +2438,282 @@ var StorageBucket = class {
|
|
|
2266
2438
|
return { data: null, error };
|
|
2267
2439
|
}
|
|
2268
2440
|
}
|
|
2441
|
+
/**
|
|
2442
|
+
* Upload a large file with resumable chunked uploads.
|
|
2443
|
+
*
|
|
2444
|
+
* Features:
|
|
2445
|
+
* - Uploads file in chunks for reliability
|
|
2446
|
+
* - Automatically retries failed chunks with exponential backoff
|
|
2447
|
+
* - Reports progress via callback with chunk-level granularity
|
|
2448
|
+
* - Can resume interrupted uploads using session ID
|
|
2449
|
+
*
|
|
2450
|
+
* @param path - The file path within the bucket
|
|
2451
|
+
* @param file - The File or Blob to upload
|
|
2452
|
+
* @param options - Upload options including chunk size, retries, and progress callback
|
|
2453
|
+
* @returns Upload result with file info
|
|
2454
|
+
*
|
|
2455
|
+
* @example
|
|
2456
|
+
* const { data, error } = await storage.from('uploads').uploadResumable('large.zip', file, {
|
|
2457
|
+
* chunkSize: 5 * 1024 * 1024, // 5MB chunks
|
|
2458
|
+
* maxRetries: 3,
|
|
2459
|
+
* onProgress: (p) => {
|
|
2460
|
+
* console.log(`${p.percentage}% (chunk ${p.currentChunk}/${p.totalChunks})`);
|
|
2461
|
+
* console.log(`Speed: ${(p.bytesPerSecond / 1024 / 1024).toFixed(2)} MB/s`);
|
|
2462
|
+
* console.log(`Session ID (for resume): ${p.sessionId}`);
|
|
2463
|
+
* }
|
|
2464
|
+
* });
|
|
2465
|
+
*
|
|
2466
|
+
* // To resume an interrupted upload:
|
|
2467
|
+
* const { data, error } = await storage.from('uploads').uploadResumable('large.zip', file, {
|
|
2468
|
+
* resumeSessionId: 'previous-session-id',
|
|
2469
|
+
* });
|
|
2470
|
+
*/
|
|
2471
|
+
async uploadResumable(path, file, options) {
|
|
2472
|
+
try {
|
|
2473
|
+
const chunkSize = options?.chunkSize ?? 5 * 1024 * 1024;
|
|
2474
|
+
const maxRetries = options?.maxRetries ?? 3;
|
|
2475
|
+
const retryDelayMs = options?.retryDelayMs ?? 1e3;
|
|
2476
|
+
const chunkTimeout = options?.chunkTimeout ?? 6e4;
|
|
2477
|
+
const totalSize = file.size;
|
|
2478
|
+
const totalChunks = Math.ceil(totalSize / chunkSize);
|
|
2479
|
+
if (options?.signal?.aborted) {
|
|
2480
|
+
return { data: null, error: new Error("Upload aborted") };
|
|
2481
|
+
}
|
|
2482
|
+
const baseUrl = this.fetch["baseUrl"];
|
|
2483
|
+
const headers = this.fetch["defaultHeaders"];
|
|
2484
|
+
let sessionId = options?.resumeSessionId;
|
|
2485
|
+
let session;
|
|
2486
|
+
let completedChunks = [];
|
|
2487
|
+
if (!sessionId) {
|
|
2488
|
+
const initResponse = await fetch(
|
|
2489
|
+
`${baseUrl}/api/v1/storage/${this.bucketName}/chunked/init`,
|
|
2490
|
+
{
|
|
2491
|
+
method: "POST",
|
|
2492
|
+
headers: {
|
|
2493
|
+
...headers,
|
|
2494
|
+
"Content-Type": "application/json"
|
|
2495
|
+
},
|
|
2496
|
+
body: JSON.stringify({
|
|
2497
|
+
path,
|
|
2498
|
+
total_size: totalSize,
|
|
2499
|
+
chunk_size: chunkSize,
|
|
2500
|
+
content_type: options?.contentType || file.type || "application/octet-stream",
|
|
2501
|
+
metadata: options?.metadata,
|
|
2502
|
+
cache_control: options?.cacheControl
|
|
2503
|
+
}),
|
|
2504
|
+
signal: options?.signal
|
|
2505
|
+
}
|
|
2506
|
+
);
|
|
2507
|
+
if (!initResponse.ok) {
|
|
2508
|
+
const errorData = await initResponse.json().catch(() => ({}));
|
|
2509
|
+
throw new Error(
|
|
2510
|
+
errorData.error || `Failed to initialize upload: ${initResponse.statusText}`
|
|
2511
|
+
);
|
|
2512
|
+
}
|
|
2513
|
+
const initData = await initResponse.json();
|
|
2514
|
+
session = {
|
|
2515
|
+
sessionId: initData.session_id,
|
|
2516
|
+
bucket: initData.bucket,
|
|
2517
|
+
path: initData.path,
|
|
2518
|
+
totalSize: initData.total_size,
|
|
2519
|
+
chunkSize: initData.chunk_size,
|
|
2520
|
+
totalChunks: initData.total_chunks,
|
|
2521
|
+
completedChunks: initData.completed_chunks || [],
|
|
2522
|
+
status: initData.status,
|
|
2523
|
+
expiresAt: initData.expires_at,
|
|
2524
|
+
createdAt: initData.created_at
|
|
2525
|
+
};
|
|
2526
|
+
sessionId = session.sessionId;
|
|
2527
|
+
} else {
|
|
2528
|
+
const statusResponse = await fetch(
|
|
2529
|
+
`${baseUrl}/api/v1/storage/${this.bucketName}/chunked/${sessionId}/status`,
|
|
2530
|
+
{
|
|
2531
|
+
method: "GET",
|
|
2532
|
+
headers,
|
|
2533
|
+
signal: options?.signal
|
|
2534
|
+
}
|
|
2535
|
+
);
|
|
2536
|
+
if (!statusResponse.ok) {
|
|
2537
|
+
const errorData = await statusResponse.json().catch(() => ({}));
|
|
2538
|
+
throw new Error(
|
|
2539
|
+
errorData.error || `Failed to get session status: ${statusResponse.statusText}`
|
|
2540
|
+
);
|
|
2541
|
+
}
|
|
2542
|
+
const statusData = await statusResponse.json();
|
|
2543
|
+
session = statusData.session;
|
|
2544
|
+
completedChunks = session.completedChunks || [];
|
|
2545
|
+
}
|
|
2546
|
+
let uploadedBytes = 0;
|
|
2547
|
+
for (const chunkIdx of completedChunks) {
|
|
2548
|
+
const chunkStart = chunkIdx * chunkSize;
|
|
2549
|
+
const chunkEnd = Math.min(chunkStart + chunkSize, totalSize);
|
|
2550
|
+
uploadedBytes += chunkEnd - chunkStart;
|
|
2551
|
+
}
|
|
2552
|
+
let lastProgressTime = Date.now();
|
|
2553
|
+
let lastProgressBytes = uploadedBytes;
|
|
2554
|
+
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
|
|
2555
|
+
if (options?.signal?.aborted) {
|
|
2556
|
+
return { data: null, error: new Error("Upload aborted") };
|
|
2557
|
+
}
|
|
2558
|
+
if (completedChunks.includes(chunkIndex)) {
|
|
2559
|
+
continue;
|
|
2560
|
+
}
|
|
2561
|
+
const start = chunkIndex * chunkSize;
|
|
2562
|
+
const end = Math.min(start + chunkSize, totalSize);
|
|
2563
|
+
const chunk = file.slice(start, end);
|
|
2564
|
+
const chunkArrayBuffer = await chunk.arrayBuffer();
|
|
2565
|
+
let retryCount = 0;
|
|
2566
|
+
let chunkUploaded = false;
|
|
2567
|
+
while (retryCount <= maxRetries && !chunkUploaded) {
|
|
2568
|
+
try {
|
|
2569
|
+
if (options?.signal?.aborted) {
|
|
2570
|
+
return { data: null, error: new Error("Upload aborted") };
|
|
2571
|
+
}
|
|
2572
|
+
const chunkController = new AbortController();
|
|
2573
|
+
const timeoutId = setTimeout(() => chunkController.abort(), chunkTimeout);
|
|
2574
|
+
if (options?.signal) {
|
|
2575
|
+
options.signal.addEventListener(
|
|
2576
|
+
"abort",
|
|
2577
|
+
() => chunkController.abort(),
|
|
2578
|
+
{ once: true }
|
|
2579
|
+
);
|
|
2580
|
+
}
|
|
2581
|
+
const chunkResponse = await fetch(
|
|
2582
|
+
`${baseUrl}/api/v1/storage/${this.bucketName}/chunked/${sessionId}/${chunkIndex}`,
|
|
2583
|
+
{
|
|
2584
|
+
method: "PUT",
|
|
2585
|
+
headers: {
|
|
2586
|
+
...headers,
|
|
2587
|
+
"Content-Type": "application/octet-stream",
|
|
2588
|
+
"Content-Length": String(chunkArrayBuffer.byteLength)
|
|
2589
|
+
},
|
|
2590
|
+
body: chunkArrayBuffer,
|
|
2591
|
+
signal: chunkController.signal
|
|
2592
|
+
}
|
|
2593
|
+
);
|
|
2594
|
+
clearTimeout(timeoutId);
|
|
2595
|
+
if (!chunkResponse.ok) {
|
|
2596
|
+
const errorData = await chunkResponse.json().catch(() => ({}));
|
|
2597
|
+
throw new Error(
|
|
2598
|
+
errorData.error || `Chunk upload failed: ${chunkResponse.statusText}`
|
|
2599
|
+
);
|
|
2600
|
+
}
|
|
2601
|
+
chunkUploaded = true;
|
|
2602
|
+
} catch (err) {
|
|
2603
|
+
if (options?.signal?.aborted) {
|
|
2604
|
+
return { data: null, error: new Error("Upload aborted") };
|
|
2605
|
+
}
|
|
2606
|
+
retryCount++;
|
|
2607
|
+
if (retryCount > maxRetries) {
|
|
2608
|
+
throw new Error(
|
|
2609
|
+
`Failed to upload chunk ${chunkIndex} after ${maxRetries} retries: ${err.message}`
|
|
2610
|
+
);
|
|
2611
|
+
}
|
|
2612
|
+
const delay = retryDelayMs * Math.pow(2, retryCount - 1);
|
|
2613
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
uploadedBytes += end - start;
|
|
2617
|
+
if (options?.onProgress) {
|
|
2618
|
+
const now = Date.now();
|
|
2619
|
+
const elapsed = (now - lastProgressTime) / 1e3;
|
|
2620
|
+
const bytesPerSecond = elapsed > 0 ? (uploadedBytes - lastProgressBytes) / elapsed : 0;
|
|
2621
|
+
lastProgressTime = now;
|
|
2622
|
+
lastProgressBytes = uploadedBytes;
|
|
2623
|
+
options.onProgress({
|
|
2624
|
+
loaded: uploadedBytes,
|
|
2625
|
+
total: totalSize,
|
|
2626
|
+
percentage: Math.round(uploadedBytes / totalSize * 100),
|
|
2627
|
+
currentChunk: chunkIndex + 1,
|
|
2628
|
+
totalChunks,
|
|
2629
|
+
bytesPerSecond,
|
|
2630
|
+
sessionId
|
|
2631
|
+
});
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
const completeResponse = await fetch(
|
|
2635
|
+
`${baseUrl}/api/v1/storage/${this.bucketName}/chunked/${sessionId}/complete`,
|
|
2636
|
+
{
|
|
2637
|
+
method: "POST",
|
|
2638
|
+
headers,
|
|
2639
|
+
signal: options?.signal
|
|
2640
|
+
}
|
|
2641
|
+
);
|
|
2642
|
+
if (!completeResponse.ok) {
|
|
2643
|
+
const errorData = await completeResponse.json().catch(() => ({}));
|
|
2644
|
+
throw new Error(
|
|
2645
|
+
errorData.error || `Failed to complete upload: ${completeResponse.statusText}`
|
|
2646
|
+
);
|
|
2647
|
+
}
|
|
2648
|
+
const result = await completeResponse.json();
|
|
2649
|
+
return {
|
|
2650
|
+
data: {
|
|
2651
|
+
id: result.id,
|
|
2652
|
+
path: result.path,
|
|
2653
|
+
fullPath: result.full_path
|
|
2654
|
+
},
|
|
2655
|
+
error: null
|
|
2656
|
+
};
|
|
2657
|
+
} catch (error) {
|
|
2658
|
+
return { data: null, error };
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
/**
|
|
2662
|
+
* Abort an in-progress resumable upload
|
|
2663
|
+
* @param sessionId - The upload session ID to abort
|
|
2664
|
+
*/
|
|
2665
|
+
async abortResumableUpload(sessionId) {
|
|
2666
|
+
try {
|
|
2667
|
+
const baseUrl = this.fetch["baseUrl"];
|
|
2668
|
+
const headers = this.fetch["defaultHeaders"];
|
|
2669
|
+
const response = await fetch(
|
|
2670
|
+
`${baseUrl}/api/v1/storage/${this.bucketName}/chunked/${sessionId}`,
|
|
2671
|
+
{
|
|
2672
|
+
method: "DELETE",
|
|
2673
|
+
headers
|
|
2674
|
+
}
|
|
2675
|
+
);
|
|
2676
|
+
if (!response.ok && response.status !== 204) {
|
|
2677
|
+
const errorData = await response.json().catch(() => ({}));
|
|
2678
|
+
throw new Error(
|
|
2679
|
+
errorData.error || `Failed to abort upload: ${response.statusText}`
|
|
2680
|
+
);
|
|
2681
|
+
}
|
|
2682
|
+
return { error: null };
|
|
2683
|
+
} catch (error) {
|
|
2684
|
+
return { error };
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
/**
|
|
2688
|
+
* Get the status of a resumable upload session
|
|
2689
|
+
* @param sessionId - The upload session ID to check
|
|
2690
|
+
*/
|
|
2691
|
+
async getResumableUploadStatus(sessionId) {
|
|
2692
|
+
try {
|
|
2693
|
+
const baseUrl = this.fetch["baseUrl"];
|
|
2694
|
+
const headers = this.fetch["defaultHeaders"];
|
|
2695
|
+
const response = await fetch(
|
|
2696
|
+
`${baseUrl}/api/v1/storage/${this.bucketName}/chunked/${sessionId}/status`,
|
|
2697
|
+
{
|
|
2698
|
+
method: "GET",
|
|
2699
|
+
headers
|
|
2700
|
+
}
|
|
2701
|
+
);
|
|
2702
|
+
if (!response.ok) {
|
|
2703
|
+
const errorData = await response.json().catch(() => ({}));
|
|
2704
|
+
throw new Error(
|
|
2705
|
+
errorData.error || `Failed to get upload status: ${response.statusText}`
|
|
2706
|
+
);
|
|
2707
|
+
}
|
|
2708
|
+
const data = await response.json();
|
|
2709
|
+
return {
|
|
2710
|
+
data: data.session,
|
|
2711
|
+
error: null
|
|
2712
|
+
};
|
|
2713
|
+
} catch (error) {
|
|
2714
|
+
return { data: null, error };
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2269
2717
|
/**
|
|
2270
2718
|
* List files in the bucket
|
|
2271
2719
|
* Supports both Supabase-style list(path, options) and Fluxbase-style list(options)
|
|
@@ -2837,40 +3285,244 @@ var FluxbaseJobs = class {
|
|
|
2837
3285
|
return { data: null, error };
|
|
2838
3286
|
}
|
|
2839
3287
|
}
|
|
2840
|
-
};
|
|
2841
|
-
|
|
2842
|
-
// src/settings.ts
|
|
2843
|
-
var SystemSettingsManager = class {
|
|
2844
|
-
constructor(fetch2) {
|
|
2845
|
-
this.fetch = fetch2;
|
|
2846
|
-
}
|
|
2847
3288
|
/**
|
|
2848
|
-
*
|
|
3289
|
+
* Get execution logs for a job
|
|
2849
3290
|
*
|
|
2850
|
-
*
|
|
3291
|
+
* Returns logs for the specified job. Only returns logs for jobs
|
|
3292
|
+
* owned by the authenticated user (unless using service_role).
|
|
3293
|
+
*
|
|
3294
|
+
* @param jobId - Job ID
|
|
3295
|
+
* @param afterLine - Optional line number to get logs after (for polling/streaming)
|
|
3296
|
+
* @returns Promise resolving to { data, error } tuple with execution logs
|
|
2851
3297
|
*
|
|
2852
3298
|
* @example
|
|
2853
3299
|
* ```typescript
|
|
2854
|
-
*
|
|
2855
|
-
*
|
|
2856
|
-
* ```
|
|
2857
|
-
*/
|
|
2858
|
-
async list() {
|
|
2859
|
-
const settings = await this.fetch.get(
|
|
2860
|
-
"/api/v1/admin/system/settings"
|
|
2861
|
-
);
|
|
2862
|
-
return { settings: Array.isArray(settings) ? settings : [] };
|
|
2863
|
-
}
|
|
2864
|
-
/**
|
|
2865
|
-
* Get a specific system setting by key
|
|
3300
|
+
* // Get all logs for a job
|
|
3301
|
+
* const { data: logs, error } = await client.jobs.getLogs('550e8400-e29b-41d4-a716-446655440000')
|
|
2866
3302
|
*
|
|
2867
|
-
*
|
|
2868
|
-
*
|
|
3303
|
+
* if (logs) {
|
|
3304
|
+
* for (const log of logs) {
|
|
3305
|
+
* console.log(`[${log.level}] ${log.message}`)
|
|
3306
|
+
* }
|
|
3307
|
+
* }
|
|
2869
3308
|
*
|
|
2870
|
-
*
|
|
2871
|
-
*
|
|
2872
|
-
*
|
|
2873
|
-
*
|
|
3309
|
+
* // Backfill + stream pattern
|
|
3310
|
+
* const { data: logs } = await client.jobs.getLogs(jobId)
|
|
3311
|
+
* let lastLine = Math.max(...(logs?.map(l => l.line_number) ?? []), 0)
|
|
3312
|
+
*
|
|
3313
|
+
* const channel = client.realtime
|
|
3314
|
+
* .executionLogs(jobId, 'job')
|
|
3315
|
+
* .onLog((log) => {
|
|
3316
|
+
* if (log.line_number > lastLine) {
|
|
3317
|
+
* displayLog(log)
|
|
3318
|
+
* lastLine = log.line_number
|
|
3319
|
+
* }
|
|
3320
|
+
* })
|
|
3321
|
+
* .subscribe()
|
|
3322
|
+
* ```
|
|
3323
|
+
*/
|
|
3324
|
+
async getLogs(jobId, afterLine) {
|
|
3325
|
+
try {
|
|
3326
|
+
const params = afterLine !== void 0 ? `?after_line=${afterLine}` : "";
|
|
3327
|
+
const response = await this.fetch.get(
|
|
3328
|
+
`/api/v1/jobs/${jobId}/logs${params}`
|
|
3329
|
+
);
|
|
3330
|
+
return { data: response.logs || [], error: null };
|
|
3331
|
+
} catch (error) {
|
|
3332
|
+
return { data: null, error };
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
};
|
|
3336
|
+
|
|
3337
|
+
// src/rpc.ts
|
|
3338
|
+
var FluxbaseRPC = class {
|
|
3339
|
+
constructor(fetch2) {
|
|
3340
|
+
this.fetch = fetch2;
|
|
3341
|
+
}
|
|
3342
|
+
/**
|
|
3343
|
+
* List available RPC procedures (public, enabled)
|
|
3344
|
+
*
|
|
3345
|
+
* @param namespace - Optional namespace filter
|
|
3346
|
+
* @returns Promise resolving to { data, error } tuple with array of procedure summaries
|
|
3347
|
+
*/
|
|
3348
|
+
async list(namespace) {
|
|
3349
|
+
try {
|
|
3350
|
+
const params = namespace ? `?namespace=${encodeURIComponent(namespace)}` : "";
|
|
3351
|
+
const response = await this.fetch.get(
|
|
3352
|
+
`/api/v1/rpc/procedures${params}`
|
|
3353
|
+
);
|
|
3354
|
+
return { data: response.procedures || [], error: null };
|
|
3355
|
+
} catch (error) {
|
|
3356
|
+
return { data: null, error };
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
/**
|
|
3360
|
+
* Invoke an RPC procedure
|
|
3361
|
+
*
|
|
3362
|
+
* @param name - Procedure name
|
|
3363
|
+
* @param params - Optional parameters to pass to the procedure
|
|
3364
|
+
* @param options - Optional invocation options
|
|
3365
|
+
* @returns Promise resolving to { data, error } tuple with invocation response
|
|
3366
|
+
*
|
|
3367
|
+
* @example
|
|
3368
|
+
* ```typescript
|
|
3369
|
+
* // Synchronous invocation
|
|
3370
|
+
* const { data, error } = await fluxbase.rpc.invoke('get-user-orders', {
|
|
3371
|
+
* user_id: '123',
|
|
3372
|
+
* limit: 10
|
|
3373
|
+
* });
|
|
3374
|
+
* console.log(data.result); // Query results
|
|
3375
|
+
*
|
|
3376
|
+
* // Asynchronous invocation
|
|
3377
|
+
* const { data: asyncData } = await fluxbase.rpc.invoke('generate-report', {
|
|
3378
|
+
* year: 2024
|
|
3379
|
+
* }, { async: true });
|
|
3380
|
+
* console.log(asyncData.execution_id); // Use to poll status
|
|
3381
|
+
* ```
|
|
3382
|
+
*/
|
|
3383
|
+
async invoke(name, params, options) {
|
|
3384
|
+
try {
|
|
3385
|
+
const namespace = options?.namespace || "default";
|
|
3386
|
+
const response = await this.fetch.post(
|
|
3387
|
+
`/api/v1/rpc/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`,
|
|
3388
|
+
{
|
|
3389
|
+
params,
|
|
3390
|
+
async: options?.async
|
|
3391
|
+
}
|
|
3392
|
+
);
|
|
3393
|
+
return { data: response, error: null };
|
|
3394
|
+
} catch (error) {
|
|
3395
|
+
return { data: null, error };
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
/**
|
|
3399
|
+
* Get execution status (for async invocations or checking history)
|
|
3400
|
+
*
|
|
3401
|
+
* @param executionId - The execution ID returned from async invoke
|
|
3402
|
+
* @returns Promise resolving to { data, error } tuple with execution details
|
|
3403
|
+
*
|
|
3404
|
+
* @example
|
|
3405
|
+
* ```typescript
|
|
3406
|
+
* const { data, error } = await fluxbase.rpc.getStatus('execution-uuid');
|
|
3407
|
+
* if (data.status === 'completed') {
|
|
3408
|
+
* console.log('Result:', data.result);
|
|
3409
|
+
* } else if (data.status === 'running') {
|
|
3410
|
+
* console.log('Still running...');
|
|
3411
|
+
* }
|
|
3412
|
+
* ```
|
|
3413
|
+
*/
|
|
3414
|
+
async getStatus(executionId) {
|
|
3415
|
+
try {
|
|
3416
|
+
const data = await this.fetch.get(
|
|
3417
|
+
`/api/v1/rpc/executions/${encodeURIComponent(executionId)}`
|
|
3418
|
+
);
|
|
3419
|
+
return { data, error: null };
|
|
3420
|
+
} catch (error) {
|
|
3421
|
+
return { data: null, error };
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
/**
|
|
3425
|
+
* Get execution logs (for debugging and monitoring)
|
|
3426
|
+
*
|
|
3427
|
+
* @param executionId - The execution ID
|
|
3428
|
+
* @param afterLine - Optional line number to get logs after (for polling)
|
|
3429
|
+
* @returns Promise resolving to { data, error } tuple with execution logs
|
|
3430
|
+
*
|
|
3431
|
+
* @example
|
|
3432
|
+
* ```typescript
|
|
3433
|
+
* const { data: logs } = await fluxbase.rpc.getLogs('execution-uuid');
|
|
3434
|
+
* for (const log of logs) {
|
|
3435
|
+
* console.log(`[${log.level}] ${log.message}`);
|
|
3436
|
+
* }
|
|
3437
|
+
* ```
|
|
3438
|
+
*/
|
|
3439
|
+
async getLogs(executionId, afterLine) {
|
|
3440
|
+
try {
|
|
3441
|
+
const params = afterLine !== void 0 ? `?after=${afterLine}` : "";
|
|
3442
|
+
const response = await this.fetch.get(
|
|
3443
|
+
`/api/v1/rpc/executions/${encodeURIComponent(executionId)}/logs${params}`
|
|
3444
|
+
);
|
|
3445
|
+
return { data: response.logs || [], error: null };
|
|
3446
|
+
} catch (error) {
|
|
3447
|
+
return { data: null, error };
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
/**
|
|
3451
|
+
* Poll for execution completion with exponential backoff
|
|
3452
|
+
*
|
|
3453
|
+
* @param executionId - The execution ID to poll
|
|
3454
|
+
* @param options - Polling options
|
|
3455
|
+
* @returns Promise resolving to final execution state
|
|
3456
|
+
*
|
|
3457
|
+
* @example
|
|
3458
|
+
* ```typescript
|
|
3459
|
+
* const { data: result } = await fluxbase.rpc.invoke('long-task', {}, { async: true });
|
|
3460
|
+
* const { data: final } = await fluxbase.rpc.waitForCompletion(result.execution_id, {
|
|
3461
|
+
* maxWaitMs: 60000, // Wait up to 1 minute
|
|
3462
|
+
* onProgress: (exec) => console.log(`Status: ${exec.status}`)
|
|
3463
|
+
* });
|
|
3464
|
+
* console.log('Final result:', final.result);
|
|
3465
|
+
* ```
|
|
3466
|
+
*/
|
|
3467
|
+
async waitForCompletion(executionId, options) {
|
|
3468
|
+
const maxWait = options?.maxWaitMs || 3e4;
|
|
3469
|
+
const initialInterval = options?.initialIntervalMs || 500;
|
|
3470
|
+
const maxInterval = options?.maxIntervalMs || 5e3;
|
|
3471
|
+
const startTime = Date.now();
|
|
3472
|
+
let interval = initialInterval;
|
|
3473
|
+
while (Date.now() - startTime < maxWait) {
|
|
3474
|
+
const { data: execution, error } = await this.getStatus(executionId);
|
|
3475
|
+
if (error) {
|
|
3476
|
+
return { data: null, error };
|
|
3477
|
+
}
|
|
3478
|
+
if (!execution) {
|
|
3479
|
+
return { data: null, error: new Error("Execution not found") };
|
|
3480
|
+
}
|
|
3481
|
+
if (options?.onProgress) {
|
|
3482
|
+
options.onProgress(execution);
|
|
3483
|
+
}
|
|
3484
|
+
if (execution.status === "completed" || execution.status === "failed" || execution.status === "cancelled" || execution.status === "timeout") {
|
|
3485
|
+
return { data: execution, error: null };
|
|
3486
|
+
}
|
|
3487
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
3488
|
+
interval = Math.min(interval * 1.5, maxInterval);
|
|
3489
|
+
}
|
|
3490
|
+
return { data: null, error: new Error("Timeout waiting for execution to complete") };
|
|
3491
|
+
}
|
|
3492
|
+
};
|
|
3493
|
+
|
|
3494
|
+
// src/settings.ts
|
|
3495
|
+
var SystemSettingsManager = class {
|
|
3496
|
+
constructor(fetch2) {
|
|
3497
|
+
this.fetch = fetch2;
|
|
3498
|
+
}
|
|
3499
|
+
/**
|
|
3500
|
+
* List all system settings
|
|
3501
|
+
*
|
|
3502
|
+
* @returns Promise resolving to ListSystemSettingsResponse
|
|
3503
|
+
*
|
|
3504
|
+
* @example
|
|
3505
|
+
* ```typescript
|
|
3506
|
+
* const response = await client.admin.settings.system.list()
|
|
3507
|
+
* console.log(response.settings)
|
|
3508
|
+
* ```
|
|
3509
|
+
*/
|
|
3510
|
+
async list() {
|
|
3511
|
+
const settings = await this.fetch.get(
|
|
3512
|
+
"/api/v1/admin/system/settings"
|
|
3513
|
+
);
|
|
3514
|
+
return { settings: Array.isArray(settings) ? settings : [] };
|
|
3515
|
+
}
|
|
3516
|
+
/**
|
|
3517
|
+
* Get a specific system setting by key
|
|
3518
|
+
*
|
|
3519
|
+
* @param key - Setting key (e.g., 'app.auth.enable_signup')
|
|
3520
|
+
* @returns Promise resolving to SystemSetting
|
|
3521
|
+
*
|
|
3522
|
+
* @example
|
|
3523
|
+
* ```typescript
|
|
3524
|
+
* const setting = await client.admin.settings.system.get('app.auth.enable_signup')
|
|
3525
|
+
* console.log(setting.value)
|
|
2874
3526
|
* ```
|
|
2875
3527
|
*/
|
|
2876
3528
|
async get(key) {
|
|
@@ -5193,54 +5845,16 @@ var FluxbaseAdminMigrations = class {
|
|
|
5193
5845
|
}
|
|
5194
5846
|
}
|
|
5195
5847
|
/**
|
|
5196
|
-
* Trigger schema refresh
|
|
5197
|
-
*
|
|
5848
|
+
* Trigger schema refresh to update the REST API cache
|
|
5849
|
+
* Note: Server no longer restarts - cache is invalidated instantly
|
|
5198
5850
|
*
|
|
5199
5851
|
* @private
|
|
5200
5852
|
*/
|
|
5201
|
-
async
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
{}
|
|
5207
|
-
);
|
|
5208
|
-
console.log("Server restart initiated:", response.message || "Schema refresh in progress");
|
|
5209
|
-
} catch (error) {
|
|
5210
|
-
const isConnectionError = error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ECONNRESET") || error.code === "ECONNREFUSED" || error.code === "ECONNRESET";
|
|
5211
|
-
if (!isConnectionError) {
|
|
5212
|
-
throw error;
|
|
5213
|
-
}
|
|
5214
|
-
console.log("Connection dropped (expected during restart)...");
|
|
5215
|
-
}
|
|
5216
|
-
console.log("Waiting 6 seconds for server to restart...");
|
|
5217
|
-
await this.sleep(6e3);
|
|
5218
|
-
const maxAttempts = 5;
|
|
5219
|
-
const baseDelay = 1e3;
|
|
5220
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
5221
|
-
try {
|
|
5222
|
-
await this.fetch.get("/health");
|
|
5223
|
-
console.log("Server is back online and ready");
|
|
5224
|
-
return;
|
|
5225
|
-
} catch (error) {
|
|
5226
|
-
const isLastAttempt = attempt === maxAttempts;
|
|
5227
|
-
if (isLastAttempt) {
|
|
5228
|
-
throw new Error(
|
|
5229
|
-
`Server did not come back online after ${maxAttempts} attempts. Please check server logs and try again.`
|
|
5230
|
-
);
|
|
5231
|
-
}
|
|
5232
|
-
const delay = baseDelay * Math.pow(2, attempt - 1);
|
|
5233
|
-
console.log(`Server not ready yet, retrying in ${delay}ms... (attempt ${attempt}/${maxAttempts})`);
|
|
5234
|
-
await this.sleep(delay);
|
|
5235
|
-
}
|
|
5236
|
-
}
|
|
5237
|
-
}
|
|
5238
|
-
/**
|
|
5239
|
-
* Helper function to sleep for a given duration
|
|
5240
|
-
* @private
|
|
5241
|
-
*/
|
|
5242
|
-
sleep(ms) {
|
|
5243
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5853
|
+
async triggerSchemaRefresh() {
|
|
5854
|
+
const response = await this.fetch.post("/api/v1/admin/schema/refresh", {});
|
|
5855
|
+
console.log(
|
|
5856
|
+
`Schema cache refreshed: ${response.tables} tables, ${response.views} views`
|
|
5857
|
+
);
|
|
5244
5858
|
}
|
|
5245
5859
|
/**
|
|
5246
5860
|
* Smart sync all registered migrations
|
|
@@ -5342,9 +5956,9 @@ var FluxbaseAdminMigrations = class {
|
|
|
5342
5956
|
const migrationsAppliedSuccessfully = combined.summary.applied > 0 && combined.summary.errors === 0;
|
|
5343
5957
|
if (!combined.dry_run && migrationsAppliedSuccessfully) {
|
|
5344
5958
|
try {
|
|
5345
|
-
await this.
|
|
5959
|
+
await this.triggerSchemaRefresh();
|
|
5346
5960
|
} catch (refreshError) {
|
|
5347
|
-
console.warn("Schema refresh
|
|
5961
|
+
console.warn("Schema refresh warning:", refreshError);
|
|
5348
5962
|
}
|
|
5349
5963
|
}
|
|
5350
5964
|
if (errors.length > 0 || combined.summary.errors > 0) {
|
|
@@ -5377,7 +5991,10 @@ var FluxbaseAdminMigrations = class {
|
|
|
5377
5991
|
*/
|
|
5378
5992
|
async create(request) {
|
|
5379
5993
|
try {
|
|
5380
|
-
const data = await this.fetch.post(
|
|
5994
|
+
const data = await this.fetch.post(
|
|
5995
|
+
"/api/v1/admin/migrations",
|
|
5996
|
+
request
|
|
5997
|
+
);
|
|
5381
5998
|
return { data, error: null };
|
|
5382
5999
|
} catch (error) {
|
|
5383
6000
|
return { data: null, error };
|
|
@@ -5478,7 +6095,9 @@ var FluxbaseAdminMigrations = class {
|
|
|
5478
6095
|
async delete(name, namespace = "default") {
|
|
5479
6096
|
try {
|
|
5480
6097
|
const params = new URLSearchParams({ namespace });
|
|
5481
|
-
await this.fetch.delete(
|
|
6098
|
+
await this.fetch.delete(
|
|
6099
|
+
`/api/v1/admin/migrations/${name}?${params.toString()}`
|
|
6100
|
+
);
|
|
5482
6101
|
return { data: null, error: null };
|
|
5483
6102
|
} catch (error) {
|
|
5484
6103
|
return { data: null, error };
|
|
@@ -5579,7 +6198,10 @@ var FluxbaseAdminMigrations = class {
|
|
|
5579
6198
|
*/
|
|
5580
6199
|
async getExecutions(name, namespace = "default", limit = 50) {
|
|
5581
6200
|
try {
|
|
5582
|
-
const params = new URLSearchParams({
|
|
6201
|
+
const params = new URLSearchParams({
|
|
6202
|
+
namespace,
|
|
6203
|
+
limit: limit.toString()
|
|
6204
|
+
});
|
|
5583
6205
|
const data = await this.fetch.get(
|
|
5584
6206
|
`/api/v1/admin/migrations/${name}/executions?${params.toString()}`
|
|
5585
6207
|
);
|
|
@@ -6379,39 +7001,444 @@ var FluxbaseAdminAI = class {
|
|
|
6379
7001
|
return { data: null, error };
|
|
6380
7002
|
}
|
|
6381
7003
|
}
|
|
6382
|
-
};
|
|
6383
|
-
|
|
6384
|
-
// src/admin-rpc.ts
|
|
6385
|
-
var FluxbaseAdminRPC = class {
|
|
6386
|
-
constructor(fetch2) {
|
|
6387
|
-
this.fetch = fetch2;
|
|
6388
|
-
}
|
|
6389
7004
|
// ============================================================================
|
|
6390
|
-
//
|
|
7005
|
+
// KNOWLEDGE BASE MANAGEMENT (RAG)
|
|
6391
7006
|
// ============================================================================
|
|
6392
7007
|
/**
|
|
6393
|
-
*
|
|
6394
|
-
*
|
|
6395
|
-
* Can sync from:
|
|
6396
|
-
* 1. Filesystem (if no procedures provided) - loads from configured procedures directory
|
|
6397
|
-
* 2. API payload (if procedures array provided) - syncs provided procedure specifications
|
|
6398
|
-
*
|
|
6399
|
-
* Requires service_role or admin authentication.
|
|
7008
|
+
* List all knowledge bases
|
|
6400
7009
|
*
|
|
6401
|
-
* @
|
|
6402
|
-
* @returns Promise resolving to { data, error } tuple with sync results
|
|
7010
|
+
* @returns Promise resolving to { data, error } tuple with array of knowledge base summaries
|
|
6403
7011
|
*
|
|
6404
7012
|
* @example
|
|
6405
7013
|
* ```typescript
|
|
6406
|
-
*
|
|
6407
|
-
*
|
|
6408
|
-
*
|
|
6409
|
-
*
|
|
6410
|
-
*
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
7014
|
+
* const { data, error } = await client.admin.ai.listKnowledgeBases()
|
|
7015
|
+
* if (data) {
|
|
7016
|
+
* console.log('Knowledge bases:', data.map(kb => kb.name))
|
|
7017
|
+
* }
|
|
7018
|
+
* ```
|
|
7019
|
+
*/
|
|
7020
|
+
async listKnowledgeBases() {
|
|
7021
|
+
try {
|
|
7022
|
+
const response = await this.fetch.get("/api/v1/admin/ai/knowledge-bases");
|
|
7023
|
+
return { data: response.knowledge_bases || [], error: null };
|
|
7024
|
+
} catch (error) {
|
|
7025
|
+
return { data: null, error };
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
7028
|
+
/**
|
|
7029
|
+
* Get a specific knowledge base
|
|
7030
|
+
*
|
|
7031
|
+
* @param id - Knowledge base ID
|
|
7032
|
+
* @returns Promise resolving to { data, error } tuple with knowledge base details
|
|
7033
|
+
*
|
|
7034
|
+
* @example
|
|
7035
|
+
* ```typescript
|
|
7036
|
+
* const { data, error } = await client.admin.ai.getKnowledgeBase('uuid')
|
|
7037
|
+
* if (data) {
|
|
7038
|
+
* console.log('Knowledge base:', data.name)
|
|
7039
|
+
* }
|
|
7040
|
+
* ```
|
|
7041
|
+
*/
|
|
7042
|
+
async getKnowledgeBase(id) {
|
|
7043
|
+
try {
|
|
7044
|
+
const data = await this.fetch.get(
|
|
7045
|
+
`/api/v1/admin/ai/knowledge-bases/${id}`
|
|
7046
|
+
);
|
|
7047
|
+
return { data, error: null };
|
|
7048
|
+
} catch (error) {
|
|
7049
|
+
return { data: null, error };
|
|
7050
|
+
}
|
|
7051
|
+
}
|
|
7052
|
+
/**
|
|
7053
|
+
* Create a new knowledge base
|
|
7054
|
+
*
|
|
7055
|
+
* @param request - Knowledge base configuration
|
|
7056
|
+
* @returns Promise resolving to { data, error } tuple with created knowledge base
|
|
7057
|
+
*
|
|
7058
|
+
* @example
|
|
7059
|
+
* ```typescript
|
|
7060
|
+
* const { data, error } = await client.admin.ai.createKnowledgeBase({
|
|
7061
|
+
* name: 'product-docs',
|
|
7062
|
+
* description: 'Product documentation',
|
|
7063
|
+
* chunk_size: 512,
|
|
7064
|
+
* chunk_overlap: 50,
|
|
7065
|
+
* })
|
|
7066
|
+
* ```
|
|
7067
|
+
*/
|
|
7068
|
+
async createKnowledgeBase(request) {
|
|
7069
|
+
try {
|
|
7070
|
+
const data = await this.fetch.post(
|
|
7071
|
+
"/api/v1/admin/ai/knowledge-bases",
|
|
7072
|
+
request
|
|
7073
|
+
);
|
|
7074
|
+
return { data, error: null };
|
|
7075
|
+
} catch (error) {
|
|
7076
|
+
return { data: null, error };
|
|
7077
|
+
}
|
|
7078
|
+
}
|
|
7079
|
+
/**
|
|
7080
|
+
* Update an existing knowledge base
|
|
7081
|
+
*
|
|
7082
|
+
* @param id - Knowledge base ID
|
|
7083
|
+
* @param updates - Fields to update
|
|
7084
|
+
* @returns Promise resolving to { data, error } tuple with updated knowledge base
|
|
7085
|
+
*
|
|
7086
|
+
* @example
|
|
7087
|
+
* ```typescript
|
|
7088
|
+
* const { data, error } = await client.admin.ai.updateKnowledgeBase('uuid', {
|
|
7089
|
+
* description: 'Updated description',
|
|
7090
|
+
* enabled: true,
|
|
7091
|
+
* })
|
|
7092
|
+
* ```
|
|
7093
|
+
*/
|
|
7094
|
+
async updateKnowledgeBase(id, updates) {
|
|
7095
|
+
try {
|
|
7096
|
+
const data = await this.fetch.put(
|
|
7097
|
+
`/api/v1/admin/ai/knowledge-bases/${id}`,
|
|
7098
|
+
updates
|
|
7099
|
+
);
|
|
7100
|
+
return { data, error: null };
|
|
7101
|
+
} catch (error) {
|
|
7102
|
+
return { data: null, error };
|
|
7103
|
+
}
|
|
7104
|
+
}
|
|
7105
|
+
/**
|
|
7106
|
+
* Delete a knowledge base
|
|
7107
|
+
*
|
|
7108
|
+
* @param id - Knowledge base ID
|
|
7109
|
+
* @returns Promise resolving to { data, error } tuple
|
|
7110
|
+
*
|
|
7111
|
+
* @example
|
|
7112
|
+
* ```typescript
|
|
7113
|
+
* const { data, error } = await client.admin.ai.deleteKnowledgeBase('uuid')
|
|
7114
|
+
* ```
|
|
7115
|
+
*/
|
|
7116
|
+
async deleteKnowledgeBase(id) {
|
|
7117
|
+
try {
|
|
7118
|
+
await this.fetch.delete(`/api/v1/admin/ai/knowledge-bases/${id}`);
|
|
7119
|
+
return { data: null, error: null };
|
|
7120
|
+
} catch (error) {
|
|
7121
|
+
return { data: null, error };
|
|
7122
|
+
}
|
|
7123
|
+
}
|
|
7124
|
+
// ============================================================================
|
|
7125
|
+
// DOCUMENT MANAGEMENT
|
|
7126
|
+
// ============================================================================
|
|
7127
|
+
/**
|
|
7128
|
+
* List documents in a knowledge base
|
|
7129
|
+
*
|
|
7130
|
+
* @param knowledgeBaseId - Knowledge base ID
|
|
7131
|
+
* @returns Promise resolving to { data, error } tuple with array of documents
|
|
7132
|
+
*
|
|
7133
|
+
* @example
|
|
7134
|
+
* ```typescript
|
|
7135
|
+
* const { data, error } = await client.admin.ai.listDocuments('kb-uuid')
|
|
7136
|
+
* if (data) {
|
|
7137
|
+
* console.log('Documents:', data.map(d => d.title))
|
|
7138
|
+
* }
|
|
7139
|
+
* ```
|
|
7140
|
+
*/
|
|
7141
|
+
async listDocuments(knowledgeBaseId) {
|
|
7142
|
+
try {
|
|
7143
|
+
const response = await this.fetch.get(`/api/v1/admin/ai/knowledge-bases/${knowledgeBaseId}/documents`);
|
|
7144
|
+
return { data: response.documents || [], error: null };
|
|
7145
|
+
} catch (error) {
|
|
7146
|
+
return { data: null, error };
|
|
7147
|
+
}
|
|
7148
|
+
}
|
|
7149
|
+
/**
|
|
7150
|
+
* Get a specific document
|
|
7151
|
+
*
|
|
7152
|
+
* @param knowledgeBaseId - Knowledge base ID
|
|
7153
|
+
* @param documentId - Document ID
|
|
7154
|
+
* @returns Promise resolving to { data, error } tuple with document details
|
|
7155
|
+
*
|
|
7156
|
+
* @example
|
|
7157
|
+
* ```typescript
|
|
7158
|
+
* const { data, error } = await client.admin.ai.getDocument('kb-uuid', 'doc-uuid')
|
|
7159
|
+
* ```
|
|
7160
|
+
*/
|
|
7161
|
+
async getDocument(knowledgeBaseId, documentId) {
|
|
7162
|
+
try {
|
|
7163
|
+
const data = await this.fetch.get(
|
|
7164
|
+
`/api/v1/admin/ai/knowledge-bases/${knowledgeBaseId}/documents/${documentId}`
|
|
7165
|
+
);
|
|
7166
|
+
return { data, error: null };
|
|
7167
|
+
} catch (error) {
|
|
7168
|
+
return { data: null, error };
|
|
7169
|
+
}
|
|
7170
|
+
}
|
|
7171
|
+
/**
|
|
7172
|
+
* Add a document to a knowledge base
|
|
7173
|
+
*
|
|
7174
|
+
* Document will be chunked and embedded asynchronously.
|
|
7175
|
+
*
|
|
7176
|
+
* @param knowledgeBaseId - Knowledge base ID
|
|
7177
|
+
* @param request - Document content and metadata
|
|
7178
|
+
* @returns Promise resolving to { data, error } tuple with document ID
|
|
7179
|
+
*
|
|
7180
|
+
* @example
|
|
7181
|
+
* ```typescript
|
|
7182
|
+
* const { data, error } = await client.admin.ai.addDocument('kb-uuid', {
|
|
7183
|
+
* title: 'Getting Started Guide',
|
|
7184
|
+
* content: 'This is the content of the document...',
|
|
7185
|
+
* metadata: { category: 'guides' },
|
|
7186
|
+
* })
|
|
7187
|
+
* if (data) {
|
|
7188
|
+
* console.log('Document ID:', data.document_id)
|
|
7189
|
+
* }
|
|
7190
|
+
* ```
|
|
7191
|
+
*/
|
|
7192
|
+
async addDocument(knowledgeBaseId, request) {
|
|
7193
|
+
try {
|
|
7194
|
+
const data = await this.fetch.post(
|
|
7195
|
+
`/api/v1/admin/ai/knowledge-bases/${knowledgeBaseId}/documents`,
|
|
7196
|
+
request
|
|
7197
|
+
);
|
|
7198
|
+
return { data, error: null };
|
|
7199
|
+
} catch (error) {
|
|
7200
|
+
return { data: null, error };
|
|
7201
|
+
}
|
|
7202
|
+
}
|
|
7203
|
+
/**
|
|
7204
|
+
* Upload a document file to a knowledge base
|
|
7205
|
+
*
|
|
7206
|
+
* Supported file types: PDF, TXT, MD, HTML, CSV, DOCX, XLSX, RTF, EPUB, JSON
|
|
7207
|
+
* Maximum file size: 50MB
|
|
7208
|
+
*
|
|
7209
|
+
* @param knowledgeBaseId - Knowledge base ID
|
|
7210
|
+
* @param file - File to upload (File or Blob)
|
|
7211
|
+
* @param title - Optional document title (defaults to filename without extension)
|
|
7212
|
+
* @returns Promise resolving to { data, error } tuple with upload result
|
|
7213
|
+
*
|
|
7214
|
+
* @example
|
|
7215
|
+
* ```typescript
|
|
7216
|
+
* // Browser
|
|
7217
|
+
* const fileInput = document.getElementById('file') as HTMLInputElement
|
|
7218
|
+
* const file = fileInput.files?.[0]
|
|
7219
|
+
* if (file) {
|
|
7220
|
+
* const { data, error } = await client.admin.ai.uploadDocument('kb-uuid', file)
|
|
7221
|
+
* if (data) {
|
|
7222
|
+
* console.log('Document ID:', data.document_id)
|
|
7223
|
+
* console.log('Extracted length:', data.extracted_length)
|
|
7224
|
+
* }
|
|
7225
|
+
* }
|
|
7226
|
+
*
|
|
7227
|
+
* // Node.js (with node-fetch or similar)
|
|
7228
|
+
* import { Blob } from 'buffer'
|
|
7229
|
+
* const content = await fs.readFile('document.pdf')
|
|
7230
|
+
* const blob = new Blob([content], { type: 'application/pdf' })
|
|
7231
|
+
* const { data, error } = await client.admin.ai.uploadDocument('kb-uuid', blob, 'My Document')
|
|
7232
|
+
* ```
|
|
7233
|
+
*/
|
|
7234
|
+
async uploadDocument(knowledgeBaseId, file, title) {
|
|
7235
|
+
try {
|
|
7236
|
+
const formData = new FormData();
|
|
7237
|
+
formData.append("file", file);
|
|
7238
|
+
if (title) {
|
|
7239
|
+
formData.append("title", title);
|
|
7240
|
+
}
|
|
7241
|
+
const data = await this.fetch.post(
|
|
7242
|
+
`/api/v1/admin/ai/knowledge-bases/${knowledgeBaseId}/documents/upload`,
|
|
7243
|
+
formData
|
|
7244
|
+
);
|
|
7245
|
+
return { data, error: null };
|
|
7246
|
+
} catch (error) {
|
|
7247
|
+
return { data: null, error };
|
|
7248
|
+
}
|
|
7249
|
+
}
|
|
7250
|
+
/**
|
|
7251
|
+
* Delete a document from a knowledge base
|
|
7252
|
+
*
|
|
7253
|
+
* @param knowledgeBaseId - Knowledge base ID
|
|
7254
|
+
* @param documentId - Document ID
|
|
7255
|
+
* @returns Promise resolving to { data, error } tuple
|
|
7256
|
+
*
|
|
7257
|
+
* @example
|
|
7258
|
+
* ```typescript
|
|
7259
|
+
* const { data, error } = await client.admin.ai.deleteDocument('kb-uuid', 'doc-uuid')
|
|
7260
|
+
* ```
|
|
7261
|
+
*/
|
|
7262
|
+
async deleteDocument(knowledgeBaseId, documentId) {
|
|
7263
|
+
try {
|
|
7264
|
+
await this.fetch.delete(
|
|
7265
|
+
`/api/v1/admin/ai/knowledge-bases/${knowledgeBaseId}/documents/${documentId}`
|
|
7266
|
+
);
|
|
7267
|
+
return { data: null, error: null };
|
|
7268
|
+
} catch (error) {
|
|
7269
|
+
return { data: null, error };
|
|
7270
|
+
}
|
|
7271
|
+
}
|
|
7272
|
+
/**
|
|
7273
|
+
* Search a knowledge base
|
|
7274
|
+
*
|
|
7275
|
+
* @param knowledgeBaseId - Knowledge base ID
|
|
7276
|
+
* @param query - Search query
|
|
7277
|
+
* @param options - Search options
|
|
7278
|
+
* @returns Promise resolving to { data, error } tuple with search results
|
|
7279
|
+
*
|
|
7280
|
+
* @example
|
|
7281
|
+
* ```typescript
|
|
7282
|
+
* const { data, error } = await client.admin.ai.searchKnowledgeBase('kb-uuid', 'how to reset password', {
|
|
7283
|
+
* max_chunks: 5,
|
|
7284
|
+
* threshold: 0.7,
|
|
7285
|
+
* })
|
|
7286
|
+
* if (data) {
|
|
7287
|
+
* console.log('Results:', data.results.map(r => r.content))
|
|
7288
|
+
* }
|
|
7289
|
+
* ```
|
|
7290
|
+
*/
|
|
7291
|
+
async searchKnowledgeBase(knowledgeBaseId, query, options) {
|
|
7292
|
+
try {
|
|
7293
|
+
const data = await this.fetch.post(
|
|
7294
|
+
`/api/v1/admin/ai/knowledge-bases/${knowledgeBaseId}/search`,
|
|
7295
|
+
{
|
|
7296
|
+
query,
|
|
7297
|
+
max_chunks: options?.max_chunks,
|
|
7298
|
+
threshold: options?.threshold
|
|
7299
|
+
}
|
|
7300
|
+
);
|
|
7301
|
+
return { data, error: null };
|
|
7302
|
+
} catch (error) {
|
|
7303
|
+
return { data: null, error };
|
|
7304
|
+
}
|
|
7305
|
+
}
|
|
7306
|
+
// ============================================================================
|
|
7307
|
+
// CHATBOT KNOWLEDGE BASE LINKING
|
|
7308
|
+
// ============================================================================
|
|
7309
|
+
/**
|
|
7310
|
+
* List knowledge bases linked to a chatbot
|
|
7311
|
+
*
|
|
7312
|
+
* @param chatbotId - Chatbot ID
|
|
7313
|
+
* @returns Promise resolving to { data, error } tuple with linked knowledge bases
|
|
7314
|
+
*
|
|
7315
|
+
* @example
|
|
7316
|
+
* ```typescript
|
|
7317
|
+
* const { data, error } = await client.admin.ai.listChatbotKnowledgeBases('chatbot-uuid')
|
|
7318
|
+
* if (data) {
|
|
7319
|
+
* console.log('Linked KBs:', data.map(l => l.knowledge_base_id))
|
|
7320
|
+
* }
|
|
7321
|
+
* ```
|
|
7322
|
+
*/
|
|
7323
|
+
async listChatbotKnowledgeBases(chatbotId) {
|
|
7324
|
+
try {
|
|
7325
|
+
const response = await this.fetch.get(`/api/v1/admin/ai/chatbots/${chatbotId}/knowledge-bases`);
|
|
7326
|
+
return { data: response.knowledge_bases || [], error: null };
|
|
7327
|
+
} catch (error) {
|
|
7328
|
+
return { data: null, error };
|
|
7329
|
+
}
|
|
7330
|
+
}
|
|
7331
|
+
/**
|
|
7332
|
+
* Link a knowledge base to a chatbot
|
|
7333
|
+
*
|
|
7334
|
+
* @param chatbotId - Chatbot ID
|
|
7335
|
+
* @param request - Link configuration
|
|
7336
|
+
* @returns Promise resolving to { data, error } tuple with link details
|
|
7337
|
+
*
|
|
7338
|
+
* @example
|
|
7339
|
+
* ```typescript
|
|
7340
|
+
* const { data, error } = await client.admin.ai.linkKnowledgeBase('chatbot-uuid', {
|
|
7341
|
+
* knowledge_base_id: 'kb-uuid',
|
|
7342
|
+
* priority: 1,
|
|
7343
|
+
* max_chunks: 5,
|
|
7344
|
+
* similarity_threshold: 0.7,
|
|
7345
|
+
* })
|
|
7346
|
+
* ```
|
|
7347
|
+
*/
|
|
7348
|
+
async linkKnowledgeBase(chatbotId, request) {
|
|
7349
|
+
try {
|
|
7350
|
+
const data = await this.fetch.post(
|
|
7351
|
+
`/api/v1/admin/ai/chatbots/${chatbotId}/knowledge-bases`,
|
|
7352
|
+
request
|
|
7353
|
+
);
|
|
7354
|
+
return { data, error: null };
|
|
7355
|
+
} catch (error) {
|
|
7356
|
+
return { data: null, error };
|
|
7357
|
+
}
|
|
7358
|
+
}
|
|
7359
|
+
/**
|
|
7360
|
+
* Update a chatbot-knowledge base link
|
|
7361
|
+
*
|
|
7362
|
+
* @param chatbotId - Chatbot ID
|
|
7363
|
+
* @param knowledgeBaseId - Knowledge base ID
|
|
7364
|
+
* @param updates - Fields to update
|
|
7365
|
+
* @returns Promise resolving to { data, error } tuple with updated link
|
|
7366
|
+
*
|
|
7367
|
+
* @example
|
|
7368
|
+
* ```typescript
|
|
7369
|
+
* const { data, error } = await client.admin.ai.updateChatbotKnowledgeBase(
|
|
7370
|
+
* 'chatbot-uuid',
|
|
7371
|
+
* 'kb-uuid',
|
|
7372
|
+
* { max_chunks: 10, enabled: true }
|
|
7373
|
+
* )
|
|
7374
|
+
* ```
|
|
7375
|
+
*/
|
|
7376
|
+
async updateChatbotKnowledgeBase(chatbotId, knowledgeBaseId, updates) {
|
|
7377
|
+
try {
|
|
7378
|
+
const data = await this.fetch.put(
|
|
7379
|
+
`/api/v1/admin/ai/chatbots/${chatbotId}/knowledge-bases/${knowledgeBaseId}`,
|
|
7380
|
+
updates
|
|
7381
|
+
);
|
|
7382
|
+
return { data, error: null };
|
|
7383
|
+
} catch (error) {
|
|
7384
|
+
return { data: null, error };
|
|
7385
|
+
}
|
|
7386
|
+
}
|
|
7387
|
+
/**
|
|
7388
|
+
* Unlink a knowledge base from a chatbot
|
|
7389
|
+
*
|
|
7390
|
+
* @param chatbotId - Chatbot ID
|
|
7391
|
+
* @param knowledgeBaseId - Knowledge base ID
|
|
7392
|
+
* @returns Promise resolving to { data, error } tuple
|
|
7393
|
+
*
|
|
7394
|
+
* @example
|
|
7395
|
+
* ```typescript
|
|
7396
|
+
* const { data, error } = await client.admin.ai.unlinkKnowledgeBase('chatbot-uuid', 'kb-uuid')
|
|
7397
|
+
* ```
|
|
7398
|
+
*/
|
|
7399
|
+
async unlinkKnowledgeBase(chatbotId, knowledgeBaseId) {
|
|
7400
|
+
try {
|
|
7401
|
+
await this.fetch.delete(
|
|
7402
|
+
`/api/v1/admin/ai/chatbots/${chatbotId}/knowledge-bases/${knowledgeBaseId}`
|
|
7403
|
+
);
|
|
7404
|
+
return { data: null, error: null };
|
|
7405
|
+
} catch (error) {
|
|
7406
|
+
return { data: null, error };
|
|
7407
|
+
}
|
|
7408
|
+
}
|
|
7409
|
+
};
|
|
7410
|
+
|
|
7411
|
+
// src/admin-rpc.ts
|
|
7412
|
+
var FluxbaseAdminRPC = class {
|
|
7413
|
+
constructor(fetch2) {
|
|
7414
|
+
this.fetch = fetch2;
|
|
7415
|
+
}
|
|
7416
|
+
// ============================================================================
|
|
7417
|
+
// PROCEDURE MANAGEMENT
|
|
7418
|
+
// ============================================================================
|
|
7419
|
+
/**
|
|
7420
|
+
* Sync RPC procedures from filesystem or API payload
|
|
7421
|
+
*
|
|
7422
|
+
* Can sync from:
|
|
7423
|
+
* 1. Filesystem (if no procedures provided) - loads from configured procedures directory
|
|
7424
|
+
* 2. API payload (if procedures array provided) - syncs provided procedure specifications
|
|
7425
|
+
*
|
|
7426
|
+
* Requires service_role or admin authentication.
|
|
7427
|
+
*
|
|
7428
|
+
* @param options - Sync options including namespace and optional procedures array
|
|
7429
|
+
* @returns Promise resolving to { data, error } tuple with sync results
|
|
7430
|
+
*
|
|
7431
|
+
* @example
|
|
7432
|
+
* ```typescript
|
|
7433
|
+
* // Sync from filesystem
|
|
7434
|
+
* const { data, error } = await client.admin.rpc.sync()
|
|
7435
|
+
*
|
|
7436
|
+
* // Sync with provided procedure code
|
|
7437
|
+
* const { data, error } = await client.admin.rpc.sync({
|
|
7438
|
+
* namespace: 'default',
|
|
7439
|
+
* procedures: [{
|
|
7440
|
+
* name: 'get-user-orders',
|
|
7441
|
+
* code: myProcedureSQL,
|
|
6415
7442
|
* }],
|
|
6416
7443
|
* options: {
|
|
6417
7444
|
* delete_missing: false, // Don't remove procedures not in this sync
|
|
@@ -6697,6 +7724,230 @@ var FluxbaseAdminRPC = class {
|
|
|
6697
7724
|
}
|
|
6698
7725
|
};
|
|
6699
7726
|
|
|
7727
|
+
// src/admin-storage.ts
|
|
7728
|
+
var FluxbaseAdminStorage = class {
|
|
7729
|
+
constructor(fetch2) {
|
|
7730
|
+
this.fetch = fetch2;
|
|
7731
|
+
}
|
|
7732
|
+
// ============================================================================
|
|
7733
|
+
// Bucket Operations
|
|
7734
|
+
// ============================================================================
|
|
7735
|
+
/**
|
|
7736
|
+
* List all storage buckets
|
|
7737
|
+
*
|
|
7738
|
+
* @returns List of buckets
|
|
7739
|
+
*
|
|
7740
|
+
* @example
|
|
7741
|
+
* ```typescript
|
|
7742
|
+
* const { data, error } = await admin.storage.listBuckets();
|
|
7743
|
+
* if (data) {
|
|
7744
|
+
* console.log(`Found ${data.buckets.length} buckets`);
|
|
7745
|
+
* }
|
|
7746
|
+
* ```
|
|
7747
|
+
*/
|
|
7748
|
+
async listBuckets() {
|
|
7749
|
+
return wrapAsync(async () => {
|
|
7750
|
+
return await this.fetch.get(
|
|
7751
|
+
"/api/v1/storage/buckets"
|
|
7752
|
+
);
|
|
7753
|
+
});
|
|
7754
|
+
}
|
|
7755
|
+
/**
|
|
7756
|
+
* Create a new storage bucket
|
|
7757
|
+
*
|
|
7758
|
+
* @param name - Bucket name
|
|
7759
|
+
* @returns Success message
|
|
7760
|
+
*
|
|
7761
|
+
* @example
|
|
7762
|
+
* ```typescript
|
|
7763
|
+
* const { error } = await admin.storage.createBucket('my-bucket');
|
|
7764
|
+
* if (!error) {
|
|
7765
|
+
* console.log('Bucket created');
|
|
7766
|
+
* }
|
|
7767
|
+
* ```
|
|
7768
|
+
*/
|
|
7769
|
+
async createBucket(name) {
|
|
7770
|
+
return wrapAsync(async () => {
|
|
7771
|
+
return await this.fetch.post(
|
|
7772
|
+
`/api/v1/storage/buckets/${encodeURIComponent(name)}`
|
|
7773
|
+
);
|
|
7774
|
+
});
|
|
7775
|
+
}
|
|
7776
|
+
/**
|
|
7777
|
+
* Delete a storage bucket
|
|
7778
|
+
*
|
|
7779
|
+
* @param name - Bucket name
|
|
7780
|
+
* @returns Success message
|
|
7781
|
+
*
|
|
7782
|
+
* @example
|
|
7783
|
+
* ```typescript
|
|
7784
|
+
* const { error } = await admin.storage.deleteBucket('my-bucket');
|
|
7785
|
+
* if (!error) {
|
|
7786
|
+
* console.log('Bucket deleted');
|
|
7787
|
+
* }
|
|
7788
|
+
* ```
|
|
7789
|
+
*/
|
|
7790
|
+
async deleteBucket(name) {
|
|
7791
|
+
return wrapAsync(async () => {
|
|
7792
|
+
return await this.fetch.delete(
|
|
7793
|
+
`/api/v1/storage/buckets/${encodeURIComponent(name)}`
|
|
7794
|
+
);
|
|
7795
|
+
});
|
|
7796
|
+
}
|
|
7797
|
+
// ============================================================================
|
|
7798
|
+
// Object Operations
|
|
7799
|
+
// ============================================================================
|
|
7800
|
+
/**
|
|
7801
|
+
* List objects in a bucket
|
|
7802
|
+
*
|
|
7803
|
+
* @param bucket - Bucket name
|
|
7804
|
+
* @param prefix - Optional path prefix to filter results
|
|
7805
|
+
* @param delimiter - Optional delimiter for hierarchical listing (usually '/')
|
|
7806
|
+
* @returns List of objects and prefixes (folders)
|
|
7807
|
+
*
|
|
7808
|
+
* @example
|
|
7809
|
+
* ```typescript
|
|
7810
|
+
* // List all objects in bucket
|
|
7811
|
+
* const { data } = await admin.storage.listObjects('my-bucket');
|
|
7812
|
+
*
|
|
7813
|
+
* // List objects in a folder
|
|
7814
|
+
* const { data } = await admin.storage.listObjects('my-bucket', 'folder/', '/');
|
|
7815
|
+
* ```
|
|
7816
|
+
*/
|
|
7817
|
+
async listObjects(bucket, prefix, delimiter) {
|
|
7818
|
+
return wrapAsync(async () => {
|
|
7819
|
+
const params = new URLSearchParams();
|
|
7820
|
+
if (prefix) params.append("prefix", prefix);
|
|
7821
|
+
if (delimiter) params.append("delimiter", delimiter);
|
|
7822
|
+
const queryString = params.toString();
|
|
7823
|
+
const url = `/api/v1/storage/${encodeURIComponent(bucket)}${queryString ? `?${queryString}` : ""}`;
|
|
7824
|
+
return await this.fetch.get(url);
|
|
7825
|
+
});
|
|
7826
|
+
}
|
|
7827
|
+
/**
|
|
7828
|
+
* Get object metadata
|
|
7829
|
+
*
|
|
7830
|
+
* @param bucket - Bucket name
|
|
7831
|
+
* @param key - Object key (path)
|
|
7832
|
+
* @returns Object metadata
|
|
7833
|
+
*
|
|
7834
|
+
* @example
|
|
7835
|
+
* ```typescript
|
|
7836
|
+
* const { data } = await admin.storage.getObjectMetadata('my-bucket', 'path/to/file.txt');
|
|
7837
|
+
* if (data) {
|
|
7838
|
+
* console.log(`File size: ${data.size} bytes`);
|
|
7839
|
+
* }
|
|
7840
|
+
* ```
|
|
7841
|
+
*/
|
|
7842
|
+
async getObjectMetadata(bucket, key) {
|
|
7843
|
+
return wrapAsync(async () => {
|
|
7844
|
+
const encodedKey = key.split("/").map((s) => encodeURIComponent(s)).join("/");
|
|
7845
|
+
return await this.fetch.get(
|
|
7846
|
+
`/api/v1/storage/${encodeURIComponent(bucket)}/${encodedKey}`,
|
|
7847
|
+
{
|
|
7848
|
+
headers: { "X-Metadata-Only": "true" }
|
|
7849
|
+
}
|
|
7850
|
+
);
|
|
7851
|
+
});
|
|
7852
|
+
}
|
|
7853
|
+
/**
|
|
7854
|
+
* Download an object as a Blob
|
|
7855
|
+
*
|
|
7856
|
+
* @param bucket - Bucket name
|
|
7857
|
+
* @param key - Object key (path)
|
|
7858
|
+
* @returns Object data as Blob
|
|
7859
|
+
*
|
|
7860
|
+
* @example
|
|
7861
|
+
* ```typescript
|
|
7862
|
+
* const { data: blob } = await admin.storage.downloadObject('my-bucket', 'file.pdf');
|
|
7863
|
+
* if (blob) {
|
|
7864
|
+
* // Use the blob
|
|
7865
|
+
* const url = URL.createObjectURL(blob);
|
|
7866
|
+
* }
|
|
7867
|
+
* ```
|
|
7868
|
+
*/
|
|
7869
|
+
async downloadObject(bucket, key) {
|
|
7870
|
+
return wrapAsync(async () => {
|
|
7871
|
+
const encodedKey = key.split("/").map((s) => encodeURIComponent(s)).join("/");
|
|
7872
|
+
const response = await this.fetch.getBlob(
|
|
7873
|
+
`/api/v1/storage/${encodeURIComponent(bucket)}/${encodedKey}`
|
|
7874
|
+
);
|
|
7875
|
+
return response;
|
|
7876
|
+
});
|
|
7877
|
+
}
|
|
7878
|
+
/**
|
|
7879
|
+
* Delete an object
|
|
7880
|
+
*
|
|
7881
|
+
* @param bucket - Bucket name
|
|
7882
|
+
* @param key - Object key (path)
|
|
7883
|
+
*
|
|
7884
|
+
* @example
|
|
7885
|
+
* ```typescript
|
|
7886
|
+
* const { error } = await admin.storage.deleteObject('my-bucket', 'path/to/file.txt');
|
|
7887
|
+
* if (!error) {
|
|
7888
|
+
* console.log('Object deleted');
|
|
7889
|
+
* }
|
|
7890
|
+
* ```
|
|
7891
|
+
*/
|
|
7892
|
+
async deleteObject(bucket, key) {
|
|
7893
|
+
return wrapAsyncVoid(async () => {
|
|
7894
|
+
const encodedKey = key.split("/").map((s) => encodeURIComponent(s)).join("/");
|
|
7895
|
+
await this.fetch.delete(
|
|
7896
|
+
`/api/v1/storage/${encodeURIComponent(bucket)}/${encodedKey}`
|
|
7897
|
+
);
|
|
7898
|
+
});
|
|
7899
|
+
}
|
|
7900
|
+
/**
|
|
7901
|
+
* Create a folder (empty object with directory content type)
|
|
7902
|
+
*
|
|
7903
|
+
* @param bucket - Bucket name
|
|
7904
|
+
* @param folderPath - Folder path (should end with /)
|
|
7905
|
+
*
|
|
7906
|
+
* @example
|
|
7907
|
+
* ```typescript
|
|
7908
|
+
* const { error } = await admin.storage.createFolder('my-bucket', 'new-folder/');
|
|
7909
|
+
* ```
|
|
7910
|
+
*/
|
|
7911
|
+
async createFolder(bucket, folderPath) {
|
|
7912
|
+
return wrapAsyncVoid(async () => {
|
|
7913
|
+
const encodedPath = folderPath.split("/").map((s) => encodeURIComponent(s)).join("/");
|
|
7914
|
+
await this.fetch.post(
|
|
7915
|
+
`/api/v1/storage/${encodeURIComponent(bucket)}/${encodedPath}`,
|
|
7916
|
+
null,
|
|
7917
|
+
{
|
|
7918
|
+
headers: { "Content-Type": "application/x-directory" }
|
|
7919
|
+
}
|
|
7920
|
+
);
|
|
7921
|
+
});
|
|
7922
|
+
}
|
|
7923
|
+
/**
|
|
7924
|
+
* Generate a signed URL for temporary access
|
|
7925
|
+
*
|
|
7926
|
+
* @param bucket - Bucket name
|
|
7927
|
+
* @param key - Object key (path)
|
|
7928
|
+
* @param expiresIn - Expiration time in seconds
|
|
7929
|
+
* @returns Signed URL and expiration info
|
|
7930
|
+
*
|
|
7931
|
+
* @example
|
|
7932
|
+
* ```typescript
|
|
7933
|
+
* const { data } = await admin.storage.generateSignedUrl('my-bucket', 'file.pdf', 3600);
|
|
7934
|
+
* if (data) {
|
|
7935
|
+
* console.log(`Download at: ${data.url}`);
|
|
7936
|
+
* console.log(`Expires in: ${data.expires_in} seconds`);
|
|
7937
|
+
* }
|
|
7938
|
+
* ```
|
|
7939
|
+
*/
|
|
7940
|
+
async generateSignedUrl(bucket, key, expiresIn) {
|
|
7941
|
+
return wrapAsync(async () => {
|
|
7942
|
+
const encodedKey = key.split("/").map((s) => encodeURIComponent(s)).join("/");
|
|
7943
|
+
return await this.fetch.post(
|
|
7944
|
+
`/api/v1/storage/${encodeURIComponent(bucket)}/${encodedKey}/signed-url`,
|
|
7945
|
+
{ expires_in: expiresIn }
|
|
7946
|
+
);
|
|
7947
|
+
});
|
|
7948
|
+
}
|
|
7949
|
+
};
|
|
7950
|
+
|
|
6700
7951
|
// src/admin.ts
|
|
6701
7952
|
var FluxbaseAdmin = class {
|
|
6702
7953
|
constructor(fetch2) {
|
|
@@ -6713,6 +7964,7 @@ var FluxbaseAdmin = class {
|
|
|
6713
7964
|
this.migrations = new FluxbaseAdminMigrations(fetch2);
|
|
6714
7965
|
this.ai = new FluxbaseAdminAI(fetch2);
|
|
6715
7966
|
this.rpc = new FluxbaseAdminRPC(fetch2);
|
|
7967
|
+
this.storage = new FluxbaseAdminStorage(fetch2);
|
|
6716
7968
|
}
|
|
6717
7969
|
/**
|
|
6718
7970
|
* Set admin authentication token
|
|
@@ -6735,6 +7987,54 @@ var FluxbaseAdmin = class {
|
|
|
6735
7987
|
this.fetch.setAuthToken(null);
|
|
6736
7988
|
}
|
|
6737
7989
|
// ============================================================================
|
|
7990
|
+
// Health Check
|
|
7991
|
+
// ============================================================================
|
|
7992
|
+
/**
|
|
7993
|
+
* Get system health status
|
|
7994
|
+
*
|
|
7995
|
+
* @returns Health status including database and realtime service status
|
|
7996
|
+
*
|
|
7997
|
+
* @example
|
|
7998
|
+
* ```typescript
|
|
7999
|
+
* const { data, error } = await admin.getHealth();
|
|
8000
|
+
* if (data) {
|
|
8001
|
+
* console.log('Status:', data.status);
|
|
8002
|
+
* console.log('Database:', data.services.database);
|
|
8003
|
+
* console.log('Realtime:', data.services.realtime);
|
|
8004
|
+
* }
|
|
8005
|
+
* ```
|
|
8006
|
+
*/
|
|
8007
|
+
async getHealth() {
|
|
8008
|
+
return wrapAsync(async () => {
|
|
8009
|
+
return await this.fetch.get("/health");
|
|
8010
|
+
});
|
|
8011
|
+
}
|
|
8012
|
+
// ============================================================================
|
|
8013
|
+
// Email
|
|
8014
|
+
// ============================================================================
|
|
8015
|
+
/**
|
|
8016
|
+
* Send an email
|
|
8017
|
+
*
|
|
8018
|
+
* @param request - Email details (to, subject, html/text)
|
|
8019
|
+
*
|
|
8020
|
+
* @example
|
|
8021
|
+
* ```typescript
|
|
8022
|
+
* const { error } = await admin.sendEmail({
|
|
8023
|
+
* to: 'user@example.com',
|
|
8024
|
+
* subject: 'Hello',
|
|
8025
|
+
* html: '<p>Your message here</p>'
|
|
8026
|
+
* });
|
|
8027
|
+
* if (!error) {
|
|
8028
|
+
* console.log('Email sent');
|
|
8029
|
+
* }
|
|
8030
|
+
* ```
|
|
8031
|
+
*/
|
|
8032
|
+
async sendEmail(request) {
|
|
8033
|
+
return wrapAsyncVoid(async () => {
|
|
8034
|
+
await this.fetch.post("/api/v1/admin/email/send", request);
|
|
8035
|
+
});
|
|
8036
|
+
}
|
|
8037
|
+
// ============================================================================
|
|
6738
8038
|
// Admin Authentication
|
|
6739
8039
|
// ============================================================================
|
|
6740
8040
|
/**
|
|
@@ -7412,7 +8712,111 @@ var FluxbaseAI = class {
|
|
|
7412
8712
|
}
|
|
7413
8713
|
};
|
|
7414
8714
|
|
|
8715
|
+
// src/vector.ts
|
|
8716
|
+
var FluxbaseVector = class {
|
|
8717
|
+
constructor(fetch2) {
|
|
8718
|
+
this.fetch = fetch2;
|
|
8719
|
+
}
|
|
8720
|
+
/**
|
|
8721
|
+
* Generate embeddings for text
|
|
8722
|
+
*
|
|
8723
|
+
* @example
|
|
8724
|
+
* ```typescript
|
|
8725
|
+
* // Single text
|
|
8726
|
+
* const { data } = await client.vector.embed({
|
|
8727
|
+
* text: 'Hello world'
|
|
8728
|
+
* })
|
|
8729
|
+
* console.log(data.embeddings[0]) // [0.1, 0.2, ...]
|
|
8730
|
+
*
|
|
8731
|
+
* // Multiple texts
|
|
8732
|
+
* const { data } = await client.vector.embed({
|
|
8733
|
+
* texts: ['Hello', 'World'],
|
|
8734
|
+
* model: 'text-embedding-3-small'
|
|
8735
|
+
* })
|
|
8736
|
+
* ```
|
|
8737
|
+
*/
|
|
8738
|
+
async embed(request) {
|
|
8739
|
+
try {
|
|
8740
|
+
const response = await this.fetch.request(
|
|
8741
|
+
"/api/v1/vector/embed",
|
|
8742
|
+
{
|
|
8743
|
+
method: "POST",
|
|
8744
|
+
body: request
|
|
8745
|
+
}
|
|
8746
|
+
);
|
|
8747
|
+
return { data: response, error: null };
|
|
8748
|
+
} catch (error) {
|
|
8749
|
+
return { data: null, error };
|
|
8750
|
+
}
|
|
8751
|
+
}
|
|
8752
|
+
/**
|
|
8753
|
+
* Search for similar vectors with automatic text embedding
|
|
8754
|
+
*
|
|
8755
|
+
* This is a convenience method that:
|
|
8756
|
+
* 1. Embeds the query text automatically (if `query` is provided)
|
|
8757
|
+
* 2. Performs vector similarity search
|
|
8758
|
+
* 3. Returns results with distance scores
|
|
8759
|
+
*
|
|
8760
|
+
* @example
|
|
8761
|
+
* ```typescript
|
|
8762
|
+
* // Search with text query (auto-embedded)
|
|
8763
|
+
* const { data } = await client.vector.search({
|
|
8764
|
+
* table: 'documents',
|
|
8765
|
+
* column: 'embedding',
|
|
8766
|
+
* query: 'How to use TypeScript?',
|
|
8767
|
+
* match_count: 10,
|
|
8768
|
+
* match_threshold: 0.8
|
|
8769
|
+
* })
|
|
8770
|
+
*
|
|
8771
|
+
* // Search with pre-computed vector
|
|
8772
|
+
* const { data } = await client.vector.search({
|
|
8773
|
+
* table: 'documents',
|
|
8774
|
+
* column: 'embedding',
|
|
8775
|
+
* vector: [0.1, 0.2, ...],
|
|
8776
|
+
* metric: 'cosine',
|
|
8777
|
+
* match_count: 10
|
|
8778
|
+
* })
|
|
8779
|
+
*
|
|
8780
|
+
* // With additional filters
|
|
8781
|
+
* const { data } = await client.vector.search({
|
|
8782
|
+
* table: 'documents',
|
|
8783
|
+
* column: 'embedding',
|
|
8784
|
+
* query: 'TypeScript tutorial',
|
|
8785
|
+
* filters: [
|
|
8786
|
+
* { column: 'status', operator: 'eq', value: 'published' }
|
|
8787
|
+
* ],
|
|
8788
|
+
* match_count: 10
|
|
8789
|
+
* })
|
|
8790
|
+
* ```
|
|
8791
|
+
*/
|
|
8792
|
+
async search(options) {
|
|
8793
|
+
try {
|
|
8794
|
+
const response = await this.fetch.request(
|
|
8795
|
+
"/api/v1/vector/search",
|
|
8796
|
+
{
|
|
8797
|
+
method: "POST",
|
|
8798
|
+
body: {
|
|
8799
|
+
table: options.table,
|
|
8800
|
+
column: options.column,
|
|
8801
|
+
query: options.query,
|
|
8802
|
+
vector: options.vector,
|
|
8803
|
+
metric: options.metric || "cosine",
|
|
8804
|
+
match_threshold: options.match_threshold,
|
|
8805
|
+
match_count: options.match_count,
|
|
8806
|
+
select: options.select,
|
|
8807
|
+
filters: options.filters
|
|
8808
|
+
}
|
|
8809
|
+
}
|
|
8810
|
+
);
|
|
8811
|
+
return { data: response, error: null };
|
|
8812
|
+
} catch (error) {
|
|
8813
|
+
return { data: null, error };
|
|
8814
|
+
}
|
|
8815
|
+
}
|
|
8816
|
+
};
|
|
8817
|
+
|
|
7415
8818
|
// src/query-builder.ts
|
|
8819
|
+
var URL_LENGTH_THRESHOLD = 4096;
|
|
7416
8820
|
var QueryBuilder = class {
|
|
7417
8821
|
constructor(fetch2, table, schema) {
|
|
7418
8822
|
this.selectQuery = "*";
|
|
@@ -7425,6 +8829,7 @@ var QueryBuilder = class {
|
|
|
7425
8829
|
this.maybeSingleRow = false;
|
|
7426
8830
|
this.operationType = "select";
|
|
7427
8831
|
this.headOnly = false;
|
|
8832
|
+
this.isCountAggregation = false;
|
|
7428
8833
|
this.fetch = fetch2;
|
|
7429
8834
|
this.table = table;
|
|
7430
8835
|
this.schema = schema;
|
|
@@ -7796,6 +9201,80 @@ var QueryBuilder = class {
|
|
|
7796
9201
|
});
|
|
7797
9202
|
return this;
|
|
7798
9203
|
}
|
|
9204
|
+
/**
|
|
9205
|
+
* Order results by vector similarity (pgvector)
|
|
9206
|
+
* Results are ordered by distance (ascending = closest first)
|
|
9207
|
+
*
|
|
9208
|
+
* @example
|
|
9209
|
+
* ```typescript
|
|
9210
|
+
* // Order by cosine similarity (closest matches first)
|
|
9211
|
+
* const { data } = await client
|
|
9212
|
+
* .from('documents')
|
|
9213
|
+
* .select('id, title, content')
|
|
9214
|
+
* .orderByVector('embedding', queryVector, 'cosine')
|
|
9215
|
+
* .limit(10)
|
|
9216
|
+
* ```
|
|
9217
|
+
*
|
|
9218
|
+
* @param column - The vector column to order by
|
|
9219
|
+
* @param vector - The query vector to compare against
|
|
9220
|
+
* @param metric - Distance metric: 'l2' (euclidean), 'cosine', or 'inner_product'
|
|
9221
|
+
* @param options - Optional: { ascending?: boolean } - defaults to true (closest first)
|
|
9222
|
+
*/
|
|
9223
|
+
orderByVector(column, vector, metric = "cosine", options) {
|
|
9224
|
+
const opMap = {
|
|
9225
|
+
l2: "vec_l2",
|
|
9226
|
+
cosine: "vec_cos",
|
|
9227
|
+
inner_product: "vec_ip"
|
|
9228
|
+
};
|
|
9229
|
+
this.orderBys.push({
|
|
9230
|
+
column,
|
|
9231
|
+
direction: options?.ascending === false ? "desc" : "asc",
|
|
9232
|
+
vectorOp: opMap[metric],
|
|
9233
|
+
vectorValue: vector
|
|
9234
|
+
});
|
|
9235
|
+
return this;
|
|
9236
|
+
}
|
|
9237
|
+
/**
|
|
9238
|
+
* Filter by vector similarity (pgvector)
|
|
9239
|
+
* This is a convenience method that adds a vector filter using the specified distance metric.
|
|
9240
|
+
* Typically used with orderByVector() for similarity search.
|
|
9241
|
+
*
|
|
9242
|
+
* @example
|
|
9243
|
+
* ```typescript
|
|
9244
|
+
* // Find the 10 most similar documents
|
|
9245
|
+
* const { data } = await client
|
|
9246
|
+
* .from('documents')
|
|
9247
|
+
* .select('id, title, content')
|
|
9248
|
+
* .vectorSearch('embedding', queryVector, 'cosine')
|
|
9249
|
+
* .limit(10)
|
|
9250
|
+
*
|
|
9251
|
+
* // Combine with other filters
|
|
9252
|
+
* const { data } = await client
|
|
9253
|
+
* .from('documents')
|
|
9254
|
+
* .select('id, title, content')
|
|
9255
|
+
* .eq('status', 'published')
|
|
9256
|
+
* .vectorSearch('embedding', queryVector)
|
|
9257
|
+
* .limit(10)
|
|
9258
|
+
* ```
|
|
9259
|
+
*
|
|
9260
|
+
* @param column - The vector column to search
|
|
9261
|
+
* @param vector - The query vector
|
|
9262
|
+
* @param metric - Distance metric: 'l2' (euclidean), 'cosine', or 'inner_product'
|
|
9263
|
+
*/
|
|
9264
|
+
vectorSearch(column, vector, metric = "cosine") {
|
|
9265
|
+
const opMap = {
|
|
9266
|
+
l2: "vec_l2",
|
|
9267
|
+
cosine: "vec_cos",
|
|
9268
|
+
inner_product: "vec_ip"
|
|
9269
|
+
};
|
|
9270
|
+
this.filters.push({
|
|
9271
|
+
column,
|
|
9272
|
+
operator: opMap[metric],
|
|
9273
|
+
value: vector
|
|
9274
|
+
});
|
|
9275
|
+
this.orderByVector(column, vector, metric, { ascending: true });
|
|
9276
|
+
return this;
|
|
9277
|
+
}
|
|
7799
9278
|
/**
|
|
7800
9279
|
* Limit number of rows returned
|
|
7801
9280
|
*/
|
|
@@ -7900,6 +9379,7 @@ var QueryBuilder = class {
|
|
|
7900
9379
|
*/
|
|
7901
9380
|
count(column = "*") {
|
|
7902
9381
|
this.selectQuery = `count(${column})`;
|
|
9382
|
+
this.isCountAggregation = true;
|
|
7903
9383
|
return this;
|
|
7904
9384
|
}
|
|
7905
9385
|
/**
|
|
@@ -8120,6 +9600,9 @@ var QueryBuilder = class {
|
|
|
8120
9600
|
statusText: "No Content"
|
|
8121
9601
|
};
|
|
8122
9602
|
}
|
|
9603
|
+
if (this.shouldUsePostQuery()) {
|
|
9604
|
+
return this.executePostQuery();
|
|
9605
|
+
}
|
|
8123
9606
|
const queryString = this.buildQueryString();
|
|
8124
9607
|
const path = `${this.buildTablePath()}${queryString}`;
|
|
8125
9608
|
if (this.countType) {
|
|
@@ -8181,7 +9664,30 @@ var QueryBuilder = class {
|
|
|
8181
9664
|
statusText: "OK"
|
|
8182
9665
|
};
|
|
8183
9666
|
}
|
|
8184
|
-
const data = await this.fetch.get(path);
|
|
9667
|
+
const data = await this.fetch.get(path);
|
|
9668
|
+
if (this.isCountAggregation && !this.groupByColumns) {
|
|
9669
|
+
if (Array.isArray(data) && data.length === 1) {
|
|
9670
|
+
const countData = data[0];
|
|
9671
|
+
if (countData && typeof countData.count === "number") {
|
|
9672
|
+
return {
|
|
9673
|
+
data,
|
|
9674
|
+
error: null,
|
|
9675
|
+
count: countData.count,
|
|
9676
|
+
status: 200,
|
|
9677
|
+
statusText: "OK"
|
|
9678
|
+
};
|
|
9679
|
+
}
|
|
9680
|
+
}
|
|
9681
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
9682
|
+
return {
|
|
9683
|
+
data,
|
|
9684
|
+
error: null,
|
|
9685
|
+
count: 0,
|
|
9686
|
+
status: 200,
|
|
9687
|
+
statusText: "OK"
|
|
9688
|
+
};
|
|
9689
|
+
}
|
|
9690
|
+
}
|
|
8185
9691
|
if (this.singleRow) {
|
|
8186
9692
|
if (Array.isArray(data) && data.length === 0) {
|
|
8187
9693
|
return {
|
|
@@ -8302,32 +9808,43 @@ var QueryBuilder = class {
|
|
|
8302
9808
|
`${filter.operator}.${this.formatValue(filter.value)}`
|
|
8303
9809
|
);
|
|
8304
9810
|
}
|
|
9811
|
+
const orExpressions = [];
|
|
8305
9812
|
for (const orFilter of this.orFilters) {
|
|
8306
|
-
|
|
8307
|
-
}
|
|
8308
|
-
for (const andFilter of this.andFilters) {
|
|
8309
|
-
params.append("and", `(${andFilter})`);
|
|
9813
|
+
orExpressions.push(`or(${orFilter})`);
|
|
8310
9814
|
}
|
|
8311
9815
|
for (const bf of this.betweenFilters) {
|
|
8312
9816
|
const minFormatted = this.formatValue(bf.min);
|
|
8313
9817
|
const maxFormatted = this.formatValue(bf.max);
|
|
8314
9818
|
if (bf.negated) {
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
`(${bf.column}.lt.${minFormatted},${bf.column}.gt.${maxFormatted})`
|
|
9819
|
+
orExpressions.push(
|
|
9820
|
+
`or(${bf.column}.lt.${minFormatted},${bf.column}.gt.${maxFormatted})`
|
|
8318
9821
|
);
|
|
8319
9822
|
} else {
|
|
8320
9823
|
params.append(bf.column, `gte.${minFormatted}`);
|
|
8321
9824
|
params.append(bf.column, `lte.${maxFormatted}`);
|
|
8322
9825
|
}
|
|
8323
9826
|
}
|
|
9827
|
+
if (orExpressions.length === 1) {
|
|
9828
|
+
const expr = orExpressions[0];
|
|
9829
|
+
const inner = expr.replace(/^or\(/, "(");
|
|
9830
|
+
params.append("or", inner);
|
|
9831
|
+
} else if (orExpressions.length > 1) {
|
|
9832
|
+
params.append("and", `(${orExpressions.join(",")})`);
|
|
9833
|
+
}
|
|
9834
|
+
for (const andFilter of this.andFilters) {
|
|
9835
|
+
params.append("and", `(${andFilter})`);
|
|
9836
|
+
}
|
|
8324
9837
|
if (this.groupByColumns && this.groupByColumns.length > 0) {
|
|
8325
9838
|
params.append("group_by", this.groupByColumns.join(","));
|
|
8326
9839
|
}
|
|
8327
9840
|
if (this.orderBys.length > 0) {
|
|
8328
|
-
const orderStr = this.orderBys.map(
|
|
8329
|
-
(o
|
|
8330
|
-
|
|
9841
|
+
const orderStr = this.orderBys.map((o) => {
|
|
9842
|
+
if (o.vectorOp && o.vectorValue) {
|
|
9843
|
+
const vectorStr = `[${o.vectorValue.join(",")}]`;
|
|
9844
|
+
return `${o.column}.${o.vectorOp}.${vectorStr}.${o.direction}`;
|
|
9845
|
+
}
|
|
9846
|
+
return `${o.column}.${o.direction}${o.nulls ? `.nulls${o.nulls}` : ""}`;
|
|
9847
|
+
}).join(",");
|
|
8331
9848
|
params.append("order", orderStr);
|
|
8332
9849
|
}
|
|
8333
9850
|
if (this.limitValue !== void 0) {
|
|
@@ -8403,6 +9920,160 @@ var QueryBuilder = class {
|
|
|
8403
9920
|
}
|
|
8404
9921
|
return null;
|
|
8405
9922
|
}
|
|
9923
|
+
/**
|
|
9924
|
+
* Check if the query should use POST-based query endpoint
|
|
9925
|
+
* Returns true if the query string would exceed the URL length threshold
|
|
9926
|
+
*/
|
|
9927
|
+
shouldUsePostQuery() {
|
|
9928
|
+
const queryString = this.buildQueryString();
|
|
9929
|
+
return queryString.length > URL_LENGTH_THRESHOLD;
|
|
9930
|
+
}
|
|
9931
|
+
/**
|
|
9932
|
+
* Execute a SELECT query using the POST /query endpoint
|
|
9933
|
+
* Used when query parameters would exceed URL length limits
|
|
9934
|
+
*/
|
|
9935
|
+
async executePostQuery() {
|
|
9936
|
+
const body = this.buildQueryBody();
|
|
9937
|
+
const path = `${this.buildTablePath()}/query`;
|
|
9938
|
+
if (this.countType) {
|
|
9939
|
+
const response = await this.fetch.postWithHeaders(path, body);
|
|
9940
|
+
const serverCount = this.parseContentRangeCount(response.headers);
|
|
9941
|
+
const data2 = response.data;
|
|
9942
|
+
if (this.headOnly) {
|
|
9943
|
+
return {
|
|
9944
|
+
data: null,
|
|
9945
|
+
error: null,
|
|
9946
|
+
count: serverCount,
|
|
9947
|
+
status: response.status,
|
|
9948
|
+
statusText: "OK"
|
|
9949
|
+
};
|
|
9950
|
+
}
|
|
9951
|
+
if (this.singleRow) {
|
|
9952
|
+
if (Array.isArray(data2) && data2.length === 0) {
|
|
9953
|
+
return {
|
|
9954
|
+
data: null,
|
|
9955
|
+
error: { message: "No rows found", code: "PGRST116" },
|
|
9956
|
+
count: serverCount ?? 0,
|
|
9957
|
+
status: 404,
|
|
9958
|
+
statusText: "Not Found"
|
|
9959
|
+
};
|
|
9960
|
+
}
|
|
9961
|
+
const singleData = Array.isArray(data2) ? data2[0] : data2;
|
|
9962
|
+
return {
|
|
9963
|
+
data: singleData,
|
|
9964
|
+
error: null,
|
|
9965
|
+
count: serverCount ?? 1,
|
|
9966
|
+
status: 200,
|
|
9967
|
+
statusText: "OK"
|
|
9968
|
+
};
|
|
9969
|
+
}
|
|
9970
|
+
if (this.maybeSingleRow) {
|
|
9971
|
+
if (Array.isArray(data2) && data2.length === 0) {
|
|
9972
|
+
return {
|
|
9973
|
+
data: null,
|
|
9974
|
+
error: null,
|
|
9975
|
+
count: serverCount ?? 0,
|
|
9976
|
+
status: 200,
|
|
9977
|
+
statusText: "OK"
|
|
9978
|
+
};
|
|
9979
|
+
}
|
|
9980
|
+
const singleData = Array.isArray(data2) ? data2[0] : data2;
|
|
9981
|
+
return {
|
|
9982
|
+
data: singleData,
|
|
9983
|
+
error: null,
|
|
9984
|
+
count: serverCount ?? 1,
|
|
9985
|
+
status: 200,
|
|
9986
|
+
statusText: "OK"
|
|
9987
|
+
};
|
|
9988
|
+
}
|
|
9989
|
+
return {
|
|
9990
|
+
data: data2,
|
|
9991
|
+
error: null,
|
|
9992
|
+
count: serverCount ?? (Array.isArray(data2) ? data2.length : null),
|
|
9993
|
+
status: 200,
|
|
9994
|
+
statusText: "OK"
|
|
9995
|
+
};
|
|
9996
|
+
}
|
|
9997
|
+
const data = await this.fetch.post(path, body);
|
|
9998
|
+
if (this.singleRow) {
|
|
9999
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
10000
|
+
return {
|
|
10001
|
+
data: null,
|
|
10002
|
+
error: { message: "No rows found", code: "PGRST116" },
|
|
10003
|
+
count: 0,
|
|
10004
|
+
status: 404,
|
|
10005
|
+
statusText: "Not Found"
|
|
10006
|
+
};
|
|
10007
|
+
}
|
|
10008
|
+
const singleData = Array.isArray(data) ? data[0] : data;
|
|
10009
|
+
return {
|
|
10010
|
+
data: singleData,
|
|
10011
|
+
error: null,
|
|
10012
|
+
count: 1,
|
|
10013
|
+
status: 200,
|
|
10014
|
+
statusText: "OK"
|
|
10015
|
+
};
|
|
10016
|
+
}
|
|
10017
|
+
if (this.maybeSingleRow) {
|
|
10018
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
10019
|
+
return {
|
|
10020
|
+
data: null,
|
|
10021
|
+
error: null,
|
|
10022
|
+
count: 0,
|
|
10023
|
+
status: 200,
|
|
10024
|
+
statusText: "OK"
|
|
10025
|
+
};
|
|
10026
|
+
}
|
|
10027
|
+
const singleData = Array.isArray(data) ? data[0] : data;
|
|
10028
|
+
return {
|
|
10029
|
+
data: singleData,
|
|
10030
|
+
error: null,
|
|
10031
|
+
count: 1,
|
|
10032
|
+
status: 200,
|
|
10033
|
+
statusText: "OK"
|
|
10034
|
+
};
|
|
10035
|
+
}
|
|
10036
|
+
return {
|
|
10037
|
+
data,
|
|
10038
|
+
error: null,
|
|
10039
|
+
count: Array.isArray(data) ? data.length : null,
|
|
10040
|
+
status: 200,
|
|
10041
|
+
statusText: "OK"
|
|
10042
|
+
};
|
|
10043
|
+
}
|
|
10044
|
+
/**
|
|
10045
|
+
* Build the request body for POST-based queries
|
|
10046
|
+
* Used when query parameters would exceed URL length limits
|
|
10047
|
+
*/
|
|
10048
|
+
buildQueryBody() {
|
|
10049
|
+
return {
|
|
10050
|
+
select: this.selectQuery !== "*" ? this.selectQuery : void 0,
|
|
10051
|
+
filters: this.filters.map((f) => ({
|
|
10052
|
+
column: f.column,
|
|
10053
|
+
operator: f.operator,
|
|
10054
|
+
value: f.value
|
|
10055
|
+
})),
|
|
10056
|
+
orFilters: this.orFilters,
|
|
10057
|
+
andFilters: this.andFilters,
|
|
10058
|
+
betweenFilters: this.betweenFilters.map((bf) => ({
|
|
10059
|
+
column: bf.column,
|
|
10060
|
+
min: bf.min,
|
|
10061
|
+
max: bf.max,
|
|
10062
|
+
negated: bf.negated
|
|
10063
|
+
})),
|
|
10064
|
+
order: this.orderBys.map((o) => ({
|
|
10065
|
+
column: o.column,
|
|
10066
|
+
direction: o.direction,
|
|
10067
|
+
nulls: o.nulls,
|
|
10068
|
+
vectorOp: o.vectorOp,
|
|
10069
|
+
vectorValue: o.vectorValue
|
|
10070
|
+
})),
|
|
10071
|
+
limit: this.limitValue,
|
|
10072
|
+
offset: this.offsetValue,
|
|
10073
|
+
count: this.countType,
|
|
10074
|
+
groupBy: this.groupByColumns
|
|
10075
|
+
};
|
|
10076
|
+
}
|
|
8406
10077
|
};
|
|
8407
10078
|
|
|
8408
10079
|
// src/schema-query-builder.ts
|
|
@@ -8475,6 +10146,23 @@ var FluxbaseClient = class {
|
|
|
8475
10146
|
const wsProtocol = fluxbaseUrl.startsWith("https") ? "wss" : "ws";
|
|
8476
10147
|
const wsBaseUrl = fluxbaseUrl.replace(/^https?:/, wsProtocol + ":");
|
|
8477
10148
|
this.ai = new FluxbaseAI(this.fetch, wsBaseUrl);
|
|
10149
|
+
this.vector = new FluxbaseVector(this.fetch);
|
|
10150
|
+
const rpcInstance = new FluxbaseRPC(this.fetch);
|
|
10151
|
+
const rpcCallable = async (fn, params) => {
|
|
10152
|
+
const result = await rpcInstance.invoke(fn, params);
|
|
10153
|
+
return {
|
|
10154
|
+
data: result.data?.result ?? null,
|
|
10155
|
+
error: result.error
|
|
10156
|
+
};
|
|
10157
|
+
};
|
|
10158
|
+
Object.assign(rpcCallable, {
|
|
10159
|
+
invoke: rpcInstance.invoke.bind(rpcInstance),
|
|
10160
|
+
list: rpcInstance.list.bind(rpcInstance),
|
|
10161
|
+
getStatus: rpcInstance.getStatus.bind(rpcInstance),
|
|
10162
|
+
getLogs: rpcInstance.getLogs.bind(rpcInstance),
|
|
10163
|
+
waitForCompletion: rpcInstance.waitForCompletion.bind(rpcInstance)
|
|
10164
|
+
});
|
|
10165
|
+
this.rpc = rpcCallable;
|
|
8478
10166
|
this.setupAuthSync();
|
|
8479
10167
|
}
|
|
8480
10168
|
/**
|
|
@@ -8514,12 +10202,12 @@ var FluxbaseClient = class {
|
|
|
8514
10202
|
*
|
|
8515
10203
|
* @example
|
|
8516
10204
|
* ```typescript
|
|
8517
|
-
* // Query the
|
|
10205
|
+
* // Query the logging.entries table
|
|
8518
10206
|
* const { data } = await client
|
|
8519
|
-
* .schema('
|
|
8520
|
-
* .from('
|
|
10207
|
+
* .schema('logging')
|
|
10208
|
+
* .from('entries')
|
|
8521
10209
|
* .select('*')
|
|
8522
|
-
* .eq('
|
|
10210
|
+
* .eq('execution_id', executionId)
|
|
8523
10211
|
* .execute()
|
|
8524
10212
|
*
|
|
8525
10213
|
* // Insert into a custom schema table
|
|
@@ -8548,7 +10236,10 @@ var FluxbaseClient = class {
|
|
|
8548
10236
|
this.realtime.setTokenRefreshCallback(async () => {
|
|
8549
10237
|
const result = await this.auth.refreshSession();
|
|
8550
10238
|
if (result.error || !result.data?.session) {
|
|
8551
|
-
console.error(
|
|
10239
|
+
console.error(
|
|
10240
|
+
"[Fluxbase] Failed to refresh token for realtime:",
|
|
10241
|
+
result.error
|
|
10242
|
+
);
|
|
8552
10243
|
return null;
|
|
8553
10244
|
}
|
|
8554
10245
|
return result.data.session.access_token;
|
|
@@ -8662,168 +10353,12 @@ function createClient(fluxbaseUrl, fluxbaseKey, options) {
|
|
|
8662
10353
|
return new FluxbaseClient(url, key, options);
|
|
8663
10354
|
}
|
|
8664
10355
|
|
|
8665
|
-
// src/rpc.ts
|
|
8666
|
-
var FluxbaseRPC = class {
|
|
8667
|
-
constructor(fetch2) {
|
|
8668
|
-
this.fetch = fetch2;
|
|
8669
|
-
}
|
|
8670
|
-
/**
|
|
8671
|
-
* List available RPC procedures (public, enabled)
|
|
8672
|
-
*
|
|
8673
|
-
* @param namespace - Optional namespace filter
|
|
8674
|
-
* @returns Promise resolving to { data, error } tuple with array of procedure summaries
|
|
8675
|
-
*/
|
|
8676
|
-
async list(namespace) {
|
|
8677
|
-
try {
|
|
8678
|
-
const params = namespace ? `?namespace=${encodeURIComponent(namespace)}` : "";
|
|
8679
|
-
const response = await this.fetch.get(
|
|
8680
|
-
`/api/v1/rpc/procedures${params}`
|
|
8681
|
-
);
|
|
8682
|
-
return { data: response.procedures || [], error: null };
|
|
8683
|
-
} catch (error) {
|
|
8684
|
-
return { data: null, error };
|
|
8685
|
-
}
|
|
8686
|
-
}
|
|
8687
|
-
/**
|
|
8688
|
-
* Invoke an RPC procedure
|
|
8689
|
-
*
|
|
8690
|
-
* @param name - Procedure name
|
|
8691
|
-
* @param params - Optional parameters to pass to the procedure
|
|
8692
|
-
* @param options - Optional invocation options
|
|
8693
|
-
* @returns Promise resolving to { data, error } tuple with invocation response
|
|
8694
|
-
*
|
|
8695
|
-
* @example
|
|
8696
|
-
* ```typescript
|
|
8697
|
-
* // Synchronous invocation
|
|
8698
|
-
* const { data, error } = await fluxbase.rpc.invoke('get-user-orders', {
|
|
8699
|
-
* user_id: '123',
|
|
8700
|
-
* limit: 10
|
|
8701
|
-
* });
|
|
8702
|
-
* console.log(data.result); // Query results
|
|
8703
|
-
*
|
|
8704
|
-
* // Asynchronous invocation
|
|
8705
|
-
* const { data: asyncData } = await fluxbase.rpc.invoke('generate-report', {
|
|
8706
|
-
* year: 2024
|
|
8707
|
-
* }, { async: true });
|
|
8708
|
-
* console.log(asyncData.execution_id); // Use to poll status
|
|
8709
|
-
* ```
|
|
8710
|
-
*/
|
|
8711
|
-
async invoke(name, params, options) {
|
|
8712
|
-
try {
|
|
8713
|
-
const namespace = options?.namespace || "default";
|
|
8714
|
-
const response = await this.fetch.post(
|
|
8715
|
-
`/api/v1/rpc/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`,
|
|
8716
|
-
{
|
|
8717
|
-
params,
|
|
8718
|
-
async: options?.async
|
|
8719
|
-
}
|
|
8720
|
-
);
|
|
8721
|
-
return { data: response, error: null };
|
|
8722
|
-
} catch (error) {
|
|
8723
|
-
return { data: null, error };
|
|
8724
|
-
}
|
|
8725
|
-
}
|
|
8726
|
-
/**
|
|
8727
|
-
* Get execution status (for async invocations or checking history)
|
|
8728
|
-
*
|
|
8729
|
-
* @param executionId - The execution ID returned from async invoke
|
|
8730
|
-
* @returns Promise resolving to { data, error } tuple with execution details
|
|
8731
|
-
*
|
|
8732
|
-
* @example
|
|
8733
|
-
* ```typescript
|
|
8734
|
-
* const { data, error } = await fluxbase.rpc.getStatus('execution-uuid');
|
|
8735
|
-
* if (data.status === 'completed') {
|
|
8736
|
-
* console.log('Result:', data.result);
|
|
8737
|
-
* } else if (data.status === 'running') {
|
|
8738
|
-
* console.log('Still running...');
|
|
8739
|
-
* }
|
|
8740
|
-
* ```
|
|
8741
|
-
*/
|
|
8742
|
-
async getStatus(executionId) {
|
|
8743
|
-
try {
|
|
8744
|
-
const data = await this.fetch.get(
|
|
8745
|
-
`/api/v1/rpc/executions/${encodeURIComponent(executionId)}`
|
|
8746
|
-
);
|
|
8747
|
-
return { data, error: null };
|
|
8748
|
-
} catch (error) {
|
|
8749
|
-
return { data: null, error };
|
|
8750
|
-
}
|
|
8751
|
-
}
|
|
8752
|
-
/**
|
|
8753
|
-
* Get execution logs (for debugging and monitoring)
|
|
8754
|
-
*
|
|
8755
|
-
* @param executionId - The execution ID
|
|
8756
|
-
* @param afterLine - Optional line number to get logs after (for polling)
|
|
8757
|
-
* @returns Promise resolving to { data, error } tuple with execution logs
|
|
8758
|
-
*
|
|
8759
|
-
* @example
|
|
8760
|
-
* ```typescript
|
|
8761
|
-
* const { data: logs } = await fluxbase.rpc.getLogs('execution-uuid');
|
|
8762
|
-
* for (const log of logs) {
|
|
8763
|
-
* console.log(`[${log.level}] ${log.message}`);
|
|
8764
|
-
* }
|
|
8765
|
-
* ```
|
|
8766
|
-
*/
|
|
8767
|
-
async getLogs(executionId, afterLine) {
|
|
8768
|
-
try {
|
|
8769
|
-
const params = afterLine !== void 0 ? `?after=${afterLine}` : "";
|
|
8770
|
-
const response = await this.fetch.get(
|
|
8771
|
-
`/api/v1/rpc/executions/${encodeURIComponent(executionId)}/logs${params}`
|
|
8772
|
-
);
|
|
8773
|
-
return { data: response.logs || [], error: null };
|
|
8774
|
-
} catch (error) {
|
|
8775
|
-
return { data: null, error };
|
|
8776
|
-
}
|
|
8777
|
-
}
|
|
8778
|
-
/**
|
|
8779
|
-
* Poll for execution completion with exponential backoff
|
|
8780
|
-
*
|
|
8781
|
-
* @param executionId - The execution ID to poll
|
|
8782
|
-
* @param options - Polling options
|
|
8783
|
-
* @returns Promise resolving to final execution state
|
|
8784
|
-
*
|
|
8785
|
-
* @example
|
|
8786
|
-
* ```typescript
|
|
8787
|
-
* const { data: result } = await fluxbase.rpc.invoke('long-task', {}, { async: true });
|
|
8788
|
-
* const { data: final } = await fluxbase.rpc.waitForCompletion(result.execution_id, {
|
|
8789
|
-
* maxWaitMs: 60000, // Wait up to 1 minute
|
|
8790
|
-
* onProgress: (exec) => console.log(`Status: ${exec.status}`)
|
|
8791
|
-
* });
|
|
8792
|
-
* console.log('Final result:', final.result);
|
|
8793
|
-
* ```
|
|
8794
|
-
*/
|
|
8795
|
-
async waitForCompletion(executionId, options) {
|
|
8796
|
-
const maxWait = options?.maxWaitMs || 3e4;
|
|
8797
|
-
const initialInterval = options?.initialIntervalMs || 500;
|
|
8798
|
-
const maxInterval = options?.maxIntervalMs || 5e3;
|
|
8799
|
-
const startTime = Date.now();
|
|
8800
|
-
let interval = initialInterval;
|
|
8801
|
-
while (Date.now() - startTime < maxWait) {
|
|
8802
|
-
const { data: execution, error } = await this.getStatus(executionId);
|
|
8803
|
-
if (error) {
|
|
8804
|
-
return { data: null, error };
|
|
8805
|
-
}
|
|
8806
|
-
if (!execution) {
|
|
8807
|
-
return { data: null, error: new Error("Execution not found") };
|
|
8808
|
-
}
|
|
8809
|
-
if (options?.onProgress) {
|
|
8810
|
-
options.onProgress(execution);
|
|
8811
|
-
}
|
|
8812
|
-
if (execution.status === "completed" || execution.status === "failed" || execution.status === "cancelled" || execution.status === "timeout") {
|
|
8813
|
-
return { data: execution, error: null };
|
|
8814
|
-
}
|
|
8815
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
8816
|
-
interval = Math.min(interval * 1.5, maxInterval);
|
|
8817
|
-
}
|
|
8818
|
-
return { data: null, error: new Error("Timeout waiting for execution to complete") };
|
|
8819
|
-
}
|
|
8820
|
-
};
|
|
8821
|
-
|
|
8822
10356
|
exports.APIKeysManager = APIKeysManager;
|
|
8823
10357
|
exports.AppSettingsManager = AppSettingsManager;
|
|
8824
10358
|
exports.AuthSettingsManager = AuthSettingsManager;
|
|
8825
10359
|
exports.DDLManager = DDLManager;
|
|
8826
10360
|
exports.EmailTemplateManager = EmailTemplateManager;
|
|
10361
|
+
exports.ExecutionLogsChannel = ExecutionLogsChannel;
|
|
8827
10362
|
exports.FluxbaseAI = FluxbaseAI;
|
|
8828
10363
|
exports.FluxbaseAIChat = FluxbaseAIChat;
|
|
8829
10364
|
exports.FluxbaseAdmin = FluxbaseAdmin;
|
|
@@ -8832,6 +10367,7 @@ exports.FluxbaseAdminFunctions = FluxbaseAdminFunctions;
|
|
|
8832
10367
|
exports.FluxbaseAdminJobs = FluxbaseAdminJobs;
|
|
8833
10368
|
exports.FluxbaseAdminMigrations = FluxbaseAdminMigrations;
|
|
8834
10369
|
exports.FluxbaseAdminRPC = FluxbaseAdminRPC;
|
|
10370
|
+
exports.FluxbaseAdminStorage = FluxbaseAdminStorage;
|
|
8835
10371
|
exports.FluxbaseAuth = FluxbaseAuth;
|
|
8836
10372
|
exports.FluxbaseClient = FluxbaseClient;
|
|
8837
10373
|
exports.FluxbaseFetch = FluxbaseFetch;
|
|
@@ -8843,6 +10379,7 @@ exports.FluxbaseRPC = FluxbaseRPC;
|
|
|
8843
10379
|
exports.FluxbaseRealtime = FluxbaseRealtime;
|
|
8844
10380
|
exports.FluxbaseSettings = FluxbaseSettings;
|
|
8845
10381
|
exports.FluxbaseStorage = FluxbaseStorage;
|
|
10382
|
+
exports.FluxbaseVector = FluxbaseVector;
|
|
8846
10383
|
exports.ImpersonationManager = ImpersonationManager;
|
|
8847
10384
|
exports.InvitationsManager = InvitationsManager;
|
|
8848
10385
|
exports.OAuthProviderManager = OAuthProviderManager;
|