@mitway/sdk 0.3.0 → 0.5.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/README.md +16 -0
- package/dist/index.cjs +530 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +196 -3
- package/dist/index.d.ts +196 -3
- package/dist/index.js +528 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,22 @@ const { data: newPost } = await client.database
|
|
|
73
73
|
const { data: stats } = await client.database
|
|
74
74
|
.rpc('get_user_stats', { user_id: session.user.id });
|
|
75
75
|
|
|
76
|
+
// Upload a file
|
|
77
|
+
const { data: uploaded, error: upErr } = await client.storage
|
|
78
|
+
.from('avatars')
|
|
79
|
+
.upload('user-123/avatar.png', file, { contentType: 'image/png' });
|
|
80
|
+
|
|
81
|
+
// Share a link that expires in an hour
|
|
82
|
+
const { data: signed } = await client.storage
|
|
83
|
+
.from('private-docs')
|
|
84
|
+
.createSignedUrl('contract.pdf', { expiresIn: 3600 });
|
|
85
|
+
console.log(signed?.url);
|
|
86
|
+
|
|
87
|
+
// Public download URL (for public buckets)
|
|
88
|
+
const { data: pub } = client.storage
|
|
89
|
+
.from('avatars')
|
|
90
|
+
.getPublicUrl('user-123/avatar.png');
|
|
91
|
+
|
|
76
92
|
// Sign out
|
|
77
93
|
await client.auth.signOut();
|
|
78
94
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,8 @@ __export(index_exports, {
|
|
|
28
28
|
MitwayBaasError: () => MitwayBaasError,
|
|
29
29
|
Realtime: () => Realtime,
|
|
30
30
|
RealtimeChannel: () => RealtimeChannel,
|
|
31
|
+
Storage: () => Storage,
|
|
32
|
+
StorageBucketClient: () => StorageBucketClient,
|
|
31
33
|
TokenManager: () => TokenManager,
|
|
32
34
|
createClient: () => createClient,
|
|
33
35
|
default: () => index_default
|
|
@@ -197,6 +199,7 @@ var Logger = class {
|
|
|
197
199
|
|
|
198
200
|
// src/lib/token-manager.ts
|
|
199
201
|
var CSRF_TOKEN_COOKIE = "mitway_baas_csrf_token";
|
|
202
|
+
var DEFAULT_STORAGE_KEY = "mitway_baas_session";
|
|
200
203
|
function getCsrfToken() {
|
|
201
204
|
if (typeof document === "undefined") return null;
|
|
202
205
|
const match = document.cookie.split(";").find((c) => c.trim().startsWith(`${CSRF_TOKEN_COOKIE}=`));
|
|
@@ -216,13 +219,24 @@ function clearCsrfToken() {
|
|
|
216
219
|
}
|
|
217
220
|
var TokenManager = class {
|
|
218
221
|
accessToken = null;
|
|
222
|
+
refreshToken = null;
|
|
219
223
|
user = null;
|
|
224
|
+
persistSession;
|
|
225
|
+
storageKey;
|
|
220
226
|
/** Fired when the access token changes (used by long-lived consumers). */
|
|
221
227
|
onTokenChange = null;
|
|
228
|
+
constructor(opts) {
|
|
229
|
+
this.persistSession = opts?.persistSession ?? true;
|
|
230
|
+
this.storageKey = opts?.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
231
|
+
}
|
|
222
232
|
saveSession(session) {
|
|
223
233
|
const tokenChanged = session.accessToken !== this.accessToken;
|
|
224
234
|
this.accessToken = session.accessToken;
|
|
225
235
|
this.user = session.user;
|
|
236
|
+
if (session.refreshToken !== void 0) {
|
|
237
|
+
this.refreshToken = session.refreshToken ?? null;
|
|
238
|
+
}
|
|
239
|
+
this.persist();
|
|
226
240
|
if (tokenChanged && this.onTokenChange) {
|
|
227
241
|
this.onTokenChange();
|
|
228
242
|
}
|
|
@@ -231,6 +245,7 @@ var TokenManager = class {
|
|
|
231
245
|
if (!this.accessToken || !this.user) return null;
|
|
232
246
|
return {
|
|
233
247
|
accessToken: this.accessToken,
|
|
248
|
+
refreshToken: this.refreshToken ?? void 0,
|
|
234
249
|
user: this.user
|
|
235
250
|
};
|
|
236
251
|
}
|
|
@@ -240,24 +255,76 @@ var TokenManager = class {
|
|
|
240
255
|
setAccessToken(token) {
|
|
241
256
|
const tokenChanged = token !== this.accessToken;
|
|
242
257
|
this.accessToken = token;
|
|
258
|
+
this.persist();
|
|
243
259
|
if (tokenChanged && this.onTokenChange) {
|
|
244
260
|
this.onTokenChange();
|
|
245
261
|
}
|
|
246
262
|
}
|
|
263
|
+
getRefreshToken() {
|
|
264
|
+
return this.refreshToken;
|
|
265
|
+
}
|
|
266
|
+
setRefreshToken(token) {
|
|
267
|
+
this.refreshToken = token;
|
|
268
|
+
this.persist();
|
|
269
|
+
}
|
|
247
270
|
getUser() {
|
|
248
271
|
return this.user;
|
|
249
272
|
}
|
|
250
273
|
setUser(user) {
|
|
251
274
|
this.user = user;
|
|
275
|
+
this.persist();
|
|
252
276
|
}
|
|
253
277
|
clearSession() {
|
|
254
278
|
const hadToken = this.accessToken !== null;
|
|
255
279
|
this.accessToken = null;
|
|
280
|
+
this.refreshToken = null;
|
|
256
281
|
this.user = null;
|
|
282
|
+
this.removePersisted();
|
|
257
283
|
if (hadToken && this.onTokenChange) {
|
|
258
284
|
this.onTokenChange();
|
|
259
285
|
}
|
|
260
286
|
}
|
|
287
|
+
/**
|
|
288
|
+
* Restore the session from localStorage. Returns true if a persisted
|
|
289
|
+
* session was found and loaded into memory.
|
|
290
|
+
*/
|
|
291
|
+
restoreSession() {
|
|
292
|
+
if (!this.persistSession || typeof localStorage === "undefined") return false;
|
|
293
|
+
try {
|
|
294
|
+
const raw = localStorage.getItem(this.storageKey);
|
|
295
|
+
if (!raw) return false;
|
|
296
|
+
const stored = JSON.parse(raw);
|
|
297
|
+
if (!stored.accessToken || !stored.user) return false;
|
|
298
|
+
this.accessToken = stored.accessToken;
|
|
299
|
+
this.refreshToken = stored.refreshToken ?? null;
|
|
300
|
+
this.user = stored.user;
|
|
301
|
+
return true;
|
|
302
|
+
} catch {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
persist() {
|
|
307
|
+
if (!this.persistSession || typeof localStorage === "undefined") return;
|
|
308
|
+
if (!this.accessToken || !this.user) return;
|
|
309
|
+
try {
|
|
310
|
+
const data = {
|
|
311
|
+
accessToken: this.accessToken,
|
|
312
|
+
user: this.user
|
|
313
|
+
};
|
|
314
|
+
if (this.refreshToken) {
|
|
315
|
+
data.refreshToken = this.refreshToken;
|
|
316
|
+
}
|
|
317
|
+
localStorage.setItem(this.storageKey, JSON.stringify(data));
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
removePersisted() {
|
|
322
|
+
if (!this.persistSession || typeof localStorage === "undefined") return;
|
|
323
|
+
try {
|
|
324
|
+
localStorage.removeItem(this.storageKey);
|
|
325
|
+
} catch {
|
|
326
|
+
}
|
|
327
|
+
}
|
|
261
328
|
};
|
|
262
329
|
|
|
263
330
|
// src/lib/auth-envelope.ts
|
|
@@ -564,6 +631,29 @@ var HttpClient = class {
|
|
|
564
631
|
throw error;
|
|
565
632
|
}
|
|
566
633
|
}
|
|
634
|
+
/**
|
|
635
|
+
* Low-level fetch helper for binary bodies (uploads) and streamed responses
|
|
636
|
+
* (downloads). Applies the current Bearer token (user session → anon key
|
|
637
|
+
* fallback) plus any configured default headers, resolves `path` against
|
|
638
|
+
* `baseUrl`, and returns the raw `Response` — it does NOT unwrap the
|
|
639
|
+
* `{ data, error }` envelope, so the caller is responsible for status
|
|
640
|
+
* checking and parsing.
|
|
641
|
+
*
|
|
642
|
+
* Used by the storage module for object upload/download paths where the
|
|
643
|
+
* body or response is not JSON.
|
|
644
|
+
*/
|
|
645
|
+
async rawFetch(path, init = {}) {
|
|
646
|
+
const url = this.buildUrl(path);
|
|
647
|
+
const headers = new Headers(init.headers ?? {});
|
|
648
|
+
for (const [k, v] of Object.entries(this.defaultHeaders)) {
|
|
649
|
+
if (!headers.has(k)) headers.set(k, v);
|
|
650
|
+
}
|
|
651
|
+
if (!headers.has("Authorization")) {
|
|
652
|
+
const token = this.userToken ?? this.anonKey;
|
|
653
|
+
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
654
|
+
}
|
|
655
|
+
return this.fetch(url, { ...init, headers });
|
|
656
|
+
}
|
|
567
657
|
get(path, options) {
|
|
568
658
|
return this.request("GET", path, options);
|
|
569
659
|
}
|
|
@@ -654,6 +744,7 @@ var Auth = class {
|
|
|
654
744
|
saveSessionFromResponse(response) {
|
|
655
745
|
const session = {
|
|
656
746
|
accessToken: response.accessToken,
|
|
747
|
+
refreshToken: response.refreshToken,
|
|
657
748
|
user: response.user
|
|
658
749
|
};
|
|
659
750
|
if (response.csrfToken) {
|
|
@@ -747,6 +838,70 @@ var Auth = class {
|
|
|
747
838
|
return wrapError(error, "Session refresh failed");
|
|
748
839
|
}
|
|
749
840
|
}
|
|
841
|
+
/**
|
|
842
|
+
* Restore the session from localStorage and validate it with the backend.
|
|
843
|
+
* Call this once on app startup (e.g. in a React AuthProvider useEffect).
|
|
844
|
+
*
|
|
845
|
+
* Flow:
|
|
846
|
+
* 1. Read persisted session from localStorage.
|
|
847
|
+
* 2. Populate in-memory state (TokenManager + HttpClient).
|
|
848
|
+
* 3. Validate with `GET /api/auth/sessions/current`.
|
|
849
|
+
* - If the access token expired, the HttpClient auto-refresh kicks in
|
|
850
|
+
* using the persisted refresh token (sent in the POST body, not
|
|
851
|
+
* cookies — works cross-site).
|
|
852
|
+
* 4. Return the validated user or an error.
|
|
853
|
+
*
|
|
854
|
+
* If no persisted session exists, returns `{ data: null, error }` — the
|
|
855
|
+
* app should show the login page.
|
|
856
|
+
*/
|
|
857
|
+
async initialize() {
|
|
858
|
+
const restored = this.tokenManager.restoreSession();
|
|
859
|
+
if (!restored) {
|
|
860
|
+
return {
|
|
861
|
+
data: null,
|
|
862
|
+
error: new MitwayBaasError("No persisted session", 0, "NO_SESSION")
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
const session = this.tokenManager.getSession();
|
|
866
|
+
if (!session) {
|
|
867
|
+
return {
|
|
868
|
+
data: null,
|
|
869
|
+
error: new MitwayBaasError("No persisted session", 0, "NO_SESSION")
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
this.http.setAuthToken(session.accessToken);
|
|
873
|
+
const refreshToken = this.tokenManager.getRefreshToken();
|
|
874
|
+
if (refreshToken) {
|
|
875
|
+
this.http.setRefreshToken(refreshToken);
|
|
876
|
+
}
|
|
877
|
+
try {
|
|
878
|
+
const response = await this.http.get(
|
|
879
|
+
"/api/auth/sessions/current"
|
|
880
|
+
);
|
|
881
|
+
if (response?.user) {
|
|
882
|
+
this.tokenManager.setUser(response.user);
|
|
883
|
+
return {
|
|
884
|
+
data: {
|
|
885
|
+
user: response.user,
|
|
886
|
+
accessToken: session.accessToken
|
|
887
|
+
},
|
|
888
|
+
error: null
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
this.tokenManager.clearSession();
|
|
892
|
+
this.http.setAuthToken(null);
|
|
893
|
+
this.http.setRefreshToken(null);
|
|
894
|
+
return {
|
|
895
|
+
data: null,
|
|
896
|
+
error: new MitwayBaasError("Invalid session", 401, "INVALID_SESSION")
|
|
897
|
+
};
|
|
898
|
+
} catch (error) {
|
|
899
|
+
this.tokenManager.clearSession();
|
|
900
|
+
this.http.setAuthToken(null);
|
|
901
|
+
this.http.setRefreshToken(null);
|
|
902
|
+
return wrapError(error, "Session restore failed");
|
|
903
|
+
}
|
|
904
|
+
}
|
|
750
905
|
/**
|
|
751
906
|
* Get the current in-memory session, or null if the user is not signed in.
|
|
752
907
|
* Synchronous — does not hit the network.
|
|
@@ -1513,6 +1668,373 @@ var Realtime = class {
|
|
|
1513
1668
|
}
|
|
1514
1669
|
};
|
|
1515
1670
|
|
|
1671
|
+
// src/modules/storage.ts
|
|
1672
|
+
function bucketFromWire(row) {
|
|
1673
|
+
return {
|
|
1674
|
+
id: row.id,
|
|
1675
|
+
name: row.name,
|
|
1676
|
+
public: row.public,
|
|
1677
|
+
fileSizeLimitBytes: row.file_size_limit_bytes,
|
|
1678
|
+
allowedMimeTypes: row.allowed_mime_types,
|
|
1679
|
+
createdAt: row.created_at,
|
|
1680
|
+
updatedAt: row.updated_at
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
function objectFromWire(row, bucketName) {
|
|
1684
|
+
return {
|
|
1685
|
+
id: row.id,
|
|
1686
|
+
bucket: bucketName,
|
|
1687
|
+
key: row.key,
|
|
1688
|
+
size: row.size,
|
|
1689
|
+
mimeType: row.mime_type,
|
|
1690
|
+
etag: row.etag,
|
|
1691
|
+
cacheControl: row.cache_control,
|
|
1692
|
+
contentDisposition: row.content_disposition,
|
|
1693
|
+
uploadedBy: row.uploaded_by,
|
|
1694
|
+
uploadedAt: row.uploaded_at,
|
|
1695
|
+
updatedAt: row.updated_at
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
function configFromWire(row) {
|
|
1699
|
+
return {
|
|
1700
|
+
defaultFileSizeLimitBytes: row.default_file_size_limit_bytes,
|
|
1701
|
+
maxFileSizeLimitBytes: row.max_file_size_limit_bytes,
|
|
1702
|
+
tenantStorageQuotaBytes: row.tenant_storage_quota_bytes,
|
|
1703
|
+
reservedSpaceBytes: row.reserved_space_bytes,
|
|
1704
|
+
signedUrlDefaultTtlSec: row.signed_url_default_ttl_sec,
|
|
1705
|
+
signedUrlMaxTtlSec: row.signed_url_max_ttl_sec
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
function encodeKey(key) {
|
|
1709
|
+
return key.split("/").map(encodeURIComponent).join("/");
|
|
1710
|
+
}
|
|
1711
|
+
function wrapError2(err, fallback) {
|
|
1712
|
+
if (err instanceof MitwayBaasError) return { data: null, error: err };
|
|
1713
|
+
return {
|
|
1714
|
+
data: null,
|
|
1715
|
+
error: new MitwayBaasError(
|
|
1716
|
+
err instanceof Error ? err.message : fallback,
|
|
1717
|
+
0,
|
|
1718
|
+
"STORAGE_ERROR"
|
|
1719
|
+
)
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
async function readEnvelopeError(response) {
|
|
1723
|
+
let code = "STORAGE_ERROR";
|
|
1724
|
+
let message = `HTTP ${response.status}`;
|
|
1725
|
+
try {
|
|
1726
|
+
const body = await response.json();
|
|
1727
|
+
if (body && body.error) {
|
|
1728
|
+
code = body.error.code ?? code;
|
|
1729
|
+
message = body.error.message ?? message;
|
|
1730
|
+
}
|
|
1731
|
+
} catch {
|
|
1732
|
+
}
|
|
1733
|
+
return new MitwayBaasError(message, response.status, code);
|
|
1734
|
+
}
|
|
1735
|
+
var StorageBucketClient = class {
|
|
1736
|
+
constructor(http, bucketName) {
|
|
1737
|
+
this.http = http;
|
|
1738
|
+
this.bucketName = bucketName;
|
|
1739
|
+
}
|
|
1740
|
+
http;
|
|
1741
|
+
bucketName;
|
|
1742
|
+
bucketBase() {
|
|
1743
|
+
return `/api/storage/buckets/${encodeURIComponent(this.bucketName)}`;
|
|
1744
|
+
}
|
|
1745
|
+
objectPath(key) {
|
|
1746
|
+
return `${this.bucketBase()}/objects/${encodeKey(key)}`;
|
|
1747
|
+
}
|
|
1748
|
+
async upload(key, body, opts = {}) {
|
|
1749
|
+
try {
|
|
1750
|
+
const method = opts.upsert ? "PUT" : "POST";
|
|
1751
|
+
const headers = {
|
|
1752
|
+
"Content-Type": opts.contentType ?? "application/octet-stream"
|
|
1753
|
+
};
|
|
1754
|
+
if (opts.cacheControl) headers["Cache-Control"] = opts.cacheControl;
|
|
1755
|
+
if (opts.contentDisposition)
|
|
1756
|
+
headers["Content-Disposition"] = opts.contentDisposition;
|
|
1757
|
+
const response = await this.http.rawFetch(this.objectPath(key), {
|
|
1758
|
+
method,
|
|
1759
|
+
headers,
|
|
1760
|
+
body,
|
|
1761
|
+
signal: opts.abortSignal
|
|
1762
|
+
});
|
|
1763
|
+
if (!response.ok) {
|
|
1764
|
+
return { data: null, error: await readEnvelopeError(response) };
|
|
1765
|
+
}
|
|
1766
|
+
const parsed = await response.json();
|
|
1767
|
+
if (parsed.error || !parsed.data) {
|
|
1768
|
+
return {
|
|
1769
|
+
data: null,
|
|
1770
|
+
error: new MitwayBaasError(
|
|
1771
|
+
parsed.error?.message ?? "Upload failed",
|
|
1772
|
+
response.status,
|
|
1773
|
+
parsed.error?.code ?? "STORAGE_ERROR"
|
|
1774
|
+
)
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
return { data: objectFromWire(parsed.data, this.bucketName), error: null };
|
|
1778
|
+
} catch (err) {
|
|
1779
|
+
return wrapError2(err, "Upload failed");
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
async download(key, opts = {}) {
|
|
1783
|
+
try {
|
|
1784
|
+
const headers = {};
|
|
1785
|
+
if (opts.range) {
|
|
1786
|
+
headers["Range"] = `bytes=${opts.range.start}-${opts.range.end}`;
|
|
1787
|
+
}
|
|
1788
|
+
const response = await this.http.rawFetch(this.objectPath(key), {
|
|
1789
|
+
method: "GET",
|
|
1790
|
+
headers,
|
|
1791
|
+
signal: opts.abortSignal
|
|
1792
|
+
});
|
|
1793
|
+
if (!response.ok) {
|
|
1794
|
+
return { data: null, error: await readEnvelopeError(response) };
|
|
1795
|
+
}
|
|
1796
|
+
const blob = await response.blob();
|
|
1797
|
+
return { data: blob, error: null };
|
|
1798
|
+
} catch (err) {
|
|
1799
|
+
return wrapError2(err, "Download failed");
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
async getStream(key, opts = {}) {
|
|
1803
|
+
try {
|
|
1804
|
+
const headers = {};
|
|
1805
|
+
if (opts.range) {
|
|
1806
|
+
headers["Range"] = `bytes=${opts.range.start}-${opts.range.end}`;
|
|
1807
|
+
}
|
|
1808
|
+
const response = await this.http.rawFetch(this.objectPath(key), {
|
|
1809
|
+
method: "GET",
|
|
1810
|
+
headers,
|
|
1811
|
+
signal: opts.abortSignal
|
|
1812
|
+
});
|
|
1813
|
+
if (!response.ok) {
|
|
1814
|
+
return { data: null, error: await readEnvelopeError(response) };
|
|
1815
|
+
}
|
|
1816
|
+
if (!response.body) {
|
|
1817
|
+
return {
|
|
1818
|
+
data: null,
|
|
1819
|
+
error: new MitwayBaasError(
|
|
1820
|
+
"Response body is not a stream",
|
|
1821
|
+
response.status,
|
|
1822
|
+
"STORAGE_ERROR"
|
|
1823
|
+
)
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
return {
|
|
1827
|
+
data: response.body,
|
|
1828
|
+
error: null
|
|
1829
|
+
};
|
|
1830
|
+
} catch (err) {
|
|
1831
|
+
return wrapError2(err, "Download failed");
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
async remove(keys) {
|
|
1835
|
+
try {
|
|
1836
|
+
const results = await Promise.allSettled(
|
|
1837
|
+
keys.map(
|
|
1838
|
+
(key) => this.http.rawFetch(this.objectPath(key), { method: "DELETE" })
|
|
1839
|
+
)
|
|
1840
|
+
);
|
|
1841
|
+
const removed = [];
|
|
1842
|
+
const errors = [];
|
|
1843
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1844
|
+
const key = keys[i];
|
|
1845
|
+
const r = results[i];
|
|
1846
|
+
if (r.status === "fulfilled" && r.value.ok) {
|
|
1847
|
+
removed.push(key);
|
|
1848
|
+
} else if (r.status === "fulfilled") {
|
|
1849
|
+
errors.push(`${key}: HTTP ${r.value.status}`);
|
|
1850
|
+
} else {
|
|
1851
|
+
const msg = r.reason instanceof Error ? r.reason.message : String(r.reason);
|
|
1852
|
+
errors.push(`${key}: ${msg}`);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
if (errors.length > 0) {
|
|
1856
|
+
return {
|
|
1857
|
+
data: null,
|
|
1858
|
+
error: new MitwayBaasError(
|
|
1859
|
+
`Failed to delete some objects: ${errors.join("; ")}`,
|
|
1860
|
+
0,
|
|
1861
|
+
"STORAGE_ERROR"
|
|
1862
|
+
)
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
return { data: { removed }, error: null };
|
|
1866
|
+
} catch (err) {
|
|
1867
|
+
return wrapError2(err, "Delete failed");
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
async list(opts = {}) {
|
|
1871
|
+
try {
|
|
1872
|
+
const params = {};
|
|
1873
|
+
if (opts.prefix !== void 0) params.prefix = opts.prefix;
|
|
1874
|
+
if (opts.limit !== void 0) params.limit = String(opts.limit);
|
|
1875
|
+
if (opts.startAfter !== void 0) params.start_after = opts.startAfter;
|
|
1876
|
+
const rows = await this.http.get(
|
|
1877
|
+
`${this.bucketBase()}/objects`,
|
|
1878
|
+
{ params }
|
|
1879
|
+
);
|
|
1880
|
+
return {
|
|
1881
|
+
data: rows.map((r) => objectFromWire(r, this.bucketName)),
|
|
1882
|
+
error: null
|
|
1883
|
+
};
|
|
1884
|
+
} catch (err) {
|
|
1885
|
+
return wrapError2(err, "List failed");
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
async copy(fromKey, toKey, toBucket) {
|
|
1889
|
+
try {
|
|
1890
|
+
const row = await this.http.post(
|
|
1891
|
+
`${this.objectPath(fromKey)}/copy`,
|
|
1892
|
+
{
|
|
1893
|
+
dest_bucket: toBucket ?? this.bucketName,
|
|
1894
|
+
dest_key: toKey
|
|
1895
|
+
}
|
|
1896
|
+
);
|
|
1897
|
+
return {
|
|
1898
|
+
data: objectFromWire(row, toBucket ?? this.bucketName),
|
|
1899
|
+
error: null
|
|
1900
|
+
};
|
|
1901
|
+
} catch (err) {
|
|
1902
|
+
return wrapError2(err, "Copy failed");
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
async move(fromKey, toKey, toBucket) {
|
|
1906
|
+
try {
|
|
1907
|
+
const row = await this.http.post(
|
|
1908
|
+
`${this.objectPath(fromKey)}/move`,
|
|
1909
|
+
{
|
|
1910
|
+
dest_bucket: toBucket ?? this.bucketName,
|
|
1911
|
+
dest_key: toKey
|
|
1912
|
+
}
|
|
1913
|
+
);
|
|
1914
|
+
return {
|
|
1915
|
+
data: objectFromWire(row, toBucket ?? this.bucketName),
|
|
1916
|
+
error: null
|
|
1917
|
+
};
|
|
1918
|
+
} catch (err) {
|
|
1919
|
+
return wrapError2(err, "Move failed");
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
async createSignedUrl(key, opts = {}) {
|
|
1923
|
+
try {
|
|
1924
|
+
const body = {};
|
|
1925
|
+
if (opts.expiresIn !== void 0) body.expires_in = opts.expiresIn;
|
|
1926
|
+
const wire = await this.http.post(`${this.objectPath(key)}/sign`, body);
|
|
1927
|
+
return {
|
|
1928
|
+
data: {
|
|
1929
|
+
url: wire.url,
|
|
1930
|
+
token: wire.token,
|
|
1931
|
+
expiresAt: wire.expiresAt
|
|
1932
|
+
},
|
|
1933
|
+
error: null
|
|
1934
|
+
};
|
|
1935
|
+
} catch (err) {
|
|
1936
|
+
return wrapError2(err, "Sign failed");
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
getPublicUrl(key) {
|
|
1940
|
+
const url = `${this.http.baseUrl.replace(/\/$/, "")}${this.objectPath(key)}`;
|
|
1941
|
+
return { data: { url } };
|
|
1942
|
+
}
|
|
1943
|
+
};
|
|
1944
|
+
var Storage = class {
|
|
1945
|
+
constructor(http) {
|
|
1946
|
+
this.http = http;
|
|
1947
|
+
}
|
|
1948
|
+
http;
|
|
1949
|
+
/** Scope subsequent operations to a single bucket. */
|
|
1950
|
+
from(bucketName) {
|
|
1951
|
+
return new StorageBucketClient(this.http, bucketName);
|
|
1952
|
+
}
|
|
1953
|
+
// --- Admin (require service_role) ---
|
|
1954
|
+
async listBuckets() {
|
|
1955
|
+
try {
|
|
1956
|
+
const rows = await this.http.get("/api/storage/buckets");
|
|
1957
|
+
return { data: rows.map(bucketFromWire), error: null };
|
|
1958
|
+
} catch (err) {
|
|
1959
|
+
return wrapError2(err, "listBuckets failed");
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
async getBucket(name) {
|
|
1963
|
+
try {
|
|
1964
|
+
const row = await this.http.get(
|
|
1965
|
+
`/api/storage/buckets/${encodeURIComponent(name)}`
|
|
1966
|
+
);
|
|
1967
|
+
return { data: bucketFromWire(row), error: null };
|
|
1968
|
+
} catch (err) {
|
|
1969
|
+
return wrapError2(err, "getBucket failed");
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
async createBucket(name, opts = {}) {
|
|
1973
|
+
try {
|
|
1974
|
+
const body = { name };
|
|
1975
|
+
if (opts.public !== void 0) body.public = opts.public;
|
|
1976
|
+
if (opts.fileSizeLimitBytes !== void 0)
|
|
1977
|
+
body.file_size_limit_bytes = opts.fileSizeLimitBytes;
|
|
1978
|
+
if (opts.allowedMimeTypes !== void 0)
|
|
1979
|
+
body.allowed_mime_types = opts.allowedMimeTypes;
|
|
1980
|
+
const row = await this.http.post(
|
|
1981
|
+
"/api/storage/buckets",
|
|
1982
|
+
body
|
|
1983
|
+
);
|
|
1984
|
+
return { data: bucketFromWire(row), error: null };
|
|
1985
|
+
} catch (err) {
|
|
1986
|
+
return wrapError2(err, "createBucket failed");
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
async updateBucket(name, opts) {
|
|
1990
|
+
try {
|
|
1991
|
+
const body = {};
|
|
1992
|
+
if (opts.public !== void 0) body.public = opts.public;
|
|
1993
|
+
if (opts.fileSizeLimitBytes !== void 0)
|
|
1994
|
+
body.file_size_limit_bytes = opts.fileSizeLimitBytes;
|
|
1995
|
+
if (opts.allowedMimeTypes !== void 0)
|
|
1996
|
+
body.allowed_mime_types = opts.allowedMimeTypes;
|
|
1997
|
+
const row = await this.http.patch(
|
|
1998
|
+
`/api/storage/buckets/${encodeURIComponent(name)}`,
|
|
1999
|
+
body
|
|
2000
|
+
);
|
|
2001
|
+
return { data: bucketFromWire(row), error: null };
|
|
2002
|
+
} catch (err) {
|
|
2003
|
+
return wrapError2(err, "updateBucket failed");
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
async deleteBucket(name) {
|
|
2007
|
+
try {
|
|
2008
|
+
await this.http.delete(
|
|
2009
|
+
`/api/storage/buckets/${encodeURIComponent(name)}`
|
|
2010
|
+
);
|
|
2011
|
+
return { data: null, error: null };
|
|
2012
|
+
} catch (err) {
|
|
2013
|
+
return wrapError2(err, "deleteBucket failed");
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
async emptyBucket(name) {
|
|
2017
|
+
try {
|
|
2018
|
+
const result = await this.http.post(
|
|
2019
|
+
`/api/storage/buckets/${encodeURIComponent(name)}/empty`,
|
|
2020
|
+
{}
|
|
2021
|
+
);
|
|
2022
|
+
return { data: result, error: null };
|
|
2023
|
+
} catch (err) {
|
|
2024
|
+
return wrapError2(err, "emptyBucket failed");
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
// --- Storage config (service_role) ---
|
|
2028
|
+
async getConfig() {
|
|
2029
|
+
try {
|
|
2030
|
+
const row = await this.http.get("/api/storage/config");
|
|
2031
|
+
return { data: configFromWire(row), error: null };
|
|
2032
|
+
} catch (err) {
|
|
2033
|
+
return wrapError2(err, "getConfig failed");
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
};
|
|
2037
|
+
|
|
1516
2038
|
// src/client.ts
|
|
1517
2039
|
var MitwayBaasClient = class {
|
|
1518
2040
|
http;
|
|
@@ -1520,9 +2042,13 @@ var MitwayBaasClient = class {
|
|
|
1520
2042
|
auth;
|
|
1521
2043
|
database;
|
|
1522
2044
|
realtime;
|
|
2045
|
+
storage;
|
|
1523
2046
|
constructor(config = {}) {
|
|
1524
2047
|
const logger = new Logger(config.debug);
|
|
1525
|
-
this.tokenManager = new TokenManager(
|
|
2048
|
+
this.tokenManager = new TokenManager({
|
|
2049
|
+
persistSession: config.persistSession,
|
|
2050
|
+
storageKey: config.storageKey
|
|
2051
|
+
});
|
|
1526
2052
|
this.http = new HttpClient(config, this.tokenManager, logger);
|
|
1527
2053
|
this.auth = new Auth(this.http, this.tokenManager);
|
|
1528
2054
|
this.database = new Database(this.http, this.tokenManager, config.anonKey);
|
|
@@ -1532,6 +2058,7 @@ var MitwayBaasClient = class {
|
|
|
1532
2058
|
config.anonKey,
|
|
1533
2059
|
config.realtime
|
|
1534
2060
|
);
|
|
2061
|
+
this.storage = new Storage(this.http);
|
|
1535
2062
|
}
|
|
1536
2063
|
/**
|
|
1537
2064
|
* Escape hatch for callers that need to make custom requests against the
|
|
@@ -1557,6 +2084,8 @@ var index_default = MitwayBaasClient;
|
|
|
1557
2084
|
MitwayBaasError,
|
|
1558
2085
|
Realtime,
|
|
1559
2086
|
RealtimeChannel,
|
|
2087
|
+
Storage,
|
|
2088
|
+
StorageBucketClient,
|
|
1560
2089
|
TokenManager,
|
|
1561
2090
|
createClient
|
|
1562
2091
|
});
|