@tinycloud/operations 0.3.0 → 0.3.1-beta.1
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/artifacts.cjs +231 -5
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.js +231 -5
- package/dist/artifacts.js.map +1 -1
- package/dist/cli-runtime.cjs +232 -6
- package/dist/cli-runtime.cjs.map +1 -1
- package/dist/cli-runtime.js +232 -6
- package/dist/cli-runtime.js.map +1 -1
- package/dist/index.cjs +232 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +232 -6
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/artifacts.cjs
CHANGED
|
@@ -1088,7 +1088,9 @@ function createKVResponseSchema(dataSchema) {
|
|
|
1088
1088
|
var GenericKVResponseSchema = createKVResponseSchema(import_zod.z.unknown());
|
|
1089
1089
|
var KVListResponseSchema = import_zod.z.object({
|
|
1090
1090
|
/** Array of keys matching the list criteria */
|
|
1091
|
-
keys: import_zod.z.array(import_zod.z.string())
|
|
1091
|
+
keys: import_zod.z.array(import_zod.z.string()),
|
|
1092
|
+
truncated: import_zod.z.boolean().optional(),
|
|
1093
|
+
nextCursor: import_zod.z.string().optional()
|
|
1092
1094
|
});
|
|
1093
1095
|
var KVListResultSchema = createResultSchema(KVListResponseSchema);
|
|
1094
1096
|
var ServiceRequestEventSchema = import_zod.z.object({
|
|
@@ -1492,6 +1494,20 @@ var PrefixedKVService = class _PrefixedKVService {
|
|
|
1492
1494
|
const fullKey = this.getFullKey(key);
|
|
1493
1495
|
return this._kv.get(fullKey, { ...options, prefix: "" });
|
|
1494
1496
|
}
|
|
1497
|
+
async batchGet(keys, options) {
|
|
1498
|
+
const response = await this._kv.batchGet(
|
|
1499
|
+
keys.map((key) => this.getFullKey(key)),
|
|
1500
|
+
{ ...options, prefix: "" }
|
|
1501
|
+
);
|
|
1502
|
+
if (!response.ok) return response;
|
|
1503
|
+
return ok({
|
|
1504
|
+
...response.data,
|
|
1505
|
+
results: response.data.results.map((item, index) => ({
|
|
1506
|
+
...item,
|
|
1507
|
+
key: keys[index]
|
|
1508
|
+
}))
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1495
1511
|
/**
|
|
1496
1512
|
* Store a value at a key.
|
|
1497
1513
|
*/
|
|
@@ -1536,6 +1552,20 @@ var PrefixedKVService = class _PrefixedKVService {
|
|
|
1536
1552
|
const fullKey = this.getFullKey(key);
|
|
1537
1553
|
return this._kv.head(fullKey, { ...options, prefix: "" });
|
|
1538
1554
|
}
|
|
1555
|
+
async batchHead(keys, options) {
|
|
1556
|
+
const response = await this._kv.batchHead(
|
|
1557
|
+
keys.map((key) => this.getFullKey(key)),
|
|
1558
|
+
{ ...options, prefix: "" }
|
|
1559
|
+
);
|
|
1560
|
+
if (!response.ok) return response;
|
|
1561
|
+
return ok({
|
|
1562
|
+
...response.data,
|
|
1563
|
+
results: response.data.results.map((item, index) => ({
|
|
1564
|
+
...item,
|
|
1565
|
+
key: keys[index]
|
|
1566
|
+
}))
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1539
1569
|
/**
|
|
1540
1570
|
* Create a short-lived signed URL for reading a KV object.
|
|
1541
1571
|
*/
|
|
@@ -1560,6 +1590,7 @@ var KVAction = {
|
|
|
1560
1590
|
DELETE: "tinycloud.kv/del",
|
|
1561
1591
|
HEAD: "tinycloud.kv/metadata"
|
|
1562
1592
|
};
|
|
1593
|
+
var MAX_KV_BATCH_READ_ITEMS = 100;
|
|
1563
1594
|
function encodeKvBatchPartName(path) {
|
|
1564
1595
|
return encodeURIComponent(path).replace(
|
|
1565
1596
|
/[!'()*]/g,
|
|
@@ -1794,6 +1825,173 @@ var KVService = class extends BaseService {
|
|
|
1794
1825
|
get: (name) => headers.get(name)
|
|
1795
1826
|
};
|
|
1796
1827
|
}
|
|
1828
|
+
createBatchResponseHeaders(values) {
|
|
1829
|
+
const normalized = new Map(
|
|
1830
|
+
Object.entries(values).map(([name, value]) => [name.toLowerCase(), value])
|
|
1831
|
+
);
|
|
1832
|
+
return this.createResponseHeaders({
|
|
1833
|
+
get: (name) => normalized.get(name.toLowerCase()) ?? null
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
parseBatchValue(dataBase64, headers, raw = false, binary = false) {
|
|
1837
|
+
const encoded = globalThis.atob(dataBase64);
|
|
1838
|
+
const bytes = Uint8Array.from(encoded, (character) => character.charCodeAt(0));
|
|
1839
|
+
if (binary) return bytes;
|
|
1840
|
+
const text = new TextDecoder().decode(bytes);
|
|
1841
|
+
if (raw) return text;
|
|
1842
|
+
const contentType = Object.entries(headers).find(
|
|
1843
|
+
([name]) => name.toLowerCase() === "content-type"
|
|
1844
|
+
)?.[1];
|
|
1845
|
+
return contentType?.includes("application/json") ? JSON.parse(text) : text;
|
|
1846
|
+
}
|
|
1847
|
+
normalizeBatchReadResponse(data, paths, requireData) {
|
|
1848
|
+
if (!data || typeof data !== "object") return void 0;
|
|
1849
|
+
const response = data;
|
|
1850
|
+
if (!Array.isArray(response.results) || response.results.length !== paths.length) {
|
|
1851
|
+
return void 0;
|
|
1852
|
+
}
|
|
1853
|
+
for (let index = 0; index < response.results.length; index++) {
|
|
1854
|
+
const item = response.results[index];
|
|
1855
|
+
if (!item || typeof item !== "object" || item.key !== paths[index] || typeof item.ok !== "boolean") {
|
|
1856
|
+
return void 0;
|
|
1857
|
+
}
|
|
1858
|
+
if (item.ok && (!item.headers || typeof item.headers !== "object" || Object.values(item.headers).some((value) => typeof value !== "string") || requireData && typeof item.dataBase64 !== "string")) {
|
|
1859
|
+
return void 0;
|
|
1860
|
+
}
|
|
1861
|
+
if (!item.ok && (!item.error || typeof item.error.code !== "string" || typeof item.error.message !== "string")) {
|
|
1862
|
+
return void 0;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
return response;
|
|
1866
|
+
}
|
|
1867
|
+
async batchRead(keys, action, options) {
|
|
1868
|
+
if (!this.requireAuth()) return err(authRequiredError("kv"));
|
|
1869
|
+
if (keys.length === 0) return ok({ results: [], count: 0 });
|
|
1870
|
+
if (keys.length > MAX_KV_BATCH_READ_ITEMS) {
|
|
1871
|
+
return err(serviceError(
|
|
1872
|
+
ErrorCodes.INVALID_INPUT,
|
|
1873
|
+
`KV batch reads accept at most ${MAX_KV_BATCH_READ_ITEMS} keys`,
|
|
1874
|
+
"kv"
|
|
1875
|
+
));
|
|
1876
|
+
}
|
|
1877
|
+
if (keys.length === 1) {
|
|
1878
|
+
const key = keys[0];
|
|
1879
|
+
const result = action === KVAction.GET ? await this.get(key, options) : await this.head(key, options);
|
|
1880
|
+
return ok({
|
|
1881
|
+
results: [{ key, result }],
|
|
1882
|
+
count: 1
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
if (!this.context.invokeAny) {
|
|
1886
|
+
return err(serviceError(
|
|
1887
|
+
ErrorCodes.INVALID_INPUT,
|
|
1888
|
+
"KV batch reads require SDK runtime support for multi-resource invocations",
|
|
1889
|
+
"kv"
|
|
1890
|
+
));
|
|
1891
|
+
}
|
|
1892
|
+
const getOptions = action === KVAction.GET ? options : void 0;
|
|
1893
|
+
if (getOptions?.maxResponseBytes !== void 0 && (!Number.isSafeInteger(getOptions.maxResponseBytes) || getOptions.maxResponseBytes <= 0)) {
|
|
1894
|
+
return err(serviceError(
|
|
1895
|
+
ErrorCodes.INVALID_INPUT,
|
|
1896
|
+
"KV maxResponseBytes must be a positive safe integer",
|
|
1897
|
+
"kv"
|
|
1898
|
+
));
|
|
1899
|
+
}
|
|
1900
|
+
const paths = keys.map((key) => this.getFullPath(key, options?.prefix));
|
|
1901
|
+
if (new Set(paths).size !== paths.length) {
|
|
1902
|
+
return err(serviceError(
|
|
1903
|
+
ErrorCodes.INVALID_INPUT,
|
|
1904
|
+
"KV batch read received duplicate keys after prefix resolution",
|
|
1905
|
+
"kv"
|
|
1906
|
+
));
|
|
1907
|
+
}
|
|
1908
|
+
try {
|
|
1909
|
+
const session = this.context.session;
|
|
1910
|
+
const invocationHeaders = this.context.invokeAny(
|
|
1911
|
+
session,
|
|
1912
|
+
paths.map((path) => ({
|
|
1913
|
+
spaceId: session.spaceId,
|
|
1914
|
+
service: "kv",
|
|
1915
|
+
path,
|
|
1916
|
+
action
|
|
1917
|
+
}))
|
|
1918
|
+
);
|
|
1919
|
+
const limitHeaders = {};
|
|
1920
|
+
if (getOptions?.maxResponseBytes !== void 0) {
|
|
1921
|
+
limitHeaders["x-tinycloud-max-response-bytes"] = String(
|
|
1922
|
+
getOptions.maxResponseBytes
|
|
1923
|
+
);
|
|
1924
|
+
}
|
|
1925
|
+
const headers = Array.isArray(invocationHeaders) ? [...invocationHeaders, ...Object.entries(limitHeaders)] : { ...invocationHeaders, ...limitHeaders };
|
|
1926
|
+
const response = await this.context.fetch(`${this.host}/invoke`, {
|
|
1927
|
+
method: "POST",
|
|
1928
|
+
headers,
|
|
1929
|
+
signal: this.combineSignals(options?.signal)
|
|
1930
|
+
});
|
|
1931
|
+
if (!response.ok) {
|
|
1932
|
+
const errorText = await response.text();
|
|
1933
|
+
if (response.status === 401 || response.status === 403) {
|
|
1934
|
+
const { resource, action: requiredAction } = parseAuthError(errorText);
|
|
1935
|
+
return err(authUnauthorizedError("kv", errorText, {
|
|
1936
|
+
status: response.status,
|
|
1937
|
+
...requiredAction && { requiredAction },
|
|
1938
|
+
...resource && { resource }
|
|
1939
|
+
}));
|
|
1940
|
+
}
|
|
1941
|
+
if (response.status === 413) {
|
|
1942
|
+
return err(serviceError(
|
|
1943
|
+
ErrorCodes.KV_RESPONSE_TOO_LARGE,
|
|
1944
|
+
"A KV batch value exceeds the requested response limit",
|
|
1945
|
+
"kv",
|
|
1946
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
1947
|
+
));
|
|
1948
|
+
}
|
|
1949
|
+
return err(serviceError(
|
|
1950
|
+
ErrorCodes.NETWORK_ERROR,
|
|
1951
|
+
`Failed to batch read ${keys.length} key(s): ${response.status} - ${errorText}`,
|
|
1952
|
+
"kv",
|
|
1953
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
1954
|
+
));
|
|
1955
|
+
}
|
|
1956
|
+
const payload = this.normalizeBatchReadResponse(
|
|
1957
|
+
await response.json(),
|
|
1958
|
+
paths,
|
|
1959
|
+
action === KVAction.GET
|
|
1960
|
+
);
|
|
1961
|
+
if (!payload) {
|
|
1962
|
+
return err(serviceError(
|
|
1963
|
+
ErrorCodes.NETWORK_ERROR,
|
|
1964
|
+
"KV batch read response did not match the requested keys",
|
|
1965
|
+
"kv"
|
|
1966
|
+
));
|
|
1967
|
+
}
|
|
1968
|
+
const results = payload.results.map((item, index) => {
|
|
1969
|
+
if (!item.ok) {
|
|
1970
|
+
return {
|
|
1971
|
+
key: keys[index],
|
|
1972
|
+
result: err(serviceError(item.error.code, item.error.message, "kv"))
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
const headers2 = item.headers;
|
|
1976
|
+
const data = action === KVAction.HEAD ? void 0 : this.parseBatchValue(
|
|
1977
|
+
item.dataBase64,
|
|
1978
|
+
headers2,
|
|
1979
|
+
getOptions?.raw,
|
|
1980
|
+
getOptions?.binary
|
|
1981
|
+
);
|
|
1982
|
+
return {
|
|
1983
|
+
key: keys[index],
|
|
1984
|
+
result: ok({
|
|
1985
|
+
data,
|
|
1986
|
+
headers: this.createBatchResponseHeaders(headers2)
|
|
1987
|
+
})
|
|
1988
|
+
};
|
|
1989
|
+
});
|
|
1990
|
+
return ok({ results, count: results.length });
|
|
1991
|
+
} catch (error) {
|
|
1992
|
+
return err(wrapError("kv", error));
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1797
1995
|
/**
|
|
1798
1996
|
* Parse response body based on content type.
|
|
1799
1997
|
*
|
|
@@ -1940,6 +2138,13 @@ var KVService = class extends BaseService {
|
|
|
1940
2138
|
}
|
|
1941
2139
|
});
|
|
1942
2140
|
}
|
|
2141
|
+
async batchGet(keys, options) {
|
|
2142
|
+
return this.withTelemetry(
|
|
2143
|
+
"batchGet",
|
|
2144
|
+
String(keys.length),
|
|
2145
|
+
() => this.batchRead(keys, KVAction.GET, options)
|
|
2146
|
+
);
|
|
2147
|
+
}
|
|
1943
2148
|
/**
|
|
1944
2149
|
* Store a value at a key.
|
|
1945
2150
|
*/
|
|
@@ -2147,7 +2352,10 @@ var KVService = class extends BaseService {
|
|
|
2147
2352
|
KVAction.LIST,
|
|
2148
2353
|
void 0,
|
|
2149
2354
|
options?.signal,
|
|
2150
|
-
options?.limit === void 0 ? void 0 : {
|
|
2355
|
+
options?.limit === void 0 && options?.cursor === void 0 ? void 0 : {
|
|
2356
|
+
...options?.limit === void 0 ? {} : { "x-tinycloud-limit": String(options.limit) },
|
|
2357
|
+
...options?.cursor === void 0 ? {} : { "x-tinycloud-cursor": options.cursor }
|
|
2358
|
+
}
|
|
2151
2359
|
);
|
|
2152
2360
|
if (!response.ok) {
|
|
2153
2361
|
if (response.status === 401) {
|
|
@@ -2179,7 +2387,8 @@ var KVService = class extends BaseService {
|
|
|
2179
2387
|
}
|
|
2180
2388
|
return ok({
|
|
2181
2389
|
keys,
|
|
2182
|
-
...response.headers.get("x-tinycloud-truncated") === null ? {} : { truncated: response.headers.get("x-tinycloud-truncated") === "true" }
|
|
2390
|
+
...response.headers.get("x-tinycloud-truncated") === null ? {} : { truncated: response.headers.get("x-tinycloud-truncated") === "true" },
|
|
2391
|
+
...response.headers.get("x-tinycloud-next-cursor") === null ? {} : { nextCursor: response.headers.get("x-tinycloud-next-cursor") ?? void 0 }
|
|
2183
2392
|
});
|
|
2184
2393
|
} catch (error) {
|
|
2185
2394
|
return err(wrapError("kv", error));
|
|
@@ -2299,6 +2508,13 @@ var KVService = class extends BaseService {
|
|
|
2299
2508
|
}
|
|
2300
2509
|
});
|
|
2301
2510
|
}
|
|
2511
|
+
async batchHead(keys, options) {
|
|
2512
|
+
return this.withTelemetry(
|
|
2513
|
+
"batchHead",
|
|
2514
|
+
String(keys.length),
|
|
2515
|
+
() => this.batchRead(keys, KVAction.HEAD, options)
|
|
2516
|
+
);
|
|
2517
|
+
}
|
|
2302
2518
|
/**
|
|
2303
2519
|
* Create a short-lived signed URL for reading a KV object.
|
|
2304
2520
|
*/
|
|
@@ -4831,6 +5047,10 @@ var DataVaultService = class extends BaseService {
|
|
|
4831
5047
|
* @returns Result with array of key names (vault/ prefix stripped)
|
|
4832
5048
|
*/
|
|
4833
5049
|
async list(options) {
|
|
5050
|
+
const page = await this.listPage(options);
|
|
5051
|
+
return page.ok ? { ok: true, data: page.data.keys } : page;
|
|
5052
|
+
}
|
|
5053
|
+
async listPage(options) {
|
|
4834
5054
|
return this.withTelemetry("list", options?.prefix, async () => {
|
|
4835
5055
|
if (!this.isUnlocked) {
|
|
4836
5056
|
return vaultError({
|
|
@@ -4848,7 +5068,9 @@ var DataVaultService = class extends BaseService {
|
|
|
4848
5068
|
const listPrefix = options?.prefix ? `vault/${options.prefix}` : "vault/";
|
|
4849
5069
|
const listResult = await this.tc.kv.list({
|
|
4850
5070
|
prefix: listPrefix,
|
|
4851
|
-
removePrefix: true
|
|
5071
|
+
removePrefix: true,
|
|
5072
|
+
...options?.limit === void 0 ? {} : { limit: options.limit },
|
|
5073
|
+
...options?.cursor === void 0 ? {} : { cursor: options.cursor }
|
|
4852
5074
|
});
|
|
4853
5075
|
if (!listResult.ok) {
|
|
4854
5076
|
return vaultError({
|
|
@@ -4865,7 +5087,11 @@ var DataVaultService = class extends BaseService {
|
|
|
4865
5087
|
(k) => k.startsWith(userPrefix) ? k.slice(userPrefix.length) : k
|
|
4866
5088
|
);
|
|
4867
5089
|
}
|
|
4868
|
-
return ok(
|
|
5090
|
+
return ok({
|
|
5091
|
+
keys,
|
|
5092
|
+
truncated: listResult.data.truncated === true,
|
|
5093
|
+
...listResult.data.nextCursor === void 0 ? {} : { nextCursor: listResult.data.nextCursor }
|
|
5094
|
+
});
|
|
4869
5095
|
} catch (error) {
|
|
4870
5096
|
return vaultError({
|
|
4871
5097
|
code: "STORAGE_ERROR",
|