@mitway/sdk 0.2.4 → 0.4.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 +440 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +227 -22
- package/dist/index.d.ts +227 -22
- package/dist/index.js +438 -25
- 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
|
|
@@ -564,6 +566,29 @@ var HttpClient = class {
|
|
|
564
566
|
throw error;
|
|
565
567
|
}
|
|
566
568
|
}
|
|
569
|
+
/**
|
|
570
|
+
* Low-level fetch helper for binary bodies (uploads) and streamed responses
|
|
571
|
+
* (downloads). Applies the current Bearer token (user session → anon key
|
|
572
|
+
* fallback) plus any configured default headers, resolves `path` against
|
|
573
|
+
* `baseUrl`, and returns the raw `Response` — it does NOT unwrap the
|
|
574
|
+
* `{ data, error }` envelope, so the caller is responsible for status
|
|
575
|
+
* checking and parsing.
|
|
576
|
+
*
|
|
577
|
+
* Used by the storage module for object upload/download paths where the
|
|
578
|
+
* body or response is not JSON.
|
|
579
|
+
*/
|
|
580
|
+
async rawFetch(path, init = {}) {
|
|
581
|
+
const url = this.buildUrl(path);
|
|
582
|
+
const headers = new Headers(init.headers ?? {});
|
|
583
|
+
for (const [k, v] of Object.entries(this.defaultHeaders)) {
|
|
584
|
+
if (!headers.has(k)) headers.set(k, v);
|
|
585
|
+
}
|
|
586
|
+
if (!headers.has("Authorization")) {
|
|
587
|
+
const token = this.userToken ?? this.anonKey;
|
|
588
|
+
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
589
|
+
}
|
|
590
|
+
return this.fetch(url, { ...init, headers });
|
|
591
|
+
}
|
|
567
592
|
get(path, options) {
|
|
568
593
|
return this.request("GET", path, options);
|
|
569
594
|
}
|
|
@@ -893,6 +918,11 @@ var Database = class {
|
|
|
893
918
|
// src/modules/realtime.ts
|
|
894
919
|
var import_socket = require("socket.io-client");
|
|
895
920
|
var PRESENCE_HEARTBEAT_MS = 2e4;
|
|
921
|
+
function makeChannelError(code, message) {
|
|
922
|
+
const err = new Error(message);
|
|
923
|
+
err.code = code;
|
|
924
|
+
return err;
|
|
925
|
+
}
|
|
896
926
|
var DEFAULT_CONNECT_TIMEOUT_MS = 1e4;
|
|
897
927
|
var RealtimeChannel = class {
|
|
898
928
|
constructor(topic, realtime, options = {}) {
|
|
@@ -963,12 +993,19 @@ var RealtimeChannel = class {
|
|
|
963
993
|
this.statusCallback?.("SUBSCRIBED");
|
|
964
994
|
} catch (err) {
|
|
965
995
|
this.state = "errored";
|
|
966
|
-
this.statusCallback?.(
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
996
|
+
this.statusCallback?.(
|
|
997
|
+
"CHANNEL_ERROR",
|
|
998
|
+
makeChannelError("REJOIN_FAILED", err instanceof Error ? err.message : String(err))
|
|
999
|
+
);
|
|
970
1000
|
}
|
|
971
1001
|
}
|
|
1002
|
+
// ── implementation signature (not in public type surface).
|
|
1003
|
+
// The callback type is intentionally broad: each overload above pins a
|
|
1004
|
+
// specific payload shape, but TypeScript overload resolution needs the
|
|
1005
|
+
// implementation to accept the union of every narrow callback without
|
|
1006
|
+
// the contravariance conflict (TS2394). `any` is the standard escape
|
|
1007
|
+
// hatch for this exact pattern and is confined to this one line — the
|
|
1008
|
+
// public surface users see is strictly typed via the overloads.
|
|
972
1009
|
on(type, filter, callback) {
|
|
973
1010
|
if (type === "postgres_changes") {
|
|
974
1011
|
this.bindings.push({
|
|
@@ -1070,18 +1107,18 @@ var RealtimeChannel = class {
|
|
|
1070
1107
|
} catch (err) {
|
|
1071
1108
|
this.state = "errored";
|
|
1072
1109
|
const message = err instanceof Error ? err.message : String(err);
|
|
1073
|
-
this.statusCallback?.(
|
|
1074
|
-
|
|
1075
|
-
message
|
|
1076
|
-
|
|
1110
|
+
this.statusCallback?.(
|
|
1111
|
+
"CHANNEL_ERROR",
|
|
1112
|
+
makeChannelError("SUBSCRIBE_FAILED", message)
|
|
1113
|
+
);
|
|
1077
1114
|
}
|
|
1078
1115
|
},
|
|
1079
1116
|
(err) => {
|
|
1080
1117
|
this.state = "errored";
|
|
1081
|
-
this.statusCallback?.(
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1118
|
+
this.statusCallback?.(
|
|
1119
|
+
"CHANNEL_ERROR",
|
|
1120
|
+
makeChannelError("CONNECT_FAILED", err.message)
|
|
1121
|
+
);
|
|
1085
1122
|
}
|
|
1086
1123
|
);
|
|
1087
1124
|
return this;
|
|
@@ -1134,6 +1171,12 @@ var RealtimeChannel = class {
|
|
|
1134
1171
|
* events, write to your own application table and enable
|
|
1135
1172
|
* `postgres_changes` on it; the SDK will surface the INSERT as a
|
|
1136
1173
|
* `postgres_changes` event without a separate channel.send call.
|
|
1174
|
+
*
|
|
1175
|
+
* The returned promise always resolves with the server ack, so callers
|
|
1176
|
+
* can `await channel.send(...)` to confirm delivery + get the server-
|
|
1177
|
+
* assigned `message_id`. There's no performance cost — Socket.IO piggy-
|
|
1178
|
+
* backs the ack on the same frame. Callers that don't need it just
|
|
1179
|
+
* don't await.
|
|
1137
1180
|
*/
|
|
1138
1181
|
async send(args) {
|
|
1139
1182
|
if (args.type !== "broadcast") {
|
|
@@ -1160,9 +1203,7 @@ var RealtimeChannel = class {
|
|
|
1160
1203
|
if (!socket) {
|
|
1161
1204
|
throw new MitwayBaasError("Socket not connected", 503, "NOT_CONNECTED");
|
|
1162
1205
|
}
|
|
1163
|
-
const
|
|
1164
|
-
const wantAck = broadcastCfg?.ack === true;
|
|
1165
|
-
const self = broadcastCfg?.self;
|
|
1206
|
+
const self = this.options.config?.broadcast?.self;
|
|
1166
1207
|
const wirePayload = {
|
|
1167
1208
|
channel: this.topic,
|
|
1168
1209
|
event: args.event,
|
|
@@ -1171,10 +1212,6 @@ var RealtimeChannel = class {
|
|
|
1171
1212
|
if (self === false) {
|
|
1172
1213
|
wirePayload.self = false;
|
|
1173
1214
|
}
|
|
1174
|
-
if (!wantAck) {
|
|
1175
|
-
socket.emit("realtime:publish", wirePayload);
|
|
1176
|
-
return;
|
|
1177
|
-
}
|
|
1178
1215
|
return await new Promise((resolve) => {
|
|
1179
1216
|
socket.emit(
|
|
1180
1217
|
"realtime:publish",
|
|
@@ -1215,7 +1252,7 @@ var RealtimeChannel = class {
|
|
|
1215
1252
|
if (!b.subscriptionId || !pcEvent.ids.includes(b.subscriptionId)) {
|
|
1216
1253
|
continue;
|
|
1217
1254
|
}
|
|
1218
|
-
const matchesEvent = b.filter.event === "*" || b.filter.event === pcEvent.data.
|
|
1255
|
+
const matchesEvent = b.filter.event === "*" || b.filter.event === pcEvent.data.eventType;
|
|
1219
1256
|
if (!matchesEvent) {
|
|
1220
1257
|
continue;
|
|
1221
1258
|
}
|
|
@@ -1274,7 +1311,13 @@ var RealtimeChannel = class {
|
|
|
1274
1311
|
continue;
|
|
1275
1312
|
}
|
|
1276
1313
|
try {
|
|
1277
|
-
|
|
1314
|
+
if (payload.event === "sync") {
|
|
1315
|
+
b.callback();
|
|
1316
|
+
} else if (payload.event === "join") {
|
|
1317
|
+
b.callback(payload);
|
|
1318
|
+
} else {
|
|
1319
|
+
b.callback(payload);
|
|
1320
|
+
}
|
|
1278
1321
|
} catch {
|
|
1279
1322
|
}
|
|
1280
1323
|
}
|
|
@@ -1293,7 +1336,7 @@ var RealtimeChannel = class {
|
|
|
1293
1336
|
"realtime:subscribe",
|
|
1294
1337
|
{ channel: this.topic, private: this.isPrivate },
|
|
1295
1338
|
(ack) => {
|
|
1296
|
-
if (ack.ok) {
|
|
1339
|
+
if (ack.status === "ok") {
|
|
1297
1340
|
resolve();
|
|
1298
1341
|
} else {
|
|
1299
1342
|
reject(new Error(ack.error?.message ?? "subscribe failed"));
|
|
@@ -1317,7 +1360,7 @@ var RealtimeChannel = class {
|
|
|
1317
1360
|
filter: b.filter.filter
|
|
1318
1361
|
},
|
|
1319
1362
|
(ack) => {
|
|
1320
|
-
if (ack.ok && ack.subscription_id) {
|
|
1363
|
+
if (ack.status === "ok" && ack.subscription_id) {
|
|
1321
1364
|
b.subscriptionId = ack.subscription_id;
|
|
1322
1365
|
resolve();
|
|
1323
1366
|
} else {
|
|
@@ -1363,13 +1406,14 @@ var Realtime = class {
|
|
|
1363
1406
|
* The optional `opts` argument lets the caller configure the channel:
|
|
1364
1407
|
* * `config.private` — enable subscribe-side authorization against
|
|
1365
1408
|
* `realtime.authorize_subscribe(...)` on the tenant DB.
|
|
1366
|
-
* * `config.broadcast.ack` — `channel.send()` resolves with the
|
|
1367
|
-
* server's ack (message_id) instead of fire-and-forget.
|
|
1368
1409
|
* * `config.broadcast.self` — `false` excludes the sender from the
|
|
1369
1410
|
* fan-out (defaults to `true`).
|
|
1370
1411
|
* * `config.presence.key` — stable presence key to group multiple
|
|
1371
1412
|
* tabs of the same user under one entry.
|
|
1372
1413
|
*
|
|
1414
|
+
* `channel.send()` always resolves with the server ack (see its own
|
|
1415
|
+
* docstring); there is no separate opt-in needed.
|
|
1416
|
+
*
|
|
1373
1417
|
* Options are locked in when the channel is first created; subsequent
|
|
1374
1418
|
* `.channel('same')` calls with different opts are ignored. Pass a
|
|
1375
1419
|
* different topic to get a different-configured channel.
|
|
@@ -1494,6 +1538,373 @@ var Realtime = class {
|
|
|
1494
1538
|
}
|
|
1495
1539
|
};
|
|
1496
1540
|
|
|
1541
|
+
// src/modules/storage.ts
|
|
1542
|
+
function bucketFromWire(row) {
|
|
1543
|
+
return {
|
|
1544
|
+
id: row.id,
|
|
1545
|
+
name: row.name,
|
|
1546
|
+
public: row.public,
|
|
1547
|
+
fileSizeLimitBytes: row.file_size_limit_bytes,
|
|
1548
|
+
allowedMimeTypes: row.allowed_mime_types,
|
|
1549
|
+
createdAt: row.created_at,
|
|
1550
|
+
updatedAt: row.updated_at
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
function objectFromWire(row, bucketName) {
|
|
1554
|
+
return {
|
|
1555
|
+
id: row.id,
|
|
1556
|
+
bucket: bucketName,
|
|
1557
|
+
key: row.key,
|
|
1558
|
+
size: row.size,
|
|
1559
|
+
mimeType: row.mime_type,
|
|
1560
|
+
etag: row.etag,
|
|
1561
|
+
cacheControl: row.cache_control,
|
|
1562
|
+
contentDisposition: row.content_disposition,
|
|
1563
|
+
uploadedBy: row.uploaded_by,
|
|
1564
|
+
uploadedAt: row.uploaded_at,
|
|
1565
|
+
updatedAt: row.updated_at
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
function configFromWire(row) {
|
|
1569
|
+
return {
|
|
1570
|
+
defaultFileSizeLimitBytes: row.default_file_size_limit_bytes,
|
|
1571
|
+
maxFileSizeLimitBytes: row.max_file_size_limit_bytes,
|
|
1572
|
+
tenantStorageQuotaBytes: row.tenant_storage_quota_bytes,
|
|
1573
|
+
reservedSpaceBytes: row.reserved_space_bytes,
|
|
1574
|
+
signedUrlDefaultTtlSec: row.signed_url_default_ttl_sec,
|
|
1575
|
+
signedUrlMaxTtlSec: row.signed_url_max_ttl_sec
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
function encodeKey(key) {
|
|
1579
|
+
return key.split("/").map(encodeURIComponent).join("/");
|
|
1580
|
+
}
|
|
1581
|
+
function wrapError2(err, fallback) {
|
|
1582
|
+
if (err instanceof MitwayBaasError) return { data: null, error: err };
|
|
1583
|
+
return {
|
|
1584
|
+
data: null,
|
|
1585
|
+
error: new MitwayBaasError(
|
|
1586
|
+
err instanceof Error ? err.message : fallback,
|
|
1587
|
+
0,
|
|
1588
|
+
"STORAGE_ERROR"
|
|
1589
|
+
)
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
async function readEnvelopeError(response) {
|
|
1593
|
+
let code = "STORAGE_ERROR";
|
|
1594
|
+
let message = `HTTP ${response.status}`;
|
|
1595
|
+
try {
|
|
1596
|
+
const body = await response.json();
|
|
1597
|
+
if (body && body.error) {
|
|
1598
|
+
code = body.error.code ?? code;
|
|
1599
|
+
message = body.error.message ?? message;
|
|
1600
|
+
}
|
|
1601
|
+
} catch {
|
|
1602
|
+
}
|
|
1603
|
+
return new MitwayBaasError(message, response.status, code);
|
|
1604
|
+
}
|
|
1605
|
+
var StorageBucketClient = class {
|
|
1606
|
+
constructor(http, bucketName) {
|
|
1607
|
+
this.http = http;
|
|
1608
|
+
this.bucketName = bucketName;
|
|
1609
|
+
}
|
|
1610
|
+
http;
|
|
1611
|
+
bucketName;
|
|
1612
|
+
bucketBase() {
|
|
1613
|
+
return `/api/storage/buckets/${encodeURIComponent(this.bucketName)}`;
|
|
1614
|
+
}
|
|
1615
|
+
objectPath(key) {
|
|
1616
|
+
return `${this.bucketBase()}/objects/${encodeKey(key)}`;
|
|
1617
|
+
}
|
|
1618
|
+
async upload(key, body, opts = {}) {
|
|
1619
|
+
try {
|
|
1620
|
+
const method = opts.upsert ? "PUT" : "POST";
|
|
1621
|
+
const headers = {
|
|
1622
|
+
"Content-Type": opts.contentType ?? "application/octet-stream"
|
|
1623
|
+
};
|
|
1624
|
+
if (opts.cacheControl) headers["Cache-Control"] = opts.cacheControl;
|
|
1625
|
+
if (opts.contentDisposition)
|
|
1626
|
+
headers["Content-Disposition"] = opts.contentDisposition;
|
|
1627
|
+
const response = await this.http.rawFetch(this.objectPath(key), {
|
|
1628
|
+
method,
|
|
1629
|
+
headers,
|
|
1630
|
+
body,
|
|
1631
|
+
signal: opts.abortSignal
|
|
1632
|
+
});
|
|
1633
|
+
if (!response.ok) {
|
|
1634
|
+
return { data: null, error: await readEnvelopeError(response) };
|
|
1635
|
+
}
|
|
1636
|
+
const parsed = await response.json();
|
|
1637
|
+
if (parsed.error || !parsed.data) {
|
|
1638
|
+
return {
|
|
1639
|
+
data: null,
|
|
1640
|
+
error: new MitwayBaasError(
|
|
1641
|
+
parsed.error?.message ?? "Upload failed",
|
|
1642
|
+
response.status,
|
|
1643
|
+
parsed.error?.code ?? "STORAGE_ERROR"
|
|
1644
|
+
)
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
return { data: objectFromWire(parsed.data, this.bucketName), error: null };
|
|
1648
|
+
} catch (err) {
|
|
1649
|
+
return wrapError2(err, "Upload failed");
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
async download(key, opts = {}) {
|
|
1653
|
+
try {
|
|
1654
|
+
const headers = {};
|
|
1655
|
+
if (opts.range) {
|
|
1656
|
+
headers["Range"] = `bytes=${opts.range.start}-${opts.range.end}`;
|
|
1657
|
+
}
|
|
1658
|
+
const response = await this.http.rawFetch(this.objectPath(key), {
|
|
1659
|
+
method: "GET",
|
|
1660
|
+
headers,
|
|
1661
|
+
signal: opts.abortSignal
|
|
1662
|
+
});
|
|
1663
|
+
if (!response.ok) {
|
|
1664
|
+
return { data: null, error: await readEnvelopeError(response) };
|
|
1665
|
+
}
|
|
1666
|
+
const blob = await response.blob();
|
|
1667
|
+
return { data: blob, error: null };
|
|
1668
|
+
} catch (err) {
|
|
1669
|
+
return wrapError2(err, "Download failed");
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
async getStream(key, opts = {}) {
|
|
1673
|
+
try {
|
|
1674
|
+
const headers = {};
|
|
1675
|
+
if (opts.range) {
|
|
1676
|
+
headers["Range"] = `bytes=${opts.range.start}-${opts.range.end}`;
|
|
1677
|
+
}
|
|
1678
|
+
const response = await this.http.rawFetch(this.objectPath(key), {
|
|
1679
|
+
method: "GET",
|
|
1680
|
+
headers,
|
|
1681
|
+
signal: opts.abortSignal
|
|
1682
|
+
});
|
|
1683
|
+
if (!response.ok) {
|
|
1684
|
+
return { data: null, error: await readEnvelopeError(response) };
|
|
1685
|
+
}
|
|
1686
|
+
if (!response.body) {
|
|
1687
|
+
return {
|
|
1688
|
+
data: null,
|
|
1689
|
+
error: new MitwayBaasError(
|
|
1690
|
+
"Response body is not a stream",
|
|
1691
|
+
response.status,
|
|
1692
|
+
"STORAGE_ERROR"
|
|
1693
|
+
)
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
return {
|
|
1697
|
+
data: response.body,
|
|
1698
|
+
error: null
|
|
1699
|
+
};
|
|
1700
|
+
} catch (err) {
|
|
1701
|
+
return wrapError2(err, "Download failed");
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
async remove(keys) {
|
|
1705
|
+
try {
|
|
1706
|
+
const results = await Promise.allSettled(
|
|
1707
|
+
keys.map(
|
|
1708
|
+
(key) => this.http.rawFetch(this.objectPath(key), { method: "DELETE" })
|
|
1709
|
+
)
|
|
1710
|
+
);
|
|
1711
|
+
const removed = [];
|
|
1712
|
+
const errors = [];
|
|
1713
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1714
|
+
const key = keys[i];
|
|
1715
|
+
const r = results[i];
|
|
1716
|
+
if (r.status === "fulfilled" && r.value.ok) {
|
|
1717
|
+
removed.push(key);
|
|
1718
|
+
} else if (r.status === "fulfilled") {
|
|
1719
|
+
errors.push(`${key}: HTTP ${r.value.status}`);
|
|
1720
|
+
} else {
|
|
1721
|
+
const msg = r.reason instanceof Error ? r.reason.message : String(r.reason);
|
|
1722
|
+
errors.push(`${key}: ${msg}`);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
if (errors.length > 0) {
|
|
1726
|
+
return {
|
|
1727
|
+
data: null,
|
|
1728
|
+
error: new MitwayBaasError(
|
|
1729
|
+
`Failed to delete some objects: ${errors.join("; ")}`,
|
|
1730
|
+
0,
|
|
1731
|
+
"STORAGE_ERROR"
|
|
1732
|
+
)
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
return { data: { removed }, error: null };
|
|
1736
|
+
} catch (err) {
|
|
1737
|
+
return wrapError2(err, "Delete failed");
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
async list(opts = {}) {
|
|
1741
|
+
try {
|
|
1742
|
+
const params = {};
|
|
1743
|
+
if (opts.prefix !== void 0) params.prefix = opts.prefix;
|
|
1744
|
+
if (opts.limit !== void 0) params.limit = String(opts.limit);
|
|
1745
|
+
if (opts.startAfter !== void 0) params.start_after = opts.startAfter;
|
|
1746
|
+
const rows = await this.http.get(
|
|
1747
|
+
`${this.bucketBase()}/objects`,
|
|
1748
|
+
{ params }
|
|
1749
|
+
);
|
|
1750
|
+
return {
|
|
1751
|
+
data: rows.map((r) => objectFromWire(r, this.bucketName)),
|
|
1752
|
+
error: null
|
|
1753
|
+
};
|
|
1754
|
+
} catch (err) {
|
|
1755
|
+
return wrapError2(err, "List failed");
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
async copy(fromKey, toKey, toBucket) {
|
|
1759
|
+
try {
|
|
1760
|
+
const row = await this.http.post(
|
|
1761
|
+
`${this.objectPath(fromKey)}/copy`,
|
|
1762
|
+
{
|
|
1763
|
+
dest_bucket: toBucket ?? this.bucketName,
|
|
1764
|
+
dest_key: toKey
|
|
1765
|
+
}
|
|
1766
|
+
);
|
|
1767
|
+
return {
|
|
1768
|
+
data: objectFromWire(row, toBucket ?? this.bucketName),
|
|
1769
|
+
error: null
|
|
1770
|
+
};
|
|
1771
|
+
} catch (err) {
|
|
1772
|
+
return wrapError2(err, "Copy failed");
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
async move(fromKey, toKey, toBucket) {
|
|
1776
|
+
try {
|
|
1777
|
+
const row = await this.http.post(
|
|
1778
|
+
`${this.objectPath(fromKey)}/move`,
|
|
1779
|
+
{
|
|
1780
|
+
dest_bucket: toBucket ?? this.bucketName,
|
|
1781
|
+
dest_key: toKey
|
|
1782
|
+
}
|
|
1783
|
+
);
|
|
1784
|
+
return {
|
|
1785
|
+
data: objectFromWire(row, toBucket ?? this.bucketName),
|
|
1786
|
+
error: null
|
|
1787
|
+
};
|
|
1788
|
+
} catch (err) {
|
|
1789
|
+
return wrapError2(err, "Move failed");
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
async createSignedUrl(key, opts = {}) {
|
|
1793
|
+
try {
|
|
1794
|
+
const body = {};
|
|
1795
|
+
if (opts.expiresIn !== void 0) body.expires_in = opts.expiresIn;
|
|
1796
|
+
const wire = await this.http.post(`${this.objectPath(key)}/sign`, body);
|
|
1797
|
+
return {
|
|
1798
|
+
data: {
|
|
1799
|
+
url: wire.url,
|
|
1800
|
+
token: wire.token,
|
|
1801
|
+
expiresAt: wire.expiresAt
|
|
1802
|
+
},
|
|
1803
|
+
error: null
|
|
1804
|
+
};
|
|
1805
|
+
} catch (err) {
|
|
1806
|
+
return wrapError2(err, "Sign failed");
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
getPublicUrl(key) {
|
|
1810
|
+
const url = `${this.http.baseUrl.replace(/\/$/, "")}${this.objectPath(key)}`;
|
|
1811
|
+
return { data: { url } };
|
|
1812
|
+
}
|
|
1813
|
+
};
|
|
1814
|
+
var Storage = class {
|
|
1815
|
+
constructor(http) {
|
|
1816
|
+
this.http = http;
|
|
1817
|
+
}
|
|
1818
|
+
http;
|
|
1819
|
+
/** Scope subsequent operations to a single bucket. */
|
|
1820
|
+
from(bucketName) {
|
|
1821
|
+
return new StorageBucketClient(this.http, bucketName);
|
|
1822
|
+
}
|
|
1823
|
+
// --- Admin (require service_role) ---
|
|
1824
|
+
async listBuckets() {
|
|
1825
|
+
try {
|
|
1826
|
+
const rows = await this.http.get("/api/storage/buckets");
|
|
1827
|
+
return { data: rows.map(bucketFromWire), error: null };
|
|
1828
|
+
} catch (err) {
|
|
1829
|
+
return wrapError2(err, "listBuckets failed");
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
async getBucket(name) {
|
|
1833
|
+
try {
|
|
1834
|
+
const row = await this.http.get(
|
|
1835
|
+
`/api/storage/buckets/${encodeURIComponent(name)}`
|
|
1836
|
+
);
|
|
1837
|
+
return { data: bucketFromWire(row), error: null };
|
|
1838
|
+
} catch (err) {
|
|
1839
|
+
return wrapError2(err, "getBucket failed");
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
async createBucket(name, opts = {}) {
|
|
1843
|
+
try {
|
|
1844
|
+
const body = { name };
|
|
1845
|
+
if (opts.public !== void 0) body.public = opts.public;
|
|
1846
|
+
if (opts.fileSizeLimitBytes !== void 0)
|
|
1847
|
+
body.file_size_limit_bytes = opts.fileSizeLimitBytes;
|
|
1848
|
+
if (opts.allowedMimeTypes !== void 0)
|
|
1849
|
+
body.allowed_mime_types = opts.allowedMimeTypes;
|
|
1850
|
+
const row = await this.http.post(
|
|
1851
|
+
"/api/storage/buckets",
|
|
1852
|
+
body
|
|
1853
|
+
);
|
|
1854
|
+
return { data: bucketFromWire(row), error: null };
|
|
1855
|
+
} catch (err) {
|
|
1856
|
+
return wrapError2(err, "createBucket failed");
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
async updateBucket(name, opts) {
|
|
1860
|
+
try {
|
|
1861
|
+
const body = {};
|
|
1862
|
+
if (opts.public !== void 0) body.public = opts.public;
|
|
1863
|
+
if (opts.fileSizeLimitBytes !== void 0)
|
|
1864
|
+
body.file_size_limit_bytes = opts.fileSizeLimitBytes;
|
|
1865
|
+
if (opts.allowedMimeTypes !== void 0)
|
|
1866
|
+
body.allowed_mime_types = opts.allowedMimeTypes;
|
|
1867
|
+
const row = await this.http.patch(
|
|
1868
|
+
`/api/storage/buckets/${encodeURIComponent(name)}`,
|
|
1869
|
+
body
|
|
1870
|
+
);
|
|
1871
|
+
return { data: bucketFromWire(row), error: null };
|
|
1872
|
+
} catch (err) {
|
|
1873
|
+
return wrapError2(err, "updateBucket failed");
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
async deleteBucket(name) {
|
|
1877
|
+
try {
|
|
1878
|
+
await this.http.delete(
|
|
1879
|
+
`/api/storage/buckets/${encodeURIComponent(name)}`
|
|
1880
|
+
);
|
|
1881
|
+
return { data: null, error: null };
|
|
1882
|
+
} catch (err) {
|
|
1883
|
+
return wrapError2(err, "deleteBucket failed");
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
async emptyBucket(name) {
|
|
1887
|
+
try {
|
|
1888
|
+
const result = await this.http.post(
|
|
1889
|
+
`/api/storage/buckets/${encodeURIComponent(name)}/empty`,
|
|
1890
|
+
{}
|
|
1891
|
+
);
|
|
1892
|
+
return { data: result, error: null };
|
|
1893
|
+
} catch (err) {
|
|
1894
|
+
return wrapError2(err, "emptyBucket failed");
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
// --- Storage config (service_role) ---
|
|
1898
|
+
async getConfig() {
|
|
1899
|
+
try {
|
|
1900
|
+
const row = await this.http.get("/api/storage/config");
|
|
1901
|
+
return { data: configFromWire(row), error: null };
|
|
1902
|
+
} catch (err) {
|
|
1903
|
+
return wrapError2(err, "getConfig failed");
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
};
|
|
1907
|
+
|
|
1497
1908
|
// src/client.ts
|
|
1498
1909
|
var MitwayBaasClient = class {
|
|
1499
1910
|
http;
|
|
@@ -1501,6 +1912,7 @@ var MitwayBaasClient = class {
|
|
|
1501
1912
|
auth;
|
|
1502
1913
|
database;
|
|
1503
1914
|
realtime;
|
|
1915
|
+
storage;
|
|
1504
1916
|
constructor(config = {}) {
|
|
1505
1917
|
const logger = new Logger(config.debug);
|
|
1506
1918
|
this.tokenManager = new TokenManager();
|
|
@@ -1513,6 +1925,7 @@ var MitwayBaasClient = class {
|
|
|
1513
1925
|
config.anonKey,
|
|
1514
1926
|
config.realtime
|
|
1515
1927
|
);
|
|
1928
|
+
this.storage = new Storage(this.http);
|
|
1516
1929
|
}
|
|
1517
1930
|
/**
|
|
1518
1931
|
* Escape hatch for callers that need to make custom requests against the
|
|
@@ -1538,6 +1951,8 @@ var index_default = MitwayBaasClient;
|
|
|
1538
1951
|
MitwayBaasError,
|
|
1539
1952
|
Realtime,
|
|
1540
1953
|
RealtimeChannel,
|
|
1954
|
+
Storage,
|
|
1955
|
+
StorageBucketClient,
|
|
1541
1956
|
TokenManager,
|
|
1542
1957
|
createClient
|
|
1543
1958
|
});
|