@fluxbase/sdk 0.0.1-rc.45 → 0.0.1-rc.48
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/README.md +29 -28
- package/dist/index.cjs +1752 -259
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1749 -135
- package/dist/index.d.ts +1749 -135
- package/dist/index.js +1745 -260
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1919,6 +1919,127 @@ var StorageBucket = class {
|
|
|
1919
1919
|
xhr.send(formData);
|
|
1920
1920
|
});
|
|
1921
1921
|
}
|
|
1922
|
+
/**
|
|
1923
|
+
* Upload a file using streaming for reduced memory usage.
|
|
1924
|
+
* This method bypasses FormData buffering and streams data directly to the server.
|
|
1925
|
+
* Ideal for large files where memory efficiency is important.
|
|
1926
|
+
*
|
|
1927
|
+
* @param path - The path/key for the file
|
|
1928
|
+
* @param stream - ReadableStream of the file data
|
|
1929
|
+
* @param size - The size of the file in bytes (required for Content-Length header)
|
|
1930
|
+
* @param options - Upload options
|
|
1931
|
+
*
|
|
1932
|
+
* @example
|
|
1933
|
+
* ```typescript
|
|
1934
|
+
* // Upload from a File's stream
|
|
1935
|
+
* const file = new File([...], 'large-video.mp4');
|
|
1936
|
+
* const { data, error } = await storage
|
|
1937
|
+
* .from('videos')
|
|
1938
|
+
* .uploadStream('video.mp4', file.stream(), file.size, {
|
|
1939
|
+
* contentType: 'video/mp4',
|
|
1940
|
+
* });
|
|
1941
|
+
*
|
|
1942
|
+
* // Upload from a fetch response stream
|
|
1943
|
+
* const response = await fetch('https://example.com/data.zip');
|
|
1944
|
+
* const size = parseInt(response.headers.get('content-length') || '0');
|
|
1945
|
+
* const { data, error } = await storage
|
|
1946
|
+
* .from('files')
|
|
1947
|
+
* .uploadStream('data.zip', response.body!, size, {
|
|
1948
|
+
* contentType: 'application/zip',
|
|
1949
|
+
* });
|
|
1950
|
+
* ```
|
|
1951
|
+
*/
|
|
1952
|
+
async uploadStream(path, stream, size, options) {
|
|
1953
|
+
try {
|
|
1954
|
+
if (size <= 0) {
|
|
1955
|
+
return { data: null, error: new Error("size must be a positive number") };
|
|
1956
|
+
}
|
|
1957
|
+
const headers = {
|
|
1958
|
+
...this.fetch["defaultHeaders"],
|
|
1959
|
+
"Content-Length": String(size)
|
|
1960
|
+
};
|
|
1961
|
+
if (options?.contentType) {
|
|
1962
|
+
headers["X-Storage-Content-Type"] = options.contentType;
|
|
1963
|
+
}
|
|
1964
|
+
if (options?.cacheControl) {
|
|
1965
|
+
headers["X-Storage-Cache-Control"] = options.cacheControl;
|
|
1966
|
+
}
|
|
1967
|
+
if (options?.metadata && Object.keys(options.metadata).length > 0) {
|
|
1968
|
+
headers["X-Storage-Metadata"] = JSON.stringify(options.metadata);
|
|
1969
|
+
}
|
|
1970
|
+
let bodyStream = stream;
|
|
1971
|
+
if (options?.onUploadProgress) {
|
|
1972
|
+
let uploadedBytes = 0;
|
|
1973
|
+
const progressCallback = options.onUploadProgress;
|
|
1974
|
+
const totalSize = size;
|
|
1975
|
+
const transformStream = new TransformStream({
|
|
1976
|
+
transform(chunk, controller) {
|
|
1977
|
+
uploadedBytes += chunk.byteLength;
|
|
1978
|
+
const percentage = Math.round(uploadedBytes / totalSize * 100);
|
|
1979
|
+
progressCallback({
|
|
1980
|
+
loaded: uploadedBytes,
|
|
1981
|
+
total: totalSize,
|
|
1982
|
+
percentage
|
|
1983
|
+
});
|
|
1984
|
+
controller.enqueue(chunk);
|
|
1985
|
+
}
|
|
1986
|
+
});
|
|
1987
|
+
bodyStream = stream.pipeThrough(transformStream);
|
|
1988
|
+
}
|
|
1989
|
+
const response = await fetch(
|
|
1990
|
+
`${this.fetch["baseUrl"]}/api/v1/storage/${this.bucketName}/stream/${path}`,
|
|
1991
|
+
{
|
|
1992
|
+
method: "POST",
|
|
1993
|
+
headers,
|
|
1994
|
+
body: bodyStream,
|
|
1995
|
+
signal: options?.signal,
|
|
1996
|
+
// @ts-expect-error - duplex is not yet in TypeScript's RequestInit type
|
|
1997
|
+
duplex: "half"
|
|
1998
|
+
}
|
|
1999
|
+
);
|
|
2000
|
+
if (!response.ok) {
|
|
2001
|
+
const errorData = await response.json().catch(() => ({ error: response.statusText }));
|
|
2002
|
+
throw new Error(errorData.error || `Upload failed: ${response.statusText}`);
|
|
2003
|
+
}
|
|
2004
|
+
const result = await response.json();
|
|
2005
|
+
return {
|
|
2006
|
+
data: {
|
|
2007
|
+
id: result.key || path,
|
|
2008
|
+
path,
|
|
2009
|
+
fullPath: `${this.bucketName}/${path}`
|
|
2010
|
+
},
|
|
2011
|
+
error: null
|
|
2012
|
+
};
|
|
2013
|
+
} catch (error) {
|
|
2014
|
+
return { data: null, error };
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
/**
|
|
2018
|
+
* Upload a large file using streaming for reduced memory usage.
|
|
2019
|
+
* This is a convenience method that converts a File or Blob to a stream.
|
|
2020
|
+
*
|
|
2021
|
+
* @param path - The path/key for the file
|
|
2022
|
+
* @param file - The File or Blob to upload
|
|
2023
|
+
* @param options - Upload options
|
|
2024
|
+
*
|
|
2025
|
+
* @example
|
|
2026
|
+
* ```typescript
|
|
2027
|
+
* const file = new File([...], 'large-video.mp4');
|
|
2028
|
+
* const { data, error } = await storage
|
|
2029
|
+
* .from('videos')
|
|
2030
|
+
* .uploadLargeFile('video.mp4', file, {
|
|
2031
|
+
* contentType: 'video/mp4',
|
|
2032
|
+
* onUploadProgress: (p) => console.log(`${p.percentage}% complete`),
|
|
2033
|
+
* });
|
|
2034
|
+
* ```
|
|
2035
|
+
*/
|
|
2036
|
+
async uploadLargeFile(path, file, options) {
|
|
2037
|
+
const opts = {
|
|
2038
|
+
...options,
|
|
2039
|
+
contentType: options?.contentType || file.type || "application/octet-stream"
|
|
2040
|
+
};
|
|
2041
|
+
return this.uploadStream(path, file.stream(), file.size, opts);
|
|
2042
|
+
}
|
|
1922
2043
|
async download(path, options) {
|
|
1923
2044
|
try {
|
|
1924
2045
|
const controller = new AbortController();
|
|
@@ -2550,7 +2671,7 @@ var FluxbaseJobs = class {
|
|
|
2550
2671
|
*
|
|
2551
2672
|
* @param jobName - Name of the job function to execute
|
|
2552
2673
|
* @param payload - Job input data
|
|
2553
|
-
* @param options - Additional options (priority, namespace, scheduled time)
|
|
2674
|
+
* @param options - Additional options (priority, namespace, scheduled time, onBehalfOf)
|
|
2554
2675
|
* @returns Promise resolving to { data, error } tuple with submitted job details
|
|
2555
2676
|
*
|
|
2556
2677
|
* @example
|
|
@@ -2576,6 +2697,14 @@ var FluxbaseJobs = class {
|
|
|
2576
2697
|
* const { data } = await client.jobs.submit('scheduled-task', payload, {
|
|
2577
2698
|
* scheduled: '2025-01-01T00:00:00Z'
|
|
2578
2699
|
* })
|
|
2700
|
+
*
|
|
2701
|
+
* // Submit on behalf of a user (service_role only)
|
|
2702
|
+
* const { data } = await serviceClient.jobs.submit('user-task', payload, {
|
|
2703
|
+
* onBehalfOf: {
|
|
2704
|
+
* user_id: 'user-uuid',
|
|
2705
|
+
* user_email: 'user@example.com'
|
|
2706
|
+
* }
|
|
2707
|
+
* })
|
|
2579
2708
|
* ```
|
|
2580
2709
|
*/
|
|
2581
2710
|
async submit(jobName, payload, options) {
|
|
@@ -2583,7 +2712,10 @@ var FluxbaseJobs = class {
|
|
|
2583
2712
|
const request = {
|
|
2584
2713
|
job_name: jobName,
|
|
2585
2714
|
payload,
|
|
2586
|
-
|
|
2715
|
+
priority: options?.priority,
|
|
2716
|
+
namespace: options?.namespace,
|
|
2717
|
+
scheduled: options?.scheduled,
|
|
2718
|
+
on_behalf_of: options?.onBehalfOf
|
|
2587
2719
|
};
|
|
2588
2720
|
const data = await this.fetch.post("/api/v1/jobs/submit", request);
|
|
2589
2721
|
return { data, error: null };
|
|
@@ -3270,11 +3402,13 @@ var AppSettingsManager = class {
|
|
|
3270
3402
|
* ```
|
|
3271
3403
|
*/
|
|
3272
3404
|
async setSetting(key, value, options) {
|
|
3405
|
+
const wrappedValue = value !== null && typeof value === "object" && !Array.isArray(value) ? value : { value };
|
|
3273
3406
|
try {
|
|
3274
3407
|
return await this.fetch.put(
|
|
3275
3408
|
`/api/v1/admin/settings/custom/${key}`,
|
|
3276
3409
|
{
|
|
3277
|
-
value,
|
|
3410
|
+
value: wrappedValue,
|
|
3411
|
+
value_type: options?.value_type || "json",
|
|
3278
3412
|
description: options?.description,
|
|
3279
3413
|
is_public: options?.is_public,
|
|
3280
3414
|
is_secret: options?.is_secret
|
|
@@ -3286,7 +3420,7 @@ var AppSettingsManager = class {
|
|
|
3286
3420
|
"/api/v1/admin/settings/custom",
|
|
3287
3421
|
{
|
|
3288
3422
|
key,
|
|
3289
|
-
value,
|
|
3423
|
+
value: wrappedValue,
|
|
3290
3424
|
value_type: options?.value_type || "json",
|
|
3291
3425
|
description: options?.description,
|
|
3292
3426
|
is_public: options?.is_public ?? false,
|
|
@@ -4524,8 +4658,146 @@ var FluxbaseManagement = class {
|
|
|
4524
4658
|
}
|
|
4525
4659
|
};
|
|
4526
4660
|
|
|
4661
|
+
// src/bundling.ts
|
|
4662
|
+
var esbuild = null;
|
|
4663
|
+
var fs = null;
|
|
4664
|
+
async function loadEsbuild() {
|
|
4665
|
+
if (esbuild) return true;
|
|
4666
|
+
try {
|
|
4667
|
+
esbuild = await import('esbuild');
|
|
4668
|
+
return true;
|
|
4669
|
+
} catch {
|
|
4670
|
+
return false;
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
async function loadFs() {
|
|
4674
|
+
if (fs) return true;
|
|
4675
|
+
try {
|
|
4676
|
+
fs = await import('fs');
|
|
4677
|
+
return true;
|
|
4678
|
+
} catch {
|
|
4679
|
+
return false;
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
var denoExternalPlugin = {
|
|
4683
|
+
name: "deno-external",
|
|
4684
|
+
setup(build) {
|
|
4685
|
+
build.onResolve({ filter: /^npm:/ }, (args) => ({
|
|
4686
|
+
path: args.path,
|
|
4687
|
+
external: true
|
|
4688
|
+
}));
|
|
4689
|
+
build.onResolve({ filter: /^https?:\/\// }, (args) => ({
|
|
4690
|
+
path: args.path,
|
|
4691
|
+
external: true
|
|
4692
|
+
}));
|
|
4693
|
+
build.onResolve({ filter: /^jsr:/ }, (args) => ({
|
|
4694
|
+
path: args.path,
|
|
4695
|
+
external: true
|
|
4696
|
+
}));
|
|
4697
|
+
}
|
|
4698
|
+
};
|
|
4699
|
+
async function loadImportMap(denoJsonPath) {
|
|
4700
|
+
const hasFs = await loadFs();
|
|
4701
|
+
if (!hasFs || !fs) {
|
|
4702
|
+
console.warn("fs module not available, cannot load import map");
|
|
4703
|
+
return null;
|
|
4704
|
+
}
|
|
4705
|
+
try {
|
|
4706
|
+
const content = fs.readFileSync(denoJsonPath, "utf-8");
|
|
4707
|
+
const config = JSON.parse(content);
|
|
4708
|
+
return config.imports || null;
|
|
4709
|
+
} catch (error) {
|
|
4710
|
+
console.warn(`Failed to load import map from ${denoJsonPath}:`, error);
|
|
4711
|
+
return null;
|
|
4712
|
+
}
|
|
4713
|
+
}
|
|
4714
|
+
async function bundleCode(options) {
|
|
4715
|
+
const hasEsbuild = await loadEsbuild();
|
|
4716
|
+
if (!hasEsbuild || !esbuild) {
|
|
4717
|
+
throw new Error(
|
|
4718
|
+
"esbuild is required for bundling. Install it with: npm install esbuild"
|
|
4719
|
+
);
|
|
4720
|
+
}
|
|
4721
|
+
const externals = [...options.external ?? []];
|
|
4722
|
+
const alias = {};
|
|
4723
|
+
if (options.importMap) {
|
|
4724
|
+
for (const [key, value] of Object.entries(options.importMap)) {
|
|
4725
|
+
if (value.startsWith("npm:")) {
|
|
4726
|
+
externals.push(key);
|
|
4727
|
+
} else if (value.startsWith("https://") || value.startsWith("http://")) {
|
|
4728
|
+
externals.push(key);
|
|
4729
|
+
} else if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../")) {
|
|
4730
|
+
alias[key] = value;
|
|
4731
|
+
} else {
|
|
4732
|
+
externals.push(key);
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
const denoPlugin = {
|
|
4737
|
+
name: "deno-external",
|
|
4738
|
+
setup(build) {
|
|
4739
|
+
build.onResolve({ filter: /^npm:/ }, (args) => ({
|
|
4740
|
+
path: args.path,
|
|
4741
|
+
external: true
|
|
4742
|
+
}));
|
|
4743
|
+
build.onResolve({ filter: /^https?:\/\// }, (args) => ({
|
|
4744
|
+
path: args.path,
|
|
4745
|
+
external: true
|
|
4746
|
+
}));
|
|
4747
|
+
build.onResolve({ filter: /^jsr:/ }, (args) => ({
|
|
4748
|
+
path: args.path,
|
|
4749
|
+
external: true
|
|
4750
|
+
}));
|
|
4751
|
+
}
|
|
4752
|
+
};
|
|
4753
|
+
const resolveDir = options.baseDir || process.cwd?.() || "/";
|
|
4754
|
+
const buildOptions = {
|
|
4755
|
+
stdin: {
|
|
4756
|
+
contents: options.code,
|
|
4757
|
+
loader: "ts",
|
|
4758
|
+
resolveDir
|
|
4759
|
+
},
|
|
4760
|
+
// Set absWorkingDir for consistent path resolution
|
|
4761
|
+
absWorkingDir: resolveDir,
|
|
4762
|
+
bundle: true,
|
|
4763
|
+
write: false,
|
|
4764
|
+
format: "esm",
|
|
4765
|
+
// Use 'node' platform for better node_modules resolution (Deno supports Node APIs)
|
|
4766
|
+
platform: "node",
|
|
4767
|
+
target: "esnext",
|
|
4768
|
+
minify: options.minify ?? false,
|
|
4769
|
+
sourcemap: options.sourcemap ? "inline" : false,
|
|
4770
|
+
external: externals,
|
|
4771
|
+
plugins: [denoPlugin],
|
|
4772
|
+
// Preserve handler export
|
|
4773
|
+
treeShaking: true,
|
|
4774
|
+
// Resolve .ts, .js, .mjs extensions
|
|
4775
|
+
resolveExtensions: [".ts", ".tsx", ".js", ".mjs", ".json"],
|
|
4776
|
+
// ESM conditions for better module resolution
|
|
4777
|
+
conditions: ["import", "module"]
|
|
4778
|
+
};
|
|
4779
|
+
if (Object.keys(alias).length > 0) {
|
|
4780
|
+
buildOptions.alias = alias;
|
|
4781
|
+
}
|
|
4782
|
+
if (options.nodePaths && options.nodePaths.length > 0) {
|
|
4783
|
+
buildOptions.nodePaths = options.nodePaths;
|
|
4784
|
+
}
|
|
4785
|
+
if (options.define) {
|
|
4786
|
+
buildOptions.define = options.define;
|
|
4787
|
+
}
|
|
4788
|
+
const result = await esbuild.build(buildOptions);
|
|
4789
|
+
const output = result.outputFiles?.[0];
|
|
4790
|
+
if (!output) {
|
|
4791
|
+
throw new Error("Bundling failed: no output generated");
|
|
4792
|
+
}
|
|
4793
|
+
return {
|
|
4794
|
+
code: output.text,
|
|
4795
|
+
sourceMap: options.sourcemap ? output.text : void 0
|
|
4796
|
+
};
|
|
4797
|
+
}
|
|
4798
|
+
|
|
4527
4799
|
// src/admin-functions.ts
|
|
4528
|
-
var FluxbaseAdminFunctions = class {
|
|
4800
|
+
var FluxbaseAdminFunctions = class _FluxbaseAdminFunctions {
|
|
4529
4801
|
constructor(fetch2) {
|
|
4530
4802
|
this.fetch = fetch2;
|
|
4531
4803
|
}
|
|
@@ -4762,6 +5034,111 @@ var FluxbaseAdminFunctions = class {
|
|
|
4762
5034
|
return { data: null, error };
|
|
4763
5035
|
}
|
|
4764
5036
|
}
|
|
5037
|
+
/**
|
|
5038
|
+
* Sync edge functions with automatic client-side bundling
|
|
5039
|
+
*
|
|
5040
|
+
* This is a convenience method that bundles all function code using esbuild
|
|
5041
|
+
* before sending to the server. Requires esbuild as a peer dependency.
|
|
5042
|
+
*
|
|
5043
|
+
* @param options - Sync options including namespace and functions array
|
|
5044
|
+
* @param bundleOptions - Optional bundling configuration
|
|
5045
|
+
* @returns Promise resolving to { data, error } tuple with sync results
|
|
5046
|
+
*
|
|
5047
|
+
* @example
|
|
5048
|
+
* ```typescript
|
|
5049
|
+
* const { data, error } = await client.admin.functions.syncWithBundling({
|
|
5050
|
+
* namespace: 'default',
|
|
5051
|
+
* functions: [
|
|
5052
|
+
* { name: 'hello', code: helloCode },
|
|
5053
|
+
* { name: 'goodbye', code: goodbyeCode },
|
|
5054
|
+
* ],
|
|
5055
|
+
* options: { delete_missing: true }
|
|
5056
|
+
* })
|
|
5057
|
+
* ```
|
|
5058
|
+
*/
|
|
5059
|
+
async syncWithBundling(options, bundleOptions) {
|
|
5060
|
+
if (!options.functions || options.functions.length === 0) {
|
|
5061
|
+
return this.sync(options);
|
|
5062
|
+
}
|
|
5063
|
+
const hasEsbuild = await loadEsbuild();
|
|
5064
|
+
if (!hasEsbuild) {
|
|
5065
|
+
return {
|
|
5066
|
+
data: null,
|
|
5067
|
+
error: new Error(
|
|
5068
|
+
"esbuild is required for client-side bundling. Install it with: npm install esbuild"
|
|
5069
|
+
)
|
|
5070
|
+
};
|
|
5071
|
+
}
|
|
5072
|
+
try {
|
|
5073
|
+
const bundledFunctions = await Promise.all(
|
|
5074
|
+
options.functions.map(async (fn) => {
|
|
5075
|
+
if (fn.is_pre_bundled) {
|
|
5076
|
+
return fn;
|
|
5077
|
+
}
|
|
5078
|
+
const bundled = await _FluxbaseAdminFunctions.bundleCode({
|
|
5079
|
+
// Apply global bundle options first
|
|
5080
|
+
...bundleOptions,
|
|
5081
|
+
// Then override with per-function values (these take priority)
|
|
5082
|
+
code: fn.code,
|
|
5083
|
+
// Use function's sourceDir for resolving relative imports
|
|
5084
|
+
baseDir: fn.sourceDir || bundleOptions?.baseDir,
|
|
5085
|
+
// Use function's nodePaths for additional module resolution
|
|
5086
|
+
nodePaths: fn.nodePaths || bundleOptions?.nodePaths
|
|
5087
|
+
});
|
|
5088
|
+
return {
|
|
5089
|
+
...fn,
|
|
5090
|
+
code: bundled.code,
|
|
5091
|
+
original_code: fn.code,
|
|
5092
|
+
is_pre_bundled: true
|
|
5093
|
+
};
|
|
5094
|
+
})
|
|
5095
|
+
);
|
|
5096
|
+
return this.sync({
|
|
5097
|
+
...options,
|
|
5098
|
+
functions: bundledFunctions
|
|
5099
|
+
});
|
|
5100
|
+
} catch (error) {
|
|
5101
|
+
return { data: null, error };
|
|
5102
|
+
}
|
|
5103
|
+
}
|
|
5104
|
+
/**
|
|
5105
|
+
* Bundle function code using esbuild (client-side)
|
|
5106
|
+
*
|
|
5107
|
+
* Transforms and bundles TypeScript/JavaScript code into a single file
|
|
5108
|
+
* that can be executed by the Fluxbase edge functions runtime.
|
|
5109
|
+
*
|
|
5110
|
+
* Requires esbuild as a peer dependency.
|
|
5111
|
+
*
|
|
5112
|
+
* @param options - Bundle options including source code
|
|
5113
|
+
* @returns Promise resolving to bundled code
|
|
5114
|
+
* @throws Error if esbuild is not available
|
|
5115
|
+
*
|
|
5116
|
+
* @example
|
|
5117
|
+
* ```typescript
|
|
5118
|
+
* const bundled = await FluxbaseAdminFunctions.bundleCode({
|
|
5119
|
+
* code: `
|
|
5120
|
+
* import { helper } from './utils'
|
|
5121
|
+
* export default async function handler(req) {
|
|
5122
|
+
* return helper(req.body)
|
|
5123
|
+
* }
|
|
5124
|
+
* `,
|
|
5125
|
+
* minify: true,
|
|
5126
|
+
* })
|
|
5127
|
+
*
|
|
5128
|
+
* // Use bundled code in sync
|
|
5129
|
+
* await client.admin.functions.sync({
|
|
5130
|
+
* namespace: 'default',
|
|
5131
|
+
* functions: [{
|
|
5132
|
+
* name: 'my-function',
|
|
5133
|
+
* code: bundled.code,
|
|
5134
|
+
* is_pre_bundled: true,
|
|
5135
|
+
* }]
|
|
5136
|
+
* })
|
|
5137
|
+
* ```
|
|
5138
|
+
*/
|
|
5139
|
+
static async bundleCode(options) {
|
|
5140
|
+
return bundleCode(options);
|
|
5141
|
+
}
|
|
4765
5142
|
};
|
|
4766
5143
|
|
|
4767
5144
|
// src/admin-migrations.ts
|
|
@@ -4962,8 +5339,8 @@ var FluxbaseAdminMigrations = class {
|
|
|
4962
5339
|
dry_run: options.dry_run ?? false,
|
|
4963
5340
|
warnings: results.flatMap((r) => r.warnings || [])
|
|
4964
5341
|
};
|
|
4965
|
-
const
|
|
4966
|
-
if (!combined.dry_run &&
|
|
5342
|
+
const migrationsAppliedSuccessfully = combined.summary.applied > 0 && combined.summary.errors === 0;
|
|
5343
|
+
if (!combined.dry_run && migrationsAppliedSuccessfully) {
|
|
4967
5344
|
try {
|
|
4968
5345
|
await this.triggerSchemaRefreshWithRestart();
|
|
4969
5346
|
} catch (refreshError) {
|
|
@@ -5214,16 +5591,6 @@ var FluxbaseAdminMigrations = class {
|
|
|
5214
5591
|
};
|
|
5215
5592
|
|
|
5216
5593
|
// src/admin-jobs.ts
|
|
5217
|
-
var esbuild = null;
|
|
5218
|
-
async function loadEsbuild() {
|
|
5219
|
-
if (esbuild) return true;
|
|
5220
|
-
try {
|
|
5221
|
-
esbuild = await import('esbuild');
|
|
5222
|
-
return true;
|
|
5223
|
-
} catch {
|
|
5224
|
-
return false;
|
|
5225
|
-
}
|
|
5226
|
-
}
|
|
5227
5594
|
var FluxbaseAdminJobs = class _FluxbaseAdminJobs {
|
|
5228
5595
|
constructor(fetch2) {
|
|
5229
5596
|
this.fetch = fetch2;
|
|
@@ -5708,158 +6075,695 @@ var FluxbaseAdminJobs = class _FluxbaseAdminJobs {
|
|
|
5708
6075
|
* ```
|
|
5709
6076
|
*/
|
|
5710
6077
|
static async bundleCode(options) {
|
|
5711
|
-
|
|
5712
|
-
if (!hasEsbuild || !esbuild) {
|
|
5713
|
-
throw new Error(
|
|
5714
|
-
"esbuild is required for bundling. Install it with: npm install esbuild"
|
|
5715
|
-
);
|
|
5716
|
-
}
|
|
5717
|
-
const externals = [...options.external ?? []];
|
|
5718
|
-
const alias = {};
|
|
5719
|
-
if (options.importMap) {
|
|
5720
|
-
for (const [key, value] of Object.entries(options.importMap)) {
|
|
5721
|
-
if (value.startsWith("npm:")) {
|
|
5722
|
-
externals.push(key);
|
|
5723
|
-
} else if (value.startsWith("https://") || value.startsWith("http://")) {
|
|
5724
|
-
externals.push(key);
|
|
5725
|
-
} else if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../")) {
|
|
5726
|
-
alias[key] = value;
|
|
5727
|
-
} else {
|
|
5728
|
-
externals.push(key);
|
|
5729
|
-
}
|
|
5730
|
-
}
|
|
5731
|
-
}
|
|
5732
|
-
const denoExternalPlugin = {
|
|
5733
|
-
name: "deno-external",
|
|
5734
|
-
setup(build) {
|
|
5735
|
-
build.onResolve({ filter: /^npm:/ }, (args) => ({
|
|
5736
|
-
path: args.path,
|
|
5737
|
-
external: true
|
|
5738
|
-
}));
|
|
5739
|
-
build.onResolve({ filter: /^https?:\/\// }, (args) => ({
|
|
5740
|
-
path: args.path,
|
|
5741
|
-
external: true
|
|
5742
|
-
}));
|
|
5743
|
-
build.onResolve({ filter: /^jsr:/ }, (args) => ({
|
|
5744
|
-
path: args.path,
|
|
5745
|
-
external: true
|
|
5746
|
-
}));
|
|
5747
|
-
}
|
|
5748
|
-
};
|
|
5749
|
-
const resolveDir = options.baseDir || process.cwd?.() || "/";
|
|
5750
|
-
const buildOptions = {
|
|
5751
|
-
stdin: {
|
|
5752
|
-
contents: options.code,
|
|
5753
|
-
loader: "ts",
|
|
5754
|
-
resolveDir
|
|
5755
|
-
},
|
|
5756
|
-
// Set absWorkingDir for consistent path resolution
|
|
5757
|
-
absWorkingDir: resolveDir,
|
|
5758
|
-
bundle: true,
|
|
5759
|
-
write: false,
|
|
5760
|
-
format: "esm",
|
|
5761
|
-
// Use 'node' platform for better node_modules resolution (Deno supports Node APIs)
|
|
5762
|
-
platform: "node",
|
|
5763
|
-
target: "esnext",
|
|
5764
|
-
minify: options.minify ?? false,
|
|
5765
|
-
sourcemap: options.sourcemap ? "inline" : false,
|
|
5766
|
-
external: externals,
|
|
5767
|
-
plugins: [denoExternalPlugin],
|
|
5768
|
-
// Preserve handler export
|
|
5769
|
-
treeShaking: true,
|
|
5770
|
-
// Resolve .ts, .js, .mjs extensions
|
|
5771
|
-
resolveExtensions: [".ts", ".tsx", ".js", ".mjs", ".json"],
|
|
5772
|
-
// ESM conditions for better module resolution
|
|
5773
|
-
conditions: ["import", "module"]
|
|
5774
|
-
};
|
|
5775
|
-
if (Object.keys(alias).length > 0) {
|
|
5776
|
-
buildOptions.alias = alias;
|
|
5777
|
-
}
|
|
5778
|
-
if (options.nodePaths && options.nodePaths.length > 0) {
|
|
5779
|
-
buildOptions.nodePaths = options.nodePaths;
|
|
5780
|
-
}
|
|
5781
|
-
if (options.define) {
|
|
5782
|
-
buildOptions.define = options.define;
|
|
5783
|
-
}
|
|
5784
|
-
const result = await esbuild.build(buildOptions);
|
|
5785
|
-
const output = result.outputFiles?.[0];
|
|
5786
|
-
if (!output) {
|
|
5787
|
-
throw new Error("Bundling failed: no output generated");
|
|
5788
|
-
}
|
|
5789
|
-
return {
|
|
5790
|
-
code: output.text,
|
|
5791
|
-
sourceMap: options.sourcemap ? output.text : void 0
|
|
5792
|
-
};
|
|
6078
|
+
return bundleCode(options);
|
|
5793
6079
|
}
|
|
5794
6080
|
};
|
|
5795
6081
|
|
|
5796
|
-
// src/admin.ts
|
|
5797
|
-
var
|
|
6082
|
+
// src/admin-ai.ts
|
|
6083
|
+
var FluxbaseAdminAI = class {
|
|
5798
6084
|
constructor(fetch2) {
|
|
5799
|
-
this.adminToken = null;
|
|
5800
6085
|
this.fetch = fetch2;
|
|
5801
|
-
this.settings = new FluxbaseSettings(fetch2);
|
|
5802
|
-
this.ddl = new DDLManager(fetch2);
|
|
5803
|
-
this.oauth = new FluxbaseOAuth(fetch2);
|
|
5804
|
-
this.impersonation = new ImpersonationManager(fetch2);
|
|
5805
|
-
this.management = new FluxbaseManagement(fetch2);
|
|
5806
|
-
this.emailTemplates = new EmailTemplateManager(fetch2);
|
|
5807
|
-
this.functions = new FluxbaseAdminFunctions(fetch2);
|
|
5808
|
-
this.jobs = new FluxbaseAdminJobs(fetch2);
|
|
5809
|
-
this.migrations = new FluxbaseAdminMigrations(fetch2);
|
|
5810
|
-
}
|
|
5811
|
-
/**
|
|
5812
|
-
* Set admin authentication token
|
|
5813
|
-
*/
|
|
5814
|
-
setToken(token) {
|
|
5815
|
-
this.adminToken = token;
|
|
5816
|
-
this.fetch.setAuthToken(token);
|
|
5817
|
-
}
|
|
5818
|
-
/**
|
|
5819
|
-
* Get current admin token
|
|
5820
|
-
*/
|
|
5821
|
-
getToken() {
|
|
5822
|
-
return this.adminToken;
|
|
5823
|
-
}
|
|
5824
|
-
/**
|
|
5825
|
-
* Clear admin token
|
|
5826
|
-
*/
|
|
5827
|
-
clearToken() {
|
|
5828
|
-
this.adminToken = null;
|
|
5829
|
-
this.fetch.setAuthToken(null);
|
|
5830
6086
|
}
|
|
5831
6087
|
// ============================================================================
|
|
5832
|
-
//
|
|
6088
|
+
// CHATBOT MANAGEMENT
|
|
5833
6089
|
// ============================================================================
|
|
5834
6090
|
/**
|
|
5835
|
-
*
|
|
6091
|
+
* List all chatbots (admin view)
|
|
5836
6092
|
*
|
|
5837
|
-
* @
|
|
6093
|
+
* @param namespace - Optional namespace filter
|
|
6094
|
+
* @returns Promise resolving to { data, error } tuple with array of chatbot summaries
|
|
5838
6095
|
*
|
|
5839
6096
|
* @example
|
|
5840
6097
|
* ```typescript
|
|
5841
|
-
* const
|
|
5842
|
-
* if (
|
|
5843
|
-
* console.log('
|
|
6098
|
+
* const { data, error } = await client.admin.ai.listChatbots()
|
|
6099
|
+
* if (data) {
|
|
6100
|
+
* console.log('Chatbots:', data.map(c => c.name))
|
|
5844
6101
|
* }
|
|
5845
6102
|
* ```
|
|
5846
6103
|
*/
|
|
5847
|
-
async
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
})
|
|
5853
|
-
|
|
6104
|
+
async listChatbots(namespace) {
|
|
6105
|
+
try {
|
|
6106
|
+
const params = namespace ? `?namespace=${namespace}` : "";
|
|
6107
|
+
const response = await this.fetch.get(`/api/v1/admin/ai/chatbots${params}`);
|
|
6108
|
+
return { data: response.chatbots || [], error: null };
|
|
6109
|
+
} catch (error) {
|
|
6110
|
+
return { data: null, error };
|
|
6111
|
+
}
|
|
6112
|
+
}
|
|
6113
|
+
/**
|
|
6114
|
+
* Get details of a specific chatbot
|
|
6115
|
+
*
|
|
6116
|
+
* @param id - Chatbot ID
|
|
6117
|
+
* @returns Promise resolving to { data, error } tuple with chatbot details
|
|
6118
|
+
*
|
|
6119
|
+
* @example
|
|
6120
|
+
* ```typescript
|
|
6121
|
+
* const { data, error } = await client.admin.ai.getChatbot('uuid')
|
|
6122
|
+
* if (data) {
|
|
6123
|
+
* console.log('Chatbot:', data.name)
|
|
6124
|
+
* }
|
|
6125
|
+
* ```
|
|
6126
|
+
*/
|
|
6127
|
+
async getChatbot(id) {
|
|
6128
|
+
try {
|
|
6129
|
+
const data = await this.fetch.get(
|
|
6130
|
+
`/api/v1/admin/ai/chatbots/${id}`
|
|
6131
|
+
);
|
|
6132
|
+
return { data, error: null };
|
|
6133
|
+
} catch (error) {
|
|
6134
|
+
return { data: null, error };
|
|
6135
|
+
}
|
|
6136
|
+
}
|
|
6137
|
+
/**
|
|
6138
|
+
* Enable or disable a chatbot
|
|
6139
|
+
*
|
|
6140
|
+
* @param id - Chatbot ID
|
|
6141
|
+
* @param enabled - Whether to enable or disable
|
|
6142
|
+
* @returns Promise resolving to { data, error } tuple with updated chatbot
|
|
6143
|
+
*
|
|
6144
|
+
* @example
|
|
6145
|
+
* ```typescript
|
|
6146
|
+
* const { data, error } = await client.admin.ai.toggleChatbot('uuid', true)
|
|
6147
|
+
* ```
|
|
6148
|
+
*/
|
|
6149
|
+
async toggleChatbot(id, enabled) {
|
|
6150
|
+
try {
|
|
6151
|
+
const data = await this.fetch.put(
|
|
6152
|
+
`/api/v1/admin/ai/chatbots/${id}/toggle`,
|
|
6153
|
+
{ enabled }
|
|
6154
|
+
);
|
|
6155
|
+
return { data, error: null };
|
|
6156
|
+
} catch (error) {
|
|
6157
|
+
return { data: null, error };
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6160
|
+
/**
|
|
6161
|
+
* Delete a chatbot
|
|
6162
|
+
*
|
|
6163
|
+
* @param id - Chatbot ID
|
|
6164
|
+
* @returns Promise resolving to { data, error } tuple
|
|
6165
|
+
*
|
|
6166
|
+
* @example
|
|
6167
|
+
* ```typescript
|
|
6168
|
+
* const { data, error } = await client.admin.ai.deleteChatbot('uuid')
|
|
6169
|
+
* ```
|
|
6170
|
+
*/
|
|
6171
|
+
async deleteChatbot(id) {
|
|
6172
|
+
try {
|
|
6173
|
+
await this.fetch.delete(`/api/v1/admin/ai/chatbots/${id}`);
|
|
6174
|
+
return { data: null, error: null };
|
|
6175
|
+
} catch (error) {
|
|
6176
|
+
return { data: null, error };
|
|
6177
|
+
}
|
|
6178
|
+
}
|
|
6179
|
+
/**
|
|
6180
|
+
* Sync chatbots from filesystem or API payload
|
|
6181
|
+
*
|
|
6182
|
+
* Can sync from:
|
|
6183
|
+
* 1. Filesystem (if no chatbots provided) - loads from configured chatbots directory
|
|
6184
|
+
* 2. API payload (if chatbots array provided) - syncs provided chatbot specifications
|
|
6185
|
+
*
|
|
6186
|
+
* Requires service_role or admin authentication.
|
|
6187
|
+
*
|
|
6188
|
+
* @param options - Sync options including namespace and optional chatbots array
|
|
6189
|
+
* @returns Promise resolving to { data, error } tuple with sync results
|
|
6190
|
+
*
|
|
6191
|
+
* @example
|
|
6192
|
+
* ```typescript
|
|
6193
|
+
* // Sync from filesystem
|
|
6194
|
+
* const { data, error } = await client.admin.ai.sync()
|
|
6195
|
+
*
|
|
6196
|
+
* // Sync with provided chatbot code
|
|
6197
|
+
* const { data, error } = await client.admin.ai.sync({
|
|
6198
|
+
* namespace: 'default',
|
|
6199
|
+
* chatbots: [{
|
|
6200
|
+
* name: 'sql-assistant',
|
|
6201
|
+
* code: myChatbotCode,
|
|
6202
|
+
* }],
|
|
6203
|
+
* options: {
|
|
6204
|
+
* delete_missing: false, // Don't remove chatbots not in this sync
|
|
6205
|
+
* dry_run: false, // Preview changes without applying
|
|
6206
|
+
* }
|
|
6207
|
+
* })
|
|
6208
|
+
*
|
|
6209
|
+
* if (data) {
|
|
6210
|
+
* console.log(`Synced: ${data.summary.created} created, ${data.summary.updated} updated`)
|
|
6211
|
+
* }
|
|
6212
|
+
* ```
|
|
6213
|
+
*/
|
|
6214
|
+
async sync(options) {
|
|
6215
|
+
try {
|
|
6216
|
+
const data = await this.fetch.post(
|
|
6217
|
+
"/api/v1/admin/ai/chatbots/sync",
|
|
6218
|
+
{
|
|
6219
|
+
namespace: options?.namespace || "default",
|
|
6220
|
+
chatbots: options?.chatbots,
|
|
6221
|
+
options: {
|
|
6222
|
+
delete_missing: options?.options?.delete_missing ?? false,
|
|
6223
|
+
dry_run: options?.options?.dry_run ?? false
|
|
6224
|
+
}
|
|
6225
|
+
}
|
|
6226
|
+
);
|
|
6227
|
+
return { data, error: null };
|
|
6228
|
+
} catch (error) {
|
|
6229
|
+
return { data: null, error };
|
|
6230
|
+
}
|
|
6231
|
+
}
|
|
6232
|
+
// ============================================================================
|
|
6233
|
+
// PROVIDER MANAGEMENT
|
|
6234
|
+
// ============================================================================
|
|
6235
|
+
/**
|
|
6236
|
+
* List all AI providers
|
|
6237
|
+
*
|
|
6238
|
+
* @returns Promise resolving to { data, error } tuple with array of providers
|
|
6239
|
+
*
|
|
6240
|
+
* @example
|
|
6241
|
+
* ```typescript
|
|
6242
|
+
* const { data, error } = await client.admin.ai.listProviders()
|
|
6243
|
+
* if (data) {
|
|
6244
|
+
* console.log('Providers:', data.map(p => p.name))
|
|
6245
|
+
* }
|
|
6246
|
+
* ```
|
|
6247
|
+
*/
|
|
6248
|
+
async listProviders() {
|
|
6249
|
+
try {
|
|
6250
|
+
const response = await this.fetch.get("/api/v1/admin/ai/providers");
|
|
6251
|
+
return { data: response.providers || [], error: null };
|
|
6252
|
+
} catch (error) {
|
|
6253
|
+
return { data: null, error };
|
|
6254
|
+
}
|
|
6255
|
+
}
|
|
6256
|
+
/**
|
|
6257
|
+
* Get details of a specific provider
|
|
6258
|
+
*
|
|
6259
|
+
* @param id - Provider ID
|
|
6260
|
+
* @returns Promise resolving to { data, error } tuple with provider details
|
|
6261
|
+
*
|
|
6262
|
+
* @example
|
|
6263
|
+
* ```typescript
|
|
6264
|
+
* const { data, error } = await client.admin.ai.getProvider('uuid')
|
|
6265
|
+
* if (data) {
|
|
6266
|
+
* console.log('Provider:', data.display_name)
|
|
6267
|
+
* }
|
|
6268
|
+
* ```
|
|
6269
|
+
*/
|
|
6270
|
+
async getProvider(id) {
|
|
6271
|
+
try {
|
|
6272
|
+
const data = await this.fetch.get(
|
|
6273
|
+
`/api/v1/admin/ai/providers/${id}`
|
|
6274
|
+
);
|
|
6275
|
+
return { data, error: null };
|
|
6276
|
+
} catch (error) {
|
|
6277
|
+
return { data: null, error };
|
|
6278
|
+
}
|
|
6279
|
+
}
|
|
6280
|
+
/**
|
|
6281
|
+
* Create a new AI provider
|
|
6282
|
+
*
|
|
6283
|
+
* @param request - Provider configuration
|
|
6284
|
+
* @returns Promise resolving to { data, error } tuple with created provider
|
|
6285
|
+
*
|
|
6286
|
+
* @example
|
|
6287
|
+
* ```typescript
|
|
6288
|
+
* const { data, error } = await client.admin.ai.createProvider({
|
|
6289
|
+
* name: 'openai-main',
|
|
6290
|
+
* display_name: 'OpenAI (Main)',
|
|
6291
|
+
* provider_type: 'openai',
|
|
6292
|
+
* is_default: true,
|
|
6293
|
+
* config: {
|
|
6294
|
+
* api_key: 'sk-...',
|
|
6295
|
+
* model: 'gpt-4-turbo',
|
|
6296
|
+
* }
|
|
6297
|
+
* })
|
|
6298
|
+
* ```
|
|
6299
|
+
*/
|
|
6300
|
+
async createProvider(request) {
|
|
6301
|
+
try {
|
|
6302
|
+
const data = await this.fetch.post(
|
|
6303
|
+
"/api/v1/admin/ai/providers",
|
|
6304
|
+
request
|
|
6305
|
+
);
|
|
6306
|
+
return { data, error: null };
|
|
6307
|
+
} catch (error) {
|
|
6308
|
+
return { data: null, error };
|
|
6309
|
+
}
|
|
6310
|
+
}
|
|
6311
|
+
/**
|
|
6312
|
+
* Update an existing AI provider
|
|
6313
|
+
*
|
|
6314
|
+
* @param id - Provider ID
|
|
6315
|
+
* @param updates - Fields to update
|
|
6316
|
+
* @returns Promise resolving to { data, error } tuple with updated provider
|
|
6317
|
+
*
|
|
6318
|
+
* @example
|
|
6319
|
+
* ```typescript
|
|
6320
|
+
* const { data, error } = await client.admin.ai.updateProvider('uuid', {
|
|
6321
|
+
* display_name: 'Updated Name',
|
|
6322
|
+
* config: {
|
|
6323
|
+
* api_key: 'new-key',
|
|
6324
|
+
* model: 'gpt-4-turbo',
|
|
6325
|
+
* },
|
|
6326
|
+
* enabled: true,
|
|
6327
|
+
* })
|
|
6328
|
+
* ```
|
|
6329
|
+
*/
|
|
6330
|
+
async updateProvider(id, updates) {
|
|
6331
|
+
try {
|
|
6332
|
+
const data = await this.fetch.put(
|
|
6333
|
+
`/api/v1/admin/ai/providers/${id}`,
|
|
6334
|
+
updates
|
|
6335
|
+
);
|
|
6336
|
+
return { data, error: null };
|
|
6337
|
+
} catch (error) {
|
|
6338
|
+
return { data: null, error };
|
|
6339
|
+
}
|
|
6340
|
+
}
|
|
6341
|
+
/**
|
|
6342
|
+
* Set a provider as the default
|
|
6343
|
+
*
|
|
6344
|
+
* @param id - Provider ID
|
|
6345
|
+
* @returns Promise resolving to { data, error } tuple with updated provider
|
|
6346
|
+
*
|
|
6347
|
+
* @example
|
|
6348
|
+
* ```typescript
|
|
6349
|
+
* const { data, error } = await client.admin.ai.setDefaultProvider('uuid')
|
|
6350
|
+
* ```
|
|
6351
|
+
*/
|
|
6352
|
+
async setDefaultProvider(id) {
|
|
6353
|
+
try {
|
|
6354
|
+
const data = await this.fetch.put(
|
|
6355
|
+
`/api/v1/admin/ai/providers/${id}/default`,
|
|
6356
|
+
{}
|
|
6357
|
+
);
|
|
6358
|
+
return { data, error: null };
|
|
6359
|
+
} catch (error) {
|
|
6360
|
+
return { data: null, error };
|
|
6361
|
+
}
|
|
6362
|
+
}
|
|
6363
|
+
/**
|
|
6364
|
+
* Delete a provider
|
|
6365
|
+
*
|
|
6366
|
+
* @param id - Provider ID
|
|
6367
|
+
* @returns Promise resolving to { data, error } tuple
|
|
6368
|
+
*
|
|
6369
|
+
* @example
|
|
6370
|
+
* ```typescript
|
|
6371
|
+
* const { data, error } = await client.admin.ai.deleteProvider('uuid')
|
|
6372
|
+
* ```
|
|
6373
|
+
*/
|
|
6374
|
+
async deleteProvider(id) {
|
|
6375
|
+
try {
|
|
6376
|
+
await this.fetch.delete(`/api/v1/admin/ai/providers/${id}`);
|
|
6377
|
+
return { data: null, error: null };
|
|
6378
|
+
} catch (error) {
|
|
6379
|
+
return { data: null, error };
|
|
6380
|
+
}
|
|
6381
|
+
}
|
|
6382
|
+
};
|
|
6383
|
+
|
|
6384
|
+
// src/admin-rpc.ts
|
|
6385
|
+
var FluxbaseAdminRPC = class {
|
|
6386
|
+
constructor(fetch2) {
|
|
6387
|
+
this.fetch = fetch2;
|
|
6388
|
+
}
|
|
6389
|
+
// ============================================================================
|
|
6390
|
+
// PROCEDURE MANAGEMENT
|
|
6391
|
+
// ============================================================================
|
|
6392
|
+
/**
|
|
6393
|
+
* Sync RPC procedures from filesystem or API payload
|
|
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.
|
|
6400
|
+
*
|
|
6401
|
+
* @param options - Sync options including namespace and optional procedures array
|
|
6402
|
+
* @returns Promise resolving to { data, error } tuple with sync results
|
|
6403
|
+
*
|
|
6404
|
+
* @example
|
|
6405
|
+
* ```typescript
|
|
6406
|
+
* // Sync from filesystem
|
|
6407
|
+
* const { data, error } = await client.admin.rpc.sync()
|
|
6408
|
+
*
|
|
6409
|
+
* // Sync with provided procedure code
|
|
6410
|
+
* const { data, error } = await client.admin.rpc.sync({
|
|
6411
|
+
* namespace: 'default',
|
|
6412
|
+
* procedures: [{
|
|
6413
|
+
* name: 'get-user-orders',
|
|
6414
|
+
* code: myProcedureSQL,
|
|
6415
|
+
* }],
|
|
6416
|
+
* options: {
|
|
6417
|
+
* delete_missing: false, // Don't remove procedures not in this sync
|
|
6418
|
+
* dry_run: false, // Preview changes without applying
|
|
6419
|
+
* }
|
|
6420
|
+
* })
|
|
6421
|
+
*
|
|
6422
|
+
* if (data) {
|
|
6423
|
+
* console.log(`Synced: ${data.summary.created} created, ${data.summary.updated} updated`)
|
|
6424
|
+
* }
|
|
6425
|
+
* ```
|
|
6426
|
+
*/
|
|
6427
|
+
async sync(options) {
|
|
6428
|
+
try {
|
|
6429
|
+
const data = await this.fetch.post(
|
|
6430
|
+
"/api/v1/admin/rpc/sync",
|
|
6431
|
+
{
|
|
6432
|
+
namespace: options?.namespace || "default",
|
|
6433
|
+
procedures: options?.procedures,
|
|
6434
|
+
options: {
|
|
6435
|
+
delete_missing: options?.options?.delete_missing ?? false,
|
|
6436
|
+
dry_run: options?.options?.dry_run ?? false
|
|
6437
|
+
}
|
|
6438
|
+
}
|
|
6439
|
+
);
|
|
6440
|
+
return { data, error: null };
|
|
6441
|
+
} catch (error) {
|
|
6442
|
+
return { data: null, error };
|
|
6443
|
+
}
|
|
6444
|
+
}
|
|
6445
|
+
/**
|
|
6446
|
+
* List all RPC procedures (admin view)
|
|
6447
|
+
*
|
|
6448
|
+
* @param namespace - Optional namespace filter
|
|
6449
|
+
* @returns Promise resolving to { data, error } tuple with array of procedure summaries
|
|
6450
|
+
*
|
|
6451
|
+
* @example
|
|
6452
|
+
* ```typescript
|
|
6453
|
+
* const { data, error } = await client.admin.rpc.list()
|
|
6454
|
+
* if (data) {
|
|
6455
|
+
* console.log('Procedures:', data.map(p => p.name))
|
|
6456
|
+
* }
|
|
6457
|
+
* ```
|
|
6458
|
+
*/
|
|
6459
|
+
async list(namespace) {
|
|
6460
|
+
try {
|
|
6461
|
+
const params = namespace ? `?namespace=${encodeURIComponent(namespace)}` : "";
|
|
6462
|
+
const response = await this.fetch.get(
|
|
6463
|
+
`/api/v1/admin/rpc/procedures${params}`
|
|
6464
|
+
);
|
|
6465
|
+
return { data: response.procedures || [], error: null };
|
|
6466
|
+
} catch (error) {
|
|
6467
|
+
return { data: null, error };
|
|
6468
|
+
}
|
|
6469
|
+
}
|
|
6470
|
+
/**
|
|
6471
|
+
* List all namespaces
|
|
6472
|
+
*
|
|
6473
|
+
* @returns Promise resolving to { data, error } tuple with array of namespace names
|
|
6474
|
+
*
|
|
6475
|
+
* @example
|
|
6476
|
+
* ```typescript
|
|
6477
|
+
* const { data, error } = await client.admin.rpc.listNamespaces()
|
|
6478
|
+
* if (data) {
|
|
6479
|
+
* console.log('Namespaces:', data)
|
|
6480
|
+
* }
|
|
6481
|
+
* ```
|
|
6482
|
+
*/
|
|
6483
|
+
async listNamespaces() {
|
|
6484
|
+
try {
|
|
6485
|
+
const response = await this.fetch.get(
|
|
6486
|
+
"/api/v1/admin/rpc/namespaces"
|
|
6487
|
+
);
|
|
6488
|
+
return { data: response.namespaces || [], error: null };
|
|
6489
|
+
} catch (error) {
|
|
6490
|
+
return { data: null, error };
|
|
6491
|
+
}
|
|
6492
|
+
}
|
|
6493
|
+
/**
|
|
6494
|
+
* Get details of a specific RPC procedure
|
|
6495
|
+
*
|
|
6496
|
+
* @param namespace - Procedure namespace
|
|
6497
|
+
* @param name - Procedure name
|
|
6498
|
+
* @returns Promise resolving to { data, error } tuple with procedure details
|
|
6499
|
+
*
|
|
6500
|
+
* @example
|
|
6501
|
+
* ```typescript
|
|
6502
|
+
* const { data, error } = await client.admin.rpc.get('default', 'get-user-orders')
|
|
6503
|
+
* if (data) {
|
|
6504
|
+
* console.log('Procedure:', data.name)
|
|
6505
|
+
* console.log('SQL:', data.sql_query)
|
|
6506
|
+
* }
|
|
6507
|
+
* ```
|
|
6508
|
+
*/
|
|
6509
|
+
async get(namespace, name) {
|
|
6510
|
+
try {
|
|
6511
|
+
const data = await this.fetch.get(
|
|
6512
|
+
`/api/v1/admin/rpc/procedures/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`
|
|
6513
|
+
);
|
|
6514
|
+
return { data, error: null };
|
|
6515
|
+
} catch (error) {
|
|
6516
|
+
return { data: null, error };
|
|
6517
|
+
}
|
|
6518
|
+
}
|
|
6519
|
+
/**
|
|
6520
|
+
* Update an RPC procedure
|
|
6521
|
+
*
|
|
6522
|
+
* @param namespace - Procedure namespace
|
|
6523
|
+
* @param name - Procedure name
|
|
6524
|
+
* @param updates - Fields to update
|
|
6525
|
+
* @returns Promise resolving to { data, error } tuple with updated procedure
|
|
6526
|
+
*
|
|
6527
|
+
* @example
|
|
6528
|
+
* ```typescript
|
|
6529
|
+
* const { data, error } = await client.admin.rpc.update('default', 'get-user-orders', {
|
|
6530
|
+
* enabled: false,
|
|
6531
|
+
* max_execution_time_seconds: 60,
|
|
6532
|
+
* })
|
|
6533
|
+
* ```
|
|
6534
|
+
*/
|
|
6535
|
+
async update(namespace, name, updates) {
|
|
6536
|
+
try {
|
|
6537
|
+
const data = await this.fetch.put(
|
|
6538
|
+
`/api/v1/admin/rpc/procedures/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`,
|
|
6539
|
+
updates
|
|
6540
|
+
);
|
|
6541
|
+
return { data, error: null };
|
|
6542
|
+
} catch (error) {
|
|
6543
|
+
return { data: null, error };
|
|
6544
|
+
}
|
|
6545
|
+
}
|
|
6546
|
+
/**
|
|
6547
|
+
* Enable or disable an RPC procedure
|
|
6548
|
+
*
|
|
6549
|
+
* @param namespace - Procedure namespace
|
|
6550
|
+
* @param name - Procedure name
|
|
6551
|
+
* @param enabled - Whether to enable or disable
|
|
6552
|
+
* @returns Promise resolving to { data, error } tuple with updated procedure
|
|
6553
|
+
*
|
|
6554
|
+
* @example
|
|
6555
|
+
* ```typescript
|
|
6556
|
+
* const { data, error } = await client.admin.rpc.toggle('default', 'get-user-orders', true)
|
|
6557
|
+
* ```
|
|
6558
|
+
*/
|
|
6559
|
+
async toggle(namespace, name, enabled) {
|
|
6560
|
+
return this.update(namespace, name, { enabled });
|
|
6561
|
+
}
|
|
6562
|
+
/**
|
|
6563
|
+
* Delete an RPC procedure
|
|
6564
|
+
*
|
|
6565
|
+
* @param namespace - Procedure namespace
|
|
6566
|
+
* @param name - Procedure name
|
|
6567
|
+
* @returns Promise resolving to { data, error } tuple
|
|
6568
|
+
*
|
|
6569
|
+
* @example
|
|
6570
|
+
* ```typescript
|
|
6571
|
+
* const { data, error } = await client.admin.rpc.delete('default', 'get-user-orders')
|
|
6572
|
+
* ```
|
|
6573
|
+
*/
|
|
6574
|
+
async delete(namespace, name) {
|
|
6575
|
+
try {
|
|
6576
|
+
await this.fetch.delete(
|
|
6577
|
+
`/api/v1/admin/rpc/procedures/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`
|
|
6578
|
+
);
|
|
6579
|
+
return { data: null, error: null };
|
|
6580
|
+
} catch (error) {
|
|
6581
|
+
return { data: null, error };
|
|
6582
|
+
}
|
|
6583
|
+
}
|
|
6584
|
+
// ============================================================================
|
|
6585
|
+
// EXECUTION MONITORING
|
|
6586
|
+
// ============================================================================
|
|
6587
|
+
/**
|
|
6588
|
+
* List RPC executions with optional filters
|
|
6589
|
+
*
|
|
6590
|
+
* @param filters - Optional filters for namespace, procedure, status, user
|
|
6591
|
+
* @returns Promise resolving to { data, error } tuple with array of executions
|
|
6592
|
+
*
|
|
6593
|
+
* @example
|
|
6594
|
+
* ```typescript
|
|
6595
|
+
* // List all executions
|
|
6596
|
+
* const { data, error } = await client.admin.rpc.listExecutions()
|
|
6597
|
+
*
|
|
6598
|
+
* // List failed executions for a specific procedure
|
|
6599
|
+
* const { data, error } = await client.admin.rpc.listExecutions({
|
|
6600
|
+
* namespace: 'default',
|
|
6601
|
+
* procedure: 'get-user-orders',
|
|
6602
|
+
* status: 'failed',
|
|
6603
|
+
* })
|
|
6604
|
+
* ```
|
|
6605
|
+
*/
|
|
6606
|
+
async listExecutions(filters) {
|
|
6607
|
+
try {
|
|
6608
|
+
const params = new URLSearchParams();
|
|
6609
|
+
if (filters?.namespace) params.set("namespace", filters.namespace);
|
|
6610
|
+
if (filters?.procedure) params.set("procedure_name", filters.procedure);
|
|
6611
|
+
if (filters?.status) params.set("status", filters.status);
|
|
6612
|
+
if (filters?.user_id) params.set("user_id", filters.user_id);
|
|
6613
|
+
if (filters?.limit) params.set("limit", filters.limit.toString());
|
|
6614
|
+
if (filters?.offset) params.set("offset", filters.offset.toString());
|
|
6615
|
+
const queryString = params.toString();
|
|
6616
|
+
const path = queryString ? `/api/v1/admin/rpc/executions?${queryString}` : "/api/v1/admin/rpc/executions";
|
|
6617
|
+
const response = await this.fetch.get(path);
|
|
6618
|
+
return { data: response.executions || [], error: null };
|
|
6619
|
+
} catch (error) {
|
|
6620
|
+
return { data: null, error };
|
|
6621
|
+
}
|
|
6622
|
+
}
|
|
6623
|
+
/**
|
|
6624
|
+
* Get details of a specific execution
|
|
6625
|
+
*
|
|
6626
|
+
* @param executionId - Execution ID
|
|
6627
|
+
* @returns Promise resolving to { data, error } tuple with execution details
|
|
6628
|
+
*
|
|
6629
|
+
* @example
|
|
6630
|
+
* ```typescript
|
|
6631
|
+
* const { data, error } = await client.admin.rpc.getExecution('execution-uuid')
|
|
6632
|
+
* if (data) {
|
|
6633
|
+
* console.log('Status:', data.status)
|
|
6634
|
+
* console.log('Duration:', data.duration_ms, 'ms')
|
|
6635
|
+
* }
|
|
6636
|
+
* ```
|
|
6637
|
+
*/
|
|
6638
|
+
async getExecution(executionId) {
|
|
6639
|
+
try {
|
|
6640
|
+
const data = await this.fetch.get(
|
|
6641
|
+
`/api/v1/admin/rpc/executions/${encodeURIComponent(executionId)}`
|
|
6642
|
+
);
|
|
6643
|
+
return { data, error: null };
|
|
6644
|
+
} catch (error) {
|
|
6645
|
+
return { data: null, error };
|
|
6646
|
+
}
|
|
6647
|
+
}
|
|
6648
|
+
/**
|
|
6649
|
+
* Get execution logs for a specific execution
|
|
6650
|
+
*
|
|
6651
|
+
* @param executionId - Execution ID
|
|
6652
|
+
* @param afterLine - Optional line number to get logs after (for polling)
|
|
6653
|
+
* @returns Promise resolving to { data, error } tuple with execution logs
|
|
6654
|
+
*
|
|
6655
|
+
* @example
|
|
6656
|
+
* ```typescript
|
|
6657
|
+
* const { data, error } = await client.admin.rpc.getExecutionLogs('execution-uuid')
|
|
6658
|
+
* if (data) {
|
|
6659
|
+
* for (const log of data) {
|
|
6660
|
+
* console.log(`[${log.level}] ${log.message}`)
|
|
6661
|
+
* }
|
|
6662
|
+
* }
|
|
6663
|
+
* ```
|
|
6664
|
+
*/
|
|
6665
|
+
async getExecutionLogs(executionId, afterLine) {
|
|
6666
|
+
try {
|
|
6667
|
+
const params = afterLine !== void 0 ? `?after=${afterLine}` : "";
|
|
6668
|
+
const response = await this.fetch.get(
|
|
6669
|
+
`/api/v1/admin/rpc/executions/${encodeURIComponent(executionId)}/logs${params}`
|
|
6670
|
+
);
|
|
6671
|
+
return { data: response.logs || [], error: null };
|
|
6672
|
+
} catch (error) {
|
|
6673
|
+
return { data: null, error };
|
|
6674
|
+
}
|
|
6675
|
+
}
|
|
6676
|
+
/**
|
|
6677
|
+
* Cancel a running execution
|
|
6678
|
+
*
|
|
6679
|
+
* @param executionId - Execution ID
|
|
6680
|
+
* @returns Promise resolving to { data, error } tuple with updated execution
|
|
6681
|
+
*
|
|
6682
|
+
* @example
|
|
6683
|
+
* ```typescript
|
|
6684
|
+
* const { data, error } = await client.admin.rpc.cancelExecution('execution-uuid')
|
|
6685
|
+
* ```
|
|
6686
|
+
*/
|
|
6687
|
+
async cancelExecution(executionId) {
|
|
6688
|
+
try {
|
|
6689
|
+
const data = await this.fetch.post(
|
|
6690
|
+
`/api/v1/admin/rpc/executions/${encodeURIComponent(executionId)}/cancel`,
|
|
6691
|
+
{}
|
|
6692
|
+
);
|
|
6693
|
+
return { data, error: null };
|
|
6694
|
+
} catch (error) {
|
|
6695
|
+
return { data: null, error };
|
|
6696
|
+
}
|
|
6697
|
+
}
|
|
6698
|
+
};
|
|
6699
|
+
|
|
6700
|
+
// src/admin.ts
|
|
6701
|
+
var FluxbaseAdmin = class {
|
|
6702
|
+
constructor(fetch2) {
|
|
6703
|
+
this.adminToken = null;
|
|
6704
|
+
this.fetch = fetch2;
|
|
6705
|
+
this.settings = new FluxbaseSettings(fetch2);
|
|
6706
|
+
this.ddl = new DDLManager(fetch2);
|
|
6707
|
+
this.oauth = new FluxbaseOAuth(fetch2);
|
|
6708
|
+
this.impersonation = new ImpersonationManager(fetch2);
|
|
6709
|
+
this.management = new FluxbaseManagement(fetch2);
|
|
6710
|
+
this.emailTemplates = new EmailTemplateManager(fetch2);
|
|
6711
|
+
this.functions = new FluxbaseAdminFunctions(fetch2);
|
|
6712
|
+
this.jobs = new FluxbaseAdminJobs(fetch2);
|
|
6713
|
+
this.migrations = new FluxbaseAdminMigrations(fetch2);
|
|
6714
|
+
this.ai = new FluxbaseAdminAI(fetch2);
|
|
6715
|
+
this.rpc = new FluxbaseAdminRPC(fetch2);
|
|
6716
|
+
}
|
|
6717
|
+
/**
|
|
6718
|
+
* Set admin authentication token
|
|
6719
|
+
*/
|
|
6720
|
+
setToken(token) {
|
|
6721
|
+
this.adminToken = token;
|
|
6722
|
+
this.fetch.setAuthToken(token);
|
|
6723
|
+
}
|
|
6724
|
+
/**
|
|
6725
|
+
* Get current admin token
|
|
6726
|
+
*/
|
|
6727
|
+
getToken() {
|
|
6728
|
+
return this.adminToken;
|
|
6729
|
+
}
|
|
6730
|
+
/**
|
|
6731
|
+
* Clear admin token
|
|
6732
|
+
*/
|
|
6733
|
+
clearToken() {
|
|
6734
|
+
this.adminToken = null;
|
|
6735
|
+
this.fetch.setAuthToken(null);
|
|
6736
|
+
}
|
|
6737
|
+
// ============================================================================
|
|
6738
|
+
// Admin Authentication
|
|
6739
|
+
// ============================================================================
|
|
6740
|
+
/**
|
|
6741
|
+
* Check if initial admin setup is needed
|
|
6742
|
+
*
|
|
6743
|
+
* @returns Setup status indicating if initial setup is required
|
|
6744
|
+
*
|
|
6745
|
+
* @example
|
|
6746
|
+
* ```typescript
|
|
6747
|
+
* const status = await admin.getSetupStatus();
|
|
6748
|
+
* if (status.needs_setup) {
|
|
6749
|
+
* console.log('Initial setup required');
|
|
6750
|
+
* }
|
|
6751
|
+
* ```
|
|
6752
|
+
*/
|
|
6753
|
+
async getSetupStatus() {
|
|
6754
|
+
return wrapAsync(async () => {
|
|
6755
|
+
return await this.fetch.get(
|
|
6756
|
+
"/api/v1/admin/setup/status"
|
|
6757
|
+
);
|
|
6758
|
+
});
|
|
6759
|
+
}
|
|
5854
6760
|
/**
|
|
5855
6761
|
* Perform initial admin setup
|
|
5856
6762
|
*
|
|
5857
6763
|
* Creates the first admin user and completes initial setup.
|
|
5858
6764
|
* This endpoint can only be called once.
|
|
5859
6765
|
*
|
|
5860
|
-
* @param
|
|
5861
|
-
* @param password - Admin password (minimum 12 characters)
|
|
5862
|
-
* @param name - Admin display name
|
|
6766
|
+
* @param request - Setup request containing email, password, and name
|
|
5863
6767
|
* @returns Authentication response with tokens
|
|
5864
6768
|
*
|
|
5865
6769
|
* @example
|
|
@@ -5889,8 +6793,7 @@ var FluxbaseAdmin = class {
|
|
|
5889
6793
|
*
|
|
5890
6794
|
* Authenticate as an admin user
|
|
5891
6795
|
*
|
|
5892
|
-
* @param
|
|
5893
|
-
* @param password - Admin password
|
|
6796
|
+
* @param request - Login request containing email and password
|
|
5894
6797
|
* @returns Authentication response with tokens
|
|
5895
6798
|
*
|
|
5896
6799
|
* @example
|
|
@@ -5917,7 +6820,7 @@ var FluxbaseAdmin = class {
|
|
|
5917
6820
|
/**
|
|
5918
6821
|
* Refresh admin access token
|
|
5919
6822
|
*
|
|
5920
|
-
* @param
|
|
6823
|
+
* @param request - Refresh request containing the refresh token
|
|
5921
6824
|
* @returns New access and refresh tokens
|
|
5922
6825
|
*
|
|
5923
6826
|
* @example
|
|
@@ -6018,121 +6921,494 @@ var FluxbaseAdmin = class {
|
|
|
6018
6921
|
});
|
|
6019
6922
|
}
|
|
6020
6923
|
/**
|
|
6021
|
-
* Get a user by ID
|
|
6022
|
-
*
|
|
6023
|
-
* Fetch a single user's details by their user ID
|
|
6924
|
+
* Get a user by ID
|
|
6925
|
+
*
|
|
6926
|
+
* Fetch a single user's details by their user ID
|
|
6927
|
+
*
|
|
6928
|
+
* @param userId - User ID to fetch
|
|
6929
|
+
* @param type - User type ('app' or 'dashboard')
|
|
6930
|
+
* @returns User details with metadata
|
|
6931
|
+
*
|
|
6932
|
+
* @example
|
|
6933
|
+
* ```typescript
|
|
6934
|
+
* // Get an app user
|
|
6935
|
+
* const user = await admin.getUserById('user-123');
|
|
6936
|
+
*
|
|
6937
|
+
* // Get a dashboard user
|
|
6938
|
+
* const dashboardUser = await admin.getUserById('admin-456', 'dashboard');
|
|
6939
|
+
* console.log('User email:', dashboardUser.email);
|
|
6940
|
+
* console.log('Last login:', dashboardUser.last_login_at);
|
|
6941
|
+
* ```
|
|
6942
|
+
*/
|
|
6943
|
+
async getUserById(userId, type = "app") {
|
|
6944
|
+
return wrapAsync(async () => {
|
|
6945
|
+
const url = `/api/v1/admin/users/${userId}?type=${type}`;
|
|
6946
|
+
return await this.fetch.get(url);
|
|
6947
|
+
});
|
|
6948
|
+
}
|
|
6949
|
+
/**
|
|
6950
|
+
* Invite a new user
|
|
6951
|
+
*
|
|
6952
|
+
* Creates a new user and optionally sends an invitation email
|
|
6953
|
+
*
|
|
6954
|
+
* @param request - User invitation details
|
|
6955
|
+
* @param type - User type ('app' or 'dashboard')
|
|
6956
|
+
* @returns Created user and invitation details
|
|
6957
|
+
*
|
|
6958
|
+
* @example
|
|
6959
|
+
* ```typescript
|
|
6960
|
+
* const response = await admin.inviteUser({
|
|
6961
|
+
* email: 'newuser@example.com',
|
|
6962
|
+
* role: 'user',
|
|
6963
|
+
* send_email: true
|
|
6964
|
+
* });
|
|
6965
|
+
*
|
|
6966
|
+
* console.log('User invited:', response.user.email);
|
|
6967
|
+
* console.log('Invitation link:', response.invitation_link);
|
|
6968
|
+
* ```
|
|
6969
|
+
*/
|
|
6970
|
+
async inviteUser(request, type = "app") {
|
|
6971
|
+
return wrapAsync(async () => {
|
|
6972
|
+
const url = `/api/v1/admin/users/invite?type=${type}`;
|
|
6973
|
+
return await this.fetch.post(url, request);
|
|
6974
|
+
});
|
|
6975
|
+
}
|
|
6976
|
+
/**
|
|
6977
|
+
* Delete a user
|
|
6978
|
+
*
|
|
6979
|
+
* Permanently deletes a user and all associated data
|
|
6980
|
+
*
|
|
6981
|
+
* @param userId - User ID to delete
|
|
6982
|
+
* @param type - User type ('app' or 'dashboard')
|
|
6983
|
+
* @returns Deletion confirmation
|
|
6984
|
+
*
|
|
6985
|
+
* @example
|
|
6986
|
+
* ```typescript
|
|
6987
|
+
* await admin.deleteUser('user-uuid');
|
|
6988
|
+
* console.log('User deleted');
|
|
6989
|
+
* ```
|
|
6990
|
+
*/
|
|
6991
|
+
async deleteUser(userId, type = "app") {
|
|
6992
|
+
return wrapAsync(async () => {
|
|
6993
|
+
const url = `/api/v1/admin/users/${userId}?type=${type}`;
|
|
6994
|
+
return await this.fetch.delete(url);
|
|
6995
|
+
});
|
|
6996
|
+
}
|
|
6997
|
+
/**
|
|
6998
|
+
* Update user role
|
|
6999
|
+
*
|
|
7000
|
+
* Changes a user's role
|
|
7001
|
+
*
|
|
7002
|
+
* @param userId - User ID
|
|
7003
|
+
* @param role - New role
|
|
7004
|
+
* @param type - User type ('app' or 'dashboard')
|
|
7005
|
+
* @returns Updated user
|
|
7006
|
+
*
|
|
7007
|
+
* @example
|
|
7008
|
+
* ```typescript
|
|
7009
|
+
* const user = await admin.updateUserRole('user-uuid', 'admin');
|
|
7010
|
+
* console.log('User role updated:', user.role);
|
|
7011
|
+
* ```
|
|
7012
|
+
*/
|
|
7013
|
+
async updateUserRole(userId, role, type = "app") {
|
|
7014
|
+
return wrapAsync(async () => {
|
|
7015
|
+
const url = `/api/v1/admin/users/${userId}/role?type=${type}`;
|
|
7016
|
+
return await this.fetch.patch(url, { role });
|
|
7017
|
+
});
|
|
7018
|
+
}
|
|
7019
|
+
/**
|
|
7020
|
+
* Reset user password
|
|
7021
|
+
*
|
|
7022
|
+
* Generates a new password for the user and optionally sends it via email
|
|
7023
|
+
*
|
|
7024
|
+
* @param userId - User ID
|
|
7025
|
+
* @param type - User type ('app' or 'dashboard')
|
|
7026
|
+
* @returns Reset confirmation message
|
|
7027
|
+
*
|
|
7028
|
+
* @example
|
|
7029
|
+
* ```typescript
|
|
7030
|
+
* const response = await admin.resetUserPassword('user-uuid');
|
|
7031
|
+
* console.log(response.message);
|
|
7032
|
+
* ```
|
|
7033
|
+
*/
|
|
7034
|
+
async resetUserPassword(userId, type = "app") {
|
|
7035
|
+
return wrapAsync(async () => {
|
|
7036
|
+
const url = `/api/v1/admin/users/${userId}/reset-password?type=${type}`;
|
|
7037
|
+
return await this.fetch.post(url, {});
|
|
7038
|
+
});
|
|
7039
|
+
}
|
|
7040
|
+
};
|
|
7041
|
+
|
|
7042
|
+
// src/ai.ts
|
|
7043
|
+
var FluxbaseAIChat = class {
|
|
7044
|
+
constructor(options) {
|
|
7045
|
+
this.ws = null;
|
|
7046
|
+
this.reconnectCount = 0;
|
|
7047
|
+
this.pendingStartResolve = null;
|
|
7048
|
+
this.pendingStartReject = null;
|
|
7049
|
+
this.accumulatedContent = /* @__PURE__ */ new Map();
|
|
7050
|
+
this.options = {
|
|
7051
|
+
reconnectAttempts: 3,
|
|
7052
|
+
reconnectDelay: 1e3,
|
|
7053
|
+
...options
|
|
7054
|
+
};
|
|
7055
|
+
}
|
|
7056
|
+
/**
|
|
7057
|
+
* Connect to the AI chat WebSocket
|
|
7058
|
+
*
|
|
7059
|
+
* @returns Promise that resolves when connected
|
|
7060
|
+
*/
|
|
7061
|
+
async connect() {
|
|
7062
|
+
return new Promise((resolve, reject) => {
|
|
7063
|
+
const url = this.buildWsUrl();
|
|
7064
|
+
try {
|
|
7065
|
+
this.ws = new WebSocket(url);
|
|
7066
|
+
this.ws.onopen = () => {
|
|
7067
|
+
this.reconnectCount = 0;
|
|
7068
|
+
this.emitEvent({ type: "connected" });
|
|
7069
|
+
resolve();
|
|
7070
|
+
};
|
|
7071
|
+
this.ws.onmessage = (event) => {
|
|
7072
|
+
this.handleMessage(event.data);
|
|
7073
|
+
};
|
|
7074
|
+
this.ws.onclose = (event) => {
|
|
7075
|
+
this.emitEvent({ type: "disconnected" });
|
|
7076
|
+
this.handleClose(event);
|
|
7077
|
+
};
|
|
7078
|
+
this.ws.onerror = () => {
|
|
7079
|
+
reject(new Error("WebSocket connection failed"));
|
|
7080
|
+
};
|
|
7081
|
+
} catch (error) {
|
|
7082
|
+
reject(error);
|
|
7083
|
+
}
|
|
7084
|
+
});
|
|
7085
|
+
}
|
|
7086
|
+
/**
|
|
7087
|
+
* Disconnect from the AI chat WebSocket
|
|
7088
|
+
*/
|
|
7089
|
+
disconnect() {
|
|
7090
|
+
if (this.ws) {
|
|
7091
|
+
this.ws.close();
|
|
7092
|
+
this.ws = null;
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
/**
|
|
7096
|
+
* Check if connected
|
|
7097
|
+
*/
|
|
7098
|
+
isConnected() {
|
|
7099
|
+
return this.ws?.readyState === WebSocket.OPEN;
|
|
7100
|
+
}
|
|
7101
|
+
/**
|
|
7102
|
+
* Start a new chat session with a chatbot
|
|
7103
|
+
*
|
|
7104
|
+
* @param chatbot - Chatbot name
|
|
7105
|
+
* @param namespace - Optional namespace (defaults to 'default')
|
|
7106
|
+
* @param conversationId - Optional conversation ID to resume
|
|
7107
|
+
* @param impersonateUserId - Optional user ID to impersonate (admin only)
|
|
7108
|
+
* @returns Promise resolving to conversation ID
|
|
7109
|
+
*/
|
|
7110
|
+
async startChat(chatbot, namespace, conversationId, impersonateUserId) {
|
|
7111
|
+
return new Promise((resolve, reject) => {
|
|
7112
|
+
if (!this.isConnected()) {
|
|
7113
|
+
reject(new Error("Not connected to AI chat"));
|
|
7114
|
+
return;
|
|
7115
|
+
}
|
|
7116
|
+
this.pendingStartResolve = resolve;
|
|
7117
|
+
this.pendingStartReject = reject;
|
|
7118
|
+
const message = {
|
|
7119
|
+
type: "start_chat",
|
|
7120
|
+
chatbot,
|
|
7121
|
+
namespace: namespace || "default",
|
|
7122
|
+
conversation_id: conversationId,
|
|
7123
|
+
impersonate_user_id: impersonateUserId
|
|
7124
|
+
};
|
|
7125
|
+
this.ws.send(JSON.stringify(message));
|
|
7126
|
+
});
|
|
7127
|
+
}
|
|
7128
|
+
/**
|
|
7129
|
+
* Send a message in a conversation
|
|
7130
|
+
*
|
|
7131
|
+
* @param conversationId - Conversation ID
|
|
7132
|
+
* @param content - Message content
|
|
7133
|
+
*/
|
|
7134
|
+
sendMessage(conversationId, content) {
|
|
7135
|
+
if (!this.isConnected()) {
|
|
7136
|
+
throw new Error("Not connected to AI chat");
|
|
7137
|
+
}
|
|
7138
|
+
this.accumulatedContent.set(conversationId, "");
|
|
7139
|
+
const message = {
|
|
7140
|
+
type: "message",
|
|
7141
|
+
conversation_id: conversationId,
|
|
7142
|
+
content
|
|
7143
|
+
};
|
|
7144
|
+
this.ws.send(JSON.stringify(message));
|
|
7145
|
+
}
|
|
7146
|
+
/**
|
|
7147
|
+
* Cancel an ongoing message generation
|
|
7148
|
+
*
|
|
7149
|
+
* @param conversationId - Conversation ID
|
|
7150
|
+
*/
|
|
7151
|
+
cancel(conversationId) {
|
|
7152
|
+
if (!this.isConnected()) {
|
|
7153
|
+
throw new Error("Not connected to AI chat");
|
|
7154
|
+
}
|
|
7155
|
+
const message = {
|
|
7156
|
+
type: "cancel",
|
|
7157
|
+
conversation_id: conversationId
|
|
7158
|
+
};
|
|
7159
|
+
this.ws.send(JSON.stringify(message));
|
|
7160
|
+
}
|
|
7161
|
+
/**
|
|
7162
|
+
* Get the full accumulated response content for a conversation
|
|
7163
|
+
*
|
|
7164
|
+
* @param conversationId - Conversation ID
|
|
7165
|
+
* @returns Accumulated content string
|
|
7166
|
+
*/
|
|
7167
|
+
getAccumulatedContent(conversationId) {
|
|
7168
|
+
return this.accumulatedContent.get(conversationId) || "";
|
|
7169
|
+
}
|
|
7170
|
+
buildWsUrl() {
|
|
7171
|
+
let url = this.options.wsUrl || "/ai/ws";
|
|
7172
|
+
if (this.options.token) {
|
|
7173
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
7174
|
+
url = `${url}${separator}token=${encodeURIComponent(this.options.token)}`;
|
|
7175
|
+
}
|
|
7176
|
+
return url;
|
|
7177
|
+
}
|
|
7178
|
+
handleMessage(data) {
|
|
7179
|
+
try {
|
|
7180
|
+
const message = JSON.parse(data);
|
|
7181
|
+
const event = this.serverMessageToEvent(message);
|
|
7182
|
+
switch (message.type) {
|
|
7183
|
+
case "chat_started":
|
|
7184
|
+
if (this.pendingStartResolve && message.conversation_id) {
|
|
7185
|
+
this.pendingStartResolve(message.conversation_id);
|
|
7186
|
+
this.pendingStartResolve = null;
|
|
7187
|
+
this.pendingStartReject = null;
|
|
7188
|
+
}
|
|
7189
|
+
break;
|
|
7190
|
+
case "content":
|
|
7191
|
+
if (message.conversation_id && message.delta) {
|
|
7192
|
+
const current = this.accumulatedContent.get(message.conversation_id) || "";
|
|
7193
|
+
this.accumulatedContent.set(message.conversation_id, current + message.delta);
|
|
7194
|
+
this.options.onContent?.(message.delta, message.conversation_id);
|
|
7195
|
+
}
|
|
7196
|
+
break;
|
|
7197
|
+
case "progress":
|
|
7198
|
+
if (message.step && message.message && message.conversation_id) {
|
|
7199
|
+
this.options.onProgress?.(message.step, message.message, message.conversation_id);
|
|
7200
|
+
}
|
|
7201
|
+
break;
|
|
7202
|
+
case "query_result":
|
|
7203
|
+
if (message.conversation_id) {
|
|
7204
|
+
this.options.onQueryResult?.(
|
|
7205
|
+
message.query || "",
|
|
7206
|
+
message.summary || "",
|
|
7207
|
+
message.row_count || 0,
|
|
7208
|
+
message.data || [],
|
|
7209
|
+
message.conversation_id
|
|
7210
|
+
);
|
|
7211
|
+
}
|
|
7212
|
+
break;
|
|
7213
|
+
case "done":
|
|
7214
|
+
if (message.conversation_id) {
|
|
7215
|
+
this.options.onDone?.(message.usage, message.conversation_id);
|
|
7216
|
+
}
|
|
7217
|
+
break;
|
|
7218
|
+
case "error":
|
|
7219
|
+
if (this.pendingStartReject) {
|
|
7220
|
+
this.pendingStartReject(new Error(message.error || "Unknown error"));
|
|
7221
|
+
this.pendingStartResolve = null;
|
|
7222
|
+
this.pendingStartReject = null;
|
|
7223
|
+
}
|
|
7224
|
+
this.options.onError?.(message.error || "Unknown error", message.code, message.conversation_id);
|
|
7225
|
+
break;
|
|
7226
|
+
}
|
|
7227
|
+
this.emitEvent(event);
|
|
7228
|
+
} catch (error) {
|
|
7229
|
+
console.error("Failed to parse AI chat message:", error);
|
|
7230
|
+
}
|
|
7231
|
+
}
|
|
7232
|
+
serverMessageToEvent(message) {
|
|
7233
|
+
return {
|
|
7234
|
+
type: message.type,
|
|
7235
|
+
conversationId: message.conversation_id,
|
|
7236
|
+
chatbot: message.chatbot,
|
|
7237
|
+
step: message.step,
|
|
7238
|
+
message: message.message,
|
|
7239
|
+
delta: message.delta,
|
|
7240
|
+
query: message.query,
|
|
7241
|
+
summary: message.summary,
|
|
7242
|
+
rowCount: message.row_count,
|
|
7243
|
+
data: message.data,
|
|
7244
|
+
usage: message.usage,
|
|
7245
|
+
error: message.error,
|
|
7246
|
+
code: message.code
|
|
7247
|
+
};
|
|
7248
|
+
}
|
|
7249
|
+
emitEvent(event) {
|
|
7250
|
+
this.options.onEvent?.(event);
|
|
7251
|
+
}
|
|
7252
|
+
handleClose(_event) {
|
|
7253
|
+
if (this.options.reconnectAttempts && this.reconnectCount < this.options.reconnectAttempts) {
|
|
7254
|
+
this.reconnectCount++;
|
|
7255
|
+
setTimeout(() => {
|
|
7256
|
+
this.connect().catch(() => {
|
|
7257
|
+
});
|
|
7258
|
+
}, this.options.reconnectDelay);
|
|
7259
|
+
}
|
|
7260
|
+
}
|
|
7261
|
+
};
|
|
7262
|
+
var FluxbaseAI = class {
|
|
7263
|
+
constructor(fetch2, wsBaseUrl) {
|
|
7264
|
+
this.fetch = fetch2;
|
|
7265
|
+
this.wsBaseUrl = wsBaseUrl;
|
|
7266
|
+
}
|
|
7267
|
+
/**
|
|
7268
|
+
* List available chatbots (public, enabled)
|
|
6024
7269
|
*
|
|
6025
|
-
* @
|
|
6026
|
-
|
|
6027
|
-
|
|
7270
|
+
* @returns Promise resolving to { data, error } tuple with array of chatbot summaries
|
|
7271
|
+
*/
|
|
7272
|
+
async listChatbots() {
|
|
7273
|
+
try {
|
|
7274
|
+
const response = await this.fetch.get(
|
|
7275
|
+
"/api/v1/ai/chatbots"
|
|
7276
|
+
);
|
|
7277
|
+
return { data: response.chatbots || [], error: null };
|
|
7278
|
+
} catch (error) {
|
|
7279
|
+
return { data: null, error };
|
|
7280
|
+
}
|
|
7281
|
+
}
|
|
7282
|
+
/**
|
|
7283
|
+
* Get details of a specific chatbot
|
|
6028
7284
|
*
|
|
6029
|
-
* @
|
|
6030
|
-
*
|
|
6031
|
-
|
|
6032
|
-
|
|
7285
|
+
* @param id - Chatbot ID
|
|
7286
|
+
* @returns Promise resolving to { data, error } tuple with chatbot details
|
|
7287
|
+
*/
|
|
7288
|
+
async getChatbot(id) {
|
|
7289
|
+
try {
|
|
7290
|
+
const data = await this.fetch.get(`/api/v1/ai/chatbots/${id}`);
|
|
7291
|
+
return { data, error: null };
|
|
7292
|
+
} catch (error) {
|
|
7293
|
+
return { data: null, error };
|
|
7294
|
+
}
|
|
7295
|
+
}
|
|
7296
|
+
/**
|
|
7297
|
+
* Create a new AI chat connection
|
|
6033
7298
|
*
|
|
6034
|
-
*
|
|
6035
|
-
*
|
|
6036
|
-
* console.log('User email:', dashboardUser.email);
|
|
6037
|
-
* console.log('Last login:', dashboardUser.last_login_at);
|
|
6038
|
-
* ```
|
|
7299
|
+
* @param options - Chat connection options
|
|
7300
|
+
* @returns FluxbaseAIChat instance
|
|
6039
7301
|
*/
|
|
6040
|
-
|
|
6041
|
-
return
|
|
6042
|
-
|
|
6043
|
-
|
|
7302
|
+
createChat(options) {
|
|
7303
|
+
return new FluxbaseAIChat({
|
|
7304
|
+
...options,
|
|
7305
|
+
wsUrl: `${this.wsBaseUrl}/ai/ws`
|
|
6044
7306
|
});
|
|
6045
7307
|
}
|
|
6046
7308
|
/**
|
|
6047
|
-
*
|
|
6048
|
-
*
|
|
6049
|
-
* Creates a new user and optionally sends an invitation email
|
|
7309
|
+
* List the authenticated user's conversations
|
|
6050
7310
|
*
|
|
6051
|
-
* @param
|
|
6052
|
-
* @
|
|
6053
|
-
* @returns Created user and invitation details
|
|
7311
|
+
* @param options - Optional filters and pagination
|
|
7312
|
+
* @returns Promise resolving to { data, error } tuple with conversations
|
|
6054
7313
|
*
|
|
6055
7314
|
* @example
|
|
6056
7315
|
* ```typescript
|
|
6057
|
-
*
|
|
6058
|
-
*
|
|
6059
|
-
* role: 'user',
|
|
6060
|
-
* send_email: true
|
|
6061
|
-
* });
|
|
7316
|
+
* // List all conversations
|
|
7317
|
+
* const { data, error } = await ai.listConversations()
|
|
6062
7318
|
*
|
|
6063
|
-
*
|
|
6064
|
-
*
|
|
7319
|
+
* // Filter by chatbot
|
|
7320
|
+
* const { data, error } = await ai.listConversations({ chatbot: 'sql-assistant' })
|
|
7321
|
+
*
|
|
7322
|
+
* // With pagination
|
|
7323
|
+
* const { data, error } = await ai.listConversations({ limit: 20, offset: 0 })
|
|
6065
7324
|
* ```
|
|
6066
7325
|
*/
|
|
6067
|
-
async
|
|
6068
|
-
|
|
6069
|
-
const
|
|
6070
|
-
|
|
6071
|
-
|
|
7326
|
+
async listConversations(options) {
|
|
7327
|
+
try {
|
|
7328
|
+
const params = new URLSearchParams();
|
|
7329
|
+
if (options?.chatbot) params.set("chatbot", options.chatbot);
|
|
7330
|
+
if (options?.namespace) params.set("namespace", options.namespace);
|
|
7331
|
+
if (options?.limit !== void 0) params.set("limit", String(options.limit));
|
|
7332
|
+
if (options?.offset !== void 0) params.set("offset", String(options.offset));
|
|
7333
|
+
const queryString = params.toString();
|
|
7334
|
+
const path = `/api/v1/ai/conversations${queryString ? `?${queryString}` : ""}`;
|
|
7335
|
+
const response = await this.fetch.get(path);
|
|
7336
|
+
return { data: response, error: null };
|
|
7337
|
+
} catch (error) {
|
|
7338
|
+
return { data: null, error };
|
|
7339
|
+
}
|
|
6072
7340
|
}
|
|
6073
7341
|
/**
|
|
6074
|
-
*
|
|
6075
|
-
*
|
|
6076
|
-
* Permanently deletes a user and all associated data
|
|
7342
|
+
* Get a single conversation with all messages
|
|
6077
7343
|
*
|
|
6078
|
-
* @param
|
|
6079
|
-
* @
|
|
6080
|
-
* @returns Deletion confirmation
|
|
7344
|
+
* @param id - Conversation ID
|
|
7345
|
+
* @returns Promise resolving to { data, error } tuple with conversation detail
|
|
6081
7346
|
*
|
|
6082
7347
|
* @example
|
|
6083
7348
|
* ```typescript
|
|
6084
|
-
* await
|
|
6085
|
-
*
|
|
7349
|
+
* const { data, error } = await ai.getConversation('conv-uuid-123')
|
|
7350
|
+
* if (data) {
|
|
7351
|
+
* console.log(`Title: ${data.title}`)
|
|
7352
|
+
* console.log(`Messages: ${data.messages.length}`)
|
|
7353
|
+
* }
|
|
6086
7354
|
* ```
|
|
6087
7355
|
*/
|
|
6088
|
-
async
|
|
6089
|
-
|
|
6090
|
-
const
|
|
6091
|
-
|
|
6092
|
-
|
|
7356
|
+
async getConversation(id) {
|
|
7357
|
+
try {
|
|
7358
|
+
const response = await this.fetch.get(
|
|
7359
|
+
`/api/v1/ai/conversations/${id}`
|
|
7360
|
+
);
|
|
7361
|
+
return { data: response, error: null };
|
|
7362
|
+
} catch (error) {
|
|
7363
|
+
return { data: null, error };
|
|
7364
|
+
}
|
|
6093
7365
|
}
|
|
6094
7366
|
/**
|
|
6095
|
-
*
|
|
6096
|
-
*
|
|
6097
|
-
* Changes a user's role
|
|
7367
|
+
* Delete a conversation
|
|
6098
7368
|
*
|
|
6099
|
-
* @param
|
|
6100
|
-
* @
|
|
6101
|
-
* @param type - User type ('app' or 'dashboard')
|
|
6102
|
-
* @returns Updated user
|
|
7369
|
+
* @param id - Conversation ID
|
|
7370
|
+
* @returns Promise resolving to { error } (null on success)
|
|
6103
7371
|
*
|
|
6104
7372
|
* @example
|
|
6105
7373
|
* ```typescript
|
|
6106
|
-
* const
|
|
6107
|
-
*
|
|
7374
|
+
* const { error } = await ai.deleteConversation('conv-uuid-123')
|
|
7375
|
+
* if (!error) {
|
|
7376
|
+
* console.log('Conversation deleted')
|
|
7377
|
+
* }
|
|
6108
7378
|
* ```
|
|
6109
7379
|
*/
|
|
6110
|
-
async
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
return
|
|
6114
|
-
})
|
|
7380
|
+
async deleteConversation(id) {
|
|
7381
|
+
try {
|
|
7382
|
+
await this.fetch.delete(`/api/v1/ai/conversations/${id}`);
|
|
7383
|
+
return { error: null };
|
|
7384
|
+
} catch (error) {
|
|
7385
|
+
return { error };
|
|
7386
|
+
}
|
|
6115
7387
|
}
|
|
6116
7388
|
/**
|
|
6117
|
-
*
|
|
7389
|
+
* Update a conversation (currently supports title update only)
|
|
6118
7390
|
*
|
|
6119
|
-
*
|
|
6120
|
-
*
|
|
6121
|
-
* @
|
|
6122
|
-
* @param type - User type ('app' or 'dashboard')
|
|
6123
|
-
* @returns Reset confirmation message
|
|
7391
|
+
* @param id - Conversation ID
|
|
7392
|
+
* @param updates - Fields to update
|
|
7393
|
+
* @returns Promise resolving to { data, error } tuple with updated conversation
|
|
6124
7394
|
*
|
|
6125
7395
|
* @example
|
|
6126
7396
|
* ```typescript
|
|
6127
|
-
* const
|
|
6128
|
-
*
|
|
7397
|
+
* const { data, error } = await ai.updateConversation('conv-uuid-123', {
|
|
7398
|
+
* title: 'My custom conversation title'
|
|
7399
|
+
* })
|
|
6129
7400
|
* ```
|
|
6130
7401
|
*/
|
|
6131
|
-
async
|
|
6132
|
-
|
|
6133
|
-
const
|
|
6134
|
-
|
|
6135
|
-
|
|
7402
|
+
async updateConversation(id, updates) {
|
|
7403
|
+
try {
|
|
7404
|
+
const response = await this.fetch.patch(
|
|
7405
|
+
`/api/v1/ai/conversations/${id}`,
|
|
7406
|
+
updates
|
|
7407
|
+
);
|
|
7408
|
+
return { data: response, error: null };
|
|
7409
|
+
} catch (error) {
|
|
7410
|
+
return { data: null, error };
|
|
7411
|
+
}
|
|
6136
7412
|
}
|
|
6137
7413
|
};
|
|
6138
7414
|
|
|
@@ -6143,6 +7419,7 @@ var QueryBuilder = class {
|
|
|
6143
7419
|
this.filters = [];
|
|
6144
7420
|
this.orFilters = [];
|
|
6145
7421
|
this.andFilters = [];
|
|
7422
|
+
this.betweenFilters = [];
|
|
6146
7423
|
this.orderBys = [];
|
|
6147
7424
|
this.singleRow = false;
|
|
6148
7425
|
this.maybeSingleRow = false;
|
|
@@ -6362,8 +7639,20 @@ var QueryBuilder = class {
|
|
|
6362
7639
|
* Generic filter method using PostgREST syntax (Supabase-compatible)
|
|
6363
7640
|
* @example filter('name', 'in', '("Han","Yoda")')
|
|
6364
7641
|
* @example filter('age', 'gte', '18')
|
|
7642
|
+
* @example filter('recorded_at', 'between', ['2024-01-01', '2024-01-10'])
|
|
7643
|
+
* @example filter('recorded_at', 'not.between', ['2024-01-01', '2024-01-10'])
|
|
6365
7644
|
*/
|
|
6366
7645
|
filter(column, operator, value) {
|
|
7646
|
+
if (operator === "between" || operator === "not.between") {
|
|
7647
|
+
const [min, max] = this.validateBetweenValue(value, operator);
|
|
7648
|
+
this.betweenFilters.push({
|
|
7649
|
+
column,
|
|
7650
|
+
min,
|
|
7651
|
+
max,
|
|
7652
|
+
negated: operator === "not.between"
|
|
7653
|
+
});
|
|
7654
|
+
return this;
|
|
7655
|
+
}
|
|
6367
7656
|
this.filters.push({ column, operator, value });
|
|
6368
7657
|
return this;
|
|
6369
7658
|
}
|
|
@@ -6384,6 +7673,33 @@ var QueryBuilder = class {
|
|
|
6384
7673
|
this.filters.push({ column, operator: "ov", value });
|
|
6385
7674
|
return this;
|
|
6386
7675
|
}
|
|
7676
|
+
/**
|
|
7677
|
+
* Filter column value within an inclusive range (BETWEEN)
|
|
7678
|
+
* Generates: AND (column >= min AND column <= max)
|
|
7679
|
+
*
|
|
7680
|
+
* @param column - Column name to filter
|
|
7681
|
+
* @param min - Minimum value (inclusive)
|
|
7682
|
+
* @param max - Maximum value (inclusive)
|
|
7683
|
+
* @example between('recorded_at', '2024-01-01', '2024-01-10')
|
|
7684
|
+
* @example between('price', 10, 100)
|
|
7685
|
+
*/
|
|
7686
|
+
between(column, min, max) {
|
|
7687
|
+
return this.filter(column, "between", [min, max]);
|
|
7688
|
+
}
|
|
7689
|
+
/**
|
|
7690
|
+
* Filter column value outside an inclusive range (NOT BETWEEN)
|
|
7691
|
+
* Generates: OR (column < min OR column > max)
|
|
7692
|
+
* Multiple notBetween calls on the same column AND together
|
|
7693
|
+
*
|
|
7694
|
+
* @param column - Column name to filter
|
|
7695
|
+
* @param min - Minimum value of excluded range
|
|
7696
|
+
* @param max - Maximum value of excluded range
|
|
7697
|
+
* @example notBetween('recorded_at', '2024-01-01', '2024-01-10')
|
|
7698
|
+
* @example notBetween('price', 0, 10) // Excludes items priced 0-10
|
|
7699
|
+
*/
|
|
7700
|
+
notBetween(column, min, max) {
|
|
7701
|
+
return this.filter(column, "not.between", [min, max]);
|
|
7702
|
+
}
|
|
6387
7703
|
// PostGIS Spatial Query Methods
|
|
6388
7704
|
/**
|
|
6389
7705
|
* Check if geometries intersect (PostGIS ST_Intersects)
|
|
@@ -6992,6 +8308,19 @@ var QueryBuilder = class {
|
|
|
6992
8308
|
for (const andFilter of this.andFilters) {
|
|
6993
8309
|
params.append("and", `(${andFilter})`);
|
|
6994
8310
|
}
|
|
8311
|
+
for (const bf of this.betweenFilters) {
|
|
8312
|
+
const minFormatted = this.formatValue(bf.min);
|
|
8313
|
+
const maxFormatted = this.formatValue(bf.max);
|
|
8314
|
+
if (bf.negated) {
|
|
8315
|
+
params.append(
|
|
8316
|
+
"or",
|
|
8317
|
+
`(${bf.column}.lt.${minFormatted},${bf.column}.gt.${maxFormatted})`
|
|
8318
|
+
);
|
|
8319
|
+
} else {
|
|
8320
|
+
params.append(bf.column, `gte.${minFormatted}`);
|
|
8321
|
+
params.append(bf.column, `lte.${maxFormatted}`);
|
|
8322
|
+
}
|
|
8323
|
+
}
|
|
6995
8324
|
if (this.groupByColumns && this.groupByColumns.length > 0) {
|
|
6996
8325
|
params.append("group_by", this.groupByColumns.join(","));
|
|
6997
8326
|
}
|
|
@@ -7031,6 +8360,34 @@ var QueryBuilder = class {
|
|
|
7031
8360
|
}
|
|
7032
8361
|
return String(value);
|
|
7033
8362
|
}
|
|
8363
|
+
/**
|
|
8364
|
+
* Validate between filter value - must be array of exactly 2 elements
|
|
8365
|
+
* @throws Error if value is invalid
|
|
8366
|
+
*/
|
|
8367
|
+
validateBetweenValue(value, operator) {
|
|
8368
|
+
if (!Array.isArray(value)) {
|
|
8369
|
+
throw new Error(
|
|
8370
|
+
`Invalid value for '${operator}' operator: expected array of [min, max], got ${typeof value}`
|
|
8371
|
+
);
|
|
8372
|
+
}
|
|
8373
|
+
if (value.length !== 2) {
|
|
8374
|
+
throw new Error(
|
|
8375
|
+
`Invalid value for '${operator}' operator: expected array with exactly 2 elements [min, max], got ${value.length} elements`
|
|
8376
|
+
);
|
|
8377
|
+
}
|
|
8378
|
+
const [min, max] = value;
|
|
8379
|
+
if (min === null || min === void 0) {
|
|
8380
|
+
throw new Error(
|
|
8381
|
+
`Invalid value for '${operator}' operator: min value cannot be null or undefined`
|
|
8382
|
+
);
|
|
8383
|
+
}
|
|
8384
|
+
if (max === null || max === void 0) {
|
|
8385
|
+
throw new Error(
|
|
8386
|
+
`Invalid value for '${operator}' operator: max value cannot be null or undefined`
|
|
8387
|
+
);
|
|
8388
|
+
}
|
|
8389
|
+
return [min, max];
|
|
8390
|
+
}
|
|
7034
8391
|
/**
|
|
7035
8392
|
* Parse the Content-Range header to extract the total count
|
|
7036
8393
|
* Header format: "0-999/50000" or "* /50000" (when no rows returned)
|
|
@@ -7115,6 +8472,9 @@ var FluxbaseClient = class {
|
|
|
7115
8472
|
this.admin = new FluxbaseAdmin(this.fetch);
|
|
7116
8473
|
this.management = new FluxbaseManagement(this.fetch);
|
|
7117
8474
|
this.settings = new SettingsClient(this.fetch);
|
|
8475
|
+
const wsProtocol = fluxbaseUrl.startsWith("https") ? "wss" : "ws";
|
|
8476
|
+
const wsBaseUrl = fluxbaseUrl.replace(/^https?:/, wsProtocol + ":");
|
|
8477
|
+
this.ai = new FluxbaseAI(this.fetch, wsBaseUrl);
|
|
7118
8478
|
this.setupAuthSync();
|
|
7119
8479
|
}
|
|
7120
8480
|
/**
|
|
@@ -7175,38 +8535,6 @@ var FluxbaseClient = class {
|
|
|
7175
8535
|
schema(schemaName) {
|
|
7176
8536
|
return new SchemaQueryBuilder(this.fetch, schemaName);
|
|
7177
8537
|
}
|
|
7178
|
-
/**
|
|
7179
|
-
* Call a PostgreSQL function (Remote Procedure Call)
|
|
7180
|
-
*
|
|
7181
|
-
* @param functionName - The name of the PostgreSQL function to call
|
|
7182
|
-
* @param params - Optional parameters to pass to the function
|
|
7183
|
-
* @returns Promise containing the function result or error
|
|
7184
|
-
*
|
|
7185
|
-
* @example
|
|
7186
|
-
* ```typescript
|
|
7187
|
-
* // Call a function without parameters
|
|
7188
|
-
* const { data, error } = await client.rpc('get_total_users')
|
|
7189
|
-
*
|
|
7190
|
-
* // Call a function with parameters
|
|
7191
|
-
* const { data, error } = await client.rpc('calculate_discount', {
|
|
7192
|
-
* product_id: 123,
|
|
7193
|
-
* coupon_code: 'SAVE20'
|
|
7194
|
-
* })
|
|
7195
|
-
* ```
|
|
7196
|
-
*
|
|
7197
|
-
* @category Database
|
|
7198
|
-
*/
|
|
7199
|
-
async rpc(functionName, params) {
|
|
7200
|
-
try {
|
|
7201
|
-
const data = await this.fetch.post(
|
|
7202
|
-
`/api/v1/rpc/${functionName}`,
|
|
7203
|
-
params || {}
|
|
7204
|
-
);
|
|
7205
|
-
return { data, error: null };
|
|
7206
|
-
} catch (error) {
|
|
7207
|
-
return { data: null, error };
|
|
7208
|
-
}
|
|
7209
|
-
}
|
|
7210
8538
|
/**
|
|
7211
8539
|
* Sync auth state with realtime connections
|
|
7212
8540
|
* @internal
|
|
@@ -7334,15 +8662,176 @@ function createClient(fluxbaseUrl, fluxbaseKey, options) {
|
|
|
7334
8662
|
return new FluxbaseClient(url, key, options);
|
|
7335
8663
|
}
|
|
7336
8664
|
|
|
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
|
+
|
|
7337
8822
|
exports.APIKeysManager = APIKeysManager;
|
|
7338
8823
|
exports.AppSettingsManager = AppSettingsManager;
|
|
7339
8824
|
exports.AuthSettingsManager = AuthSettingsManager;
|
|
7340
8825
|
exports.DDLManager = DDLManager;
|
|
7341
8826
|
exports.EmailTemplateManager = EmailTemplateManager;
|
|
8827
|
+
exports.FluxbaseAI = FluxbaseAI;
|
|
8828
|
+
exports.FluxbaseAIChat = FluxbaseAIChat;
|
|
7342
8829
|
exports.FluxbaseAdmin = FluxbaseAdmin;
|
|
8830
|
+
exports.FluxbaseAdminAI = FluxbaseAdminAI;
|
|
7343
8831
|
exports.FluxbaseAdminFunctions = FluxbaseAdminFunctions;
|
|
7344
8832
|
exports.FluxbaseAdminJobs = FluxbaseAdminJobs;
|
|
7345
8833
|
exports.FluxbaseAdminMigrations = FluxbaseAdminMigrations;
|
|
8834
|
+
exports.FluxbaseAdminRPC = FluxbaseAdminRPC;
|
|
7346
8835
|
exports.FluxbaseAuth = FluxbaseAuth;
|
|
7347
8836
|
exports.FluxbaseClient = FluxbaseClient;
|
|
7348
8837
|
exports.FluxbaseFetch = FluxbaseFetch;
|
|
@@ -7350,6 +8839,7 @@ exports.FluxbaseFunctions = FluxbaseFunctions;
|
|
|
7350
8839
|
exports.FluxbaseJobs = FluxbaseJobs;
|
|
7351
8840
|
exports.FluxbaseManagement = FluxbaseManagement;
|
|
7352
8841
|
exports.FluxbaseOAuth = FluxbaseOAuth;
|
|
8842
|
+
exports.FluxbaseRPC = FluxbaseRPC;
|
|
7353
8843
|
exports.FluxbaseRealtime = FluxbaseRealtime;
|
|
7354
8844
|
exports.FluxbaseSettings = FluxbaseSettings;
|
|
7355
8845
|
exports.FluxbaseStorage = FluxbaseStorage;
|
|
@@ -7363,6 +8853,9 @@ exports.SettingsClient = SettingsClient;
|
|
|
7363
8853
|
exports.StorageBucket = StorageBucket;
|
|
7364
8854
|
exports.SystemSettingsManager = SystemSettingsManager;
|
|
7365
8855
|
exports.WebhooksManager = WebhooksManager;
|
|
8856
|
+
exports.bundleCode = bundleCode;
|
|
7366
8857
|
exports.createClient = createClient;
|
|
8858
|
+
exports.denoExternalPlugin = denoExternalPlugin;
|
|
8859
|
+
exports.loadImportMap = loadImportMap;
|
|
7367
8860
|
//# sourceMappingURL=index.cjs.map
|
|
7368
8861
|
//# sourceMappingURL=index.cjs.map
|