@fluxbase/sdk 0.0.1-rc.2
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 +141 -0
- package/dist/index.cjs +3260 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3424 -0
- package/dist/index.d.ts +3424 -0
- package/dist/index.js +3237 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3260 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/fetch.ts
|
|
4
|
+
var FluxbaseFetch = class {
|
|
5
|
+
constructor(baseUrl, options = {}) {
|
|
6
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
7
|
+
this.defaultHeaders = {
|
|
8
|
+
"Content-Type": "application/json",
|
|
9
|
+
...options.headers
|
|
10
|
+
};
|
|
11
|
+
this.timeout = options.timeout ?? 3e4;
|
|
12
|
+
this.debug = options.debug ?? false;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Update the authorization header
|
|
16
|
+
*/
|
|
17
|
+
setAuthToken(token) {
|
|
18
|
+
if (token) {
|
|
19
|
+
this.defaultHeaders["Authorization"] = `Bearer ${token}`;
|
|
20
|
+
} else {
|
|
21
|
+
delete this.defaultHeaders["Authorization"];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Make an HTTP request
|
|
26
|
+
*/
|
|
27
|
+
async request(path, options) {
|
|
28
|
+
const url = `${this.baseUrl}${path}`;
|
|
29
|
+
const headers = { ...this.defaultHeaders, ...options.headers };
|
|
30
|
+
const controller = new AbortController();
|
|
31
|
+
const timeoutId = setTimeout(() => controller.abort(), options.timeout ?? this.timeout);
|
|
32
|
+
if (this.debug) {
|
|
33
|
+
console.log(`[Fluxbase SDK] ${options.method} ${url}`, options.body);
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch(url, {
|
|
37
|
+
method: options.method,
|
|
38
|
+
headers,
|
|
39
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
40
|
+
signal: controller.signal
|
|
41
|
+
});
|
|
42
|
+
clearTimeout(timeoutId);
|
|
43
|
+
const contentType = response.headers.get("content-type");
|
|
44
|
+
let data;
|
|
45
|
+
if (contentType?.includes("application/json")) {
|
|
46
|
+
data = await response.json();
|
|
47
|
+
} else {
|
|
48
|
+
data = await response.text();
|
|
49
|
+
}
|
|
50
|
+
if (this.debug) {
|
|
51
|
+
console.log(`[Fluxbase SDK] Response:`, response.status, data);
|
|
52
|
+
}
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const error = new Error(
|
|
55
|
+
typeof data === "object" && data && "error" in data ? String(data.error) : response.statusText
|
|
56
|
+
);
|
|
57
|
+
error.status = response.status;
|
|
58
|
+
error.details = data;
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
return data;
|
|
62
|
+
} catch (err) {
|
|
63
|
+
clearTimeout(timeoutId);
|
|
64
|
+
if (err instanceof Error) {
|
|
65
|
+
if (err.name === "AbortError") {
|
|
66
|
+
const timeoutError = new Error("Request timeout");
|
|
67
|
+
timeoutError.status = 408;
|
|
68
|
+
throw timeoutError;
|
|
69
|
+
}
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
throw new Error("Unknown error occurred");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* GET request
|
|
77
|
+
*/
|
|
78
|
+
async get(path, options = {}) {
|
|
79
|
+
return this.request(path, { ...options, method: "GET" });
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* POST request
|
|
83
|
+
*/
|
|
84
|
+
async post(path, body, options = {}) {
|
|
85
|
+
return this.request(path, { ...options, method: "POST", body });
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* PUT request
|
|
89
|
+
*/
|
|
90
|
+
async put(path, body, options = {}) {
|
|
91
|
+
return this.request(path, { ...options, method: "PUT", body });
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* PATCH request
|
|
95
|
+
*/
|
|
96
|
+
async patch(path, body, options = {}) {
|
|
97
|
+
return this.request(path, { ...options, method: "PATCH", body });
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* DELETE request
|
|
101
|
+
*/
|
|
102
|
+
async delete(path, options = {}) {
|
|
103
|
+
return this.request(path, { ...options, method: "DELETE" });
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* HEAD request
|
|
107
|
+
*/
|
|
108
|
+
async head(path, options = {}) {
|
|
109
|
+
const url = `${this.baseUrl}${path}`;
|
|
110
|
+
const headers = { ...this.defaultHeaders, ...options.headers };
|
|
111
|
+
const response = await fetch(url, {
|
|
112
|
+
method: "HEAD",
|
|
113
|
+
headers
|
|
114
|
+
});
|
|
115
|
+
return response.headers;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// src/auth.ts
|
|
120
|
+
var AUTH_STORAGE_KEY = "fluxbase.auth.session";
|
|
121
|
+
var FluxbaseAuth = class {
|
|
122
|
+
constructor(fetch2, autoRefresh = true, persist = true) {
|
|
123
|
+
this.session = null;
|
|
124
|
+
this.refreshTimer = null;
|
|
125
|
+
this.stateChangeListeners = /* @__PURE__ */ new Set();
|
|
126
|
+
this.fetch = fetch2;
|
|
127
|
+
this.persist = persist;
|
|
128
|
+
this.autoRefresh = autoRefresh;
|
|
129
|
+
if (this.persist && typeof localStorage !== "undefined") {
|
|
130
|
+
const stored = localStorage.getItem(AUTH_STORAGE_KEY);
|
|
131
|
+
if (stored) {
|
|
132
|
+
try {
|
|
133
|
+
this.session = JSON.parse(stored);
|
|
134
|
+
if (this.session) {
|
|
135
|
+
this.fetch.setAuthToken(this.session.access_token);
|
|
136
|
+
this.scheduleTokenRefresh();
|
|
137
|
+
}
|
|
138
|
+
} catch {
|
|
139
|
+
localStorage.removeItem(AUTH_STORAGE_KEY);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get the current session
|
|
146
|
+
*/
|
|
147
|
+
getSession() {
|
|
148
|
+
return this.session;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get the current user
|
|
152
|
+
*/
|
|
153
|
+
getUser() {
|
|
154
|
+
return this.session?.user ?? null;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get the current access token
|
|
158
|
+
*/
|
|
159
|
+
getAccessToken() {
|
|
160
|
+
return this.session?.access_token ?? null;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Listen to auth state changes
|
|
164
|
+
* @param callback - Function called when auth state changes
|
|
165
|
+
* @returns Subscription object with unsubscribe method
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* const { data: { subscription } } = client.auth.onAuthStateChange((event, session) => {
|
|
170
|
+
* console.log('Auth event:', event, session)
|
|
171
|
+
* })
|
|
172
|
+
*
|
|
173
|
+
* // Later, to unsubscribe:
|
|
174
|
+
* subscription.unsubscribe()
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
onAuthStateChange(callback) {
|
|
178
|
+
this.stateChangeListeners.add(callback);
|
|
179
|
+
return {
|
|
180
|
+
unsubscribe: () => {
|
|
181
|
+
this.stateChangeListeners.delete(callback);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Sign in with email and password
|
|
187
|
+
* Returns AuthSession if successful, or SignInWith2FAResponse if 2FA is required
|
|
188
|
+
*/
|
|
189
|
+
async signIn(credentials) {
|
|
190
|
+
const response = await this.fetch.post(
|
|
191
|
+
"/api/v1/auth/signin",
|
|
192
|
+
credentials
|
|
193
|
+
);
|
|
194
|
+
if ("requires_2fa" in response && response.requires_2fa) {
|
|
195
|
+
return response;
|
|
196
|
+
}
|
|
197
|
+
const authResponse = response;
|
|
198
|
+
const session = {
|
|
199
|
+
...authResponse,
|
|
200
|
+
expires_at: Date.now() + authResponse.expires_in * 1e3
|
|
201
|
+
};
|
|
202
|
+
this.setSession(session);
|
|
203
|
+
return session;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Sign up with email and password
|
|
207
|
+
*/
|
|
208
|
+
async signUp(credentials) {
|
|
209
|
+
const response = await this.fetch.post("/api/v1/auth/signup", credentials);
|
|
210
|
+
const session = {
|
|
211
|
+
...response,
|
|
212
|
+
expires_at: Date.now() + response.expires_in * 1e3
|
|
213
|
+
};
|
|
214
|
+
this.setSession(session);
|
|
215
|
+
return session;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Sign out the current user
|
|
219
|
+
*/
|
|
220
|
+
async signOut() {
|
|
221
|
+
try {
|
|
222
|
+
await this.fetch.post("/api/v1/auth/signout");
|
|
223
|
+
} finally {
|
|
224
|
+
this.clearSession();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Refresh the access token
|
|
229
|
+
*/
|
|
230
|
+
async refreshToken() {
|
|
231
|
+
if (!this.session?.refresh_token) {
|
|
232
|
+
throw new Error("No refresh token available");
|
|
233
|
+
}
|
|
234
|
+
const response = await this.fetch.post("/api/v1/auth/refresh", {
|
|
235
|
+
refresh_token: this.session.refresh_token
|
|
236
|
+
});
|
|
237
|
+
const session = {
|
|
238
|
+
...response,
|
|
239
|
+
expires_at: Date.now() + response.expires_in * 1e3
|
|
240
|
+
};
|
|
241
|
+
this.setSession(session, "TOKEN_REFRESHED");
|
|
242
|
+
return session;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get the current user from the server
|
|
246
|
+
*/
|
|
247
|
+
async getCurrentUser() {
|
|
248
|
+
if (!this.session) {
|
|
249
|
+
throw new Error("Not authenticated");
|
|
250
|
+
}
|
|
251
|
+
return await this.fetch.get("/api/v1/auth/user");
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Update the current user
|
|
255
|
+
*/
|
|
256
|
+
async updateUser(data) {
|
|
257
|
+
if (!this.session) {
|
|
258
|
+
throw new Error("Not authenticated");
|
|
259
|
+
}
|
|
260
|
+
const user = await this.fetch.patch("/api/v1/auth/user", data);
|
|
261
|
+
if (this.session) {
|
|
262
|
+
this.session.user = user;
|
|
263
|
+
this.saveSession();
|
|
264
|
+
this.emitAuthChange("USER_UPDATED", this.session);
|
|
265
|
+
}
|
|
266
|
+
return user;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Set the auth token manually
|
|
270
|
+
*/
|
|
271
|
+
setToken(token) {
|
|
272
|
+
this.fetch.setAuthToken(token);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Setup 2FA for the current user
|
|
276
|
+
* Returns TOTP secret and QR code URL
|
|
277
|
+
*/
|
|
278
|
+
async setup2FA() {
|
|
279
|
+
if (!this.session) {
|
|
280
|
+
throw new Error("Not authenticated");
|
|
281
|
+
}
|
|
282
|
+
return await this.fetch.post("/api/v1/auth/2fa/setup");
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Enable 2FA after verifying the TOTP code
|
|
286
|
+
* Returns backup codes that should be saved by the user
|
|
287
|
+
*/
|
|
288
|
+
async enable2FA(code) {
|
|
289
|
+
if (!this.session) {
|
|
290
|
+
throw new Error("Not authenticated");
|
|
291
|
+
}
|
|
292
|
+
return await this.fetch.post("/api/v1/auth/2fa/enable", { code });
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Disable 2FA for the current user
|
|
296
|
+
* Requires password confirmation
|
|
297
|
+
*/
|
|
298
|
+
async disable2FA(password) {
|
|
299
|
+
if (!this.session) {
|
|
300
|
+
throw new Error("Not authenticated");
|
|
301
|
+
}
|
|
302
|
+
return await this.fetch.post(
|
|
303
|
+
"/api/v1/auth/2fa/disable",
|
|
304
|
+
{ password }
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Check 2FA status for the current user
|
|
309
|
+
*/
|
|
310
|
+
async get2FAStatus() {
|
|
311
|
+
if (!this.session) {
|
|
312
|
+
throw new Error("Not authenticated");
|
|
313
|
+
}
|
|
314
|
+
return await this.fetch.get("/api/v1/auth/2fa/status");
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Verify 2FA code during login
|
|
318
|
+
* Call this after signIn returns requires_2fa: true
|
|
319
|
+
*/
|
|
320
|
+
async verify2FA(request) {
|
|
321
|
+
const response = await this.fetch.post("/api/v1/auth/2fa/verify", request);
|
|
322
|
+
const session = {
|
|
323
|
+
...response,
|
|
324
|
+
expires_at: Date.now() + response.expires_in * 1e3
|
|
325
|
+
};
|
|
326
|
+
this.setSession(session, "MFA_CHALLENGE_VERIFIED");
|
|
327
|
+
return session;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Send password reset email
|
|
331
|
+
* Sends a password reset link to the provided email address
|
|
332
|
+
* @param email - Email address to send reset link to
|
|
333
|
+
*/
|
|
334
|
+
async sendPasswordReset(email) {
|
|
335
|
+
return await this.fetch.post("/api/v1/auth/password/reset", { email });
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Verify password reset token
|
|
339
|
+
* Check if a password reset token is valid before allowing password reset
|
|
340
|
+
* @param token - Password reset token to verify
|
|
341
|
+
*/
|
|
342
|
+
async verifyResetToken(token) {
|
|
343
|
+
return await this.fetch.post("/api/v1/auth/password/reset/verify", {
|
|
344
|
+
token
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Reset password with token
|
|
349
|
+
* Complete the password reset process with a valid token
|
|
350
|
+
* @param token - Password reset token
|
|
351
|
+
* @param newPassword - New password to set
|
|
352
|
+
*/
|
|
353
|
+
async resetPassword(token, newPassword) {
|
|
354
|
+
return await this.fetch.post("/api/v1/auth/password/reset/confirm", {
|
|
355
|
+
token,
|
|
356
|
+
new_password: newPassword
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Send magic link for passwordless authentication
|
|
361
|
+
* @param email - Email address to send magic link to
|
|
362
|
+
* @param options - Optional configuration for magic link
|
|
363
|
+
*/
|
|
364
|
+
async sendMagicLink(email, options) {
|
|
365
|
+
return await this.fetch.post("/api/v1/auth/magiclink", {
|
|
366
|
+
email,
|
|
367
|
+
redirect_to: options?.redirect_to
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Verify magic link token and sign in
|
|
372
|
+
* @param token - Magic link token from email
|
|
373
|
+
*/
|
|
374
|
+
async verifyMagicLink(token) {
|
|
375
|
+
const response = await this.fetch.post("/api/v1/auth/magiclink/verify", {
|
|
376
|
+
token
|
|
377
|
+
});
|
|
378
|
+
const session = {
|
|
379
|
+
...response,
|
|
380
|
+
expires_at: Date.now() + response.expires_in * 1e3
|
|
381
|
+
};
|
|
382
|
+
this.setSession(session);
|
|
383
|
+
return session;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Sign in anonymously
|
|
387
|
+
* Creates a temporary anonymous user session
|
|
388
|
+
*/
|
|
389
|
+
async signInAnonymously() {
|
|
390
|
+
const response = await this.fetch.post("/api/v1/auth/signin/anonymous");
|
|
391
|
+
const session = {
|
|
392
|
+
...response,
|
|
393
|
+
expires_at: Date.now() + response.expires_in * 1e3
|
|
394
|
+
};
|
|
395
|
+
this.setSession(session);
|
|
396
|
+
return session;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Get list of enabled OAuth providers
|
|
400
|
+
*/
|
|
401
|
+
async getOAuthProviders() {
|
|
402
|
+
return await this.fetch.get("/api/v1/auth/oauth/providers");
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Get OAuth authorization URL for a provider
|
|
406
|
+
* @param provider - OAuth provider name (e.g., 'google', 'github')
|
|
407
|
+
* @param options - Optional OAuth configuration
|
|
408
|
+
*/
|
|
409
|
+
async getOAuthUrl(provider, options) {
|
|
410
|
+
const params = new URLSearchParams();
|
|
411
|
+
if (options?.redirect_to) {
|
|
412
|
+
params.append("redirect_to", options.redirect_to);
|
|
413
|
+
}
|
|
414
|
+
if (options?.scopes && options.scopes.length > 0) {
|
|
415
|
+
params.append("scopes", options.scopes.join(","));
|
|
416
|
+
}
|
|
417
|
+
const queryString = params.toString();
|
|
418
|
+
const url = queryString ? `/api/v1/auth/oauth/${provider}/authorize?${queryString}` : `/api/v1/auth/oauth/${provider}/authorize`;
|
|
419
|
+
const response = await this.fetch.get(url);
|
|
420
|
+
return response;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Exchange OAuth authorization code for session
|
|
424
|
+
* This is typically called in your OAuth callback handler
|
|
425
|
+
* @param code - Authorization code from OAuth callback
|
|
426
|
+
*/
|
|
427
|
+
async exchangeCodeForSession(code) {
|
|
428
|
+
const response = await this.fetch.post("/api/v1/auth/oauth/callback", { code });
|
|
429
|
+
const session = {
|
|
430
|
+
...response,
|
|
431
|
+
expires_at: Date.now() + response.expires_in * 1e3
|
|
432
|
+
};
|
|
433
|
+
this.setSession(session);
|
|
434
|
+
return session;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Convenience method to initiate OAuth sign-in
|
|
438
|
+
* Redirects the user to the OAuth provider's authorization page
|
|
439
|
+
* @param provider - OAuth provider name (e.g., 'google', 'github')
|
|
440
|
+
* @param options - Optional OAuth configuration
|
|
441
|
+
*/
|
|
442
|
+
async signInWithOAuth(provider, options) {
|
|
443
|
+
const { url } = await this.getOAuthUrl(provider, options);
|
|
444
|
+
if (typeof window !== "undefined") {
|
|
445
|
+
window.location.href = url;
|
|
446
|
+
} else {
|
|
447
|
+
throw new Error("signInWithOAuth can only be called in a browser environment");
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Internal: Set the session and persist it
|
|
452
|
+
*/
|
|
453
|
+
setSession(session, event = "SIGNED_IN") {
|
|
454
|
+
this.session = session;
|
|
455
|
+
this.fetch.setAuthToken(session.access_token);
|
|
456
|
+
this.saveSession();
|
|
457
|
+
this.scheduleTokenRefresh();
|
|
458
|
+
this.emitAuthChange(event, session);
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Internal: Clear the session
|
|
462
|
+
*/
|
|
463
|
+
clearSession() {
|
|
464
|
+
this.session = null;
|
|
465
|
+
this.fetch.setAuthToken(null);
|
|
466
|
+
if (this.persist && typeof localStorage !== "undefined") {
|
|
467
|
+
localStorage.removeItem(AUTH_STORAGE_KEY);
|
|
468
|
+
}
|
|
469
|
+
if (this.refreshTimer) {
|
|
470
|
+
clearTimeout(this.refreshTimer);
|
|
471
|
+
this.refreshTimer = null;
|
|
472
|
+
}
|
|
473
|
+
this.emitAuthChange("SIGNED_OUT", null);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Internal: Save session to storage
|
|
477
|
+
*/
|
|
478
|
+
saveSession() {
|
|
479
|
+
if (this.persist && typeof localStorage !== "undefined" && this.session) {
|
|
480
|
+
localStorage.setItem(AUTH_STORAGE_KEY, JSON.stringify(this.session));
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Internal: Schedule automatic token refresh
|
|
485
|
+
*/
|
|
486
|
+
scheduleTokenRefresh() {
|
|
487
|
+
if (!this.autoRefresh || !this.session?.expires_at) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (this.refreshTimer) {
|
|
491
|
+
clearTimeout(this.refreshTimer);
|
|
492
|
+
}
|
|
493
|
+
const refreshAt = this.session.expires_at - 60 * 1e3;
|
|
494
|
+
const delay = refreshAt - Date.now();
|
|
495
|
+
if (delay > 0) {
|
|
496
|
+
this.refreshTimer = setTimeout(() => {
|
|
497
|
+
this.refreshToken().catch((err) => {
|
|
498
|
+
console.error("Failed to refresh token:", err);
|
|
499
|
+
this.clearSession();
|
|
500
|
+
});
|
|
501
|
+
}, delay);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Internal: Emit auth state change event to all listeners
|
|
506
|
+
*/
|
|
507
|
+
emitAuthChange(event, session) {
|
|
508
|
+
this.stateChangeListeners.forEach((callback) => {
|
|
509
|
+
try {
|
|
510
|
+
callback(event, session);
|
|
511
|
+
} catch (error) {
|
|
512
|
+
console.error("Error in auth state change listener:", error);
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
// src/realtime.ts
|
|
519
|
+
var RealtimeChannel = class {
|
|
520
|
+
constructor(url, channelName, token = null) {
|
|
521
|
+
this.ws = null;
|
|
522
|
+
this.callbacks = /* @__PURE__ */ new Map();
|
|
523
|
+
this.subscriptionConfig = null;
|
|
524
|
+
this.reconnectAttempts = 0;
|
|
525
|
+
this.maxReconnectAttempts = 10;
|
|
526
|
+
this.reconnectDelay = 1e3;
|
|
527
|
+
this.heartbeatInterval = null;
|
|
528
|
+
this.url = url;
|
|
529
|
+
this.channelName = channelName;
|
|
530
|
+
this.token = token;
|
|
531
|
+
}
|
|
532
|
+
// Implementation
|
|
533
|
+
on(event, configOrCallback, callback) {
|
|
534
|
+
if (event === "postgres_changes" && typeof configOrCallback !== "function") {
|
|
535
|
+
const config = configOrCallback;
|
|
536
|
+
this.subscriptionConfig = config;
|
|
537
|
+
const actualCallback = callback;
|
|
538
|
+
const eventType = config.event;
|
|
539
|
+
if (!this.callbacks.has(eventType)) {
|
|
540
|
+
this.callbacks.set(eventType, /* @__PURE__ */ new Set());
|
|
541
|
+
}
|
|
542
|
+
this.callbacks.get(eventType).add(actualCallback);
|
|
543
|
+
} else {
|
|
544
|
+
const actualEvent = event;
|
|
545
|
+
const actualCallback = configOrCallback;
|
|
546
|
+
if (!this.callbacks.has(actualEvent)) {
|
|
547
|
+
this.callbacks.set(actualEvent, /* @__PURE__ */ new Set());
|
|
548
|
+
}
|
|
549
|
+
this.callbacks.get(actualEvent).add(actualCallback);
|
|
550
|
+
}
|
|
551
|
+
return this;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Remove a callback
|
|
555
|
+
*/
|
|
556
|
+
off(event, callback) {
|
|
557
|
+
const callbacks = this.callbacks.get(event);
|
|
558
|
+
if (callbacks) {
|
|
559
|
+
callbacks.delete(callback);
|
|
560
|
+
}
|
|
561
|
+
return this;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Subscribe to the channel
|
|
565
|
+
*/
|
|
566
|
+
subscribe() {
|
|
567
|
+
this.connect();
|
|
568
|
+
return this;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Unsubscribe from the channel
|
|
572
|
+
*/
|
|
573
|
+
unsubscribe() {
|
|
574
|
+
if (this.ws) {
|
|
575
|
+
this.send({
|
|
576
|
+
type: "unsubscribe",
|
|
577
|
+
channel: this.channelName
|
|
578
|
+
});
|
|
579
|
+
this.disconnect();
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Internal: Connect to WebSocket
|
|
584
|
+
*/
|
|
585
|
+
connect() {
|
|
586
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
const wsUrl = new URL(this.url);
|
|
590
|
+
wsUrl.protocol = wsUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
591
|
+
wsUrl.pathname = "/realtime";
|
|
592
|
+
if (this.token) {
|
|
593
|
+
wsUrl.searchParams.set("token", this.token);
|
|
594
|
+
}
|
|
595
|
+
this.ws = new WebSocket(wsUrl.toString());
|
|
596
|
+
this.ws.onopen = () => {
|
|
597
|
+
console.log("[Fluxbase Realtime] Connected");
|
|
598
|
+
this.reconnectAttempts = 0;
|
|
599
|
+
const subscribeMessage = {
|
|
600
|
+
type: "subscribe",
|
|
601
|
+
channel: this.channelName
|
|
602
|
+
};
|
|
603
|
+
if (this.subscriptionConfig) {
|
|
604
|
+
subscribeMessage.config = this.subscriptionConfig;
|
|
605
|
+
}
|
|
606
|
+
this.send(subscribeMessage);
|
|
607
|
+
this.startHeartbeat();
|
|
608
|
+
};
|
|
609
|
+
this.ws.onmessage = (event) => {
|
|
610
|
+
try {
|
|
611
|
+
const message = JSON.parse(event.data);
|
|
612
|
+
this.handleMessage(message);
|
|
613
|
+
} catch (err) {
|
|
614
|
+
console.error("[Fluxbase Realtime] Failed to parse message:", err);
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
this.ws.onerror = (error) => {
|
|
618
|
+
console.error("[Fluxbase Realtime] WebSocket error:", error);
|
|
619
|
+
};
|
|
620
|
+
this.ws.onclose = () => {
|
|
621
|
+
console.log("[Fluxbase Realtime] Disconnected");
|
|
622
|
+
this.stopHeartbeat();
|
|
623
|
+
this.attemptReconnect();
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Internal: Disconnect WebSocket
|
|
628
|
+
*/
|
|
629
|
+
disconnect() {
|
|
630
|
+
this.stopHeartbeat();
|
|
631
|
+
if (this.ws) {
|
|
632
|
+
this.ws.close();
|
|
633
|
+
this.ws = null;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Internal: Send a message
|
|
638
|
+
*/
|
|
639
|
+
send(message) {
|
|
640
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
641
|
+
this.ws.send(JSON.stringify(message));
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Internal: Handle incoming message
|
|
646
|
+
*/
|
|
647
|
+
handleMessage(message) {
|
|
648
|
+
switch (message.type) {
|
|
649
|
+
case "heartbeat":
|
|
650
|
+
this.send({ type: "heartbeat" });
|
|
651
|
+
break;
|
|
652
|
+
case "broadcast":
|
|
653
|
+
if (message.payload) {
|
|
654
|
+
this.handleBroadcast(message.payload);
|
|
655
|
+
}
|
|
656
|
+
break;
|
|
657
|
+
case "ack":
|
|
658
|
+
console.log("[Fluxbase Realtime] Subscription acknowledged");
|
|
659
|
+
break;
|
|
660
|
+
case "error":
|
|
661
|
+
console.error("[Fluxbase Realtime] Error:", message.error);
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Internal: Handle broadcast message
|
|
667
|
+
*/
|
|
668
|
+
handleBroadcast(payload) {
|
|
669
|
+
const callbacks = this.callbacks.get(payload.type);
|
|
670
|
+
if (callbacks) {
|
|
671
|
+
callbacks.forEach((callback) => callback(payload));
|
|
672
|
+
}
|
|
673
|
+
const wildcardCallbacks = this.callbacks.get("*");
|
|
674
|
+
if (wildcardCallbacks) {
|
|
675
|
+
wildcardCallbacks.forEach((callback) => callback(payload));
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Internal: Start heartbeat interval
|
|
680
|
+
*/
|
|
681
|
+
startHeartbeat() {
|
|
682
|
+
this.heartbeatInterval = setInterval(() => {
|
|
683
|
+
this.send({ type: "heartbeat" });
|
|
684
|
+
}, 3e4);
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Internal: Stop heartbeat interval
|
|
688
|
+
*/
|
|
689
|
+
stopHeartbeat() {
|
|
690
|
+
if (this.heartbeatInterval) {
|
|
691
|
+
clearInterval(this.heartbeatInterval);
|
|
692
|
+
this.heartbeatInterval = null;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Internal: Attempt to reconnect
|
|
697
|
+
*/
|
|
698
|
+
attemptReconnect() {
|
|
699
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
700
|
+
console.error("[Fluxbase Realtime] Max reconnect attempts reached");
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
this.reconnectAttempts++;
|
|
704
|
+
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
|
|
705
|
+
console.log(`[Fluxbase Realtime] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
|
706
|
+
setTimeout(() => {
|
|
707
|
+
this.connect();
|
|
708
|
+
}, delay);
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
var FluxbaseRealtime = class {
|
|
712
|
+
constructor(url, token = null) {
|
|
713
|
+
this.channels = /* @__PURE__ */ new Map();
|
|
714
|
+
this.url = url;
|
|
715
|
+
this.token = token;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Create or get a channel
|
|
719
|
+
* @param channelName - Channel name (e.g., 'table:public.products')
|
|
720
|
+
*/
|
|
721
|
+
channel(channelName) {
|
|
722
|
+
if (this.channels.has(channelName)) {
|
|
723
|
+
return this.channels.get(channelName);
|
|
724
|
+
}
|
|
725
|
+
const channel = new RealtimeChannel(this.url, channelName, this.token);
|
|
726
|
+
this.channels.set(channelName, channel);
|
|
727
|
+
return channel;
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Remove all channels
|
|
731
|
+
*/
|
|
732
|
+
removeAllChannels() {
|
|
733
|
+
this.channels.forEach((channel) => channel.unsubscribe());
|
|
734
|
+
this.channels.clear();
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Update auth token for all channels
|
|
738
|
+
*/
|
|
739
|
+
setToken(token) {
|
|
740
|
+
this.token = token;
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
// src/storage.ts
|
|
745
|
+
var StorageBucket = class {
|
|
746
|
+
constructor(fetch2, bucketName) {
|
|
747
|
+
this.fetch = fetch2;
|
|
748
|
+
this.bucketName = bucketName;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Upload a file to the bucket
|
|
752
|
+
* @param path - The path/key for the file
|
|
753
|
+
* @param file - The file to upload (File, Blob, or ArrayBuffer)
|
|
754
|
+
* @param options - Upload options
|
|
755
|
+
*/
|
|
756
|
+
async upload(path, file, options) {
|
|
757
|
+
try {
|
|
758
|
+
const formData = new FormData();
|
|
759
|
+
const blob = file instanceof ArrayBuffer ? new Blob([file]) : file;
|
|
760
|
+
formData.append("file", blob);
|
|
761
|
+
if (options?.contentType) {
|
|
762
|
+
formData.append("content_type", options.contentType);
|
|
763
|
+
}
|
|
764
|
+
if (options?.metadata) {
|
|
765
|
+
formData.append("metadata", JSON.stringify(options.metadata));
|
|
766
|
+
}
|
|
767
|
+
if (options?.cacheControl) {
|
|
768
|
+
formData.append("cache_control", options.cacheControl);
|
|
769
|
+
}
|
|
770
|
+
if (options?.upsert !== void 0) {
|
|
771
|
+
formData.append("upsert", String(options.upsert));
|
|
772
|
+
}
|
|
773
|
+
const data = await this.fetch.request(
|
|
774
|
+
`/api/v1/storage/${this.bucketName}/${path}`,
|
|
775
|
+
{
|
|
776
|
+
method: "POST",
|
|
777
|
+
body: formData,
|
|
778
|
+
headers: {}
|
|
779
|
+
// Let browser set Content-Type for FormData
|
|
780
|
+
}
|
|
781
|
+
);
|
|
782
|
+
return { data, error: null };
|
|
783
|
+
} catch (error) {
|
|
784
|
+
return { data: null, error };
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Download a file from the bucket
|
|
789
|
+
* @param path - The path/key of the file
|
|
790
|
+
*/
|
|
791
|
+
async download(path) {
|
|
792
|
+
try {
|
|
793
|
+
const response = await fetch(
|
|
794
|
+
`${this.fetch["baseUrl"]}/api/v1/storage/${this.bucketName}/${path}`,
|
|
795
|
+
{
|
|
796
|
+
headers: this.fetch["defaultHeaders"]
|
|
797
|
+
}
|
|
798
|
+
);
|
|
799
|
+
if (!response.ok) {
|
|
800
|
+
throw new Error(`Failed to download file: ${response.statusText}`);
|
|
801
|
+
}
|
|
802
|
+
const blob = await response.blob();
|
|
803
|
+
return { data: blob, error: null };
|
|
804
|
+
} catch (error) {
|
|
805
|
+
return { data: null, error };
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* List files in the bucket
|
|
810
|
+
* @param options - List options (prefix, limit, offset)
|
|
811
|
+
*/
|
|
812
|
+
async list(options) {
|
|
813
|
+
try {
|
|
814
|
+
const params = new URLSearchParams();
|
|
815
|
+
if (options?.prefix) {
|
|
816
|
+
params.set("prefix", options.prefix);
|
|
817
|
+
}
|
|
818
|
+
if (options?.limit) {
|
|
819
|
+
params.set("limit", String(options.limit));
|
|
820
|
+
}
|
|
821
|
+
if (options?.offset) {
|
|
822
|
+
params.set("offset", String(options.offset));
|
|
823
|
+
}
|
|
824
|
+
const queryString = params.toString();
|
|
825
|
+
const path = `/api/v1/storage/${this.bucketName}${queryString ? `?${queryString}` : ""}`;
|
|
826
|
+
const data = await this.fetch.get(path);
|
|
827
|
+
return { data: data.objects || [], error: null };
|
|
828
|
+
} catch (error) {
|
|
829
|
+
return { data: null, error };
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Remove files from the bucket
|
|
834
|
+
* @param paths - Array of file paths to remove
|
|
835
|
+
*/
|
|
836
|
+
async remove(paths) {
|
|
837
|
+
try {
|
|
838
|
+
for (const path of paths) {
|
|
839
|
+
await this.fetch.delete(`/api/v1/storage/${this.bucketName}/${path}`);
|
|
840
|
+
}
|
|
841
|
+
return { data: null, error: null };
|
|
842
|
+
} catch (error) {
|
|
843
|
+
return { data: null, error };
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Get a public URL for a file
|
|
848
|
+
* @param path - The file path
|
|
849
|
+
*/
|
|
850
|
+
getPublicUrl(path) {
|
|
851
|
+
const publicUrl = `${this.fetch["baseUrl"]}/api/v1/storage/${this.bucketName}/${path}`;
|
|
852
|
+
return { data: { publicUrl } };
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Create a signed URL for temporary access to a file
|
|
856
|
+
* @param path - The file path
|
|
857
|
+
* @param options - Signed URL options
|
|
858
|
+
*/
|
|
859
|
+
async createSignedUrl(path, options) {
|
|
860
|
+
try {
|
|
861
|
+
const expiresIn = options?.expiresIn || 3600;
|
|
862
|
+
const data = await this.fetch.post(
|
|
863
|
+
`/api/v1/storage/${this.bucketName}/sign/${path}`,
|
|
864
|
+
{ expires_in: expiresIn }
|
|
865
|
+
);
|
|
866
|
+
return { data: { signedUrl: data.signed_url }, error: null };
|
|
867
|
+
} catch (error) {
|
|
868
|
+
return { data: null, error };
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Move a file to a new location
|
|
873
|
+
* @param fromPath - Current file path
|
|
874
|
+
* @param toPath - New file path
|
|
875
|
+
*/
|
|
876
|
+
async move(fromPath, toPath) {
|
|
877
|
+
try {
|
|
878
|
+
const data = await this.fetch.post(
|
|
879
|
+
`/api/v1/storage/${this.bucketName}/move`,
|
|
880
|
+
{
|
|
881
|
+
from_path: fromPath,
|
|
882
|
+
to_path: toPath
|
|
883
|
+
}
|
|
884
|
+
);
|
|
885
|
+
return { data, error: null };
|
|
886
|
+
} catch (error) {
|
|
887
|
+
return { data: null, error };
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Copy a file to a new location
|
|
892
|
+
* @param fromPath - Source file path
|
|
893
|
+
* @param toPath - Destination file path
|
|
894
|
+
*/
|
|
895
|
+
async copy(fromPath, toPath) {
|
|
896
|
+
try {
|
|
897
|
+
const data = await this.fetch.post(
|
|
898
|
+
`/api/v1/storage/${this.bucketName}/copy`,
|
|
899
|
+
{
|
|
900
|
+
from_path: fromPath,
|
|
901
|
+
to_path: toPath
|
|
902
|
+
}
|
|
903
|
+
);
|
|
904
|
+
return { data, error: null };
|
|
905
|
+
} catch (error) {
|
|
906
|
+
return { data: null, error };
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
var FluxbaseStorage = class {
|
|
911
|
+
constructor(fetch2) {
|
|
912
|
+
this.fetch = fetch2;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Get a reference to a storage bucket
|
|
916
|
+
* @param bucketName - The name of the bucket
|
|
917
|
+
*/
|
|
918
|
+
from(bucketName) {
|
|
919
|
+
return new StorageBucket(this.fetch, bucketName);
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* List all buckets
|
|
923
|
+
*/
|
|
924
|
+
async listBuckets() {
|
|
925
|
+
try {
|
|
926
|
+
const data = await this.fetch.get(
|
|
927
|
+
"/api/v1/storage/buckets"
|
|
928
|
+
);
|
|
929
|
+
return { data: data.buckets || [], error: null };
|
|
930
|
+
} catch (error) {
|
|
931
|
+
return { data: null, error };
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Create a new bucket
|
|
936
|
+
* @param bucketName - The name of the bucket to create
|
|
937
|
+
*/
|
|
938
|
+
async createBucket(bucketName) {
|
|
939
|
+
try {
|
|
940
|
+
await this.fetch.post(`/api/v1/storage/buckets/${bucketName}`);
|
|
941
|
+
return { data: null, error: null };
|
|
942
|
+
} catch (error) {
|
|
943
|
+
return { data: null, error };
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Delete a bucket
|
|
948
|
+
* @param bucketName - The name of the bucket to delete
|
|
949
|
+
*/
|
|
950
|
+
async deleteBucket(bucketName) {
|
|
951
|
+
try {
|
|
952
|
+
await this.fetch.delete(`/api/v1/storage/buckets/${bucketName}`);
|
|
953
|
+
return { data: null, error: null };
|
|
954
|
+
} catch (error) {
|
|
955
|
+
return { data: null, error };
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Empty a bucket (delete all files)
|
|
960
|
+
* @param bucketName - The name of the bucket to empty
|
|
961
|
+
*/
|
|
962
|
+
async emptyBucket(bucketName) {
|
|
963
|
+
try {
|
|
964
|
+
const bucket = this.from(bucketName);
|
|
965
|
+
const { data: objects, error: listError } = await bucket.list();
|
|
966
|
+
if (listError) {
|
|
967
|
+
return { data: null, error: listError };
|
|
968
|
+
}
|
|
969
|
+
if (objects && objects.length > 0) {
|
|
970
|
+
const paths = objects.map((obj) => obj.key);
|
|
971
|
+
const { error: removeError } = await bucket.remove(paths);
|
|
972
|
+
if (removeError) {
|
|
973
|
+
return { data: null, error: removeError };
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return { data: null, error: null };
|
|
977
|
+
} catch (error) {
|
|
978
|
+
return { data: null, error };
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
// src/settings.ts
|
|
984
|
+
var SystemSettingsManager = class {
|
|
985
|
+
constructor(fetch2) {
|
|
986
|
+
this.fetch = fetch2;
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* List all system settings
|
|
990
|
+
*
|
|
991
|
+
* @returns Promise resolving to ListSystemSettingsResponse
|
|
992
|
+
*
|
|
993
|
+
* @example
|
|
994
|
+
* ```typescript
|
|
995
|
+
* const response = await client.admin.settings.system.list()
|
|
996
|
+
* console.log(response.settings)
|
|
997
|
+
* ```
|
|
998
|
+
*/
|
|
999
|
+
async list() {
|
|
1000
|
+
const settings = await this.fetch.get("/api/v1/admin/system/settings");
|
|
1001
|
+
return { settings: Array.isArray(settings) ? settings : [] };
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Get a specific system setting by key
|
|
1005
|
+
*
|
|
1006
|
+
* @param key - Setting key (e.g., 'app.auth.enable_signup')
|
|
1007
|
+
* @returns Promise resolving to SystemSetting
|
|
1008
|
+
*
|
|
1009
|
+
* @example
|
|
1010
|
+
* ```typescript
|
|
1011
|
+
* const setting = await client.admin.settings.system.get('app.auth.enable_signup')
|
|
1012
|
+
* console.log(setting.value)
|
|
1013
|
+
* ```
|
|
1014
|
+
*/
|
|
1015
|
+
async get(key) {
|
|
1016
|
+
return await this.fetch.get(`/api/v1/admin/system/settings/${key}`);
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Update or create a system setting
|
|
1020
|
+
*
|
|
1021
|
+
* @param key - Setting key
|
|
1022
|
+
* @param request - Update request with value and optional description
|
|
1023
|
+
* @returns Promise resolving to SystemSetting
|
|
1024
|
+
*
|
|
1025
|
+
* @example
|
|
1026
|
+
* ```typescript
|
|
1027
|
+
* const updated = await client.admin.settings.system.update('app.auth.enable_signup', {
|
|
1028
|
+
* value: { value: true },
|
|
1029
|
+
* description: 'Enable user signup'
|
|
1030
|
+
* })
|
|
1031
|
+
* ```
|
|
1032
|
+
*/
|
|
1033
|
+
async update(key, request) {
|
|
1034
|
+
return await this.fetch.put(`/api/v1/admin/system/settings/${key}`, request);
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Delete a system setting
|
|
1038
|
+
*
|
|
1039
|
+
* @param key - Setting key to delete
|
|
1040
|
+
* @returns Promise<void>
|
|
1041
|
+
*
|
|
1042
|
+
* @example
|
|
1043
|
+
* ```typescript
|
|
1044
|
+
* await client.admin.settings.system.delete('app.auth.enable_signup')
|
|
1045
|
+
* ```
|
|
1046
|
+
*/
|
|
1047
|
+
async delete(key) {
|
|
1048
|
+
await this.fetch.delete(`/api/v1/admin/system/settings/${key}`);
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
var AppSettingsManager = class {
|
|
1052
|
+
constructor(fetch2) {
|
|
1053
|
+
this.fetch = fetch2;
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Get all application settings
|
|
1057
|
+
*
|
|
1058
|
+
* Returns structured settings for authentication, features, email, and security.
|
|
1059
|
+
*
|
|
1060
|
+
* @returns Promise resolving to AppSettings
|
|
1061
|
+
*
|
|
1062
|
+
* @example
|
|
1063
|
+
* ```typescript
|
|
1064
|
+
* const settings = await client.admin.settings.app.get()
|
|
1065
|
+
*
|
|
1066
|
+
* console.log('Signup enabled:', settings.authentication.enable_signup)
|
|
1067
|
+
* console.log('Realtime enabled:', settings.features.enable_realtime)
|
|
1068
|
+
* console.log('Email provider:', settings.email.provider)
|
|
1069
|
+
* ```
|
|
1070
|
+
*/
|
|
1071
|
+
async get() {
|
|
1072
|
+
return await this.fetch.get("/api/v1/admin/app/settings");
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Update application settings
|
|
1076
|
+
*
|
|
1077
|
+
* Supports partial updates - only provide the fields you want to change.
|
|
1078
|
+
*
|
|
1079
|
+
* @param request - Settings to update (partial update supported)
|
|
1080
|
+
* @returns Promise resolving to AppSettings - Updated settings
|
|
1081
|
+
*
|
|
1082
|
+
* @example
|
|
1083
|
+
* ```typescript
|
|
1084
|
+
* // Update authentication settings
|
|
1085
|
+
* const updated = await client.admin.settings.app.update({
|
|
1086
|
+
* authentication: {
|
|
1087
|
+
* enable_signup: true,
|
|
1088
|
+
* password_min_length: 12
|
|
1089
|
+
* }
|
|
1090
|
+
* })
|
|
1091
|
+
*
|
|
1092
|
+
* // Update multiple categories
|
|
1093
|
+
* await client.admin.settings.app.update({
|
|
1094
|
+
* authentication: { enable_signup: false },
|
|
1095
|
+
* features: { enable_realtime: true },
|
|
1096
|
+
* security: { enable_global_rate_limit: true }
|
|
1097
|
+
* })
|
|
1098
|
+
* ```
|
|
1099
|
+
*/
|
|
1100
|
+
async update(request) {
|
|
1101
|
+
return await this.fetch.put("/api/v1/admin/app/settings", request);
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Reset all application settings to defaults
|
|
1105
|
+
*
|
|
1106
|
+
* This will delete all custom settings and return to default values.
|
|
1107
|
+
*
|
|
1108
|
+
* @returns Promise resolving to AppSettings - Default settings
|
|
1109
|
+
*
|
|
1110
|
+
* @example
|
|
1111
|
+
* ```typescript
|
|
1112
|
+
* const defaults = await client.admin.settings.app.reset()
|
|
1113
|
+
* console.log('Settings reset to defaults:', defaults)
|
|
1114
|
+
* ```
|
|
1115
|
+
*/
|
|
1116
|
+
async reset() {
|
|
1117
|
+
return await this.fetch.post("/api/v1/admin/app/settings/reset", {});
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Enable user signup
|
|
1121
|
+
*
|
|
1122
|
+
* Convenience method to enable user registration.
|
|
1123
|
+
*
|
|
1124
|
+
* @returns Promise resolving to AppSettings
|
|
1125
|
+
*
|
|
1126
|
+
* @example
|
|
1127
|
+
* ```typescript
|
|
1128
|
+
* await client.admin.settings.app.enableSignup()
|
|
1129
|
+
* ```
|
|
1130
|
+
*/
|
|
1131
|
+
async enableSignup() {
|
|
1132
|
+
return await this.update({
|
|
1133
|
+
authentication: { enable_signup: true }
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Disable user signup
|
|
1138
|
+
*
|
|
1139
|
+
* Convenience method to disable user registration.
|
|
1140
|
+
*
|
|
1141
|
+
* @returns Promise resolving to AppSettings
|
|
1142
|
+
*
|
|
1143
|
+
* @example
|
|
1144
|
+
* ```typescript
|
|
1145
|
+
* await client.admin.settings.app.disableSignup()
|
|
1146
|
+
* ```
|
|
1147
|
+
*/
|
|
1148
|
+
async disableSignup() {
|
|
1149
|
+
return await this.update({
|
|
1150
|
+
authentication: { enable_signup: false }
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Update password minimum length
|
|
1155
|
+
*
|
|
1156
|
+
* Convenience method to set password requirements.
|
|
1157
|
+
*
|
|
1158
|
+
* @param length - Minimum password length (8-128 characters)
|
|
1159
|
+
* @returns Promise resolving to AppSettings
|
|
1160
|
+
*
|
|
1161
|
+
* @example
|
|
1162
|
+
* ```typescript
|
|
1163
|
+
* await client.admin.settings.app.setPasswordMinLength(12)
|
|
1164
|
+
* ```
|
|
1165
|
+
*/
|
|
1166
|
+
async setPasswordMinLength(length) {
|
|
1167
|
+
if (length < 8 || length > 128) {
|
|
1168
|
+
throw new Error("Password minimum length must be between 8 and 128 characters");
|
|
1169
|
+
}
|
|
1170
|
+
return await this.update({
|
|
1171
|
+
authentication: { password_min_length: length }
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Enable or disable a feature
|
|
1176
|
+
*
|
|
1177
|
+
* Convenience method to toggle feature flags.
|
|
1178
|
+
*
|
|
1179
|
+
* @param feature - Feature name ('realtime' | 'storage' | 'functions')
|
|
1180
|
+
* @param enabled - Whether to enable or disable the feature
|
|
1181
|
+
* @returns Promise resolving to AppSettings
|
|
1182
|
+
*
|
|
1183
|
+
* @example
|
|
1184
|
+
* ```typescript
|
|
1185
|
+
* // Enable realtime
|
|
1186
|
+
* await client.admin.settings.app.setFeature('realtime', true)
|
|
1187
|
+
*
|
|
1188
|
+
* // Disable storage
|
|
1189
|
+
* await client.admin.settings.app.setFeature('storage', false)
|
|
1190
|
+
* ```
|
|
1191
|
+
*/
|
|
1192
|
+
async setFeature(feature, enabled) {
|
|
1193
|
+
const featureKey = feature === "realtime" ? "enable_realtime" : feature === "storage" ? "enable_storage" : "enable_functions";
|
|
1194
|
+
return await this.update({
|
|
1195
|
+
features: { [featureKey]: enabled }
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* Enable or disable global rate limiting
|
|
1200
|
+
*
|
|
1201
|
+
* Convenience method to toggle global rate limiting.
|
|
1202
|
+
*
|
|
1203
|
+
* @param enabled - Whether to enable rate limiting
|
|
1204
|
+
* @returns Promise resolving to AppSettings
|
|
1205
|
+
*
|
|
1206
|
+
* @example
|
|
1207
|
+
* ```typescript
|
|
1208
|
+
* await client.admin.settings.app.setRateLimiting(true)
|
|
1209
|
+
* ```
|
|
1210
|
+
*/
|
|
1211
|
+
async setRateLimiting(enabled) {
|
|
1212
|
+
return await this.update({
|
|
1213
|
+
security: { enable_global_rate_limit: enabled }
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
var FluxbaseSettings = class {
|
|
1218
|
+
constructor(fetch2) {
|
|
1219
|
+
this.system = new SystemSettingsManager(fetch2);
|
|
1220
|
+
this.app = new AppSettingsManager(fetch2);
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
|
|
1224
|
+
// src/ddl.ts
|
|
1225
|
+
var DDLManager = class {
|
|
1226
|
+
constructor(fetch2) {
|
|
1227
|
+
this.fetch = fetch2;
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Create a new database schema
|
|
1231
|
+
*
|
|
1232
|
+
* Creates a new schema in the database. Schemas are used to organize tables
|
|
1233
|
+
* into logical groups and provide namespace isolation.
|
|
1234
|
+
*
|
|
1235
|
+
* @param name - Schema name (must be valid PostgreSQL identifier)
|
|
1236
|
+
* @returns Promise resolving to CreateSchemaResponse
|
|
1237
|
+
*
|
|
1238
|
+
* @example
|
|
1239
|
+
* ```typescript
|
|
1240
|
+
* // Create a schema for analytics data
|
|
1241
|
+
* const result = await client.admin.ddl.createSchema('analytics')
|
|
1242
|
+
* console.log(result.message) // "Schema created successfully"
|
|
1243
|
+
* console.log(result.schema) // "analytics"
|
|
1244
|
+
* ```
|
|
1245
|
+
*/
|
|
1246
|
+
async createSchema(name) {
|
|
1247
|
+
const request = { name };
|
|
1248
|
+
return await this.fetch.post("/api/v1/admin/ddl/schemas", request);
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* List all database schemas
|
|
1252
|
+
*
|
|
1253
|
+
* Retrieves a list of all schemas in the database. This includes both
|
|
1254
|
+
* system schemas (like 'public', 'pg_catalog') and user-created schemas.
|
|
1255
|
+
*
|
|
1256
|
+
* @returns Promise resolving to ListSchemasResponse
|
|
1257
|
+
*
|
|
1258
|
+
* @example
|
|
1259
|
+
* ```typescript
|
|
1260
|
+
* const { schemas } = await client.admin.ddl.listSchemas()
|
|
1261
|
+
*
|
|
1262
|
+
* schemas.forEach(schema => {
|
|
1263
|
+
* console.log(`Schema: ${schema.name}, Owner: ${schema.owner}`)
|
|
1264
|
+
* })
|
|
1265
|
+
* ```
|
|
1266
|
+
*/
|
|
1267
|
+
async listSchemas() {
|
|
1268
|
+
return await this.fetch.get("/api/v1/admin/ddl/schemas");
|
|
1269
|
+
}
|
|
1270
|
+
/**
|
|
1271
|
+
* Create a new table in a schema
|
|
1272
|
+
*
|
|
1273
|
+
* Creates a new table with the specified columns. Supports various column
|
|
1274
|
+
* options including primary keys, nullability, data types, and default values.
|
|
1275
|
+
*
|
|
1276
|
+
* @param schema - Schema name where the table will be created
|
|
1277
|
+
* @param name - Table name (must be valid PostgreSQL identifier)
|
|
1278
|
+
* @param columns - Array of column definitions
|
|
1279
|
+
* @returns Promise resolving to CreateTableResponse
|
|
1280
|
+
*
|
|
1281
|
+
* @example
|
|
1282
|
+
* ```typescript
|
|
1283
|
+
* // Create a users table
|
|
1284
|
+
* await client.admin.ddl.createTable('public', 'users', [
|
|
1285
|
+
* {
|
|
1286
|
+
* name: 'id',
|
|
1287
|
+
* type: 'UUID',
|
|
1288
|
+
* primaryKey: true,
|
|
1289
|
+
* defaultValue: 'gen_random_uuid()'
|
|
1290
|
+
* },
|
|
1291
|
+
* {
|
|
1292
|
+
* name: 'email',
|
|
1293
|
+
* type: 'TEXT',
|
|
1294
|
+
* nullable: false
|
|
1295
|
+
* },
|
|
1296
|
+
* {
|
|
1297
|
+
* name: 'name',
|
|
1298
|
+
* type: 'TEXT'
|
|
1299
|
+
* },
|
|
1300
|
+
* {
|
|
1301
|
+
* name: 'created_at',
|
|
1302
|
+
* type: 'TIMESTAMPTZ',
|
|
1303
|
+
* nullable: false,
|
|
1304
|
+
* defaultValue: 'NOW()'
|
|
1305
|
+
* }
|
|
1306
|
+
* ])
|
|
1307
|
+
* ```
|
|
1308
|
+
*
|
|
1309
|
+
* @example
|
|
1310
|
+
* ```typescript
|
|
1311
|
+
* // Create a products table with JSONB metadata
|
|
1312
|
+
* await client.admin.ddl.createTable('public', 'products', [
|
|
1313
|
+
* { name: 'id', type: 'SERIAL', primaryKey: true },
|
|
1314
|
+
* { name: 'name', type: 'TEXT', nullable: false },
|
|
1315
|
+
* { name: 'price', type: 'DECIMAL(10,2)', nullable: false },
|
|
1316
|
+
* { name: 'metadata', type: 'JSONB' },
|
|
1317
|
+
* { name: 'in_stock', type: 'BOOLEAN', defaultValue: 'true' }
|
|
1318
|
+
* ])
|
|
1319
|
+
* ```
|
|
1320
|
+
*/
|
|
1321
|
+
async createTable(schema, name, columns) {
|
|
1322
|
+
const request = { schema, name, columns };
|
|
1323
|
+
return await this.fetch.post("/api/v1/admin/ddl/tables", request);
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* List all tables in the database or a specific schema
|
|
1327
|
+
*
|
|
1328
|
+
* Retrieves a list of all tables. If a schema is specified, only tables
|
|
1329
|
+
* from that schema are returned. Otherwise, all tables from all schemas
|
|
1330
|
+
* are returned.
|
|
1331
|
+
*
|
|
1332
|
+
* @param schema - Optional schema name to filter tables
|
|
1333
|
+
* @returns Promise resolving to ListTablesResponse
|
|
1334
|
+
*
|
|
1335
|
+
* @example
|
|
1336
|
+
* ```typescript
|
|
1337
|
+
* // List all tables in the public schema
|
|
1338
|
+
* const { tables } = await client.admin.ddl.listTables('public')
|
|
1339
|
+
*
|
|
1340
|
+
* tables.forEach(table => {
|
|
1341
|
+
* console.log(`Table: ${table.schema}.${table.name}`)
|
|
1342
|
+
* table.columns?.forEach(col => {
|
|
1343
|
+
* console.log(` - ${col.name}: ${col.type}`)
|
|
1344
|
+
* })
|
|
1345
|
+
* })
|
|
1346
|
+
* ```
|
|
1347
|
+
*
|
|
1348
|
+
* @example
|
|
1349
|
+
* ```typescript
|
|
1350
|
+
* // List all tables across all schemas
|
|
1351
|
+
* const { tables } = await client.admin.ddl.listTables()
|
|
1352
|
+
*
|
|
1353
|
+
* const tablesBySchema = tables.reduce((acc, table) => {
|
|
1354
|
+
* if (!acc[table.schema]) acc[table.schema] = []
|
|
1355
|
+
* acc[table.schema].push(table.name)
|
|
1356
|
+
* return acc
|
|
1357
|
+
* }, {} as Record<string, string[]>)
|
|
1358
|
+
*
|
|
1359
|
+
* console.log(tablesBySchema)
|
|
1360
|
+
* ```
|
|
1361
|
+
*/
|
|
1362
|
+
async listTables(schema) {
|
|
1363
|
+
const params = schema ? `?schema=${encodeURIComponent(schema)}` : "";
|
|
1364
|
+
return await this.fetch.get(`/api/v1/admin/ddl/tables${params}`);
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Delete a table from a schema
|
|
1368
|
+
*
|
|
1369
|
+
* Permanently deletes a table and all its data. This operation cannot be undone.
|
|
1370
|
+
*
|
|
1371
|
+
* @param schema - Schema name containing the table
|
|
1372
|
+
* @param name - Table name to delete
|
|
1373
|
+
* @returns Promise resolving to DeleteTableResponse
|
|
1374
|
+
*
|
|
1375
|
+
* @example
|
|
1376
|
+
* ```typescript
|
|
1377
|
+
* // Delete a table
|
|
1378
|
+
* const result = await client.admin.ddl.deleteTable('public', 'old_data')
|
|
1379
|
+
* console.log(result.message) // "Table deleted successfully"
|
|
1380
|
+
* ```
|
|
1381
|
+
*
|
|
1382
|
+
* @example
|
|
1383
|
+
* ```typescript
|
|
1384
|
+
* // Safe deletion with confirmation
|
|
1385
|
+
* const confirm = await askUser('Are you sure you want to delete this table?')
|
|
1386
|
+
* if (confirm) {
|
|
1387
|
+
* await client.admin.ddl.deleteTable('analytics', 'events')
|
|
1388
|
+
* console.log('Table deleted')
|
|
1389
|
+
* }
|
|
1390
|
+
* ```
|
|
1391
|
+
*/
|
|
1392
|
+
async deleteTable(schema, name) {
|
|
1393
|
+
return await this.fetch.delete(
|
|
1394
|
+
`/api/v1/admin/ddl/tables/${encodeURIComponent(schema)}/${encodeURIComponent(name)}`
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1399
|
+
// src/oauth.ts
|
|
1400
|
+
var OAuthProviderManager = class {
|
|
1401
|
+
constructor(fetch2) {
|
|
1402
|
+
this.fetch = fetch2;
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* List all OAuth providers
|
|
1406
|
+
*
|
|
1407
|
+
* Retrieves all configured OAuth providers including both enabled and disabled providers.
|
|
1408
|
+
* Note: Client secrets are not included in the response for security reasons.
|
|
1409
|
+
*
|
|
1410
|
+
* @returns Promise resolving to ListOAuthProvidersResponse
|
|
1411
|
+
*
|
|
1412
|
+
* @example
|
|
1413
|
+
* ```typescript
|
|
1414
|
+
* const { providers } = await client.admin.oauth.listProviders()
|
|
1415
|
+
*
|
|
1416
|
+
* providers.forEach(provider => {
|
|
1417
|
+
* console.log(`${provider.display_name}: ${provider.enabled ? 'enabled' : 'disabled'}`)
|
|
1418
|
+
* })
|
|
1419
|
+
* ```
|
|
1420
|
+
*/
|
|
1421
|
+
async listProviders() {
|
|
1422
|
+
const providers = await this.fetch.get("/api/v1/admin/oauth/providers");
|
|
1423
|
+
return Array.isArray(providers) ? providers : [];
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Get a specific OAuth provider by ID
|
|
1427
|
+
*
|
|
1428
|
+
* Retrieves detailed configuration for a single OAuth provider.
|
|
1429
|
+
* Note: Client secret is not included in the response.
|
|
1430
|
+
*
|
|
1431
|
+
* @param providerId - Provider ID (UUID)
|
|
1432
|
+
* @returns Promise resolving to OAuthProvider
|
|
1433
|
+
*
|
|
1434
|
+
* @example
|
|
1435
|
+
* ```typescript
|
|
1436
|
+
* const provider = await client.admin.oauth.getProvider('provider-uuid')
|
|
1437
|
+
*
|
|
1438
|
+
* console.log('Provider:', provider.display_name)
|
|
1439
|
+
* console.log('Scopes:', provider.scopes.join(', '))
|
|
1440
|
+
* console.log('Redirect URL:', provider.redirect_url)
|
|
1441
|
+
* ```
|
|
1442
|
+
*/
|
|
1443
|
+
async getProvider(providerId) {
|
|
1444
|
+
return await this.fetch.get(`/api/v1/admin/oauth/providers/${providerId}`);
|
|
1445
|
+
}
|
|
1446
|
+
/**
|
|
1447
|
+
* Create a new OAuth provider
|
|
1448
|
+
*
|
|
1449
|
+
* Creates a new OAuth provider configuration. For built-in providers (Google, GitHub, etc.),
|
|
1450
|
+
* set `is_custom` to false. For custom OAuth2 providers, set `is_custom` to true and provide
|
|
1451
|
+
* the authorization, token, and user info URLs.
|
|
1452
|
+
*
|
|
1453
|
+
* @param request - OAuth provider configuration
|
|
1454
|
+
* @returns Promise resolving to CreateOAuthProviderResponse
|
|
1455
|
+
*
|
|
1456
|
+
* @example
|
|
1457
|
+
* ```typescript
|
|
1458
|
+
* // Create GitHub provider
|
|
1459
|
+
* const result = await client.admin.oauth.createProvider({
|
|
1460
|
+
* provider_name: 'github',
|
|
1461
|
+
* display_name: 'GitHub',
|
|
1462
|
+
* enabled: true,
|
|
1463
|
+
* client_id: process.env.GITHUB_CLIENT_ID,
|
|
1464
|
+
* client_secret: process.env.GITHUB_CLIENT_SECRET,
|
|
1465
|
+
* redirect_url: 'https://yourapp.com/auth/callback',
|
|
1466
|
+
* scopes: ['user:email', 'read:user'],
|
|
1467
|
+
* is_custom: false
|
|
1468
|
+
* })
|
|
1469
|
+
*
|
|
1470
|
+
* console.log('Provider created:', result.id)
|
|
1471
|
+
* ```
|
|
1472
|
+
*
|
|
1473
|
+
* @example
|
|
1474
|
+
* ```typescript
|
|
1475
|
+
* // Create custom OAuth2 provider
|
|
1476
|
+
* await client.admin.oauth.createProvider({
|
|
1477
|
+
* provider_name: 'custom_sso',
|
|
1478
|
+
* display_name: 'Custom SSO',
|
|
1479
|
+
* enabled: true,
|
|
1480
|
+
* client_id: 'client-id',
|
|
1481
|
+
* client_secret: 'client-secret',
|
|
1482
|
+
* redirect_url: 'https://yourapp.com/auth/callback',
|
|
1483
|
+
* scopes: ['openid', 'profile', 'email'],
|
|
1484
|
+
* is_custom: true,
|
|
1485
|
+
* authorization_url: 'https://sso.example.com/oauth/authorize',
|
|
1486
|
+
* token_url: 'https://sso.example.com/oauth/token',
|
|
1487
|
+
* user_info_url: 'https://sso.example.com/oauth/userinfo'
|
|
1488
|
+
* })
|
|
1489
|
+
* ```
|
|
1490
|
+
*/
|
|
1491
|
+
async createProvider(request) {
|
|
1492
|
+
return await this.fetch.post("/api/v1/admin/oauth/providers", request);
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Update an existing OAuth provider
|
|
1496
|
+
*
|
|
1497
|
+
* Updates an OAuth provider configuration. All fields are optional - only provided fields
|
|
1498
|
+
* will be updated. To update the client secret, provide a non-empty value.
|
|
1499
|
+
*
|
|
1500
|
+
* @param providerId - Provider ID (UUID)
|
|
1501
|
+
* @param request - Fields to update
|
|
1502
|
+
* @returns Promise resolving to UpdateOAuthProviderResponse
|
|
1503
|
+
*
|
|
1504
|
+
* @example
|
|
1505
|
+
* ```typescript
|
|
1506
|
+
* // Disable a provider
|
|
1507
|
+
* await client.admin.oauth.updateProvider('provider-id', {
|
|
1508
|
+
* enabled: false
|
|
1509
|
+
* })
|
|
1510
|
+
* ```
|
|
1511
|
+
*
|
|
1512
|
+
* @example
|
|
1513
|
+
* ```typescript
|
|
1514
|
+
* // Update scopes and redirect URL
|
|
1515
|
+
* await client.admin.oauth.updateProvider('provider-id', {
|
|
1516
|
+
* scopes: ['user:email', 'read:user', 'read:org'],
|
|
1517
|
+
* redirect_url: 'https://newdomain.com/auth/callback'
|
|
1518
|
+
* })
|
|
1519
|
+
* ```
|
|
1520
|
+
*
|
|
1521
|
+
* @example
|
|
1522
|
+
* ```typescript
|
|
1523
|
+
* // Rotate client secret
|
|
1524
|
+
* await client.admin.oauth.updateProvider('provider-id', {
|
|
1525
|
+
* client_id: 'new-client-id',
|
|
1526
|
+
* client_secret: 'new-client-secret'
|
|
1527
|
+
* })
|
|
1528
|
+
* ```
|
|
1529
|
+
*/
|
|
1530
|
+
async updateProvider(providerId, request) {
|
|
1531
|
+
return await this.fetch.put(
|
|
1532
|
+
`/api/v1/admin/oauth/providers/${providerId}`,
|
|
1533
|
+
request
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Delete an OAuth provider
|
|
1538
|
+
*
|
|
1539
|
+
* Permanently deletes an OAuth provider configuration. This will prevent users from
|
|
1540
|
+
* authenticating with this provider.
|
|
1541
|
+
*
|
|
1542
|
+
* @param providerId - Provider ID (UUID) to delete
|
|
1543
|
+
* @returns Promise resolving to DeleteOAuthProviderResponse
|
|
1544
|
+
*
|
|
1545
|
+
* @example
|
|
1546
|
+
* ```typescript
|
|
1547
|
+
* await client.admin.oauth.deleteProvider('provider-id')
|
|
1548
|
+
* console.log('Provider deleted')
|
|
1549
|
+
* ```
|
|
1550
|
+
*
|
|
1551
|
+
* @example
|
|
1552
|
+
* ```typescript
|
|
1553
|
+
* // Safe deletion with confirmation
|
|
1554
|
+
* const provider = await client.admin.oauth.getProvider('provider-id')
|
|
1555
|
+
* const confirmed = await confirm(`Delete ${provider.display_name}?`)
|
|
1556
|
+
*
|
|
1557
|
+
* if (confirmed) {
|
|
1558
|
+
* await client.admin.oauth.deleteProvider('provider-id')
|
|
1559
|
+
* }
|
|
1560
|
+
* ```
|
|
1561
|
+
*/
|
|
1562
|
+
async deleteProvider(providerId) {
|
|
1563
|
+
return await this.fetch.delete(
|
|
1564
|
+
`/api/v1/admin/oauth/providers/${providerId}`
|
|
1565
|
+
);
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Enable an OAuth provider
|
|
1569
|
+
*
|
|
1570
|
+
* Convenience method to enable a provider.
|
|
1571
|
+
*
|
|
1572
|
+
* @param providerId - Provider ID (UUID)
|
|
1573
|
+
* @returns Promise resolving to UpdateOAuthProviderResponse
|
|
1574
|
+
*
|
|
1575
|
+
* @example
|
|
1576
|
+
* ```typescript
|
|
1577
|
+
* await client.admin.oauth.enableProvider('provider-id')
|
|
1578
|
+
* ```
|
|
1579
|
+
*/
|
|
1580
|
+
async enableProvider(providerId) {
|
|
1581
|
+
return await this.updateProvider(providerId, { enabled: true });
|
|
1582
|
+
}
|
|
1583
|
+
/**
|
|
1584
|
+
* Disable an OAuth provider
|
|
1585
|
+
*
|
|
1586
|
+
* Convenience method to disable a provider.
|
|
1587
|
+
*
|
|
1588
|
+
* @param providerId - Provider ID (UUID)
|
|
1589
|
+
* @returns Promise resolving to UpdateOAuthProviderResponse
|
|
1590
|
+
*
|
|
1591
|
+
* @example
|
|
1592
|
+
* ```typescript
|
|
1593
|
+
* await client.admin.oauth.disableProvider('provider-id')
|
|
1594
|
+
* ```
|
|
1595
|
+
*/
|
|
1596
|
+
async disableProvider(providerId) {
|
|
1597
|
+
return await this.updateProvider(providerId, { enabled: false });
|
|
1598
|
+
}
|
|
1599
|
+
};
|
|
1600
|
+
var AuthSettingsManager = class {
|
|
1601
|
+
constructor(fetch2) {
|
|
1602
|
+
this.fetch = fetch2;
|
|
1603
|
+
}
|
|
1604
|
+
/**
|
|
1605
|
+
* Get current authentication settings
|
|
1606
|
+
*
|
|
1607
|
+
* Retrieves all authentication configuration settings.
|
|
1608
|
+
*
|
|
1609
|
+
* @returns Promise resolving to AuthSettings
|
|
1610
|
+
*
|
|
1611
|
+
* @example
|
|
1612
|
+
* ```typescript
|
|
1613
|
+
* const settings = await client.admin.authSettings.get()
|
|
1614
|
+
*
|
|
1615
|
+
* console.log('Password min length:', settings.password_min_length)
|
|
1616
|
+
* console.log('Signup enabled:', settings.enable_signup)
|
|
1617
|
+
* console.log('Session timeout:', settings.session_timeout_minutes, 'minutes')
|
|
1618
|
+
* ```
|
|
1619
|
+
*/
|
|
1620
|
+
async get() {
|
|
1621
|
+
return await this.fetch.get("/api/v1/admin/auth/settings");
|
|
1622
|
+
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Update authentication settings
|
|
1625
|
+
*
|
|
1626
|
+
* Updates one or more authentication settings. All fields are optional - only provided
|
|
1627
|
+
* fields will be updated.
|
|
1628
|
+
*
|
|
1629
|
+
* @param request - Settings to update
|
|
1630
|
+
* @returns Promise resolving to UpdateAuthSettingsResponse
|
|
1631
|
+
*
|
|
1632
|
+
* @example
|
|
1633
|
+
* ```typescript
|
|
1634
|
+
* // Strengthen password requirements
|
|
1635
|
+
* await client.admin.authSettings.update({
|
|
1636
|
+
* password_min_length: 16,
|
|
1637
|
+
* password_require_uppercase: true,
|
|
1638
|
+
* password_require_lowercase: true,
|
|
1639
|
+
* password_require_number: true,
|
|
1640
|
+
* password_require_special: true
|
|
1641
|
+
* })
|
|
1642
|
+
* ```
|
|
1643
|
+
*
|
|
1644
|
+
* @example
|
|
1645
|
+
* ```typescript
|
|
1646
|
+
* // Extend session timeout
|
|
1647
|
+
* await client.admin.authSettings.update({
|
|
1648
|
+
* session_timeout_minutes: 240,
|
|
1649
|
+
* max_sessions_per_user: 10
|
|
1650
|
+
* })
|
|
1651
|
+
* ```
|
|
1652
|
+
*
|
|
1653
|
+
* @example
|
|
1654
|
+
* ```typescript
|
|
1655
|
+
* // Disable email verification during development
|
|
1656
|
+
* await client.admin.authSettings.update({
|
|
1657
|
+
* require_email_verification: false
|
|
1658
|
+
* })
|
|
1659
|
+
* ```
|
|
1660
|
+
*/
|
|
1661
|
+
async update(request) {
|
|
1662
|
+
return await this.fetch.put("/api/v1/admin/auth/settings", request);
|
|
1663
|
+
}
|
|
1664
|
+
};
|
|
1665
|
+
var FluxbaseOAuth = class {
|
|
1666
|
+
constructor(fetch2) {
|
|
1667
|
+
this.providers = new OAuthProviderManager(fetch2);
|
|
1668
|
+
this.authSettings = new AuthSettingsManager(fetch2);
|
|
1669
|
+
}
|
|
1670
|
+
};
|
|
1671
|
+
|
|
1672
|
+
// src/impersonation.ts
|
|
1673
|
+
var ImpersonationManager = class {
|
|
1674
|
+
constructor(fetch2) {
|
|
1675
|
+
this.fetch = fetch2;
|
|
1676
|
+
}
|
|
1677
|
+
/**
|
|
1678
|
+
* Impersonate a specific user
|
|
1679
|
+
*
|
|
1680
|
+
* Start an impersonation session as a specific user. This allows you to see data
|
|
1681
|
+
* exactly as that user would see it, respecting all RLS policies and permissions.
|
|
1682
|
+
*
|
|
1683
|
+
* @param request - Impersonation request with target user ID and reason
|
|
1684
|
+
* @returns Promise resolving to impersonation session with access token
|
|
1685
|
+
*
|
|
1686
|
+
* @example
|
|
1687
|
+
* ```typescript
|
|
1688
|
+
* const result = await client.admin.impersonation.impersonateUser({
|
|
1689
|
+
* target_user_id: 'user-123',
|
|
1690
|
+
* reason: 'Support ticket #5678 - user reports missing data'
|
|
1691
|
+
* })
|
|
1692
|
+
*
|
|
1693
|
+
* console.log('Impersonating:', result.target_user.email)
|
|
1694
|
+
* console.log('Session ID:', result.session.id)
|
|
1695
|
+
*
|
|
1696
|
+
* // Use the access token for subsequent requests
|
|
1697
|
+
* // (typically handled automatically by the SDK)
|
|
1698
|
+
* ```
|
|
1699
|
+
*/
|
|
1700
|
+
async impersonateUser(request) {
|
|
1701
|
+
return await this.fetch.post("/api/v1/auth/impersonate", request);
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* Impersonate anonymous user
|
|
1705
|
+
*
|
|
1706
|
+
* Start an impersonation session as an unauthenticated user. This allows you to see
|
|
1707
|
+
* what data is publicly accessible and test RLS policies for anonymous access.
|
|
1708
|
+
*
|
|
1709
|
+
* @param request - Impersonation request with reason
|
|
1710
|
+
* @returns Promise resolving to impersonation session with access token
|
|
1711
|
+
*
|
|
1712
|
+
* @example
|
|
1713
|
+
* ```typescript
|
|
1714
|
+
* await client.admin.impersonation.impersonateAnon({
|
|
1715
|
+
* reason: 'Testing public data access for blog posts'
|
|
1716
|
+
* })
|
|
1717
|
+
*
|
|
1718
|
+
* // Now all queries will use anonymous permissions
|
|
1719
|
+
* const publicPosts = await client.from('posts').select('*')
|
|
1720
|
+
* console.log('Public posts:', publicPosts.length)
|
|
1721
|
+
* ```
|
|
1722
|
+
*/
|
|
1723
|
+
async impersonateAnon(request) {
|
|
1724
|
+
return await this.fetch.post("/api/v1/auth/impersonate/anon", request);
|
|
1725
|
+
}
|
|
1726
|
+
/**
|
|
1727
|
+
* Impersonate with service role
|
|
1728
|
+
*
|
|
1729
|
+
* Start an impersonation session with service-level permissions. This provides elevated
|
|
1730
|
+
* access that may bypass RLS policies, useful for administrative operations.
|
|
1731
|
+
*
|
|
1732
|
+
* @param request - Impersonation request with reason
|
|
1733
|
+
* @returns Promise resolving to impersonation session with access token
|
|
1734
|
+
*
|
|
1735
|
+
* @example
|
|
1736
|
+
* ```typescript
|
|
1737
|
+
* await client.admin.impersonation.impersonateService({
|
|
1738
|
+
* reason: 'Administrative data cleanup'
|
|
1739
|
+
* })
|
|
1740
|
+
*
|
|
1741
|
+
* // Now all queries will use service role permissions
|
|
1742
|
+
* const allRecords = await client.from('sensitive_data').select('*')
|
|
1743
|
+
* console.log('All records:', allRecords.length)
|
|
1744
|
+
* ```
|
|
1745
|
+
*/
|
|
1746
|
+
async impersonateService(request) {
|
|
1747
|
+
return await this.fetch.post("/api/v1/auth/impersonate/service", request);
|
|
1748
|
+
}
|
|
1749
|
+
/**
|
|
1750
|
+
* Stop impersonation
|
|
1751
|
+
*
|
|
1752
|
+
* Ends the current impersonation session and returns to admin context.
|
|
1753
|
+
* The session is marked as ended in the audit trail.
|
|
1754
|
+
*
|
|
1755
|
+
* @returns Promise resolving to stop confirmation
|
|
1756
|
+
*
|
|
1757
|
+
* @example
|
|
1758
|
+
* ```typescript
|
|
1759
|
+
* await client.admin.impersonation.stop()
|
|
1760
|
+
* console.log('Impersonation ended')
|
|
1761
|
+
*
|
|
1762
|
+
* // Subsequent queries will use admin permissions
|
|
1763
|
+
* ```
|
|
1764
|
+
*/
|
|
1765
|
+
async stop() {
|
|
1766
|
+
return await this.fetch.delete("/api/v1/auth/impersonate");
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Get current impersonation session
|
|
1770
|
+
*
|
|
1771
|
+
* Retrieves information about the active impersonation session, if any.
|
|
1772
|
+
*
|
|
1773
|
+
* @returns Promise resolving to current impersonation session or null
|
|
1774
|
+
*
|
|
1775
|
+
* @example
|
|
1776
|
+
* ```typescript
|
|
1777
|
+
* const current = await client.admin.impersonation.getCurrent()
|
|
1778
|
+
*
|
|
1779
|
+
* if (current.session) {
|
|
1780
|
+
* console.log('Currently impersonating:', current.target_user?.email)
|
|
1781
|
+
* console.log('Reason:', current.session.reason)
|
|
1782
|
+
* console.log('Started:', current.session.started_at)
|
|
1783
|
+
* } else {
|
|
1784
|
+
* console.log('No active impersonation')
|
|
1785
|
+
* }
|
|
1786
|
+
* ```
|
|
1787
|
+
*/
|
|
1788
|
+
async getCurrent() {
|
|
1789
|
+
return await this.fetch.get("/api/v1/auth/impersonate");
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* List impersonation sessions (audit trail)
|
|
1793
|
+
*
|
|
1794
|
+
* Retrieves a list of impersonation sessions for audit and compliance purposes.
|
|
1795
|
+
* Can be filtered by admin user, target user, type, and active status.
|
|
1796
|
+
*
|
|
1797
|
+
* @param options - Filter and pagination options
|
|
1798
|
+
* @returns Promise resolving to list of impersonation sessions
|
|
1799
|
+
*
|
|
1800
|
+
* @example
|
|
1801
|
+
* ```typescript
|
|
1802
|
+
* // List all sessions
|
|
1803
|
+
* const { sessions, total } = await client.admin.impersonation.listSessions()
|
|
1804
|
+
* console.log(`Total sessions: ${total}`)
|
|
1805
|
+
*
|
|
1806
|
+
* // List active sessions only
|
|
1807
|
+
* const active = await client.admin.impersonation.listSessions({
|
|
1808
|
+
* is_active: true
|
|
1809
|
+
* })
|
|
1810
|
+
* console.log('Active sessions:', active.sessions.length)
|
|
1811
|
+
*
|
|
1812
|
+
* // List sessions for a specific admin
|
|
1813
|
+
* const adminSessions = await client.admin.impersonation.listSessions({
|
|
1814
|
+
* admin_user_id: 'admin-uuid',
|
|
1815
|
+
* limit: 50
|
|
1816
|
+
* })
|
|
1817
|
+
*
|
|
1818
|
+
* // List user impersonation sessions only
|
|
1819
|
+
* const userSessions = await client.admin.impersonation.listSessions({
|
|
1820
|
+
* impersonation_type: 'user',
|
|
1821
|
+
* offset: 0,
|
|
1822
|
+
* limit: 100
|
|
1823
|
+
* })
|
|
1824
|
+
* ```
|
|
1825
|
+
*
|
|
1826
|
+
* @example
|
|
1827
|
+
* ```typescript
|
|
1828
|
+
* // Audit trail: Find who impersonated a specific user
|
|
1829
|
+
* const userHistory = await client.admin.impersonation.listSessions({
|
|
1830
|
+
* target_user_id: 'user-uuid'
|
|
1831
|
+
* })
|
|
1832
|
+
*
|
|
1833
|
+
* userHistory.sessions.forEach(session => {
|
|
1834
|
+
* console.log(`Admin ${session.admin_user_id} impersonated user`)
|
|
1835
|
+
* console.log(`Reason: ${session.reason}`)
|
|
1836
|
+
* console.log(`Duration: ${session.started_at} - ${session.ended_at}`)
|
|
1837
|
+
* })
|
|
1838
|
+
* ```
|
|
1839
|
+
*/
|
|
1840
|
+
async listSessions(options = {}) {
|
|
1841
|
+
const params = new URLSearchParams();
|
|
1842
|
+
if (options.limit !== void 0) {
|
|
1843
|
+
params.append("limit", String(options.limit));
|
|
1844
|
+
}
|
|
1845
|
+
if (options.offset !== void 0) {
|
|
1846
|
+
params.append("offset", String(options.offset));
|
|
1847
|
+
}
|
|
1848
|
+
if (options.admin_user_id) {
|
|
1849
|
+
params.append("admin_user_id", options.admin_user_id);
|
|
1850
|
+
}
|
|
1851
|
+
if (options.target_user_id) {
|
|
1852
|
+
params.append("target_user_id", options.target_user_id);
|
|
1853
|
+
}
|
|
1854
|
+
if (options.impersonation_type) {
|
|
1855
|
+
params.append("impersonation_type", options.impersonation_type);
|
|
1856
|
+
}
|
|
1857
|
+
if (options.is_active !== void 0) {
|
|
1858
|
+
params.append("is_active", String(options.is_active));
|
|
1859
|
+
}
|
|
1860
|
+
const queryString = params.toString();
|
|
1861
|
+
const url = queryString ? `/api/v1/auth/impersonate/sessions?${queryString}` : "/api/v1/auth/impersonate/sessions";
|
|
1862
|
+
return await this.fetch.get(url);
|
|
1863
|
+
}
|
|
1864
|
+
};
|
|
1865
|
+
|
|
1866
|
+
// src/management.ts
|
|
1867
|
+
var APIKeysManager = class {
|
|
1868
|
+
constructor(fetch2) {
|
|
1869
|
+
this.fetch = fetch2;
|
|
1870
|
+
}
|
|
1871
|
+
/**
|
|
1872
|
+
* Create a new API key
|
|
1873
|
+
*
|
|
1874
|
+
* @param request - API key configuration
|
|
1875
|
+
* @returns Created API key with the full key value (only shown once)
|
|
1876
|
+
*
|
|
1877
|
+
* @example
|
|
1878
|
+
* ```typescript
|
|
1879
|
+
* const { api_key, key } = await client.management.apiKeys.create({
|
|
1880
|
+
* name: 'Production Service',
|
|
1881
|
+
* description: 'API key for production service',
|
|
1882
|
+
* scopes: ['read:users', 'write:users'],
|
|
1883
|
+
* rate_limit_per_minute: 100,
|
|
1884
|
+
* expires_at: '2025-12-31T23:59:59Z'
|
|
1885
|
+
* })
|
|
1886
|
+
*
|
|
1887
|
+
* // Store the key securely - it won't be shown again
|
|
1888
|
+
* console.log('API Key:', key)
|
|
1889
|
+
* ```
|
|
1890
|
+
*/
|
|
1891
|
+
async create(request) {
|
|
1892
|
+
return await this.fetch.post("/api/v1/api-keys", request);
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* List all API keys for the authenticated user
|
|
1896
|
+
*
|
|
1897
|
+
* @returns List of API keys (without full key values)
|
|
1898
|
+
*
|
|
1899
|
+
* @example
|
|
1900
|
+
* ```typescript
|
|
1901
|
+
* const { api_keys, total } = await client.management.apiKeys.list()
|
|
1902
|
+
*
|
|
1903
|
+
* api_keys.forEach(key => {
|
|
1904
|
+
* console.log(`${key.name}: ${key.key_prefix}... (expires: ${key.expires_at})`)
|
|
1905
|
+
* })
|
|
1906
|
+
* ```
|
|
1907
|
+
*/
|
|
1908
|
+
async list() {
|
|
1909
|
+
return await this.fetch.get("/api/v1/api-keys");
|
|
1910
|
+
}
|
|
1911
|
+
/**
|
|
1912
|
+
* Get a specific API key by ID
|
|
1913
|
+
*
|
|
1914
|
+
* @param keyId - API key ID
|
|
1915
|
+
* @returns API key details
|
|
1916
|
+
*
|
|
1917
|
+
* @example
|
|
1918
|
+
* ```typescript
|
|
1919
|
+
* const apiKey = await client.management.apiKeys.get('key-uuid')
|
|
1920
|
+
* console.log('Last used:', apiKey.last_used_at)
|
|
1921
|
+
* ```
|
|
1922
|
+
*/
|
|
1923
|
+
async get(keyId) {
|
|
1924
|
+
return await this.fetch.get(`/api/v1/api-keys/${keyId}`);
|
|
1925
|
+
}
|
|
1926
|
+
/**
|
|
1927
|
+
* Update an API key
|
|
1928
|
+
*
|
|
1929
|
+
* @param keyId - API key ID
|
|
1930
|
+
* @param updates - Fields to update
|
|
1931
|
+
* @returns Updated API key
|
|
1932
|
+
*
|
|
1933
|
+
* @example
|
|
1934
|
+
* ```typescript
|
|
1935
|
+
* const updated = await client.management.apiKeys.update('key-uuid', {
|
|
1936
|
+
* name: 'Updated Name',
|
|
1937
|
+
* rate_limit_per_minute: 200
|
|
1938
|
+
* })
|
|
1939
|
+
* ```
|
|
1940
|
+
*/
|
|
1941
|
+
async update(keyId, updates) {
|
|
1942
|
+
return await this.fetch.patch(`/api/v1/api-keys/${keyId}`, updates);
|
|
1943
|
+
}
|
|
1944
|
+
/**
|
|
1945
|
+
* Revoke an API key
|
|
1946
|
+
*
|
|
1947
|
+
* Revoked keys can no longer be used but remain in the system for audit purposes.
|
|
1948
|
+
*
|
|
1949
|
+
* @param keyId - API key ID
|
|
1950
|
+
* @returns Revocation confirmation
|
|
1951
|
+
*
|
|
1952
|
+
* @example
|
|
1953
|
+
* ```typescript
|
|
1954
|
+
* await client.management.apiKeys.revoke('key-uuid')
|
|
1955
|
+
* console.log('API key revoked')
|
|
1956
|
+
* ```
|
|
1957
|
+
*/
|
|
1958
|
+
async revoke(keyId) {
|
|
1959
|
+
return await this.fetch.post(`/api/v1/api-keys/${keyId}/revoke`, {});
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Delete an API key
|
|
1963
|
+
*
|
|
1964
|
+
* Permanently removes the API key from the system.
|
|
1965
|
+
*
|
|
1966
|
+
* @param keyId - API key ID
|
|
1967
|
+
* @returns Deletion confirmation
|
|
1968
|
+
*
|
|
1969
|
+
* @example
|
|
1970
|
+
* ```typescript
|
|
1971
|
+
* await client.management.apiKeys.delete('key-uuid')
|
|
1972
|
+
* console.log('API key deleted')
|
|
1973
|
+
* ```
|
|
1974
|
+
*/
|
|
1975
|
+
async delete(keyId) {
|
|
1976
|
+
return await this.fetch.delete(`/api/v1/api-keys/${keyId}`);
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
1979
|
+
var WebhooksManager = class {
|
|
1980
|
+
constructor(fetch2) {
|
|
1981
|
+
this.fetch = fetch2;
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Create a new webhook
|
|
1985
|
+
*
|
|
1986
|
+
* @param request - Webhook configuration
|
|
1987
|
+
* @returns Created webhook
|
|
1988
|
+
*
|
|
1989
|
+
* @example
|
|
1990
|
+
* ```typescript
|
|
1991
|
+
* const webhook = await client.management.webhooks.create({
|
|
1992
|
+
* url: 'https://myapp.com/webhook',
|
|
1993
|
+
* events: ['user.created', 'user.updated', 'user.deleted'],
|
|
1994
|
+
* description: 'User events webhook',
|
|
1995
|
+
* secret: 'my-webhook-secret'
|
|
1996
|
+
* })
|
|
1997
|
+
* ```
|
|
1998
|
+
*/
|
|
1999
|
+
async create(request) {
|
|
2000
|
+
return await this.fetch.post("/api/v1/webhooks", request);
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* List all webhooks for the authenticated user
|
|
2004
|
+
*
|
|
2005
|
+
* @returns List of webhooks
|
|
2006
|
+
*
|
|
2007
|
+
* @example
|
|
2008
|
+
* ```typescript
|
|
2009
|
+
* const { webhooks, total } = await client.management.webhooks.list()
|
|
2010
|
+
*
|
|
2011
|
+
* webhooks.forEach(webhook => {
|
|
2012
|
+
* console.log(`${webhook.url}: ${webhook.is_active ? 'active' : 'inactive'}`)
|
|
2013
|
+
* })
|
|
2014
|
+
* ```
|
|
2015
|
+
*/
|
|
2016
|
+
async list() {
|
|
2017
|
+
return await this.fetch.get("/api/v1/webhooks");
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* Get a specific webhook by ID
|
|
2021
|
+
*
|
|
2022
|
+
* @param webhookId - Webhook ID
|
|
2023
|
+
* @returns Webhook details
|
|
2024
|
+
*
|
|
2025
|
+
* @example
|
|
2026
|
+
* ```typescript
|
|
2027
|
+
* const webhook = await client.management.webhooks.get('webhook-uuid')
|
|
2028
|
+
* console.log('Events:', webhook.events)
|
|
2029
|
+
* ```
|
|
2030
|
+
*/
|
|
2031
|
+
async get(webhookId) {
|
|
2032
|
+
return await this.fetch.get(`/api/v1/webhooks/${webhookId}`);
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Update a webhook
|
|
2036
|
+
*
|
|
2037
|
+
* @param webhookId - Webhook ID
|
|
2038
|
+
* @param updates - Fields to update
|
|
2039
|
+
* @returns Updated webhook
|
|
2040
|
+
*
|
|
2041
|
+
* @example
|
|
2042
|
+
* ```typescript
|
|
2043
|
+
* const updated = await client.management.webhooks.update('webhook-uuid', {
|
|
2044
|
+
* events: ['user.created', 'user.deleted'],
|
|
2045
|
+
* is_active: false
|
|
2046
|
+
* })
|
|
2047
|
+
* ```
|
|
2048
|
+
*/
|
|
2049
|
+
async update(webhookId, updates) {
|
|
2050
|
+
return await this.fetch.patch(`/api/v1/webhooks/${webhookId}`, updates);
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* Delete a webhook
|
|
2054
|
+
*
|
|
2055
|
+
* @param webhookId - Webhook ID
|
|
2056
|
+
* @returns Deletion confirmation
|
|
2057
|
+
*
|
|
2058
|
+
* @example
|
|
2059
|
+
* ```typescript
|
|
2060
|
+
* await client.management.webhooks.delete('webhook-uuid')
|
|
2061
|
+
* console.log('Webhook deleted')
|
|
2062
|
+
* ```
|
|
2063
|
+
*/
|
|
2064
|
+
async delete(webhookId) {
|
|
2065
|
+
return await this.fetch.delete(`/api/v1/webhooks/${webhookId}`);
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Test a webhook by sending a test payload
|
|
2069
|
+
*
|
|
2070
|
+
* @param webhookId - Webhook ID
|
|
2071
|
+
* @returns Test result with status and response
|
|
2072
|
+
*
|
|
2073
|
+
* @example
|
|
2074
|
+
* ```typescript
|
|
2075
|
+
* const result = await client.management.webhooks.test('webhook-uuid')
|
|
2076
|
+
*
|
|
2077
|
+
* if (result.success) {
|
|
2078
|
+
* console.log('Webhook test successful')
|
|
2079
|
+
* } else {
|
|
2080
|
+
* console.error('Webhook test failed:', result.error)
|
|
2081
|
+
* }
|
|
2082
|
+
* ```
|
|
2083
|
+
*/
|
|
2084
|
+
async test(webhookId) {
|
|
2085
|
+
return await this.fetch.post(`/api/v1/webhooks/${webhookId}/test`, {});
|
|
2086
|
+
}
|
|
2087
|
+
/**
|
|
2088
|
+
* List webhook delivery history
|
|
2089
|
+
*
|
|
2090
|
+
* @param webhookId - Webhook ID
|
|
2091
|
+
* @param limit - Maximum number of deliveries to return (default: 50)
|
|
2092
|
+
* @returns List of webhook deliveries
|
|
2093
|
+
*
|
|
2094
|
+
* @example
|
|
2095
|
+
* ```typescript
|
|
2096
|
+
* const { deliveries } = await client.management.webhooks.listDeliveries('webhook-uuid', 100)
|
|
2097
|
+
*
|
|
2098
|
+
* deliveries.forEach(delivery => {
|
|
2099
|
+
* console.log(`Event: ${delivery.event}, Status: ${delivery.status_code}`)
|
|
2100
|
+
* })
|
|
2101
|
+
* ```
|
|
2102
|
+
*/
|
|
2103
|
+
async listDeliveries(webhookId, limit = 50) {
|
|
2104
|
+
return await this.fetch.get(
|
|
2105
|
+
`/api/v1/webhooks/${webhookId}/deliveries?limit=${limit}`
|
|
2106
|
+
);
|
|
2107
|
+
}
|
|
2108
|
+
};
|
|
2109
|
+
var InvitationsManager = class {
|
|
2110
|
+
constructor(fetch2) {
|
|
2111
|
+
this.fetch = fetch2;
|
|
2112
|
+
}
|
|
2113
|
+
/**
|
|
2114
|
+
* Create a new invitation (admin only)
|
|
2115
|
+
*
|
|
2116
|
+
* @param request - Invitation details
|
|
2117
|
+
* @returns Created invitation with invite link
|
|
2118
|
+
*
|
|
2119
|
+
* @example
|
|
2120
|
+
* ```typescript
|
|
2121
|
+
* const invitation = await client.management.invitations.create({
|
|
2122
|
+
* email: 'newuser@example.com',
|
|
2123
|
+
* role: 'dashboard_user',
|
|
2124
|
+
* expiry_duration: 604800 // 7 days in seconds
|
|
2125
|
+
* })
|
|
2126
|
+
*
|
|
2127
|
+
* // Share the invite link
|
|
2128
|
+
* console.log('Send this link to the user:', invitation.invite_link)
|
|
2129
|
+
* ```
|
|
2130
|
+
*/
|
|
2131
|
+
async create(request) {
|
|
2132
|
+
return await this.fetch.post("/api/v1/admin/invitations", request);
|
|
2133
|
+
}
|
|
2134
|
+
/**
|
|
2135
|
+
* List all invitations (admin only)
|
|
2136
|
+
*
|
|
2137
|
+
* @param options - Filter options
|
|
2138
|
+
* @returns List of invitations
|
|
2139
|
+
*
|
|
2140
|
+
* @example
|
|
2141
|
+
* ```typescript
|
|
2142
|
+
* // List pending invitations only
|
|
2143
|
+
* const { invitations } = await client.management.invitations.list({
|
|
2144
|
+
* include_accepted: false,
|
|
2145
|
+
* include_expired: false
|
|
2146
|
+
* })
|
|
2147
|
+
*
|
|
2148
|
+
* // List all invitations including accepted and expired
|
|
2149
|
+
* const all = await client.management.invitations.list({
|
|
2150
|
+
* include_accepted: true,
|
|
2151
|
+
* include_expired: true
|
|
2152
|
+
* })
|
|
2153
|
+
* ```
|
|
2154
|
+
*/
|
|
2155
|
+
async list(options = {}) {
|
|
2156
|
+
const params = new URLSearchParams();
|
|
2157
|
+
if (options.include_accepted !== void 0) {
|
|
2158
|
+
params.append("include_accepted", String(options.include_accepted));
|
|
2159
|
+
}
|
|
2160
|
+
if (options.include_expired !== void 0) {
|
|
2161
|
+
params.append("include_expired", String(options.include_expired));
|
|
2162
|
+
}
|
|
2163
|
+
const queryString = params.toString();
|
|
2164
|
+
const url = queryString ? `/api/v1/admin/invitations?${queryString}` : "/api/v1/admin/invitations";
|
|
2165
|
+
return await this.fetch.get(url);
|
|
2166
|
+
}
|
|
2167
|
+
/**
|
|
2168
|
+
* Validate an invitation token (public endpoint)
|
|
2169
|
+
*
|
|
2170
|
+
* @param token - Invitation token
|
|
2171
|
+
* @returns Validation result with invitation details
|
|
2172
|
+
*
|
|
2173
|
+
* @example
|
|
2174
|
+
* ```typescript
|
|
2175
|
+
* const result = await client.management.invitations.validate('invitation-token')
|
|
2176
|
+
*
|
|
2177
|
+
* if (result.valid) {
|
|
2178
|
+
* console.log('Valid invitation for:', result.invitation?.email)
|
|
2179
|
+
* } else {
|
|
2180
|
+
* console.error('Invalid:', result.error)
|
|
2181
|
+
* }
|
|
2182
|
+
* ```
|
|
2183
|
+
*/
|
|
2184
|
+
async validate(token) {
|
|
2185
|
+
return await this.fetch.get(`/api/v1/invitations/${token}/validate`);
|
|
2186
|
+
}
|
|
2187
|
+
/**
|
|
2188
|
+
* Accept an invitation and create a new user (public endpoint)
|
|
2189
|
+
*
|
|
2190
|
+
* @param token - Invitation token
|
|
2191
|
+
* @param request - User details (password and name)
|
|
2192
|
+
* @returns Created user with authentication tokens
|
|
2193
|
+
*
|
|
2194
|
+
* @example
|
|
2195
|
+
* ```typescript
|
|
2196
|
+
* const response = await client.management.invitations.accept('invitation-token', {
|
|
2197
|
+
* password: 'SecurePassword123!',
|
|
2198
|
+
* name: 'John Doe'
|
|
2199
|
+
* })
|
|
2200
|
+
*
|
|
2201
|
+
* // Store tokens
|
|
2202
|
+
* localStorage.setItem('access_token', response.access_token)
|
|
2203
|
+
* console.log('Welcome:', response.user.name)
|
|
2204
|
+
* ```
|
|
2205
|
+
*/
|
|
2206
|
+
async accept(token, request) {
|
|
2207
|
+
return await this.fetch.post(`/api/v1/invitations/${token}/accept`, request);
|
|
2208
|
+
}
|
|
2209
|
+
/**
|
|
2210
|
+
* Revoke an invitation (admin only)
|
|
2211
|
+
*
|
|
2212
|
+
* @param token - Invitation token
|
|
2213
|
+
* @returns Revocation confirmation
|
|
2214
|
+
*
|
|
2215
|
+
* @example
|
|
2216
|
+
* ```typescript
|
|
2217
|
+
* await client.management.invitations.revoke('invitation-token')
|
|
2218
|
+
* console.log('Invitation revoked')
|
|
2219
|
+
* ```
|
|
2220
|
+
*/
|
|
2221
|
+
async revoke(token) {
|
|
2222
|
+
return await this.fetch.delete(`/api/v1/admin/invitations/${token}`);
|
|
2223
|
+
}
|
|
2224
|
+
};
|
|
2225
|
+
var FluxbaseManagement = class {
|
|
2226
|
+
constructor(fetch2) {
|
|
2227
|
+
this.apiKeys = new APIKeysManager(fetch2);
|
|
2228
|
+
this.webhooks = new WebhooksManager(fetch2);
|
|
2229
|
+
this.invitations = new InvitationsManager(fetch2);
|
|
2230
|
+
}
|
|
2231
|
+
};
|
|
2232
|
+
|
|
2233
|
+
// src/admin.ts
|
|
2234
|
+
var FluxbaseAdmin = class {
|
|
2235
|
+
constructor(fetch2) {
|
|
2236
|
+
this.adminToken = null;
|
|
2237
|
+
this.fetch = fetch2;
|
|
2238
|
+
this.settings = new FluxbaseSettings(fetch2);
|
|
2239
|
+
this.ddl = new DDLManager(fetch2);
|
|
2240
|
+
this.oauth = new FluxbaseOAuth(fetch2);
|
|
2241
|
+
this.impersonation = new ImpersonationManager(fetch2);
|
|
2242
|
+
this.management = new FluxbaseManagement(fetch2);
|
|
2243
|
+
}
|
|
2244
|
+
/**
|
|
2245
|
+
* Set admin authentication token
|
|
2246
|
+
*/
|
|
2247
|
+
setToken(token) {
|
|
2248
|
+
this.adminToken = token;
|
|
2249
|
+
this.fetch.setAuthToken(token);
|
|
2250
|
+
}
|
|
2251
|
+
/**
|
|
2252
|
+
* Get current admin token
|
|
2253
|
+
*/
|
|
2254
|
+
getToken() {
|
|
2255
|
+
return this.adminToken;
|
|
2256
|
+
}
|
|
2257
|
+
/**
|
|
2258
|
+
* Clear admin token
|
|
2259
|
+
*/
|
|
2260
|
+
clearToken() {
|
|
2261
|
+
this.adminToken = null;
|
|
2262
|
+
this.fetch.setAuthToken(null);
|
|
2263
|
+
}
|
|
2264
|
+
// ============================================================================
|
|
2265
|
+
// Admin Authentication
|
|
2266
|
+
// ============================================================================
|
|
2267
|
+
/**
|
|
2268
|
+
* Check if initial admin setup is needed
|
|
2269
|
+
*
|
|
2270
|
+
* @returns Setup status indicating if initial setup is required
|
|
2271
|
+
*
|
|
2272
|
+
* @example
|
|
2273
|
+
* ```typescript
|
|
2274
|
+
* const status = await admin.getSetupStatus();
|
|
2275
|
+
* if (status.needs_setup) {
|
|
2276
|
+
* console.log('Initial setup required');
|
|
2277
|
+
* }
|
|
2278
|
+
* ```
|
|
2279
|
+
*/
|
|
2280
|
+
async getSetupStatus() {
|
|
2281
|
+
return await this.fetch.get(
|
|
2282
|
+
"/api/v1/admin/setup/status"
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2285
|
+
/**
|
|
2286
|
+
* Perform initial admin setup
|
|
2287
|
+
*
|
|
2288
|
+
* Creates the first admin user and completes initial setup.
|
|
2289
|
+
* This endpoint can only be called once.
|
|
2290
|
+
*
|
|
2291
|
+
* @param email - Admin email address
|
|
2292
|
+
* @param password - Admin password (minimum 12 characters)
|
|
2293
|
+
* @param name - Admin display name
|
|
2294
|
+
* @returns Authentication response with tokens
|
|
2295
|
+
*
|
|
2296
|
+
* @example
|
|
2297
|
+
* ```typescript
|
|
2298
|
+
* const response = await admin.setup({
|
|
2299
|
+
* email: 'admin@example.com',
|
|
2300
|
+
* password: 'SecurePassword123!',
|
|
2301
|
+
* name: 'Admin User'
|
|
2302
|
+
* });
|
|
2303
|
+
*
|
|
2304
|
+
* // Store tokens
|
|
2305
|
+
* localStorage.setItem('admin_token', response.access_token);
|
|
2306
|
+
* ```
|
|
2307
|
+
*/
|
|
2308
|
+
async setup(request) {
|
|
2309
|
+
const response = await this.fetch.post(
|
|
2310
|
+
"/api/v1/admin/setup",
|
|
2311
|
+
request
|
|
2312
|
+
);
|
|
2313
|
+
this.setToken(response.access_token);
|
|
2314
|
+
return response;
|
|
2315
|
+
}
|
|
2316
|
+
/**
|
|
2317
|
+
* Admin login
|
|
2318
|
+
*
|
|
2319
|
+
* Authenticate as an admin user
|
|
2320
|
+
*
|
|
2321
|
+
* @param email - Admin email
|
|
2322
|
+
* @param password - Admin password
|
|
2323
|
+
* @returns Authentication response with tokens
|
|
2324
|
+
*
|
|
2325
|
+
* @example
|
|
2326
|
+
* ```typescript
|
|
2327
|
+
* const response = await admin.login({
|
|
2328
|
+
* email: 'admin@example.com',
|
|
2329
|
+
* password: 'password123'
|
|
2330
|
+
* });
|
|
2331
|
+
*
|
|
2332
|
+
* // Token is automatically set in the client
|
|
2333
|
+
* console.log('Logged in as:', response.user.email);
|
|
2334
|
+
* ```
|
|
2335
|
+
*/
|
|
2336
|
+
async login(request) {
|
|
2337
|
+
const response = await this.fetch.post(
|
|
2338
|
+
"/api/v1/admin/login",
|
|
2339
|
+
request
|
|
2340
|
+
);
|
|
2341
|
+
this.setToken(response.access_token);
|
|
2342
|
+
return response;
|
|
2343
|
+
}
|
|
2344
|
+
/**
|
|
2345
|
+
* Refresh admin access token
|
|
2346
|
+
*
|
|
2347
|
+
* @param refreshToken - Refresh token
|
|
2348
|
+
* @returns New access and refresh tokens
|
|
2349
|
+
*
|
|
2350
|
+
* @example
|
|
2351
|
+
* ```typescript
|
|
2352
|
+
* const refreshToken = localStorage.getItem('admin_refresh_token');
|
|
2353
|
+
* const response = await admin.refreshToken({ refresh_token: refreshToken });
|
|
2354
|
+
*
|
|
2355
|
+
* // Update stored tokens
|
|
2356
|
+
* localStorage.setItem('admin_token', response.access_token);
|
|
2357
|
+
* localStorage.setItem('admin_refresh_token', response.refresh_token);
|
|
2358
|
+
* ```
|
|
2359
|
+
*/
|
|
2360
|
+
async refreshToken(request) {
|
|
2361
|
+
const response = await this.fetch.post(
|
|
2362
|
+
"/api/v1/admin/refresh",
|
|
2363
|
+
request
|
|
2364
|
+
);
|
|
2365
|
+
this.setToken(response.access_token);
|
|
2366
|
+
return response;
|
|
2367
|
+
}
|
|
2368
|
+
/**
|
|
2369
|
+
* Admin logout
|
|
2370
|
+
*
|
|
2371
|
+
* Invalidates the current admin session
|
|
2372
|
+
*
|
|
2373
|
+
* @example
|
|
2374
|
+
* ```typescript
|
|
2375
|
+
* await admin.logout();
|
|
2376
|
+
* localStorage.removeItem('admin_token');
|
|
2377
|
+
* ```
|
|
2378
|
+
*/
|
|
2379
|
+
async logout() {
|
|
2380
|
+
await this.fetch.post("/api/v1/admin/logout", {});
|
|
2381
|
+
this.clearToken();
|
|
2382
|
+
}
|
|
2383
|
+
/**
|
|
2384
|
+
* Get current admin user information
|
|
2385
|
+
*
|
|
2386
|
+
* @returns Current admin user details
|
|
2387
|
+
*
|
|
2388
|
+
* @example
|
|
2389
|
+
* ```typescript
|
|
2390
|
+
* const { user } = await admin.me();
|
|
2391
|
+
* console.log('Logged in as:', user.email);
|
|
2392
|
+
* console.log('Role:', user.role);
|
|
2393
|
+
* ```
|
|
2394
|
+
*/
|
|
2395
|
+
async me() {
|
|
2396
|
+
return await this.fetch.get("/api/v1/admin/me");
|
|
2397
|
+
}
|
|
2398
|
+
// ============================================================================
|
|
2399
|
+
// User Management
|
|
2400
|
+
// ============================================================================
|
|
2401
|
+
/**
|
|
2402
|
+
* List all users
|
|
2403
|
+
*
|
|
2404
|
+
* @param options - Filter and pagination options
|
|
2405
|
+
* @returns List of users with metadata
|
|
2406
|
+
*
|
|
2407
|
+
* @example
|
|
2408
|
+
* ```typescript
|
|
2409
|
+
* // List all users
|
|
2410
|
+
* const { users, total } = await admin.listUsers();
|
|
2411
|
+
*
|
|
2412
|
+
* // List with filters
|
|
2413
|
+
* const result = await admin.listUsers({
|
|
2414
|
+
* exclude_admins: true,
|
|
2415
|
+
* search: 'john',
|
|
2416
|
+
* limit: 50,
|
|
2417
|
+
* type: 'app'
|
|
2418
|
+
* });
|
|
2419
|
+
* ```
|
|
2420
|
+
*/
|
|
2421
|
+
async listUsers(options = {}) {
|
|
2422
|
+
const params = new URLSearchParams();
|
|
2423
|
+
if (options.exclude_admins !== void 0) {
|
|
2424
|
+
params.append("exclude_admins", String(options.exclude_admins));
|
|
2425
|
+
}
|
|
2426
|
+
if (options.search) {
|
|
2427
|
+
params.append("search", options.search);
|
|
2428
|
+
}
|
|
2429
|
+
if (options.limit !== void 0) {
|
|
2430
|
+
params.append("limit", String(options.limit));
|
|
2431
|
+
}
|
|
2432
|
+
if (options.type) {
|
|
2433
|
+
params.append("type", options.type);
|
|
2434
|
+
}
|
|
2435
|
+
const queryString = params.toString();
|
|
2436
|
+
const url = queryString ? `/api/v1/admin/users?${queryString}` : "/api/v1/admin/users";
|
|
2437
|
+
return await this.fetch.get(url);
|
|
2438
|
+
}
|
|
2439
|
+
/**
|
|
2440
|
+
* Get a user by ID
|
|
2441
|
+
*
|
|
2442
|
+
* Fetch a single user's details by their user ID
|
|
2443
|
+
*
|
|
2444
|
+
* @param userId - User ID to fetch
|
|
2445
|
+
* @param type - User type ('app' or 'dashboard')
|
|
2446
|
+
* @returns User details with metadata
|
|
2447
|
+
*
|
|
2448
|
+
* @example
|
|
2449
|
+
* ```typescript
|
|
2450
|
+
* // Get an app user
|
|
2451
|
+
* const user = await admin.getUserById('user-123');
|
|
2452
|
+
*
|
|
2453
|
+
* // Get a dashboard user
|
|
2454
|
+
* const dashboardUser = await admin.getUserById('admin-456', 'dashboard');
|
|
2455
|
+
* console.log('User email:', dashboardUser.email);
|
|
2456
|
+
* console.log('Last login:', dashboardUser.last_login_at);
|
|
2457
|
+
* ```
|
|
2458
|
+
*/
|
|
2459
|
+
async getUserById(userId, type = "app") {
|
|
2460
|
+
const url = `/api/v1/admin/users/${userId}?type=${type}`;
|
|
2461
|
+
return await this.fetch.get(url);
|
|
2462
|
+
}
|
|
2463
|
+
/**
|
|
2464
|
+
* Invite a new user
|
|
2465
|
+
*
|
|
2466
|
+
* Creates a new user and optionally sends an invitation email
|
|
2467
|
+
*
|
|
2468
|
+
* @param request - User invitation details
|
|
2469
|
+
* @param type - User type ('app' or 'dashboard')
|
|
2470
|
+
* @returns Created user and invitation details
|
|
2471
|
+
*
|
|
2472
|
+
* @example
|
|
2473
|
+
* ```typescript
|
|
2474
|
+
* const response = await admin.inviteUser({
|
|
2475
|
+
* email: 'newuser@example.com',
|
|
2476
|
+
* role: 'user',
|
|
2477
|
+
* send_email: true
|
|
2478
|
+
* });
|
|
2479
|
+
*
|
|
2480
|
+
* console.log('User invited:', response.user.email);
|
|
2481
|
+
* console.log('Invitation link:', response.invitation_link);
|
|
2482
|
+
* ```
|
|
2483
|
+
*/
|
|
2484
|
+
async inviteUser(request, type = "app") {
|
|
2485
|
+
const url = `/api/v1/admin/users/invite?type=${type}`;
|
|
2486
|
+
return await this.fetch.post(url, request);
|
|
2487
|
+
}
|
|
2488
|
+
/**
|
|
2489
|
+
* Delete a user
|
|
2490
|
+
*
|
|
2491
|
+
* Permanently deletes a user and all associated data
|
|
2492
|
+
*
|
|
2493
|
+
* @param userId - User ID to delete
|
|
2494
|
+
* @param type - User type ('app' or 'dashboard')
|
|
2495
|
+
* @returns Deletion confirmation
|
|
2496
|
+
*
|
|
2497
|
+
* @example
|
|
2498
|
+
* ```typescript
|
|
2499
|
+
* await admin.deleteUser('user-uuid');
|
|
2500
|
+
* console.log('User deleted');
|
|
2501
|
+
* ```
|
|
2502
|
+
*/
|
|
2503
|
+
async deleteUser(userId, type = "app") {
|
|
2504
|
+
const url = `/api/v1/admin/users/${userId}?type=${type}`;
|
|
2505
|
+
return await this.fetch.delete(url);
|
|
2506
|
+
}
|
|
2507
|
+
/**
|
|
2508
|
+
* Update user role
|
|
2509
|
+
*
|
|
2510
|
+
* Changes a user's role
|
|
2511
|
+
*
|
|
2512
|
+
* @param userId - User ID
|
|
2513
|
+
* @param role - New role
|
|
2514
|
+
* @param type - User type ('app' or 'dashboard')
|
|
2515
|
+
* @returns Updated user
|
|
2516
|
+
*
|
|
2517
|
+
* @example
|
|
2518
|
+
* ```typescript
|
|
2519
|
+
* const user = await admin.updateUserRole('user-uuid', 'admin');
|
|
2520
|
+
* console.log('User role updated:', user.role);
|
|
2521
|
+
* ```
|
|
2522
|
+
*/
|
|
2523
|
+
async updateUserRole(userId, role, type = "app") {
|
|
2524
|
+
const url = `/api/v1/admin/users/${userId}/role?type=${type}`;
|
|
2525
|
+
return await this.fetch.patch(url, { role });
|
|
2526
|
+
}
|
|
2527
|
+
/**
|
|
2528
|
+
* Reset user password
|
|
2529
|
+
*
|
|
2530
|
+
* Generates a new password for the user and optionally sends it via email
|
|
2531
|
+
*
|
|
2532
|
+
* @param userId - User ID
|
|
2533
|
+
* @param type - User type ('app' or 'dashboard')
|
|
2534
|
+
* @returns Reset confirmation message
|
|
2535
|
+
*
|
|
2536
|
+
* @example
|
|
2537
|
+
* ```typescript
|
|
2538
|
+
* const response = await admin.resetUserPassword('user-uuid');
|
|
2539
|
+
* console.log(response.message);
|
|
2540
|
+
* ```
|
|
2541
|
+
*/
|
|
2542
|
+
async resetUserPassword(userId, type = "app") {
|
|
2543
|
+
const url = `/api/v1/admin/users/${userId}/reset-password?type=${type}`;
|
|
2544
|
+
return await this.fetch.post(url, {});
|
|
2545
|
+
}
|
|
2546
|
+
};
|
|
2547
|
+
|
|
2548
|
+
// src/query-builder.ts
|
|
2549
|
+
var QueryBuilder = class {
|
|
2550
|
+
constructor(fetch2, table) {
|
|
2551
|
+
this.selectQuery = "*";
|
|
2552
|
+
this.filters = [];
|
|
2553
|
+
this.orderBys = [];
|
|
2554
|
+
this.singleRow = false;
|
|
2555
|
+
this.fetch = fetch2;
|
|
2556
|
+
this.table = table;
|
|
2557
|
+
}
|
|
2558
|
+
/**
|
|
2559
|
+
* Select columns to return
|
|
2560
|
+
* @example select('*')
|
|
2561
|
+
* @example select('id, name, email')
|
|
2562
|
+
* @example select('id, name, posts(title, content)')
|
|
2563
|
+
*/
|
|
2564
|
+
select(columns = "*") {
|
|
2565
|
+
this.selectQuery = columns;
|
|
2566
|
+
return this;
|
|
2567
|
+
}
|
|
2568
|
+
/**
|
|
2569
|
+
* Insert a single row or multiple rows
|
|
2570
|
+
*/
|
|
2571
|
+
async insert(data) {
|
|
2572
|
+
const body = Array.isArray(data) ? data : data;
|
|
2573
|
+
const response = await this.fetch.post(`/api/v1/tables/${this.table}`, body);
|
|
2574
|
+
return {
|
|
2575
|
+
data: response,
|
|
2576
|
+
error: null,
|
|
2577
|
+
count: Array.isArray(data) ? data.length : 1,
|
|
2578
|
+
status: 201,
|
|
2579
|
+
statusText: "Created"
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2582
|
+
/**
|
|
2583
|
+
* Upsert (insert or update) rows
|
|
2584
|
+
*/
|
|
2585
|
+
async upsert(data) {
|
|
2586
|
+
const body = Array.isArray(data) ? data : data;
|
|
2587
|
+
const response = await this.fetch.post(`/api/v1/tables/${this.table}`, body, {
|
|
2588
|
+
headers: {
|
|
2589
|
+
Prefer: "resolution=merge-duplicates"
|
|
2590
|
+
}
|
|
2591
|
+
});
|
|
2592
|
+
return {
|
|
2593
|
+
data: response,
|
|
2594
|
+
error: null,
|
|
2595
|
+
count: Array.isArray(data) ? data.length : 1,
|
|
2596
|
+
status: 201,
|
|
2597
|
+
statusText: "Created"
|
|
2598
|
+
};
|
|
2599
|
+
}
|
|
2600
|
+
/**
|
|
2601
|
+
* Update rows matching the filters
|
|
2602
|
+
*/
|
|
2603
|
+
async update(data) {
|
|
2604
|
+
const queryString = this.buildQueryString();
|
|
2605
|
+
const path = `/api/v1/tables/${this.table}${queryString}`;
|
|
2606
|
+
const response = await this.fetch.patch(path, data);
|
|
2607
|
+
return {
|
|
2608
|
+
data: response,
|
|
2609
|
+
error: null,
|
|
2610
|
+
count: null,
|
|
2611
|
+
status: 200,
|
|
2612
|
+
statusText: "OK"
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
/**
|
|
2616
|
+
* Delete rows matching the filters
|
|
2617
|
+
*/
|
|
2618
|
+
async delete() {
|
|
2619
|
+
const queryString = this.buildQueryString();
|
|
2620
|
+
const path = `/api/v1/tables/${this.table}${queryString}`;
|
|
2621
|
+
await this.fetch.delete(path);
|
|
2622
|
+
return {
|
|
2623
|
+
data: null,
|
|
2624
|
+
error: null,
|
|
2625
|
+
count: null,
|
|
2626
|
+
status: 204,
|
|
2627
|
+
statusText: "No Content"
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
2630
|
+
/**
|
|
2631
|
+
* Equal to
|
|
2632
|
+
*/
|
|
2633
|
+
eq(column, value) {
|
|
2634
|
+
this.filters.push({ column, operator: "eq", value });
|
|
2635
|
+
return this;
|
|
2636
|
+
}
|
|
2637
|
+
/**
|
|
2638
|
+
* Not equal to
|
|
2639
|
+
*/
|
|
2640
|
+
neq(column, value) {
|
|
2641
|
+
this.filters.push({ column, operator: "neq", value });
|
|
2642
|
+
return this;
|
|
2643
|
+
}
|
|
2644
|
+
/**
|
|
2645
|
+
* Greater than
|
|
2646
|
+
*/
|
|
2647
|
+
gt(column, value) {
|
|
2648
|
+
this.filters.push({ column, operator: "gt", value });
|
|
2649
|
+
return this;
|
|
2650
|
+
}
|
|
2651
|
+
/**
|
|
2652
|
+
* Greater than or equal to
|
|
2653
|
+
*/
|
|
2654
|
+
gte(column, value) {
|
|
2655
|
+
this.filters.push({ column, operator: "gte", value });
|
|
2656
|
+
return this;
|
|
2657
|
+
}
|
|
2658
|
+
/**
|
|
2659
|
+
* Less than
|
|
2660
|
+
*/
|
|
2661
|
+
lt(column, value) {
|
|
2662
|
+
this.filters.push({ column, operator: "lt", value });
|
|
2663
|
+
return this;
|
|
2664
|
+
}
|
|
2665
|
+
/**
|
|
2666
|
+
* Less than or equal to
|
|
2667
|
+
*/
|
|
2668
|
+
lte(column, value) {
|
|
2669
|
+
this.filters.push({ column, operator: "lte", value });
|
|
2670
|
+
return this;
|
|
2671
|
+
}
|
|
2672
|
+
/**
|
|
2673
|
+
* Pattern matching (case-sensitive)
|
|
2674
|
+
*/
|
|
2675
|
+
like(column, pattern) {
|
|
2676
|
+
this.filters.push({ column, operator: "like", value: pattern });
|
|
2677
|
+
return this;
|
|
2678
|
+
}
|
|
2679
|
+
/**
|
|
2680
|
+
* Pattern matching (case-insensitive)
|
|
2681
|
+
*/
|
|
2682
|
+
ilike(column, pattern) {
|
|
2683
|
+
this.filters.push({ column, operator: "ilike", value: pattern });
|
|
2684
|
+
return this;
|
|
2685
|
+
}
|
|
2686
|
+
/**
|
|
2687
|
+
* Check if value is null or not null
|
|
2688
|
+
*/
|
|
2689
|
+
is(column, value) {
|
|
2690
|
+
this.filters.push({ column, operator: "is", value });
|
|
2691
|
+
return this;
|
|
2692
|
+
}
|
|
2693
|
+
/**
|
|
2694
|
+
* Check if value is in array
|
|
2695
|
+
*/
|
|
2696
|
+
in(column, values) {
|
|
2697
|
+
this.filters.push({ column, operator: "in", value: values });
|
|
2698
|
+
return this;
|
|
2699
|
+
}
|
|
2700
|
+
/**
|
|
2701
|
+
* Contains (for arrays and JSONB)
|
|
2702
|
+
*/
|
|
2703
|
+
contains(column, value) {
|
|
2704
|
+
this.filters.push({ column, operator: "cs", value });
|
|
2705
|
+
return this;
|
|
2706
|
+
}
|
|
2707
|
+
/**
|
|
2708
|
+
* Full-text search
|
|
2709
|
+
*/
|
|
2710
|
+
textSearch(column, query) {
|
|
2711
|
+
this.filters.push({ column, operator: "fts", value: query });
|
|
2712
|
+
return this;
|
|
2713
|
+
}
|
|
2714
|
+
/**
|
|
2715
|
+
* Order results
|
|
2716
|
+
*/
|
|
2717
|
+
order(column, options) {
|
|
2718
|
+
this.orderBys.push({
|
|
2719
|
+
column,
|
|
2720
|
+
direction: options?.ascending === false ? "desc" : "asc",
|
|
2721
|
+
nulls: options?.nullsFirst ? "first" : "last"
|
|
2722
|
+
});
|
|
2723
|
+
return this;
|
|
2724
|
+
}
|
|
2725
|
+
/**
|
|
2726
|
+
* Limit number of rows returned
|
|
2727
|
+
*/
|
|
2728
|
+
limit(count) {
|
|
2729
|
+
this.limitValue = count;
|
|
2730
|
+
return this;
|
|
2731
|
+
}
|
|
2732
|
+
/**
|
|
2733
|
+
* Skip rows
|
|
2734
|
+
*/
|
|
2735
|
+
offset(count) {
|
|
2736
|
+
this.offsetValue = count;
|
|
2737
|
+
return this;
|
|
2738
|
+
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Return a single row (adds limit(1))
|
|
2741
|
+
*/
|
|
2742
|
+
single() {
|
|
2743
|
+
this.singleRow = true;
|
|
2744
|
+
this.limitValue = 1;
|
|
2745
|
+
return this;
|
|
2746
|
+
}
|
|
2747
|
+
/**
|
|
2748
|
+
* Range selection (pagination)
|
|
2749
|
+
*/
|
|
2750
|
+
range(from, to) {
|
|
2751
|
+
this.offsetValue = from;
|
|
2752
|
+
this.limitValue = to - from + 1;
|
|
2753
|
+
return this;
|
|
2754
|
+
}
|
|
2755
|
+
/**
|
|
2756
|
+
* Group results by one or more columns (for use with aggregations)
|
|
2757
|
+
*
|
|
2758
|
+
* @param columns - Column name(s) to group by
|
|
2759
|
+
* @returns Query builder for chaining
|
|
2760
|
+
*
|
|
2761
|
+
* @example
|
|
2762
|
+
* ```typescript
|
|
2763
|
+
* // Group by single column
|
|
2764
|
+
* const { data } = await client.from('orders')
|
|
2765
|
+
* .count('*')
|
|
2766
|
+
* .groupBy('status')
|
|
2767
|
+
* .execute()
|
|
2768
|
+
*
|
|
2769
|
+
* // Group by multiple columns
|
|
2770
|
+
* const { data } = await client.from('sales')
|
|
2771
|
+
* .sum('amount')
|
|
2772
|
+
* .groupBy(['region', 'product_category'])
|
|
2773
|
+
* .execute()
|
|
2774
|
+
* ```
|
|
2775
|
+
*
|
|
2776
|
+
* @category Aggregation
|
|
2777
|
+
*/
|
|
2778
|
+
groupBy(columns) {
|
|
2779
|
+
this.groupByColumns = Array.isArray(columns) ? columns : [columns];
|
|
2780
|
+
return this;
|
|
2781
|
+
}
|
|
2782
|
+
/**
|
|
2783
|
+
* Count rows or a specific column
|
|
2784
|
+
*
|
|
2785
|
+
* @param column - Column to count (default: '*' for row count)
|
|
2786
|
+
* @returns Query builder for chaining
|
|
2787
|
+
*
|
|
2788
|
+
* @example
|
|
2789
|
+
* ```typescript
|
|
2790
|
+
* // Count all rows
|
|
2791
|
+
* const { data } = await client.from('users').count().execute()
|
|
2792
|
+
* // Returns: { count: 150 }
|
|
2793
|
+
*
|
|
2794
|
+
* // Count non-null values in a column
|
|
2795
|
+
* const { data } = await client.from('orders').count('completed_at').execute()
|
|
2796
|
+
*
|
|
2797
|
+
* // Count with grouping
|
|
2798
|
+
* const { data } = await client.from('products')
|
|
2799
|
+
* .count('*')
|
|
2800
|
+
* .groupBy('category')
|
|
2801
|
+
* .execute()
|
|
2802
|
+
* // Returns: [{ category: 'electronics', count: 45 }, { category: 'books', count: 23 }]
|
|
2803
|
+
* ```
|
|
2804
|
+
*
|
|
2805
|
+
* @category Aggregation
|
|
2806
|
+
*/
|
|
2807
|
+
count(column = "*") {
|
|
2808
|
+
this.selectQuery = `count(${column})`;
|
|
2809
|
+
return this;
|
|
2810
|
+
}
|
|
2811
|
+
/**
|
|
2812
|
+
* Calculate the sum of a numeric column
|
|
2813
|
+
*
|
|
2814
|
+
* @param column - Column to sum
|
|
2815
|
+
* @returns Query builder for chaining
|
|
2816
|
+
*
|
|
2817
|
+
* @example
|
|
2818
|
+
* ```typescript
|
|
2819
|
+
* // Sum all prices
|
|
2820
|
+
* const { data } = await client.from('products').sum('price').execute()
|
|
2821
|
+
* // Returns: { sum_price: 15420.50 }
|
|
2822
|
+
*
|
|
2823
|
+
* // Sum by category
|
|
2824
|
+
* const { data } = await client.from('orders')
|
|
2825
|
+
* .sum('total')
|
|
2826
|
+
* .groupBy('status')
|
|
2827
|
+
* .execute()
|
|
2828
|
+
* // Returns: [{ status: 'completed', sum_total: 12500 }, { status: 'pending', sum_total: 3200 }]
|
|
2829
|
+
* ```
|
|
2830
|
+
*
|
|
2831
|
+
* @category Aggregation
|
|
2832
|
+
*/
|
|
2833
|
+
sum(column) {
|
|
2834
|
+
this.selectQuery = `sum(${column})`;
|
|
2835
|
+
return this;
|
|
2836
|
+
}
|
|
2837
|
+
/**
|
|
2838
|
+
* Calculate the average of a numeric column
|
|
2839
|
+
*
|
|
2840
|
+
* @param column - Column to average
|
|
2841
|
+
* @returns Query builder for chaining
|
|
2842
|
+
*
|
|
2843
|
+
* @example
|
|
2844
|
+
* ```typescript
|
|
2845
|
+
* // Average price
|
|
2846
|
+
* const { data } = await client.from('products').avg('price').execute()
|
|
2847
|
+
* // Returns: { avg_price: 129.99 }
|
|
2848
|
+
*
|
|
2849
|
+
* // Average by category
|
|
2850
|
+
* const { data } = await client.from('products')
|
|
2851
|
+
* .avg('price')
|
|
2852
|
+
* .groupBy('category')
|
|
2853
|
+
* .execute()
|
|
2854
|
+
* ```
|
|
2855
|
+
*
|
|
2856
|
+
* @category Aggregation
|
|
2857
|
+
*/
|
|
2858
|
+
avg(column) {
|
|
2859
|
+
this.selectQuery = `avg(${column})`;
|
|
2860
|
+
return this;
|
|
2861
|
+
}
|
|
2862
|
+
/**
|
|
2863
|
+
* Find the minimum value in a column
|
|
2864
|
+
*
|
|
2865
|
+
* @param column - Column to find minimum value
|
|
2866
|
+
* @returns Query builder for chaining
|
|
2867
|
+
*
|
|
2868
|
+
* @example
|
|
2869
|
+
* ```typescript
|
|
2870
|
+
* // Find lowest price
|
|
2871
|
+
* const { data } = await client.from('products').min('price').execute()
|
|
2872
|
+
* // Returns: { min_price: 9.99 }
|
|
2873
|
+
*
|
|
2874
|
+
* // Find earliest date
|
|
2875
|
+
* const { data } = await client.from('orders').min('created_at').execute()
|
|
2876
|
+
* ```
|
|
2877
|
+
*
|
|
2878
|
+
* @category Aggregation
|
|
2879
|
+
*/
|
|
2880
|
+
min(column) {
|
|
2881
|
+
this.selectQuery = `min(${column})`;
|
|
2882
|
+
return this;
|
|
2883
|
+
}
|
|
2884
|
+
/**
|
|
2885
|
+
* Find the maximum value in a column
|
|
2886
|
+
*
|
|
2887
|
+
* @param column - Column to find maximum value
|
|
2888
|
+
* @returns Query builder for chaining
|
|
2889
|
+
*
|
|
2890
|
+
* @example
|
|
2891
|
+
* ```typescript
|
|
2892
|
+
* // Find highest price
|
|
2893
|
+
* const { data } = await client.from('products').max('price').execute()
|
|
2894
|
+
* // Returns: { max_price: 1999.99 }
|
|
2895
|
+
*
|
|
2896
|
+
* // Find most recent order
|
|
2897
|
+
* const { data } = await client.from('orders').max('created_at').execute()
|
|
2898
|
+
* ```
|
|
2899
|
+
*
|
|
2900
|
+
* @category Aggregation
|
|
2901
|
+
*/
|
|
2902
|
+
max(column) {
|
|
2903
|
+
this.selectQuery = `max(${column})`;
|
|
2904
|
+
return this;
|
|
2905
|
+
}
|
|
2906
|
+
/**
|
|
2907
|
+
* Insert multiple rows in a single request (batch insert)
|
|
2908
|
+
*
|
|
2909
|
+
* This is a convenience method that explicitly shows intent for batch operations.
|
|
2910
|
+
* Internally calls `insert()` with an array.
|
|
2911
|
+
*
|
|
2912
|
+
* @param rows - Array of row objects to insert
|
|
2913
|
+
* @returns Promise with the inserted rows
|
|
2914
|
+
*
|
|
2915
|
+
* @example
|
|
2916
|
+
* ```typescript
|
|
2917
|
+
* // Insert multiple users at once
|
|
2918
|
+
* const { data } = await client.from('users').insertMany([
|
|
2919
|
+
* { name: 'Alice', email: 'alice@example.com' },
|
|
2920
|
+
* { name: 'Bob', email: 'bob@example.com' },
|
|
2921
|
+
* { name: 'Charlie', email: 'charlie@example.com' }
|
|
2922
|
+
* ]).execute()
|
|
2923
|
+
* ```
|
|
2924
|
+
*
|
|
2925
|
+
* @category Batch Operations
|
|
2926
|
+
*/
|
|
2927
|
+
async insertMany(rows) {
|
|
2928
|
+
return this.insert(rows);
|
|
2929
|
+
}
|
|
2930
|
+
/**
|
|
2931
|
+
* Update multiple rows matching the filters (batch update)
|
|
2932
|
+
*
|
|
2933
|
+
* Updates all rows that match the current query filters.
|
|
2934
|
+
* This is a convenience method that explicitly shows intent for batch operations.
|
|
2935
|
+
*
|
|
2936
|
+
* @param data - Data to update matching rows with
|
|
2937
|
+
* @returns Promise with the updated rows
|
|
2938
|
+
*
|
|
2939
|
+
* @example
|
|
2940
|
+
* ```typescript
|
|
2941
|
+
* // Apply discount to all electronics
|
|
2942
|
+
* const { data } = await client.from('products')
|
|
2943
|
+
* .eq('category', 'electronics')
|
|
2944
|
+
* .updateMany({ discount: 10, updated_at: new Date() })
|
|
2945
|
+
* .execute()
|
|
2946
|
+
*
|
|
2947
|
+
* // Mark all pending orders as processing
|
|
2948
|
+
* const { data } = await client.from('orders')
|
|
2949
|
+
* .eq('status', 'pending')
|
|
2950
|
+
* .updateMany({ status: 'processing' })
|
|
2951
|
+
* .execute()
|
|
2952
|
+
* ```
|
|
2953
|
+
*
|
|
2954
|
+
* @category Batch Operations
|
|
2955
|
+
*/
|
|
2956
|
+
async updateMany(data) {
|
|
2957
|
+
return this.update(data);
|
|
2958
|
+
}
|
|
2959
|
+
/**
|
|
2960
|
+
* Delete multiple rows matching the filters (batch delete)
|
|
2961
|
+
*
|
|
2962
|
+
* Deletes all rows that match the current query filters.
|
|
2963
|
+
* This is a convenience method that explicitly shows intent for batch operations.
|
|
2964
|
+
*
|
|
2965
|
+
* @returns Promise confirming deletion
|
|
2966
|
+
*
|
|
2967
|
+
* @example
|
|
2968
|
+
* ```typescript
|
|
2969
|
+
* // Delete all inactive users
|
|
2970
|
+
* await client.from('users')
|
|
2971
|
+
* .eq('active', false)
|
|
2972
|
+
* .deleteMany()
|
|
2973
|
+
* .execute()
|
|
2974
|
+
*
|
|
2975
|
+
* // Delete old logs
|
|
2976
|
+
* await client.from('logs')
|
|
2977
|
+
* .lt('created_at', '2024-01-01')
|
|
2978
|
+
* .deleteMany()
|
|
2979
|
+
* .execute()
|
|
2980
|
+
* ```
|
|
2981
|
+
*
|
|
2982
|
+
* @category Batch Operations
|
|
2983
|
+
*/
|
|
2984
|
+
async deleteMany() {
|
|
2985
|
+
return this.delete();
|
|
2986
|
+
}
|
|
2987
|
+
/**
|
|
2988
|
+
* Execute the query and return results
|
|
2989
|
+
*/
|
|
2990
|
+
async execute() {
|
|
2991
|
+
const queryString = this.buildQueryString();
|
|
2992
|
+
const path = `/api/v1/tables/${this.table}${queryString}`;
|
|
2993
|
+
try {
|
|
2994
|
+
const data = await this.fetch.get(path);
|
|
2995
|
+
if (this.singleRow) {
|
|
2996
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
2997
|
+
return {
|
|
2998
|
+
data: null,
|
|
2999
|
+
error: { message: "No rows found", code: "PGRST116" },
|
|
3000
|
+
count: 0,
|
|
3001
|
+
status: 404,
|
|
3002
|
+
statusText: "Not Found"
|
|
3003
|
+
};
|
|
3004
|
+
}
|
|
3005
|
+
const singleData = Array.isArray(data) ? data[0] : data;
|
|
3006
|
+
return {
|
|
3007
|
+
data: singleData,
|
|
3008
|
+
error: null,
|
|
3009
|
+
count: 1,
|
|
3010
|
+
status: 200,
|
|
3011
|
+
statusText: "OK"
|
|
3012
|
+
};
|
|
3013
|
+
}
|
|
3014
|
+
return {
|
|
3015
|
+
data,
|
|
3016
|
+
error: null,
|
|
3017
|
+
count: Array.isArray(data) ? data.length : null,
|
|
3018
|
+
status: 200,
|
|
3019
|
+
statusText: "OK"
|
|
3020
|
+
};
|
|
3021
|
+
} catch (err) {
|
|
3022
|
+
const error = err;
|
|
3023
|
+
return {
|
|
3024
|
+
data: null,
|
|
3025
|
+
error: {
|
|
3026
|
+
message: error.message,
|
|
3027
|
+
code: "PGRST000"
|
|
3028
|
+
},
|
|
3029
|
+
count: null,
|
|
3030
|
+
status: 500,
|
|
3031
|
+
statusText: "Internal Server Error"
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
/**
|
|
3036
|
+
* Make QueryBuilder awaitable (implements PromiseLike)
|
|
3037
|
+
* This allows using `await client.from('table').select()` without calling `.execute()`
|
|
3038
|
+
*
|
|
3039
|
+
* @example
|
|
3040
|
+
* ```typescript
|
|
3041
|
+
* // Without .execute() (new way)
|
|
3042
|
+
* const { data } = await client.from('users').select('*')
|
|
3043
|
+
*
|
|
3044
|
+
* // With .execute() (old way, still supported)
|
|
3045
|
+
* const { data } = await client.from('users').select('*').execute()
|
|
3046
|
+
* ```
|
|
3047
|
+
*/
|
|
3048
|
+
then(onfulfilled, onrejected) {
|
|
3049
|
+
return this.execute().then(onfulfilled, onrejected);
|
|
3050
|
+
}
|
|
3051
|
+
/**
|
|
3052
|
+
* Build the query string from filters, ordering, etc.
|
|
3053
|
+
*/
|
|
3054
|
+
buildQueryString() {
|
|
3055
|
+
const params = new URLSearchParams();
|
|
3056
|
+
if (this.selectQuery && this.selectQuery !== "*") {
|
|
3057
|
+
params.append("select", this.selectQuery);
|
|
3058
|
+
}
|
|
3059
|
+
for (const filter of this.filters) {
|
|
3060
|
+
params.append(filter.column, `${filter.operator}.${this.formatValue(filter.value)}`);
|
|
3061
|
+
}
|
|
3062
|
+
if (this.groupByColumns && this.groupByColumns.length > 0) {
|
|
3063
|
+
params.append("group_by", this.groupByColumns.join(","));
|
|
3064
|
+
}
|
|
3065
|
+
if (this.orderBys.length > 0) {
|
|
3066
|
+
const orderStr = this.orderBys.map((o) => `${o.column}.${o.direction}${o.nulls ? `.nulls${o.nulls}` : ""}`).join(",");
|
|
3067
|
+
params.append("order", orderStr);
|
|
3068
|
+
}
|
|
3069
|
+
if (this.limitValue !== void 0) {
|
|
3070
|
+
params.append("limit", String(this.limitValue));
|
|
3071
|
+
}
|
|
3072
|
+
if (this.offsetValue !== void 0) {
|
|
3073
|
+
params.append("offset", String(this.offsetValue));
|
|
3074
|
+
}
|
|
3075
|
+
const queryString = params.toString();
|
|
3076
|
+
return queryString ? `?${queryString}` : "";
|
|
3077
|
+
}
|
|
3078
|
+
/**
|
|
3079
|
+
* Format a value for the query string
|
|
3080
|
+
*/
|
|
3081
|
+
formatValue(value) {
|
|
3082
|
+
if (value === null) {
|
|
3083
|
+
return "null";
|
|
3084
|
+
}
|
|
3085
|
+
if (typeof value === "boolean") {
|
|
3086
|
+
return value ? "true" : "false";
|
|
3087
|
+
}
|
|
3088
|
+
if (Array.isArray(value)) {
|
|
3089
|
+
return `(${value.map((v) => this.formatValue(v)).join(",")})`;
|
|
3090
|
+
}
|
|
3091
|
+
if (typeof value === "object") {
|
|
3092
|
+
return JSON.stringify(value);
|
|
3093
|
+
}
|
|
3094
|
+
return String(value);
|
|
3095
|
+
}
|
|
3096
|
+
};
|
|
3097
|
+
|
|
3098
|
+
// src/client.ts
|
|
3099
|
+
var FluxbaseClient = class {
|
|
3100
|
+
/**
|
|
3101
|
+
* Create a new Fluxbase client instance
|
|
3102
|
+
* @param options - Client configuration options
|
|
3103
|
+
*/
|
|
3104
|
+
constructor(options) {
|
|
3105
|
+
this.fetch = new FluxbaseFetch(options.url, {
|
|
3106
|
+
headers: options.headers,
|
|
3107
|
+
timeout: options.timeout,
|
|
3108
|
+
debug: options.debug
|
|
3109
|
+
});
|
|
3110
|
+
this.auth = new FluxbaseAuth(
|
|
3111
|
+
this.fetch,
|
|
3112
|
+
options.auth?.autoRefresh ?? true,
|
|
3113
|
+
options.auth?.persist ?? true
|
|
3114
|
+
);
|
|
3115
|
+
if (options.auth?.token) {
|
|
3116
|
+
this.fetch.setAuthToken(options.auth.token);
|
|
3117
|
+
}
|
|
3118
|
+
this.realtime = new FluxbaseRealtime(options.url, options.auth?.token || null);
|
|
3119
|
+
this.storage = new FluxbaseStorage(this.fetch);
|
|
3120
|
+
this.admin = new FluxbaseAdmin(this.fetch);
|
|
3121
|
+
this.management = new FluxbaseManagement(this.fetch);
|
|
3122
|
+
this.setupAuthSync();
|
|
3123
|
+
}
|
|
3124
|
+
/**
|
|
3125
|
+
* Create a query builder for a database table
|
|
3126
|
+
*
|
|
3127
|
+
* @param table - The table name (can include schema, e.g., 'public.users')
|
|
3128
|
+
* @returns A query builder instance for constructing and executing queries
|
|
3129
|
+
*
|
|
3130
|
+
* @example
|
|
3131
|
+
* ```typescript
|
|
3132
|
+
* // Simple select
|
|
3133
|
+
* const { data } = await client.from('users').select('*').execute()
|
|
3134
|
+
*
|
|
3135
|
+
* // With filters
|
|
3136
|
+
* const { data } = await client.from('products')
|
|
3137
|
+
* .select('id, name, price')
|
|
3138
|
+
* .gt('price', 100)
|
|
3139
|
+
* .eq('category', 'electronics')
|
|
3140
|
+
* .execute()
|
|
3141
|
+
*
|
|
3142
|
+
* // Insert
|
|
3143
|
+
* await client.from('users').insert({ name: 'John', email: 'john@example.com' }).execute()
|
|
3144
|
+
* ```
|
|
3145
|
+
*
|
|
3146
|
+
* @category Database
|
|
3147
|
+
*/
|
|
3148
|
+
from(table) {
|
|
3149
|
+
return new QueryBuilder(this.fetch, table);
|
|
3150
|
+
}
|
|
3151
|
+
/**
|
|
3152
|
+
* Call a PostgreSQL function (Remote Procedure Call)
|
|
3153
|
+
*
|
|
3154
|
+
* @param functionName - The name of the PostgreSQL function to call
|
|
3155
|
+
* @param params - Optional parameters to pass to the function
|
|
3156
|
+
* @returns Promise containing the function result or error
|
|
3157
|
+
*
|
|
3158
|
+
* @example
|
|
3159
|
+
* ```typescript
|
|
3160
|
+
* // Call a function without parameters
|
|
3161
|
+
* const { data, error } = await client.rpc('get_total_users')
|
|
3162
|
+
*
|
|
3163
|
+
* // Call a function with parameters
|
|
3164
|
+
* const { data, error } = await client.rpc('calculate_discount', {
|
|
3165
|
+
* product_id: 123,
|
|
3166
|
+
* coupon_code: 'SAVE20'
|
|
3167
|
+
* })
|
|
3168
|
+
* ```
|
|
3169
|
+
*
|
|
3170
|
+
* @category Database
|
|
3171
|
+
*/
|
|
3172
|
+
async rpc(functionName, params) {
|
|
3173
|
+
try {
|
|
3174
|
+
const data = await this.fetch.post(`/api/v1/rpc/${functionName}`, params || {});
|
|
3175
|
+
return { data, error: null };
|
|
3176
|
+
} catch (error) {
|
|
3177
|
+
return { data: null, error };
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
/**
|
|
3181
|
+
* Sync auth state with realtime connections
|
|
3182
|
+
* @internal
|
|
3183
|
+
*/
|
|
3184
|
+
setupAuthSync() {
|
|
3185
|
+
const originalSetAuthToken = this.fetch.setAuthToken.bind(this.fetch);
|
|
3186
|
+
this.fetch.setAuthToken = (token) => {
|
|
3187
|
+
originalSetAuthToken(token);
|
|
3188
|
+
this.realtime.setToken(token);
|
|
3189
|
+
};
|
|
3190
|
+
}
|
|
3191
|
+
/**
|
|
3192
|
+
* Get the current authentication token
|
|
3193
|
+
*
|
|
3194
|
+
* @returns The current JWT access token, or null if not authenticated
|
|
3195
|
+
*
|
|
3196
|
+
* @category Authentication
|
|
3197
|
+
*/
|
|
3198
|
+
getAuthToken() {
|
|
3199
|
+
return this.auth.getAccessToken();
|
|
3200
|
+
}
|
|
3201
|
+
/**
|
|
3202
|
+
* Set a new authentication token
|
|
3203
|
+
*
|
|
3204
|
+
* This updates both the HTTP client and realtime connection with the new token.
|
|
3205
|
+
*
|
|
3206
|
+
* @param token - The JWT access token to set, or null to clear authentication
|
|
3207
|
+
*
|
|
3208
|
+
* @category Authentication
|
|
3209
|
+
*/
|
|
3210
|
+
setAuthToken(token) {
|
|
3211
|
+
this.fetch.setAuthToken(token);
|
|
3212
|
+
this.realtime.setToken(token);
|
|
3213
|
+
}
|
|
3214
|
+
/**
|
|
3215
|
+
* Get the internal HTTP client
|
|
3216
|
+
*
|
|
3217
|
+
* Use this for advanced scenarios like making custom API calls or admin operations.
|
|
3218
|
+
*
|
|
3219
|
+
* @returns The internal FluxbaseFetch instance
|
|
3220
|
+
*
|
|
3221
|
+
* @example
|
|
3222
|
+
* ```typescript
|
|
3223
|
+
* // Make a custom API call
|
|
3224
|
+
* const data = await client.http.get('/api/custom-endpoint')
|
|
3225
|
+
* ```
|
|
3226
|
+
*
|
|
3227
|
+
* @category Advanced
|
|
3228
|
+
*/
|
|
3229
|
+
get http() {
|
|
3230
|
+
return this.fetch;
|
|
3231
|
+
}
|
|
3232
|
+
};
|
|
3233
|
+
function createClient(options) {
|
|
3234
|
+
return new FluxbaseClient(options);
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
exports.APIKeysManager = APIKeysManager;
|
|
3238
|
+
exports.AppSettingsManager = AppSettingsManager;
|
|
3239
|
+
exports.AuthSettingsManager = AuthSettingsManager;
|
|
3240
|
+
exports.DDLManager = DDLManager;
|
|
3241
|
+
exports.FluxbaseAdmin = FluxbaseAdmin;
|
|
3242
|
+
exports.FluxbaseAuth = FluxbaseAuth;
|
|
3243
|
+
exports.FluxbaseClient = FluxbaseClient;
|
|
3244
|
+
exports.FluxbaseFetch = FluxbaseFetch;
|
|
3245
|
+
exports.FluxbaseManagement = FluxbaseManagement;
|
|
3246
|
+
exports.FluxbaseOAuth = FluxbaseOAuth;
|
|
3247
|
+
exports.FluxbaseRealtime = FluxbaseRealtime;
|
|
3248
|
+
exports.FluxbaseSettings = FluxbaseSettings;
|
|
3249
|
+
exports.FluxbaseStorage = FluxbaseStorage;
|
|
3250
|
+
exports.ImpersonationManager = ImpersonationManager;
|
|
3251
|
+
exports.InvitationsManager = InvitationsManager;
|
|
3252
|
+
exports.OAuthProviderManager = OAuthProviderManager;
|
|
3253
|
+
exports.QueryBuilder = QueryBuilder;
|
|
3254
|
+
exports.RealtimeChannel = RealtimeChannel;
|
|
3255
|
+
exports.StorageBucket = StorageBucket;
|
|
3256
|
+
exports.SystemSettingsManager = SystemSettingsManager;
|
|
3257
|
+
exports.WebhooksManager = WebhooksManager;
|
|
3258
|
+
exports.createClient = createClient;
|
|
3259
|
+
//# sourceMappingURL=index.cjs.map
|
|
3260
|
+
//# sourceMappingURL=index.cjs.map
|