@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/dist/index.js
CHANGED
|
@@ -528,6 +528,29 @@ var HttpClient = class {
|
|
|
528
528
|
throw error;
|
|
529
529
|
}
|
|
530
530
|
}
|
|
531
|
+
/**
|
|
532
|
+
* Low-level fetch helper for binary bodies (uploads) and streamed responses
|
|
533
|
+
* (downloads). Applies the current Bearer token (user session → anon key
|
|
534
|
+
* fallback) plus any configured default headers, resolves `path` against
|
|
535
|
+
* `baseUrl`, and returns the raw `Response` — it does NOT unwrap the
|
|
536
|
+
* `{ data, error }` envelope, so the caller is responsible for status
|
|
537
|
+
* checking and parsing.
|
|
538
|
+
*
|
|
539
|
+
* Used by the storage module for object upload/download paths where the
|
|
540
|
+
* body or response is not JSON.
|
|
541
|
+
*/
|
|
542
|
+
async rawFetch(path, init = {}) {
|
|
543
|
+
const url = this.buildUrl(path);
|
|
544
|
+
const headers = new Headers(init.headers ?? {});
|
|
545
|
+
for (const [k, v] of Object.entries(this.defaultHeaders)) {
|
|
546
|
+
if (!headers.has(k)) headers.set(k, v);
|
|
547
|
+
}
|
|
548
|
+
if (!headers.has("Authorization")) {
|
|
549
|
+
const token = this.userToken ?? this.anonKey;
|
|
550
|
+
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
551
|
+
}
|
|
552
|
+
return this.fetch(url, { ...init, headers });
|
|
553
|
+
}
|
|
531
554
|
get(path, options) {
|
|
532
555
|
return this.request("GET", path, options);
|
|
533
556
|
}
|
|
@@ -857,6 +880,11 @@ var Database = class {
|
|
|
857
880
|
// src/modules/realtime.ts
|
|
858
881
|
import { io } from "socket.io-client";
|
|
859
882
|
var PRESENCE_HEARTBEAT_MS = 2e4;
|
|
883
|
+
function makeChannelError(code, message) {
|
|
884
|
+
const err = new Error(message);
|
|
885
|
+
err.code = code;
|
|
886
|
+
return err;
|
|
887
|
+
}
|
|
860
888
|
var DEFAULT_CONNECT_TIMEOUT_MS = 1e4;
|
|
861
889
|
var RealtimeChannel = class {
|
|
862
890
|
constructor(topic, realtime, options = {}) {
|
|
@@ -927,12 +955,19 @@ var RealtimeChannel = class {
|
|
|
927
955
|
this.statusCallback?.("SUBSCRIBED");
|
|
928
956
|
} catch (err) {
|
|
929
957
|
this.state = "errored";
|
|
930
|
-
this.statusCallback?.(
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
958
|
+
this.statusCallback?.(
|
|
959
|
+
"CHANNEL_ERROR",
|
|
960
|
+
makeChannelError("REJOIN_FAILED", err instanceof Error ? err.message : String(err))
|
|
961
|
+
);
|
|
934
962
|
}
|
|
935
963
|
}
|
|
964
|
+
// ── implementation signature (not in public type surface).
|
|
965
|
+
// The callback type is intentionally broad: each overload above pins a
|
|
966
|
+
// specific payload shape, but TypeScript overload resolution needs the
|
|
967
|
+
// implementation to accept the union of every narrow callback without
|
|
968
|
+
// the contravariance conflict (TS2394). `any` is the standard escape
|
|
969
|
+
// hatch for this exact pattern and is confined to this one line — the
|
|
970
|
+
// public surface users see is strictly typed via the overloads.
|
|
936
971
|
on(type, filter, callback) {
|
|
937
972
|
if (type === "postgres_changes") {
|
|
938
973
|
this.bindings.push({
|
|
@@ -1034,18 +1069,18 @@ var RealtimeChannel = class {
|
|
|
1034
1069
|
} catch (err) {
|
|
1035
1070
|
this.state = "errored";
|
|
1036
1071
|
const message = err instanceof Error ? err.message : String(err);
|
|
1037
|
-
this.statusCallback?.(
|
|
1038
|
-
|
|
1039
|
-
message
|
|
1040
|
-
|
|
1072
|
+
this.statusCallback?.(
|
|
1073
|
+
"CHANNEL_ERROR",
|
|
1074
|
+
makeChannelError("SUBSCRIBE_FAILED", message)
|
|
1075
|
+
);
|
|
1041
1076
|
}
|
|
1042
1077
|
},
|
|
1043
1078
|
(err) => {
|
|
1044
1079
|
this.state = "errored";
|
|
1045
|
-
this.statusCallback?.(
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1080
|
+
this.statusCallback?.(
|
|
1081
|
+
"CHANNEL_ERROR",
|
|
1082
|
+
makeChannelError("CONNECT_FAILED", err.message)
|
|
1083
|
+
);
|
|
1049
1084
|
}
|
|
1050
1085
|
);
|
|
1051
1086
|
return this;
|
|
@@ -1098,6 +1133,12 @@ var RealtimeChannel = class {
|
|
|
1098
1133
|
* events, write to your own application table and enable
|
|
1099
1134
|
* `postgres_changes` on it; the SDK will surface the INSERT as a
|
|
1100
1135
|
* `postgres_changes` event without a separate channel.send call.
|
|
1136
|
+
*
|
|
1137
|
+
* The returned promise always resolves with the server ack, so callers
|
|
1138
|
+
* can `await channel.send(...)` to confirm delivery + get the server-
|
|
1139
|
+
* assigned `message_id`. There's no performance cost — Socket.IO piggy-
|
|
1140
|
+
* backs the ack on the same frame. Callers that don't need it just
|
|
1141
|
+
* don't await.
|
|
1101
1142
|
*/
|
|
1102
1143
|
async send(args) {
|
|
1103
1144
|
if (args.type !== "broadcast") {
|
|
@@ -1124,9 +1165,7 @@ var RealtimeChannel = class {
|
|
|
1124
1165
|
if (!socket) {
|
|
1125
1166
|
throw new MitwayBaasError("Socket not connected", 503, "NOT_CONNECTED");
|
|
1126
1167
|
}
|
|
1127
|
-
const
|
|
1128
|
-
const wantAck = broadcastCfg?.ack === true;
|
|
1129
|
-
const self = broadcastCfg?.self;
|
|
1168
|
+
const self = this.options.config?.broadcast?.self;
|
|
1130
1169
|
const wirePayload = {
|
|
1131
1170
|
channel: this.topic,
|
|
1132
1171
|
event: args.event,
|
|
@@ -1135,10 +1174,6 @@ var RealtimeChannel = class {
|
|
|
1135
1174
|
if (self === false) {
|
|
1136
1175
|
wirePayload.self = false;
|
|
1137
1176
|
}
|
|
1138
|
-
if (!wantAck) {
|
|
1139
|
-
socket.emit("realtime:publish", wirePayload);
|
|
1140
|
-
return;
|
|
1141
|
-
}
|
|
1142
1177
|
return await new Promise((resolve) => {
|
|
1143
1178
|
socket.emit(
|
|
1144
1179
|
"realtime:publish",
|
|
@@ -1179,7 +1214,7 @@ var RealtimeChannel = class {
|
|
|
1179
1214
|
if (!b.subscriptionId || !pcEvent.ids.includes(b.subscriptionId)) {
|
|
1180
1215
|
continue;
|
|
1181
1216
|
}
|
|
1182
|
-
const matchesEvent = b.filter.event === "*" || b.filter.event === pcEvent.data.
|
|
1217
|
+
const matchesEvent = b.filter.event === "*" || b.filter.event === pcEvent.data.eventType;
|
|
1183
1218
|
if (!matchesEvent) {
|
|
1184
1219
|
continue;
|
|
1185
1220
|
}
|
|
@@ -1238,7 +1273,13 @@ var RealtimeChannel = class {
|
|
|
1238
1273
|
continue;
|
|
1239
1274
|
}
|
|
1240
1275
|
try {
|
|
1241
|
-
|
|
1276
|
+
if (payload.event === "sync") {
|
|
1277
|
+
b.callback();
|
|
1278
|
+
} else if (payload.event === "join") {
|
|
1279
|
+
b.callback(payload);
|
|
1280
|
+
} else {
|
|
1281
|
+
b.callback(payload);
|
|
1282
|
+
}
|
|
1242
1283
|
} catch {
|
|
1243
1284
|
}
|
|
1244
1285
|
}
|
|
@@ -1257,7 +1298,7 @@ var RealtimeChannel = class {
|
|
|
1257
1298
|
"realtime:subscribe",
|
|
1258
1299
|
{ channel: this.topic, private: this.isPrivate },
|
|
1259
1300
|
(ack) => {
|
|
1260
|
-
if (ack.ok) {
|
|
1301
|
+
if (ack.status === "ok") {
|
|
1261
1302
|
resolve();
|
|
1262
1303
|
} else {
|
|
1263
1304
|
reject(new Error(ack.error?.message ?? "subscribe failed"));
|
|
@@ -1281,7 +1322,7 @@ var RealtimeChannel = class {
|
|
|
1281
1322
|
filter: b.filter.filter
|
|
1282
1323
|
},
|
|
1283
1324
|
(ack) => {
|
|
1284
|
-
if (ack.ok && ack.subscription_id) {
|
|
1325
|
+
if (ack.status === "ok" && ack.subscription_id) {
|
|
1285
1326
|
b.subscriptionId = ack.subscription_id;
|
|
1286
1327
|
resolve();
|
|
1287
1328
|
} else {
|
|
@@ -1327,13 +1368,14 @@ var Realtime = class {
|
|
|
1327
1368
|
* The optional `opts` argument lets the caller configure the channel:
|
|
1328
1369
|
* * `config.private` — enable subscribe-side authorization against
|
|
1329
1370
|
* `realtime.authorize_subscribe(...)` on the tenant DB.
|
|
1330
|
-
* * `config.broadcast.ack` — `channel.send()` resolves with the
|
|
1331
|
-
* server's ack (message_id) instead of fire-and-forget.
|
|
1332
1371
|
* * `config.broadcast.self` — `false` excludes the sender from the
|
|
1333
1372
|
* fan-out (defaults to `true`).
|
|
1334
1373
|
* * `config.presence.key` — stable presence key to group multiple
|
|
1335
1374
|
* tabs of the same user under one entry.
|
|
1336
1375
|
*
|
|
1376
|
+
* `channel.send()` always resolves with the server ack (see its own
|
|
1377
|
+
* docstring); there is no separate opt-in needed.
|
|
1378
|
+
*
|
|
1337
1379
|
* Options are locked in when the channel is first created; subsequent
|
|
1338
1380
|
* `.channel('same')` calls with different opts are ignored. Pass a
|
|
1339
1381
|
* different topic to get a different-configured channel.
|
|
@@ -1458,6 +1500,373 @@ var Realtime = class {
|
|
|
1458
1500
|
}
|
|
1459
1501
|
};
|
|
1460
1502
|
|
|
1503
|
+
// src/modules/storage.ts
|
|
1504
|
+
function bucketFromWire(row) {
|
|
1505
|
+
return {
|
|
1506
|
+
id: row.id,
|
|
1507
|
+
name: row.name,
|
|
1508
|
+
public: row.public,
|
|
1509
|
+
fileSizeLimitBytes: row.file_size_limit_bytes,
|
|
1510
|
+
allowedMimeTypes: row.allowed_mime_types,
|
|
1511
|
+
createdAt: row.created_at,
|
|
1512
|
+
updatedAt: row.updated_at
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
function objectFromWire(row, bucketName) {
|
|
1516
|
+
return {
|
|
1517
|
+
id: row.id,
|
|
1518
|
+
bucket: bucketName,
|
|
1519
|
+
key: row.key,
|
|
1520
|
+
size: row.size,
|
|
1521
|
+
mimeType: row.mime_type,
|
|
1522
|
+
etag: row.etag,
|
|
1523
|
+
cacheControl: row.cache_control,
|
|
1524
|
+
contentDisposition: row.content_disposition,
|
|
1525
|
+
uploadedBy: row.uploaded_by,
|
|
1526
|
+
uploadedAt: row.uploaded_at,
|
|
1527
|
+
updatedAt: row.updated_at
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
function configFromWire(row) {
|
|
1531
|
+
return {
|
|
1532
|
+
defaultFileSizeLimitBytes: row.default_file_size_limit_bytes,
|
|
1533
|
+
maxFileSizeLimitBytes: row.max_file_size_limit_bytes,
|
|
1534
|
+
tenantStorageQuotaBytes: row.tenant_storage_quota_bytes,
|
|
1535
|
+
reservedSpaceBytes: row.reserved_space_bytes,
|
|
1536
|
+
signedUrlDefaultTtlSec: row.signed_url_default_ttl_sec,
|
|
1537
|
+
signedUrlMaxTtlSec: row.signed_url_max_ttl_sec
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
function encodeKey(key) {
|
|
1541
|
+
return key.split("/").map(encodeURIComponent).join("/");
|
|
1542
|
+
}
|
|
1543
|
+
function wrapError2(err, fallback) {
|
|
1544
|
+
if (err instanceof MitwayBaasError) return { data: null, error: err };
|
|
1545
|
+
return {
|
|
1546
|
+
data: null,
|
|
1547
|
+
error: new MitwayBaasError(
|
|
1548
|
+
err instanceof Error ? err.message : fallback,
|
|
1549
|
+
0,
|
|
1550
|
+
"STORAGE_ERROR"
|
|
1551
|
+
)
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
async function readEnvelopeError(response) {
|
|
1555
|
+
let code = "STORAGE_ERROR";
|
|
1556
|
+
let message = `HTTP ${response.status}`;
|
|
1557
|
+
try {
|
|
1558
|
+
const body = await response.json();
|
|
1559
|
+
if (body && body.error) {
|
|
1560
|
+
code = body.error.code ?? code;
|
|
1561
|
+
message = body.error.message ?? message;
|
|
1562
|
+
}
|
|
1563
|
+
} catch {
|
|
1564
|
+
}
|
|
1565
|
+
return new MitwayBaasError(message, response.status, code);
|
|
1566
|
+
}
|
|
1567
|
+
var StorageBucketClient = class {
|
|
1568
|
+
constructor(http, bucketName) {
|
|
1569
|
+
this.http = http;
|
|
1570
|
+
this.bucketName = bucketName;
|
|
1571
|
+
}
|
|
1572
|
+
http;
|
|
1573
|
+
bucketName;
|
|
1574
|
+
bucketBase() {
|
|
1575
|
+
return `/api/storage/buckets/${encodeURIComponent(this.bucketName)}`;
|
|
1576
|
+
}
|
|
1577
|
+
objectPath(key) {
|
|
1578
|
+
return `${this.bucketBase()}/objects/${encodeKey(key)}`;
|
|
1579
|
+
}
|
|
1580
|
+
async upload(key, body, opts = {}) {
|
|
1581
|
+
try {
|
|
1582
|
+
const method = opts.upsert ? "PUT" : "POST";
|
|
1583
|
+
const headers = {
|
|
1584
|
+
"Content-Type": opts.contentType ?? "application/octet-stream"
|
|
1585
|
+
};
|
|
1586
|
+
if (opts.cacheControl) headers["Cache-Control"] = opts.cacheControl;
|
|
1587
|
+
if (opts.contentDisposition)
|
|
1588
|
+
headers["Content-Disposition"] = opts.contentDisposition;
|
|
1589
|
+
const response = await this.http.rawFetch(this.objectPath(key), {
|
|
1590
|
+
method,
|
|
1591
|
+
headers,
|
|
1592
|
+
body,
|
|
1593
|
+
signal: opts.abortSignal
|
|
1594
|
+
});
|
|
1595
|
+
if (!response.ok) {
|
|
1596
|
+
return { data: null, error: await readEnvelopeError(response) };
|
|
1597
|
+
}
|
|
1598
|
+
const parsed = await response.json();
|
|
1599
|
+
if (parsed.error || !parsed.data) {
|
|
1600
|
+
return {
|
|
1601
|
+
data: null,
|
|
1602
|
+
error: new MitwayBaasError(
|
|
1603
|
+
parsed.error?.message ?? "Upload failed",
|
|
1604
|
+
response.status,
|
|
1605
|
+
parsed.error?.code ?? "STORAGE_ERROR"
|
|
1606
|
+
)
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
return { data: objectFromWire(parsed.data, this.bucketName), error: null };
|
|
1610
|
+
} catch (err) {
|
|
1611
|
+
return wrapError2(err, "Upload failed");
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
async download(key, opts = {}) {
|
|
1615
|
+
try {
|
|
1616
|
+
const headers = {};
|
|
1617
|
+
if (opts.range) {
|
|
1618
|
+
headers["Range"] = `bytes=${opts.range.start}-${opts.range.end}`;
|
|
1619
|
+
}
|
|
1620
|
+
const response = await this.http.rawFetch(this.objectPath(key), {
|
|
1621
|
+
method: "GET",
|
|
1622
|
+
headers,
|
|
1623
|
+
signal: opts.abortSignal
|
|
1624
|
+
});
|
|
1625
|
+
if (!response.ok) {
|
|
1626
|
+
return { data: null, error: await readEnvelopeError(response) };
|
|
1627
|
+
}
|
|
1628
|
+
const blob = await response.blob();
|
|
1629
|
+
return { data: blob, error: null };
|
|
1630
|
+
} catch (err) {
|
|
1631
|
+
return wrapError2(err, "Download failed");
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
async getStream(key, opts = {}) {
|
|
1635
|
+
try {
|
|
1636
|
+
const headers = {};
|
|
1637
|
+
if (opts.range) {
|
|
1638
|
+
headers["Range"] = `bytes=${opts.range.start}-${opts.range.end}`;
|
|
1639
|
+
}
|
|
1640
|
+
const response = await this.http.rawFetch(this.objectPath(key), {
|
|
1641
|
+
method: "GET",
|
|
1642
|
+
headers,
|
|
1643
|
+
signal: opts.abortSignal
|
|
1644
|
+
});
|
|
1645
|
+
if (!response.ok) {
|
|
1646
|
+
return { data: null, error: await readEnvelopeError(response) };
|
|
1647
|
+
}
|
|
1648
|
+
if (!response.body) {
|
|
1649
|
+
return {
|
|
1650
|
+
data: null,
|
|
1651
|
+
error: new MitwayBaasError(
|
|
1652
|
+
"Response body is not a stream",
|
|
1653
|
+
response.status,
|
|
1654
|
+
"STORAGE_ERROR"
|
|
1655
|
+
)
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
return {
|
|
1659
|
+
data: response.body,
|
|
1660
|
+
error: null
|
|
1661
|
+
};
|
|
1662
|
+
} catch (err) {
|
|
1663
|
+
return wrapError2(err, "Download failed");
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
async remove(keys) {
|
|
1667
|
+
try {
|
|
1668
|
+
const results = await Promise.allSettled(
|
|
1669
|
+
keys.map(
|
|
1670
|
+
(key) => this.http.rawFetch(this.objectPath(key), { method: "DELETE" })
|
|
1671
|
+
)
|
|
1672
|
+
);
|
|
1673
|
+
const removed = [];
|
|
1674
|
+
const errors = [];
|
|
1675
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1676
|
+
const key = keys[i];
|
|
1677
|
+
const r = results[i];
|
|
1678
|
+
if (r.status === "fulfilled" && r.value.ok) {
|
|
1679
|
+
removed.push(key);
|
|
1680
|
+
} else if (r.status === "fulfilled") {
|
|
1681
|
+
errors.push(`${key}: HTTP ${r.value.status}`);
|
|
1682
|
+
} else {
|
|
1683
|
+
const msg = r.reason instanceof Error ? r.reason.message : String(r.reason);
|
|
1684
|
+
errors.push(`${key}: ${msg}`);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
if (errors.length > 0) {
|
|
1688
|
+
return {
|
|
1689
|
+
data: null,
|
|
1690
|
+
error: new MitwayBaasError(
|
|
1691
|
+
`Failed to delete some objects: ${errors.join("; ")}`,
|
|
1692
|
+
0,
|
|
1693
|
+
"STORAGE_ERROR"
|
|
1694
|
+
)
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
return { data: { removed }, error: null };
|
|
1698
|
+
} catch (err) {
|
|
1699
|
+
return wrapError2(err, "Delete failed");
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
async list(opts = {}) {
|
|
1703
|
+
try {
|
|
1704
|
+
const params = {};
|
|
1705
|
+
if (opts.prefix !== void 0) params.prefix = opts.prefix;
|
|
1706
|
+
if (opts.limit !== void 0) params.limit = String(opts.limit);
|
|
1707
|
+
if (opts.startAfter !== void 0) params.start_after = opts.startAfter;
|
|
1708
|
+
const rows = await this.http.get(
|
|
1709
|
+
`${this.bucketBase()}/objects`,
|
|
1710
|
+
{ params }
|
|
1711
|
+
);
|
|
1712
|
+
return {
|
|
1713
|
+
data: rows.map((r) => objectFromWire(r, this.bucketName)),
|
|
1714
|
+
error: null
|
|
1715
|
+
};
|
|
1716
|
+
} catch (err) {
|
|
1717
|
+
return wrapError2(err, "List failed");
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
async copy(fromKey, toKey, toBucket) {
|
|
1721
|
+
try {
|
|
1722
|
+
const row = await this.http.post(
|
|
1723
|
+
`${this.objectPath(fromKey)}/copy`,
|
|
1724
|
+
{
|
|
1725
|
+
dest_bucket: toBucket ?? this.bucketName,
|
|
1726
|
+
dest_key: toKey
|
|
1727
|
+
}
|
|
1728
|
+
);
|
|
1729
|
+
return {
|
|
1730
|
+
data: objectFromWire(row, toBucket ?? this.bucketName),
|
|
1731
|
+
error: null
|
|
1732
|
+
};
|
|
1733
|
+
} catch (err) {
|
|
1734
|
+
return wrapError2(err, "Copy failed");
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
async move(fromKey, toKey, toBucket) {
|
|
1738
|
+
try {
|
|
1739
|
+
const row = await this.http.post(
|
|
1740
|
+
`${this.objectPath(fromKey)}/move`,
|
|
1741
|
+
{
|
|
1742
|
+
dest_bucket: toBucket ?? this.bucketName,
|
|
1743
|
+
dest_key: toKey
|
|
1744
|
+
}
|
|
1745
|
+
);
|
|
1746
|
+
return {
|
|
1747
|
+
data: objectFromWire(row, toBucket ?? this.bucketName),
|
|
1748
|
+
error: null
|
|
1749
|
+
};
|
|
1750
|
+
} catch (err) {
|
|
1751
|
+
return wrapError2(err, "Move failed");
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
async createSignedUrl(key, opts = {}) {
|
|
1755
|
+
try {
|
|
1756
|
+
const body = {};
|
|
1757
|
+
if (opts.expiresIn !== void 0) body.expires_in = opts.expiresIn;
|
|
1758
|
+
const wire = await this.http.post(`${this.objectPath(key)}/sign`, body);
|
|
1759
|
+
return {
|
|
1760
|
+
data: {
|
|
1761
|
+
url: wire.url,
|
|
1762
|
+
token: wire.token,
|
|
1763
|
+
expiresAt: wire.expiresAt
|
|
1764
|
+
},
|
|
1765
|
+
error: null
|
|
1766
|
+
};
|
|
1767
|
+
} catch (err) {
|
|
1768
|
+
return wrapError2(err, "Sign failed");
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
getPublicUrl(key) {
|
|
1772
|
+
const url = `${this.http.baseUrl.replace(/\/$/, "")}${this.objectPath(key)}`;
|
|
1773
|
+
return { data: { url } };
|
|
1774
|
+
}
|
|
1775
|
+
};
|
|
1776
|
+
var Storage = class {
|
|
1777
|
+
constructor(http) {
|
|
1778
|
+
this.http = http;
|
|
1779
|
+
}
|
|
1780
|
+
http;
|
|
1781
|
+
/** Scope subsequent operations to a single bucket. */
|
|
1782
|
+
from(bucketName) {
|
|
1783
|
+
return new StorageBucketClient(this.http, bucketName);
|
|
1784
|
+
}
|
|
1785
|
+
// --- Admin (require service_role) ---
|
|
1786
|
+
async listBuckets() {
|
|
1787
|
+
try {
|
|
1788
|
+
const rows = await this.http.get("/api/storage/buckets");
|
|
1789
|
+
return { data: rows.map(bucketFromWire), error: null };
|
|
1790
|
+
} catch (err) {
|
|
1791
|
+
return wrapError2(err, "listBuckets failed");
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
async getBucket(name) {
|
|
1795
|
+
try {
|
|
1796
|
+
const row = await this.http.get(
|
|
1797
|
+
`/api/storage/buckets/${encodeURIComponent(name)}`
|
|
1798
|
+
);
|
|
1799
|
+
return { data: bucketFromWire(row), error: null };
|
|
1800
|
+
} catch (err) {
|
|
1801
|
+
return wrapError2(err, "getBucket failed");
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
async createBucket(name, opts = {}) {
|
|
1805
|
+
try {
|
|
1806
|
+
const body = { name };
|
|
1807
|
+
if (opts.public !== void 0) body.public = opts.public;
|
|
1808
|
+
if (opts.fileSizeLimitBytes !== void 0)
|
|
1809
|
+
body.file_size_limit_bytes = opts.fileSizeLimitBytes;
|
|
1810
|
+
if (opts.allowedMimeTypes !== void 0)
|
|
1811
|
+
body.allowed_mime_types = opts.allowedMimeTypes;
|
|
1812
|
+
const row = await this.http.post(
|
|
1813
|
+
"/api/storage/buckets",
|
|
1814
|
+
body
|
|
1815
|
+
);
|
|
1816
|
+
return { data: bucketFromWire(row), error: null };
|
|
1817
|
+
} catch (err) {
|
|
1818
|
+
return wrapError2(err, "createBucket failed");
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
async updateBucket(name, opts) {
|
|
1822
|
+
try {
|
|
1823
|
+
const body = {};
|
|
1824
|
+
if (opts.public !== void 0) body.public = opts.public;
|
|
1825
|
+
if (opts.fileSizeLimitBytes !== void 0)
|
|
1826
|
+
body.file_size_limit_bytes = opts.fileSizeLimitBytes;
|
|
1827
|
+
if (opts.allowedMimeTypes !== void 0)
|
|
1828
|
+
body.allowed_mime_types = opts.allowedMimeTypes;
|
|
1829
|
+
const row = await this.http.patch(
|
|
1830
|
+
`/api/storage/buckets/${encodeURIComponent(name)}`,
|
|
1831
|
+
body
|
|
1832
|
+
);
|
|
1833
|
+
return { data: bucketFromWire(row), error: null };
|
|
1834
|
+
} catch (err) {
|
|
1835
|
+
return wrapError2(err, "updateBucket failed");
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
async deleteBucket(name) {
|
|
1839
|
+
try {
|
|
1840
|
+
await this.http.delete(
|
|
1841
|
+
`/api/storage/buckets/${encodeURIComponent(name)}`
|
|
1842
|
+
);
|
|
1843
|
+
return { data: null, error: null };
|
|
1844
|
+
} catch (err) {
|
|
1845
|
+
return wrapError2(err, "deleteBucket failed");
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
async emptyBucket(name) {
|
|
1849
|
+
try {
|
|
1850
|
+
const result = await this.http.post(
|
|
1851
|
+
`/api/storage/buckets/${encodeURIComponent(name)}/empty`,
|
|
1852
|
+
{}
|
|
1853
|
+
);
|
|
1854
|
+
return { data: result, error: null };
|
|
1855
|
+
} catch (err) {
|
|
1856
|
+
return wrapError2(err, "emptyBucket failed");
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
// --- Storage config (service_role) ---
|
|
1860
|
+
async getConfig() {
|
|
1861
|
+
try {
|
|
1862
|
+
const row = await this.http.get("/api/storage/config");
|
|
1863
|
+
return { data: configFromWire(row), error: null };
|
|
1864
|
+
} catch (err) {
|
|
1865
|
+
return wrapError2(err, "getConfig failed");
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
};
|
|
1869
|
+
|
|
1461
1870
|
// src/client.ts
|
|
1462
1871
|
var MitwayBaasClient = class {
|
|
1463
1872
|
http;
|
|
@@ -1465,6 +1874,7 @@ var MitwayBaasClient = class {
|
|
|
1465
1874
|
auth;
|
|
1466
1875
|
database;
|
|
1467
1876
|
realtime;
|
|
1877
|
+
storage;
|
|
1468
1878
|
constructor(config = {}) {
|
|
1469
1879
|
const logger = new Logger(config.debug);
|
|
1470
1880
|
this.tokenManager = new TokenManager();
|
|
@@ -1477,6 +1887,7 @@ var MitwayBaasClient = class {
|
|
|
1477
1887
|
config.anonKey,
|
|
1478
1888
|
config.realtime
|
|
1479
1889
|
);
|
|
1890
|
+
this.storage = new Storage(this.http);
|
|
1480
1891
|
}
|
|
1481
1892
|
/**
|
|
1482
1893
|
* Escape hatch for callers that need to make custom requests against the
|
|
@@ -1501,6 +1912,8 @@ export {
|
|
|
1501
1912
|
MitwayBaasError,
|
|
1502
1913
|
Realtime,
|
|
1503
1914
|
RealtimeChannel,
|
|
1915
|
+
Storage,
|
|
1916
|
+
StorageBucketClient,
|
|
1504
1917
|
TokenManager,
|
|
1505
1918
|
createClient,
|
|
1506
1919
|
index_default as default
|