@tinycloud/sdk-services 2.8.0-beta.0 → 2.10.0-beta.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/dist/index.cjs +210 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +210 -0
- package/dist/index.js.map +1 -1
- package/dist/kv/index.cjs +210 -0
- package/dist/kv/index.cjs.map +1 -1
- package/dist/kv/index.d.cts +28 -1
- package/dist/kv/index.d.ts +28 -1
- package/dist/kv/index.js +210 -0
- package/dist/kv/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1418,6 +1418,20 @@ var PrefixedKVService = class _PrefixedKVService {
|
|
|
1418
1418
|
const fullKey = this.getFullKey(key);
|
|
1419
1419
|
return this._kv.get(fullKey, { ...options, prefix: "" });
|
|
1420
1420
|
}
|
|
1421
|
+
async batchGet(keys, options) {
|
|
1422
|
+
const response = await this._kv.batchGet(
|
|
1423
|
+
keys.map((key) => this.getFullKey(key)),
|
|
1424
|
+
{ ...options, prefix: "" }
|
|
1425
|
+
);
|
|
1426
|
+
if (!response.ok) return response;
|
|
1427
|
+
return ok({
|
|
1428
|
+
...response.data,
|
|
1429
|
+
results: response.data.results.map((item, index) => ({
|
|
1430
|
+
...item,
|
|
1431
|
+
key: keys[index]
|
|
1432
|
+
}))
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1421
1435
|
/**
|
|
1422
1436
|
* Store a value at a key.
|
|
1423
1437
|
*/
|
|
@@ -1462,6 +1476,20 @@ var PrefixedKVService = class _PrefixedKVService {
|
|
|
1462
1476
|
const fullKey = this.getFullKey(key);
|
|
1463
1477
|
return this._kv.head(fullKey, { ...options, prefix: "" });
|
|
1464
1478
|
}
|
|
1479
|
+
async batchHead(keys, options) {
|
|
1480
|
+
const response = await this._kv.batchHead(
|
|
1481
|
+
keys.map((key) => this.getFullKey(key)),
|
|
1482
|
+
{ ...options, prefix: "" }
|
|
1483
|
+
);
|
|
1484
|
+
if (!response.ok) return response;
|
|
1485
|
+
return ok({
|
|
1486
|
+
...response.data,
|
|
1487
|
+
results: response.data.results.map((item, index) => ({
|
|
1488
|
+
...item,
|
|
1489
|
+
key: keys[index]
|
|
1490
|
+
}))
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1465
1493
|
/**
|
|
1466
1494
|
* Create a short-lived signed URL for reading a KV object.
|
|
1467
1495
|
*/
|
|
@@ -1490,6 +1518,7 @@ var KVAction = {
|
|
|
1490
1518
|
};
|
|
1491
1519
|
|
|
1492
1520
|
// src/kv/KVService.ts
|
|
1521
|
+
var MAX_KV_BATCH_READ_ITEMS = 100;
|
|
1493
1522
|
function encodeKvBatchPartName(path) {
|
|
1494
1523
|
return encodeURIComponent(path).replace(
|
|
1495
1524
|
/[!'()*]/g,
|
|
@@ -1724,6 +1753,173 @@ var KVService = class extends BaseService {
|
|
|
1724
1753
|
get: (name) => headers.get(name)
|
|
1725
1754
|
};
|
|
1726
1755
|
}
|
|
1756
|
+
createBatchResponseHeaders(values) {
|
|
1757
|
+
const normalized = new Map(
|
|
1758
|
+
Object.entries(values).map(([name, value]) => [name.toLowerCase(), value])
|
|
1759
|
+
);
|
|
1760
|
+
return this.createResponseHeaders({
|
|
1761
|
+
get: (name) => normalized.get(name.toLowerCase()) ?? null
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
parseBatchValue(dataBase64, headers, raw = false, binary = false) {
|
|
1765
|
+
const encoded = globalThis.atob(dataBase64);
|
|
1766
|
+
const bytes = Uint8Array.from(encoded, (character) => character.charCodeAt(0));
|
|
1767
|
+
if (binary) return bytes;
|
|
1768
|
+
const text = new TextDecoder().decode(bytes);
|
|
1769
|
+
if (raw) return text;
|
|
1770
|
+
const contentType = Object.entries(headers).find(
|
|
1771
|
+
([name]) => name.toLowerCase() === "content-type"
|
|
1772
|
+
)?.[1];
|
|
1773
|
+
return contentType?.includes("application/json") ? JSON.parse(text) : text;
|
|
1774
|
+
}
|
|
1775
|
+
normalizeBatchReadResponse(data, paths, requireData) {
|
|
1776
|
+
if (!data || typeof data !== "object") return void 0;
|
|
1777
|
+
const response = data;
|
|
1778
|
+
if (!Array.isArray(response.results) || response.results.length !== paths.length) {
|
|
1779
|
+
return void 0;
|
|
1780
|
+
}
|
|
1781
|
+
for (let index = 0; index < response.results.length; index++) {
|
|
1782
|
+
const item = response.results[index];
|
|
1783
|
+
if (!item || typeof item !== "object" || item.key !== paths[index] || typeof item.ok !== "boolean") {
|
|
1784
|
+
return void 0;
|
|
1785
|
+
}
|
|
1786
|
+
if (item.ok && (!item.headers || typeof item.headers !== "object" || Object.values(item.headers).some((value) => typeof value !== "string") || requireData && typeof item.dataBase64 !== "string")) {
|
|
1787
|
+
return void 0;
|
|
1788
|
+
}
|
|
1789
|
+
if (!item.ok && (!item.error || typeof item.error.code !== "string" || typeof item.error.message !== "string")) {
|
|
1790
|
+
return void 0;
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
return response;
|
|
1794
|
+
}
|
|
1795
|
+
async batchRead(keys, action, options) {
|
|
1796
|
+
if (!this.requireAuth()) return err(authRequiredError("kv"));
|
|
1797
|
+
if (keys.length === 0) return ok({ results: [], count: 0 });
|
|
1798
|
+
if (keys.length > MAX_KV_BATCH_READ_ITEMS) {
|
|
1799
|
+
return err(serviceError(
|
|
1800
|
+
ErrorCodes.INVALID_INPUT,
|
|
1801
|
+
`KV batch reads accept at most ${MAX_KV_BATCH_READ_ITEMS} keys`,
|
|
1802
|
+
"kv"
|
|
1803
|
+
));
|
|
1804
|
+
}
|
|
1805
|
+
if (keys.length === 1) {
|
|
1806
|
+
const key = keys[0];
|
|
1807
|
+
const result = action === KVAction.GET ? await this.get(key, options) : await this.head(key, options);
|
|
1808
|
+
return ok({
|
|
1809
|
+
results: [{ key, result }],
|
|
1810
|
+
count: 1
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
if (!this.context.invokeAny) {
|
|
1814
|
+
return err(serviceError(
|
|
1815
|
+
ErrorCodes.INVALID_INPUT,
|
|
1816
|
+
"KV batch reads require SDK runtime support for multi-resource invocations",
|
|
1817
|
+
"kv"
|
|
1818
|
+
));
|
|
1819
|
+
}
|
|
1820
|
+
const getOptions = action === KVAction.GET ? options : void 0;
|
|
1821
|
+
if (getOptions?.maxResponseBytes !== void 0 && (!Number.isSafeInteger(getOptions.maxResponseBytes) || getOptions.maxResponseBytes <= 0)) {
|
|
1822
|
+
return err(serviceError(
|
|
1823
|
+
ErrorCodes.INVALID_INPUT,
|
|
1824
|
+
"KV maxResponseBytes must be a positive safe integer",
|
|
1825
|
+
"kv"
|
|
1826
|
+
));
|
|
1827
|
+
}
|
|
1828
|
+
const paths = keys.map((key) => this.getFullPath(key, options?.prefix));
|
|
1829
|
+
if (new Set(paths).size !== paths.length) {
|
|
1830
|
+
return err(serviceError(
|
|
1831
|
+
ErrorCodes.INVALID_INPUT,
|
|
1832
|
+
"KV batch read received duplicate keys after prefix resolution",
|
|
1833
|
+
"kv"
|
|
1834
|
+
));
|
|
1835
|
+
}
|
|
1836
|
+
try {
|
|
1837
|
+
const session = this.context.session;
|
|
1838
|
+
const invocationHeaders = this.context.invokeAny(
|
|
1839
|
+
session,
|
|
1840
|
+
paths.map((path) => ({
|
|
1841
|
+
spaceId: session.spaceId,
|
|
1842
|
+
service: "kv",
|
|
1843
|
+
path,
|
|
1844
|
+
action
|
|
1845
|
+
}))
|
|
1846
|
+
);
|
|
1847
|
+
const limitHeaders = {};
|
|
1848
|
+
if (getOptions?.maxResponseBytes !== void 0) {
|
|
1849
|
+
limitHeaders["x-tinycloud-max-response-bytes"] = String(
|
|
1850
|
+
getOptions.maxResponseBytes
|
|
1851
|
+
);
|
|
1852
|
+
}
|
|
1853
|
+
const headers = Array.isArray(invocationHeaders) ? [...invocationHeaders, ...Object.entries(limitHeaders)] : { ...invocationHeaders, ...limitHeaders };
|
|
1854
|
+
const response = await this.context.fetch(`${this.host}/invoke`, {
|
|
1855
|
+
method: "POST",
|
|
1856
|
+
headers,
|
|
1857
|
+
signal: this.combineSignals(options?.signal)
|
|
1858
|
+
});
|
|
1859
|
+
if (!response.ok) {
|
|
1860
|
+
const errorText = await response.text();
|
|
1861
|
+
if (response.status === 401 || response.status === 403) {
|
|
1862
|
+
const { resource, action: requiredAction } = parseAuthError(errorText);
|
|
1863
|
+
return err(authUnauthorizedError("kv", errorText, {
|
|
1864
|
+
status: response.status,
|
|
1865
|
+
...requiredAction && { requiredAction },
|
|
1866
|
+
...resource && { resource }
|
|
1867
|
+
}));
|
|
1868
|
+
}
|
|
1869
|
+
if (response.status === 413) {
|
|
1870
|
+
return err(serviceError(
|
|
1871
|
+
ErrorCodes.KV_RESPONSE_TOO_LARGE,
|
|
1872
|
+
"A KV batch value exceeds the requested response limit",
|
|
1873
|
+
"kv",
|
|
1874
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
1875
|
+
));
|
|
1876
|
+
}
|
|
1877
|
+
return err(serviceError(
|
|
1878
|
+
ErrorCodes.NETWORK_ERROR,
|
|
1879
|
+
`Failed to batch read ${keys.length} key(s): ${response.status} - ${errorText}`,
|
|
1880
|
+
"kv",
|
|
1881
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
1882
|
+
));
|
|
1883
|
+
}
|
|
1884
|
+
const payload = this.normalizeBatchReadResponse(
|
|
1885
|
+
await response.json(),
|
|
1886
|
+
paths,
|
|
1887
|
+
action === KVAction.GET
|
|
1888
|
+
);
|
|
1889
|
+
if (!payload) {
|
|
1890
|
+
return err(serviceError(
|
|
1891
|
+
ErrorCodes.NETWORK_ERROR,
|
|
1892
|
+
"KV batch read response did not match the requested keys",
|
|
1893
|
+
"kv"
|
|
1894
|
+
));
|
|
1895
|
+
}
|
|
1896
|
+
const results = payload.results.map((item, index) => {
|
|
1897
|
+
if (!item.ok) {
|
|
1898
|
+
return {
|
|
1899
|
+
key: keys[index],
|
|
1900
|
+
result: err(serviceError(item.error.code, item.error.message, "kv"))
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
const headers2 = item.headers;
|
|
1904
|
+
const data = action === KVAction.HEAD ? void 0 : this.parseBatchValue(
|
|
1905
|
+
item.dataBase64,
|
|
1906
|
+
headers2,
|
|
1907
|
+
getOptions?.raw,
|
|
1908
|
+
getOptions?.binary
|
|
1909
|
+
);
|
|
1910
|
+
return {
|
|
1911
|
+
key: keys[index],
|
|
1912
|
+
result: ok({
|
|
1913
|
+
data,
|
|
1914
|
+
headers: this.createBatchResponseHeaders(headers2)
|
|
1915
|
+
})
|
|
1916
|
+
};
|
|
1917
|
+
});
|
|
1918
|
+
return ok({ results, count: results.length });
|
|
1919
|
+
} catch (error) {
|
|
1920
|
+
return err(wrapError("kv", error));
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1727
1923
|
/**
|
|
1728
1924
|
* Parse response body based on content type.
|
|
1729
1925
|
*
|
|
@@ -1870,6 +2066,13 @@ var KVService = class extends BaseService {
|
|
|
1870
2066
|
}
|
|
1871
2067
|
});
|
|
1872
2068
|
}
|
|
2069
|
+
async batchGet(keys, options) {
|
|
2070
|
+
return this.withTelemetry(
|
|
2071
|
+
"batchGet",
|
|
2072
|
+
String(keys.length),
|
|
2073
|
+
() => this.batchRead(keys, KVAction.GET, options)
|
|
2074
|
+
);
|
|
2075
|
+
}
|
|
1873
2076
|
/**
|
|
1874
2077
|
* Store a value at a key.
|
|
1875
2078
|
*/
|
|
@@ -2229,6 +2432,13 @@ var KVService = class extends BaseService {
|
|
|
2229
2432
|
}
|
|
2230
2433
|
});
|
|
2231
2434
|
}
|
|
2435
|
+
async batchHead(keys, options) {
|
|
2436
|
+
return this.withTelemetry(
|
|
2437
|
+
"batchHead",
|
|
2438
|
+
String(keys.length),
|
|
2439
|
+
() => this.batchRead(keys, KVAction.HEAD, options)
|
|
2440
|
+
);
|
|
2441
|
+
}
|
|
2232
2442
|
/**
|
|
2233
2443
|
* Create a short-lived signed URL for reading a KV object.
|
|
2234
2444
|
*/
|