@giaeulate/baas-sdk 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/LICENSE +21 -0
- package/README.md +54 -0
- package/dist/cli.js +416 -0
- package/dist/index.cjs +272 -10
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +150 -5
- package/dist/index.d.ts +150 -5
- package/dist/index.js +272 -10
- package/dist/index.js.map +1 -0
- package/package.json +43 -18
package/dist/index.cjs
CHANGED
|
@@ -30,14 +30,28 @@ module.exports = __toCommonJS(index_exports);
|
|
|
30
30
|
var HttpClient = class {
|
|
31
31
|
url;
|
|
32
32
|
apiKey = "";
|
|
33
|
+
keyType;
|
|
33
34
|
token = null;
|
|
34
35
|
environment = "prod";
|
|
35
36
|
_onForceLogout = null;
|
|
36
|
-
constructor(url, apiKey) {
|
|
37
|
+
constructor(url, apiKey, options) {
|
|
37
38
|
let baseUrl = url || (typeof window !== "undefined" ? window.location.origin : "http://localhost:8080");
|
|
38
39
|
this.url = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
39
40
|
if (apiKey) this.apiKey = apiKey;
|
|
40
|
-
this.
|
|
41
|
+
this.keyType = options?.keyType ?? "service_role";
|
|
42
|
+
this.warnIfUnsafeKey();
|
|
43
|
+
this.token = typeof localStorage !== "undefined" ? this.cleanValue(localStorage.getItem("baas_token")) : null;
|
|
44
|
+
}
|
|
45
|
+
/** SECURITY: a service_role key bypasses RLS and grants admin over every
|
|
46
|
+
* tenant. If it ends up in a browser bundle, anyone can extract it. Warn
|
|
47
|
+
* loudly when a non-anon key is constructed in a browser context. */
|
|
48
|
+
warnIfUnsafeKey() {
|
|
49
|
+
const inBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
50
|
+
if (inBrowser && this.apiKey && this.keyType !== "anon") {
|
|
51
|
+
console.warn(
|
|
52
|
+
'[baas-sdk] SECURITY WARNING: a non-anon (service_role) key is running in a browser. It bypasses Row-Level Security and grants admin over ALL tenants, and is extractable from the bundle. Mint an anon key in the dashboard and construct the client with { keyType: "anon" }.'
|
|
53
|
+
);
|
|
54
|
+
}
|
|
41
55
|
}
|
|
42
56
|
/**
|
|
43
57
|
* Set the auth token manually (e.g. restoring from SecureStore in React Native).
|
|
@@ -114,7 +128,14 @@ var HttpClient = class {
|
|
|
114
128
|
if (res.status === 403 && data?.error === "ip_not_allowed") {
|
|
115
129
|
this.handleIPBlocked(data.ip);
|
|
116
130
|
}
|
|
117
|
-
|
|
131
|
+
const rateLimited = res.status === 429;
|
|
132
|
+
const retryHint = res.headers.get("retry-after") || res.headers.get("x-ratelimit-reset");
|
|
133
|
+
return {
|
|
134
|
+
error: data?.error || `Request failed: ${res.status}`,
|
|
135
|
+
status: res.status,
|
|
136
|
+
...rateLimited ? { rateLimited: true, retryAfter: retryHint ? parseInt(retryHint, 10) : void 0 } : {},
|
|
137
|
+
...data
|
|
138
|
+
};
|
|
118
139
|
}
|
|
119
140
|
return data;
|
|
120
141
|
}
|
|
@@ -231,10 +252,49 @@ var QueryBuilder = class {
|
|
|
231
252
|
this.queryParams.set(column, `not.${operator}.${value}`);
|
|
232
253
|
return this;
|
|
233
254
|
}
|
|
255
|
+
/** Array/jsonb/range contains (@>). Arrays become a `{a,b}` literal. */
|
|
256
|
+
contains(column, values) {
|
|
257
|
+
this.queryParams.set(column, `cs.${this.arrayLiteral(values)}`);
|
|
258
|
+
return this;
|
|
259
|
+
}
|
|
260
|
+
/** Array/jsonb/range contained-by (<@). */
|
|
261
|
+
containedBy(column, values) {
|
|
262
|
+
this.queryParams.set(column, `cd.${this.arrayLiteral(values)}`);
|
|
263
|
+
return this;
|
|
264
|
+
}
|
|
265
|
+
/** Array/range overlap (&&). */
|
|
266
|
+
overlaps(column, values) {
|
|
267
|
+
this.queryParams.set(column, `ov.${this.arrayLiteral(values)}`);
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
/** Full-text search (@@). type: fts|plfts|phfts|wfts (tsquery flavour). */
|
|
271
|
+
textSearch(column, query, type = "fts") {
|
|
272
|
+
this.queryParams.set(column, `${type}.${query}`);
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
275
|
+
arrayLiteral(values) {
|
|
276
|
+
return Array.isArray(values) ? `{${values.join(",")}}` : `${values}`;
|
|
277
|
+
}
|
|
278
|
+
/** OR group: `or=(c1,c2)`. Members are dotted `col.op.value` strings. */
|
|
234
279
|
or(filters) {
|
|
235
280
|
this.queryParams.set("or", `(${filters})`);
|
|
236
281
|
return this;
|
|
237
282
|
}
|
|
283
|
+
/** AND group: `and=(c1,c2)` — e.g. a range `qty.gt.5,qty.lt.20`. */
|
|
284
|
+
and(filters) {
|
|
285
|
+
this.queryParams.set("and", `(${filters})`);
|
|
286
|
+
return this;
|
|
287
|
+
}
|
|
288
|
+
/** Negated AND group: `not.and=(...)`. */
|
|
289
|
+
notAnd(filters) {
|
|
290
|
+
this.queryParams.set("not.and", `(${filters})`);
|
|
291
|
+
return this;
|
|
292
|
+
}
|
|
293
|
+
/** Negated OR group: `not.or=(...)`. */
|
|
294
|
+
notOr(filters) {
|
|
295
|
+
this.queryParams.set("not.or", `(${filters})`);
|
|
296
|
+
return this;
|
|
297
|
+
}
|
|
238
298
|
order(column, { ascending = true } = {}) {
|
|
239
299
|
this.queryParams.set("order", `${column}.${ascending ? "asc" : "desc"}`);
|
|
240
300
|
return this;
|
|
@@ -278,6 +338,34 @@ var QueryBuilder = class {
|
|
|
278
338
|
if (res.status === 204) return { success: true };
|
|
279
339
|
return await this.handleResponse(res);
|
|
280
340
|
}
|
|
341
|
+
/** Insert-or-update on conflict. [onConflict] is the conflict-target column(s). */
|
|
342
|
+
async upsert(data, onConflict) {
|
|
343
|
+
const res = await fetch(`${this.url}/api/v1/data/${this.table}/upsert`, {
|
|
344
|
+
method: "POST",
|
|
345
|
+
headers: this.getHeaders(),
|
|
346
|
+
body: JSON.stringify({ ...data, on_conflict: onConflict })
|
|
347
|
+
});
|
|
348
|
+
return await this.handleResponse(res);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Count rows matching the current filters (PostgREST parity). Sends
|
|
352
|
+
* `Prefer: count=exact` and parses the total from the `Content-Range`
|
|
353
|
+
* response header. Returns `{ count, error, status }`.
|
|
354
|
+
*/
|
|
355
|
+
async count() {
|
|
356
|
+
const res = await fetch(`${this.url}/api/v1/data/${this.table}?${this.queryParams.toString()}`, {
|
|
357
|
+
headers: { ...this.getHeaders(), Prefer: "count=exact", "Cache-Control": "no-cache" },
|
|
358
|
+
cache: "no-store"
|
|
359
|
+
});
|
|
360
|
+
if (!res.ok) {
|
|
361
|
+
if (res.status === 401) this.client.forceLogout();
|
|
362
|
+
const body = await res.json().catch(() => null);
|
|
363
|
+
return { count: 0, error: body?.error || "Request failed", status: res.status };
|
|
364
|
+
}
|
|
365
|
+
const cr = res.headers.get("content-range");
|
|
366
|
+
const total = cr && cr.includes("/") ? parseInt(cr.split("/").pop() || "", 10) : NaN;
|
|
367
|
+
return { count: Number.isNaN(total) ? 0 : total, error: null, status: res.status };
|
|
368
|
+
}
|
|
281
369
|
getHeaders() {
|
|
282
370
|
return this.client.getHeaders();
|
|
283
371
|
}
|
|
@@ -333,6 +421,43 @@ function createAuthModule(client) {
|
|
|
333
421
|
localStorage.removeItem("baas_token");
|
|
334
422
|
}
|
|
335
423
|
},
|
|
424
|
+
// Anonymous sign-in (Supabase parity). Response carries access_token (+ a
|
|
425
|
+
// back-compat `token`); store whichever is present.
|
|
426
|
+
async signInAnonymous() {
|
|
427
|
+
const data = await post("/api/auth/anonymous");
|
|
428
|
+
const tok = data?.access_token || data?.token;
|
|
429
|
+
if (tok) {
|
|
430
|
+
client.token = tok;
|
|
431
|
+
localStorage.setItem("baas_token", tok);
|
|
432
|
+
}
|
|
433
|
+
return data;
|
|
434
|
+
},
|
|
435
|
+
// Passwordless: request a magic link + 6-digit email OTP. Always resolves on
|
|
436
|
+
// a well-formed request (no account enumeration).
|
|
437
|
+
async requestOtp(email, createUser = true) {
|
|
438
|
+
return post("/api/auth/otp", { email, create_user: createUser });
|
|
439
|
+
},
|
|
440
|
+
// Verify a passwordless OTP/magic-link and complete sign-in.
|
|
441
|
+
async verifyOtp(type, email, token) {
|
|
442
|
+
const data = await post("/api/auth/verify", { type, email, token });
|
|
443
|
+
const tok = data?.access_token || data?.token;
|
|
444
|
+
if (tok) {
|
|
445
|
+
client.token = tok;
|
|
446
|
+
localStorage.setItem("baas_token", tok);
|
|
447
|
+
}
|
|
448
|
+
return data;
|
|
449
|
+
},
|
|
450
|
+
// Upgrade the current anonymous guest to a real account (preserves user id).
|
|
451
|
+
// Requires the anonymous Bearer to be set (call after signInAnonymous).
|
|
452
|
+
async upgradeAnonymous(email, password) {
|
|
453
|
+
const data = await post("/api/auth/upgrade", { email, password });
|
|
454
|
+
const tok = data?.access_token || data?.token;
|
|
455
|
+
if (tok) {
|
|
456
|
+
client.token = tok;
|
|
457
|
+
localStorage.setItem("baas_token", tok);
|
|
458
|
+
}
|
|
459
|
+
return data;
|
|
460
|
+
},
|
|
336
461
|
async verifyMFA(mfaToken, code) {
|
|
337
462
|
const data = await post("/api/mfa/finalize", { mfa_token: mfaToken, code });
|
|
338
463
|
if (data.token) {
|
|
@@ -530,6 +655,7 @@ function createDatabaseModule(client) {
|
|
|
530
655
|
function createStorageModule(client) {
|
|
531
656
|
const get = (endpoint) => client.get(endpoint);
|
|
532
657
|
const post = (endpoint, body) => client.post(endpoint, body);
|
|
658
|
+
const put = (endpoint, body) => client.put(endpoint, body);
|
|
533
659
|
const del = (endpoint) => client.delete(endpoint);
|
|
534
660
|
async function uploadFile(fileOrTable, fileOrBucketId) {
|
|
535
661
|
let file;
|
|
@@ -564,8 +690,15 @@ function createStorageModule(client) {
|
|
|
564
690
|
async listFiles() {
|
|
565
691
|
return get("/api/storage/files");
|
|
566
692
|
},
|
|
567
|
-
async getFile(fileId) {
|
|
568
|
-
|
|
693
|
+
async getFile(fileId, expiresIn) {
|
|
694
|
+
const q = expiresIn && expiresIn > 0 ? `?expiresIn=${expiresIn}` : "";
|
|
695
|
+
return get(`/api/storage/files/${fileId}${q}`);
|
|
696
|
+
},
|
|
697
|
+
async createSignedUrl(fileId, expiresIn) {
|
|
698
|
+
const res = await get(
|
|
699
|
+
`/api/storage/files/${fileId}${expiresIn && expiresIn > 0 ? `?expiresIn=${expiresIn}` : ""}`
|
|
700
|
+
);
|
|
701
|
+
return { signedUrl: res?.url };
|
|
569
702
|
},
|
|
570
703
|
async deleteFile(fileId) {
|
|
571
704
|
return del(`/api/storage/files/${fileId}`);
|
|
@@ -573,6 +706,9 @@ function createStorageModule(client) {
|
|
|
573
706
|
async createBucket(name, isPublic = false) {
|
|
574
707
|
return post("/api/storage/buckets", { name, is_public: isPublic });
|
|
575
708
|
},
|
|
709
|
+
async updateBucket(bucketId, opts) {
|
|
710
|
+
return put(`/api/storage/buckets/${bucketId}`, { is_public: opts.isPublic });
|
|
711
|
+
},
|
|
576
712
|
async listBuckets() {
|
|
577
713
|
return get("/api/storage/buckets");
|
|
578
714
|
},
|
|
@@ -584,6 +720,17 @@ function createStorageModule(client) {
|
|
|
584
720
|
},
|
|
585
721
|
async listBucketFiles(bucketId) {
|
|
586
722
|
return get(`/api/storage/buckets/${bucketId}/files`);
|
|
723
|
+
},
|
|
724
|
+
async download(fileId) {
|
|
725
|
+
const res = await fetch(`${client.url}/api/storage/files/${fileId}/download`, {
|
|
726
|
+
headers: client.getHeaders("")
|
|
727
|
+
});
|
|
728
|
+
if (!res.ok) {
|
|
729
|
+
if (res.status === 401) client.forceLogout();
|
|
730
|
+
const err = await res.json().catch(() => ({ error: `Download failed: ${res.status}` }));
|
|
731
|
+
throw new Error(err.error ?? `Download failed: ${res.status}`);
|
|
732
|
+
}
|
|
733
|
+
return res.blob();
|
|
587
734
|
}
|
|
588
735
|
};
|
|
589
736
|
}
|
|
@@ -684,7 +831,9 @@ function createFunctionsModule(client) {
|
|
|
684
831
|
return del(`/api/functions/${id}`);
|
|
685
832
|
},
|
|
686
833
|
async update(id, code, options) {
|
|
687
|
-
|
|
834
|
+
const current = await get(`/api/functions/${id}`);
|
|
835
|
+
const name = current?.name ?? current?.data?.name;
|
|
836
|
+
return post("/api/functions", { name, code, ...options });
|
|
688
837
|
},
|
|
689
838
|
async executeCode(code, options) {
|
|
690
839
|
return post("/api/functions/execute", { code, ...options });
|
|
@@ -830,7 +979,9 @@ function createGraphQLModule(client) {
|
|
|
830
979
|
return post("/api/graphql", { query, variables });
|
|
831
980
|
},
|
|
832
981
|
async getSchema() {
|
|
833
|
-
return
|
|
982
|
+
return post("/api/graphql", {
|
|
983
|
+
query: "{ __schema { queryType { name } mutationType { name } types { name kind } } }"
|
|
984
|
+
});
|
|
834
985
|
},
|
|
835
986
|
getPlaygroundUrl() {
|
|
836
987
|
return `${client.url}/api/graphql/playground`;
|
|
@@ -876,6 +1027,7 @@ function createMetricsModule(client) {
|
|
|
876
1027
|
// src/modules/audit.ts
|
|
877
1028
|
function createAuditModule(client) {
|
|
878
1029
|
const get = (endpoint) => client.get(endpoint);
|
|
1030
|
+
const del = (endpoint) => client.delete(endpoint);
|
|
879
1031
|
return {
|
|
880
1032
|
async list(options) {
|
|
881
1033
|
const params = new URLSearchParams();
|
|
@@ -892,6 +1044,40 @@ function createAuditModule(client) {
|
|
|
892
1044
|
},
|
|
893
1045
|
async getActions() {
|
|
894
1046
|
return get("/api/audit/actions");
|
|
1047
|
+
},
|
|
1048
|
+
async exportLogs(format, options) {
|
|
1049
|
+
const params = new URLSearchParams();
|
|
1050
|
+
params.set("format", format);
|
|
1051
|
+
if (options?.action) params.set("action", options.action);
|
|
1052
|
+
if (options?.method) params.set("method", options.method);
|
|
1053
|
+
if (options?.path) params.set("path", options.path);
|
|
1054
|
+
if (options?.status_code) params.set("status_code", options.status_code.toString());
|
|
1055
|
+
if (options?.start_date) params.set("start_date", options.start_date);
|
|
1056
|
+
if (options?.end_date) params.set("end_date", options.end_date);
|
|
1057
|
+
const res = await fetch(`${client.url}/api/audit/export?${params.toString()}`, {
|
|
1058
|
+
headers: client.getHeaders(),
|
|
1059
|
+
credentials: "include"
|
|
1060
|
+
});
|
|
1061
|
+
if (!res.ok) {
|
|
1062
|
+
const data = await res.json();
|
|
1063
|
+
return { error: data.error || `Export failed: ${res.status}` };
|
|
1064
|
+
}
|
|
1065
|
+
const blob = await res.blob();
|
|
1066
|
+
const url = window.URL.createObjectURL(blob);
|
|
1067
|
+
const a = document.createElement("a");
|
|
1068
|
+
a.href = url;
|
|
1069
|
+
a.download = `audit_logs_${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.${format}`;
|
|
1070
|
+
document.body.appendChild(a);
|
|
1071
|
+
a.click();
|
|
1072
|
+
window.URL.revokeObjectURL(url);
|
|
1073
|
+
document.body.removeChild(a);
|
|
1074
|
+
return { success: true };
|
|
1075
|
+
},
|
|
1076
|
+
async purgePreview(olderThanDays) {
|
|
1077
|
+
return get(`/api/audit/purge/preview?older_than_days=${olderThanDays}`);
|
|
1078
|
+
},
|
|
1079
|
+
async purge(olderThanDays) {
|
|
1080
|
+
return del(`/api/audit/purge?older_than_days=${olderThanDays}`);
|
|
895
1081
|
}
|
|
896
1082
|
};
|
|
897
1083
|
}
|
|
@@ -1017,6 +1203,9 @@ var RealtimeService = class {
|
|
|
1017
1203
|
socket.onopen = () => {
|
|
1018
1204
|
console.log("BaaS Realtime: Connected");
|
|
1019
1205
|
this.reconnectAttempts = 0;
|
|
1206
|
+
for (const table of this.subscribers.keys()) {
|
|
1207
|
+
this.sendSub("subscribe", table);
|
|
1208
|
+
}
|
|
1020
1209
|
resolve();
|
|
1021
1210
|
};
|
|
1022
1211
|
socket.onmessage = (event) => {
|
|
@@ -1054,11 +1243,15 @@ var RealtimeService = class {
|
|
|
1054
1243
|
*/
|
|
1055
1244
|
async subscribe(table, action, callback) {
|
|
1056
1245
|
await this.connect();
|
|
1057
|
-
|
|
1246
|
+
const isNewTable = !this.subscribers.has(table);
|
|
1247
|
+
if (isNewTable) {
|
|
1058
1248
|
this.subscribers.set(table, /* @__PURE__ */ new Set());
|
|
1059
1249
|
}
|
|
1060
1250
|
const sub = { action, callback };
|
|
1061
1251
|
this.subscribers.get(table).add(sub);
|
|
1252
|
+
if (isNewTable && table !== "*") {
|
|
1253
|
+
this.sendSub("subscribe", table);
|
|
1254
|
+
}
|
|
1062
1255
|
return {
|
|
1063
1256
|
unsubscribe: () => {
|
|
1064
1257
|
const tableSubs = this.subscribers.get(table);
|
|
@@ -1066,11 +1259,18 @@ var RealtimeService = class {
|
|
|
1066
1259
|
tableSubs.delete(sub);
|
|
1067
1260
|
if (tableSubs.size === 0) {
|
|
1068
1261
|
this.subscribers.delete(table);
|
|
1262
|
+
if (table !== "*") this.sendSub("unsubscribe", table);
|
|
1069
1263
|
}
|
|
1070
1264
|
}
|
|
1071
1265
|
}
|
|
1072
1266
|
};
|
|
1073
1267
|
}
|
|
1268
|
+
/** Send a subscribe/unsubscribe control frame to the server. */
|
|
1269
|
+
sendSub(event, table) {
|
|
1270
|
+
if (this.socket?.readyState === WebSocket.OPEN) {
|
|
1271
|
+
this.socket.send(JSON.stringify({ event, table }));
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1074
1274
|
/**
|
|
1075
1275
|
* Handle incoming CDC events from the server
|
|
1076
1276
|
*/
|
|
@@ -1242,6 +1442,44 @@ function createIPWhitelistModule(client) {
|
|
|
1242
1442
|
};
|
|
1243
1443
|
}
|
|
1244
1444
|
|
|
1445
|
+
// src/modules/cache.ts
|
|
1446
|
+
function createCacheModule(client) {
|
|
1447
|
+
const get = (endpoint) => client.get(endpoint);
|
|
1448
|
+
const post = (endpoint, body) => client.post(endpoint, body);
|
|
1449
|
+
const patch = (endpoint, body) => client.patch(endpoint, body);
|
|
1450
|
+
const del = (endpoint) => client.delete(endpoint);
|
|
1451
|
+
return {
|
|
1452
|
+
async getStatus() {
|
|
1453
|
+
return get("/api/cache/status");
|
|
1454
|
+
},
|
|
1455
|
+
async getStats() {
|
|
1456
|
+
return get("/api/cache/stats");
|
|
1457
|
+
},
|
|
1458
|
+
async listPolicies() {
|
|
1459
|
+
return get("/api/cache/policies");
|
|
1460
|
+
},
|
|
1461
|
+
async updatePolicy(namespace, body) {
|
|
1462
|
+
return patch(`/api/cache/policies/${encodeURIComponent(namespace)}`, body);
|
|
1463
|
+
},
|
|
1464
|
+
async removePolicy(namespace) {
|
|
1465
|
+
return del(`/api/cache/policies/${encodeURIComponent(namespace)}`);
|
|
1466
|
+
},
|
|
1467
|
+
async invalidateNamespace(namespace) {
|
|
1468
|
+
return post(`/api/cache/invalidate/${encodeURIComponent(namespace)}`);
|
|
1469
|
+
},
|
|
1470
|
+
async invalidateAll() {
|
|
1471
|
+
return post("/api/cache/invalidate-all");
|
|
1472
|
+
},
|
|
1473
|
+
async listKeys(prefix, cursor = 0, count = 100) {
|
|
1474
|
+
const params = new URLSearchParams({ prefix, cursor: String(cursor), count: String(count) });
|
|
1475
|
+
return get(`/api/cache/keys?${params}`);
|
|
1476
|
+
},
|
|
1477
|
+
async inspectKey(key) {
|
|
1478
|
+
return get(`/api/cache/keys/inspect?key=${encodeURIComponent(key)}`);
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1245
1483
|
// src/client.ts
|
|
1246
1484
|
var BaasClient = class extends HttpClient {
|
|
1247
1485
|
// Feature modules
|
|
@@ -1268,8 +1506,9 @@ var BaasClient = class extends HttpClient {
|
|
|
1268
1506
|
corsOrigins;
|
|
1269
1507
|
policies;
|
|
1270
1508
|
ipWhitelist;
|
|
1271
|
-
|
|
1272
|
-
|
|
1509
|
+
cache;
|
|
1510
|
+
constructor(url, apiKey, options) {
|
|
1511
|
+
super(url, apiKey, options);
|
|
1273
1512
|
this.auth = createAuthModule(this);
|
|
1274
1513
|
this.users = createUsersModule(this);
|
|
1275
1514
|
this.database = createDatabaseModule(this);
|
|
@@ -1293,6 +1532,7 @@ var BaasClient = class extends HttpClient {
|
|
|
1293
1532
|
this.corsOrigins = createCorsOriginsModule(this);
|
|
1294
1533
|
this.policies = createPoliciesModule(this);
|
|
1295
1534
|
this.ipWhitelist = createIPWhitelistModule(this);
|
|
1535
|
+
this.cache = createCacheModule(this);
|
|
1296
1536
|
}
|
|
1297
1537
|
/**
|
|
1298
1538
|
* Create a query builder for fluent data queries
|
|
@@ -1300,6 +1540,18 @@ var BaasClient = class extends HttpClient {
|
|
|
1300
1540
|
from(table) {
|
|
1301
1541
|
return new QueryBuilder(table, this.url, this);
|
|
1302
1542
|
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Invoke a PL/pgSQL function (PostgREST `.rpc()` parity). Runs under the
|
|
1545
|
+
* caller's RLS context; args bind by name and are $N-safe. Pass [params] for
|
|
1546
|
+
* POST (named args in the body) or set `opts.get = true` for the GET variant.
|
|
1547
|
+
*/
|
|
1548
|
+
async rpc(fn, params, opts) {
|
|
1549
|
+
if (opts?.get) {
|
|
1550
|
+
const qs = params ? "?" + new URLSearchParams(Object.entries(params).map(([k, v]) => [k, String(v)])).toString() : "";
|
|
1551
|
+
return this.get(`/api/v1/rpc/${fn}${qs}`);
|
|
1552
|
+
}
|
|
1553
|
+
return this.post(`/api/v1/rpc/${fn}`, params ?? {});
|
|
1554
|
+
}
|
|
1303
1555
|
// ============================================
|
|
1304
1556
|
// BACKWARD COMPATIBILITY METHODS
|
|
1305
1557
|
// These delegate to the appropriate modules
|
|
@@ -1667,6 +1919,15 @@ var BaasClient = class extends HttpClient {
|
|
|
1667
1919
|
async getAuditActions() {
|
|
1668
1920
|
return this.audit.getActions();
|
|
1669
1921
|
}
|
|
1922
|
+
async exportAuditLogs(format, options) {
|
|
1923
|
+
return this.audit.exportLogs(format, options);
|
|
1924
|
+
}
|
|
1925
|
+
async previewPurgeAuditLogs(olderThanDays) {
|
|
1926
|
+
return this.audit.purgePreview(olderThanDays);
|
|
1927
|
+
}
|
|
1928
|
+
async purgeAuditLogs(olderThanDays) {
|
|
1929
|
+
return this.audit.purge(olderThanDays);
|
|
1930
|
+
}
|
|
1670
1931
|
// Realtime shortcuts
|
|
1671
1932
|
subscribe(table, action, callback) {
|
|
1672
1933
|
return this.realtime.subscribe(table, action, callback);
|
|
@@ -1721,3 +1982,4 @@ var BaasClient = class extends HttpClient {
|
|
|
1721
1982
|
HttpClient,
|
|
1722
1983
|
QueryBuilder
|
|
1723
1984
|
});
|
|
1985
|
+
//# sourceMappingURL=index.cjs.map
|