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