@tinycloud/sdk-services 2.8.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/kv/index.cjs
CHANGED
|
@@ -450,6 +450,20 @@ var PrefixedKVService = class _PrefixedKVService {
|
|
|
450
450
|
const fullKey = this.getFullKey(key);
|
|
451
451
|
return this._kv.get(fullKey, { ...options, prefix: "" });
|
|
452
452
|
}
|
|
453
|
+
async batchGet(keys, options) {
|
|
454
|
+
const response = await this._kv.batchGet(
|
|
455
|
+
keys.map((key) => this.getFullKey(key)),
|
|
456
|
+
{ ...options, prefix: "" }
|
|
457
|
+
);
|
|
458
|
+
if (!response.ok) return response;
|
|
459
|
+
return ok({
|
|
460
|
+
...response.data,
|
|
461
|
+
results: response.data.results.map((item, index) => ({
|
|
462
|
+
...item,
|
|
463
|
+
key: keys[index]
|
|
464
|
+
}))
|
|
465
|
+
});
|
|
466
|
+
}
|
|
453
467
|
/**
|
|
454
468
|
* Store a value at a key.
|
|
455
469
|
*/
|
|
@@ -494,6 +508,20 @@ var PrefixedKVService = class _PrefixedKVService {
|
|
|
494
508
|
const fullKey = this.getFullKey(key);
|
|
495
509
|
return this._kv.head(fullKey, { ...options, prefix: "" });
|
|
496
510
|
}
|
|
511
|
+
async batchHead(keys, options) {
|
|
512
|
+
const response = await this._kv.batchHead(
|
|
513
|
+
keys.map((key) => this.getFullKey(key)),
|
|
514
|
+
{ ...options, prefix: "" }
|
|
515
|
+
);
|
|
516
|
+
if (!response.ok) return response;
|
|
517
|
+
return ok({
|
|
518
|
+
...response.data,
|
|
519
|
+
results: response.data.results.map((item, index) => ({
|
|
520
|
+
...item,
|
|
521
|
+
key: keys[index]
|
|
522
|
+
}))
|
|
523
|
+
});
|
|
524
|
+
}
|
|
497
525
|
/**
|
|
498
526
|
* Create a short-lived signed URL for reading a KV object.
|
|
499
527
|
*/
|
|
@@ -522,6 +550,7 @@ var KVAction = {
|
|
|
522
550
|
};
|
|
523
551
|
|
|
524
552
|
// src/kv/KVService.ts
|
|
553
|
+
var MAX_KV_BATCH_READ_ITEMS = 100;
|
|
525
554
|
function encodeKvBatchPartName(path) {
|
|
526
555
|
return encodeURIComponent(path).replace(
|
|
527
556
|
/[!'()*]/g,
|
|
@@ -756,6 +785,173 @@ var KVService = class extends BaseService {
|
|
|
756
785
|
get: (name) => headers.get(name)
|
|
757
786
|
};
|
|
758
787
|
}
|
|
788
|
+
createBatchResponseHeaders(values) {
|
|
789
|
+
const normalized = new Map(
|
|
790
|
+
Object.entries(values).map(([name, value]) => [name.toLowerCase(), value])
|
|
791
|
+
);
|
|
792
|
+
return this.createResponseHeaders({
|
|
793
|
+
get: (name) => normalized.get(name.toLowerCase()) ?? null
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
parseBatchValue(dataBase64, headers, raw = false, binary = false) {
|
|
797
|
+
const encoded = globalThis.atob(dataBase64);
|
|
798
|
+
const bytes = Uint8Array.from(encoded, (character) => character.charCodeAt(0));
|
|
799
|
+
if (binary) return bytes;
|
|
800
|
+
const text = new TextDecoder().decode(bytes);
|
|
801
|
+
if (raw) return text;
|
|
802
|
+
const contentType = Object.entries(headers).find(
|
|
803
|
+
([name]) => name.toLowerCase() === "content-type"
|
|
804
|
+
)?.[1];
|
|
805
|
+
return contentType?.includes("application/json") ? JSON.parse(text) : text;
|
|
806
|
+
}
|
|
807
|
+
normalizeBatchReadResponse(data, paths, requireData) {
|
|
808
|
+
if (!data || typeof data !== "object") return void 0;
|
|
809
|
+
const response = data;
|
|
810
|
+
if (!Array.isArray(response.results) || response.results.length !== paths.length) {
|
|
811
|
+
return void 0;
|
|
812
|
+
}
|
|
813
|
+
for (let index = 0; index < response.results.length; index++) {
|
|
814
|
+
const item = response.results[index];
|
|
815
|
+
if (!item || typeof item !== "object" || item.key !== paths[index] || typeof item.ok !== "boolean") {
|
|
816
|
+
return void 0;
|
|
817
|
+
}
|
|
818
|
+
if (item.ok && (!item.headers || typeof item.headers !== "object" || Object.values(item.headers).some((value) => typeof value !== "string") || requireData && typeof item.dataBase64 !== "string")) {
|
|
819
|
+
return void 0;
|
|
820
|
+
}
|
|
821
|
+
if (!item.ok && (!item.error || typeof item.error.code !== "string" || typeof item.error.message !== "string")) {
|
|
822
|
+
return void 0;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return response;
|
|
826
|
+
}
|
|
827
|
+
async batchRead(keys, action, options) {
|
|
828
|
+
if (!this.requireAuth()) return err(authRequiredError("kv"));
|
|
829
|
+
if (keys.length === 0) return ok({ results: [], count: 0 });
|
|
830
|
+
if (keys.length > MAX_KV_BATCH_READ_ITEMS) {
|
|
831
|
+
return err(serviceError(
|
|
832
|
+
ErrorCodes.INVALID_INPUT,
|
|
833
|
+
`KV batch reads accept at most ${MAX_KV_BATCH_READ_ITEMS} keys`,
|
|
834
|
+
"kv"
|
|
835
|
+
));
|
|
836
|
+
}
|
|
837
|
+
if (keys.length === 1) {
|
|
838
|
+
const key = keys[0];
|
|
839
|
+
const result = action === KVAction.GET ? await this.get(key, options) : await this.head(key, options);
|
|
840
|
+
return ok({
|
|
841
|
+
results: [{ key, result }],
|
|
842
|
+
count: 1
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
if (!this.context.invokeAny) {
|
|
846
|
+
return err(serviceError(
|
|
847
|
+
ErrorCodes.INVALID_INPUT,
|
|
848
|
+
"KV batch reads require SDK runtime support for multi-resource invocations",
|
|
849
|
+
"kv"
|
|
850
|
+
));
|
|
851
|
+
}
|
|
852
|
+
const getOptions = action === KVAction.GET ? options : void 0;
|
|
853
|
+
if (getOptions?.maxResponseBytes !== void 0 && (!Number.isSafeInteger(getOptions.maxResponseBytes) || getOptions.maxResponseBytes <= 0)) {
|
|
854
|
+
return err(serviceError(
|
|
855
|
+
ErrorCodes.INVALID_INPUT,
|
|
856
|
+
"KV maxResponseBytes must be a positive safe integer",
|
|
857
|
+
"kv"
|
|
858
|
+
));
|
|
859
|
+
}
|
|
860
|
+
const paths = keys.map((key) => this.getFullPath(key, options?.prefix));
|
|
861
|
+
if (new Set(paths).size !== paths.length) {
|
|
862
|
+
return err(serviceError(
|
|
863
|
+
ErrorCodes.INVALID_INPUT,
|
|
864
|
+
"KV batch read received duplicate keys after prefix resolution",
|
|
865
|
+
"kv"
|
|
866
|
+
));
|
|
867
|
+
}
|
|
868
|
+
try {
|
|
869
|
+
const session = this.context.session;
|
|
870
|
+
const invocationHeaders = this.context.invokeAny(
|
|
871
|
+
session,
|
|
872
|
+
paths.map((path) => ({
|
|
873
|
+
spaceId: session.spaceId,
|
|
874
|
+
service: "kv",
|
|
875
|
+
path,
|
|
876
|
+
action
|
|
877
|
+
}))
|
|
878
|
+
);
|
|
879
|
+
const limitHeaders = {};
|
|
880
|
+
if (getOptions?.maxResponseBytes !== void 0) {
|
|
881
|
+
limitHeaders["x-tinycloud-max-response-bytes"] = String(
|
|
882
|
+
getOptions.maxResponseBytes
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
const headers = Array.isArray(invocationHeaders) ? [...invocationHeaders, ...Object.entries(limitHeaders)] : { ...invocationHeaders, ...limitHeaders };
|
|
886
|
+
const response = await this.context.fetch(`${this.host}/invoke`, {
|
|
887
|
+
method: "POST",
|
|
888
|
+
headers,
|
|
889
|
+
signal: this.combineSignals(options?.signal)
|
|
890
|
+
});
|
|
891
|
+
if (!response.ok) {
|
|
892
|
+
const errorText = await response.text();
|
|
893
|
+
if (response.status === 401 || response.status === 403) {
|
|
894
|
+
const { resource, action: requiredAction } = parseAuthError(errorText);
|
|
895
|
+
return err(authUnauthorizedError("kv", errorText, {
|
|
896
|
+
status: response.status,
|
|
897
|
+
...requiredAction && { requiredAction },
|
|
898
|
+
...resource && { resource }
|
|
899
|
+
}));
|
|
900
|
+
}
|
|
901
|
+
if (response.status === 413) {
|
|
902
|
+
return err(serviceError(
|
|
903
|
+
ErrorCodes.KV_RESPONSE_TOO_LARGE,
|
|
904
|
+
"A KV batch value exceeds the requested response limit",
|
|
905
|
+
"kv",
|
|
906
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
907
|
+
));
|
|
908
|
+
}
|
|
909
|
+
return err(serviceError(
|
|
910
|
+
ErrorCodes.NETWORK_ERROR,
|
|
911
|
+
`Failed to batch read ${keys.length} key(s): ${response.status} - ${errorText}`,
|
|
912
|
+
"kv",
|
|
913
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
914
|
+
));
|
|
915
|
+
}
|
|
916
|
+
const payload = this.normalizeBatchReadResponse(
|
|
917
|
+
await response.json(),
|
|
918
|
+
paths,
|
|
919
|
+
action === KVAction.GET
|
|
920
|
+
);
|
|
921
|
+
if (!payload) {
|
|
922
|
+
return err(serviceError(
|
|
923
|
+
ErrorCodes.NETWORK_ERROR,
|
|
924
|
+
"KV batch read response did not match the requested keys",
|
|
925
|
+
"kv"
|
|
926
|
+
));
|
|
927
|
+
}
|
|
928
|
+
const results = payload.results.map((item, index) => {
|
|
929
|
+
if (!item.ok) {
|
|
930
|
+
return {
|
|
931
|
+
key: keys[index],
|
|
932
|
+
result: err(serviceError(item.error.code, item.error.message, "kv"))
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
const headers2 = item.headers;
|
|
936
|
+
const data = action === KVAction.HEAD ? void 0 : this.parseBatchValue(
|
|
937
|
+
item.dataBase64,
|
|
938
|
+
headers2,
|
|
939
|
+
getOptions?.raw,
|
|
940
|
+
getOptions?.binary
|
|
941
|
+
);
|
|
942
|
+
return {
|
|
943
|
+
key: keys[index],
|
|
944
|
+
result: ok({
|
|
945
|
+
data,
|
|
946
|
+
headers: this.createBatchResponseHeaders(headers2)
|
|
947
|
+
})
|
|
948
|
+
};
|
|
949
|
+
});
|
|
950
|
+
return ok({ results, count: results.length });
|
|
951
|
+
} catch (error) {
|
|
952
|
+
return err(wrapError("kv", error));
|
|
953
|
+
}
|
|
954
|
+
}
|
|
759
955
|
/**
|
|
760
956
|
* Parse response body based on content type.
|
|
761
957
|
*
|
|
@@ -902,6 +1098,13 @@ var KVService = class extends BaseService {
|
|
|
902
1098
|
}
|
|
903
1099
|
});
|
|
904
1100
|
}
|
|
1101
|
+
async batchGet(keys, options) {
|
|
1102
|
+
return this.withTelemetry(
|
|
1103
|
+
"batchGet",
|
|
1104
|
+
String(keys.length),
|
|
1105
|
+
() => this.batchRead(keys, KVAction.GET, options)
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
905
1108
|
/**
|
|
906
1109
|
* Store a value at a key.
|
|
907
1110
|
*/
|
|
@@ -1261,6 +1464,13 @@ var KVService = class extends BaseService {
|
|
|
1261
1464
|
}
|
|
1262
1465
|
});
|
|
1263
1466
|
}
|
|
1467
|
+
async batchHead(keys, options) {
|
|
1468
|
+
return this.withTelemetry(
|
|
1469
|
+
"batchHead",
|
|
1470
|
+
String(keys.length),
|
|
1471
|
+
() => this.batchRead(keys, KVAction.HEAD, options)
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1264
1474
|
/**
|
|
1265
1475
|
* Create a short-lived signed URL for reading a KV object.
|
|
1266
1476
|
*/
|