@mitway/sdk 0.3.0 → 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 +396 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +144 -1
- package/dist/index.d.ts +144 -1
- package/dist/index.js +394 -0
- 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
|
}
|
|
@@ -1477,6 +1500,373 @@ var Realtime = class {
|
|
|
1477
1500
|
}
|
|
1478
1501
|
};
|
|
1479
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
|
+
|
|
1480
1870
|
// src/client.ts
|
|
1481
1871
|
var MitwayBaasClient = class {
|
|
1482
1872
|
http;
|
|
@@ -1484,6 +1874,7 @@ var MitwayBaasClient = class {
|
|
|
1484
1874
|
auth;
|
|
1485
1875
|
database;
|
|
1486
1876
|
realtime;
|
|
1877
|
+
storage;
|
|
1487
1878
|
constructor(config = {}) {
|
|
1488
1879
|
const logger = new Logger(config.debug);
|
|
1489
1880
|
this.tokenManager = new TokenManager();
|
|
@@ -1496,6 +1887,7 @@ var MitwayBaasClient = class {
|
|
|
1496
1887
|
config.anonKey,
|
|
1497
1888
|
config.realtime
|
|
1498
1889
|
);
|
|
1890
|
+
this.storage = new Storage(this.http);
|
|
1499
1891
|
}
|
|
1500
1892
|
/**
|
|
1501
1893
|
* Escape hatch for callers that need to make custom requests against the
|
|
@@ -1520,6 +1912,8 @@ export {
|
|
|
1520
1912
|
MitwayBaasError,
|
|
1521
1913
|
Realtime,
|
|
1522
1914
|
RealtimeChannel,
|
|
1915
|
+
Storage,
|
|
1916
|
+
StorageBucketClient,
|
|
1523
1917
|
TokenManager,
|
|
1524
1918
|
createClient,
|
|
1525
1919
|
index_default as default
|