@tinycloud/sdk-services 3.1.0-beta.9 → 3.2.0-beta.2

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/kv/index.cjs CHANGED
@@ -830,6 +830,24 @@ var MAX_KV_BATCH_READ_ITEMS = 100;
830
830
  function isJsonObject(value) {
831
831
  return typeof value === "object" && value !== null && !Array.isArray(value);
832
832
  }
833
+ var pathByteEncoder = new TextEncoder();
834
+ function comparePathsByBytes(a, b) {
835
+ const left = pathByteEncoder.encode(a);
836
+ const right = pathByteEncoder.encode(b);
837
+ const length = Math.min(left.length, right.length);
838
+ for (let index = 0; index < length; index++) {
839
+ if (left[index] !== right[index]) {
840
+ return left[index] - right[index];
841
+ }
842
+ }
843
+ return left.length - right.length;
844
+ }
845
+ var REVOKED_BODY = /^(?:Invalid invocation: )?delegation-(?:(revoked): [A-Za-z0-9]+|(ancestor-revoked): ancestor=[A-Za-z0-9]+ invoked=[A-Za-z0-9]+)$/;
846
+ function revocationCodeOf(body) {
847
+ const match = REVOKED_BODY.exec(body.trim());
848
+ if (match === null) return void 0;
849
+ return match[1] !== void 0 ? ErrorCodes.AUTH_DELEGATION_REVOKED : ErrorCodes.AUTH_DELEGATION_ANCESTOR_REVOKED;
850
+ }
833
851
  function encodeKvBatchPartName(path) {
834
852
  return encodeURIComponent(path).replace(
835
853
  /[!'()*]/g,
@@ -1057,15 +1075,25 @@ var KVService = class extends BaseService {
1057
1075
  )?.[1];
1058
1076
  return contentType?.includes("application/json") ? JSON.parse(text) : text;
1059
1077
  }
1078
+ /**
1079
+ * Validate a batch read response and return its items in the caller's
1080
+ * request order. The node flattens invocation abilities from a `BTreeMap`,
1081
+ * so results arrive sorted by UTF-8 byte order of the path, not in request
1082
+ * order. Items that carry their path in `key` are matched by path; if no
1083
+ * item carries a key, items are assumed to be in the node's byte-sorted
1084
+ * order and the request is aligned the same way. Any mix of keyed and
1085
+ * keyless items, a missing requested path, an unrequested path, or a
1086
+ * malformed item fails closed (returns undefined).
1087
+ */
1060
1088
  normalizeBatchReadResponse(data, paths, requireData) {
1061
1089
  if (!data || typeof data !== "object") return void 0;
1062
1090
  const response = data;
1063
- if (!Array.isArray(response.results) || response.results.length !== paths.length) {
1091
+ const items = response.results;
1092
+ if (!Array.isArray(items) || items.length !== paths.length) {
1064
1093
  return void 0;
1065
1094
  }
1066
- for (let index = 0; index < response.results.length; index++) {
1067
- const item = response.results[index];
1068
- if (!item || typeof item !== "object" || item.key !== paths[index] || typeof item.ok !== "boolean") {
1095
+ for (const item of items) {
1096
+ if (!item || typeof item !== "object" || typeof item.ok !== "boolean") {
1069
1097
  return void 0;
1070
1098
  }
1071
1099
  if (item.ok && (!item.headers || typeof item.headers !== "object" || Object.values(item.headers).some((value) => typeof value !== "string") || requireData && typeof item.dataBase64 !== "string")) {
@@ -1075,7 +1103,24 @@ var KVService = class extends BaseService {
1075
1103
  return void 0;
1076
1104
  }
1077
1105
  }
1078
- return response;
1106
+ if (items.every((item) => typeof item.key === "string")) {
1107
+ const byPath = new Map(items.map((item) => [item.key, item]));
1108
+ if (byPath.size !== items.length) return void 0;
1109
+ const aligned2 = [];
1110
+ for (const path of paths) {
1111
+ const item = byPath.get(path);
1112
+ if (!item) return void 0;
1113
+ aligned2.push(item);
1114
+ }
1115
+ return aligned2;
1116
+ }
1117
+ if (items.some((item) => item.key !== void 0)) return void 0;
1118
+ const requestOrder = paths.map((_, index) => index).sort((a, b) => comparePathsByBytes(paths[a], paths[b]));
1119
+ const aligned = new Array(items.length);
1120
+ for (let position = 0; position < requestOrder.length; position++) {
1121
+ aligned[requestOrder[position]] = items[position];
1122
+ }
1123
+ return aligned;
1079
1124
  }
1080
1125
  async batchRead(keys, action, options) {
1081
1126
  if (!this.requireAuth()) return err(authRequiredError("kv"));
@@ -1168,19 +1213,19 @@ var KVService = class extends BaseService {
1168
1213
  { meta: { status: response.status, statusText: response.statusText } }
1169
1214
  ));
1170
1215
  }
1171
- const payload = this.normalizeBatchReadResponse(
1216
+ const items = this.normalizeBatchReadResponse(
1172
1217
  await response.json(),
1173
1218
  paths,
1174
1219
  action === KVAction.GET
1175
1220
  );
1176
- if (!payload) {
1221
+ if (!items) {
1177
1222
  return err(serviceError(
1178
1223
  ErrorCodes.NETWORK_ERROR,
1179
1224
  "KV batch read response did not match the requested keys",
1180
1225
  "kv"
1181
1226
  ));
1182
1227
  }
1183
- const results = payload.results.map((item, index) => {
1228
+ const results = items.map((item, index) => {
1184
1229
  if (!item.ok) {
1185
1230
  return {
1186
1231
  key: keys[index],
@@ -1371,7 +1416,7 @@ var KVService = class extends BaseService {
1371
1416
  );
1372
1417
  }
1373
1418
  if (response.status === 404) {
1374
- return this.classifyNotFound(response, key);
1419
+ return await this.classifyNotFound(response, key);
1375
1420
  }
1376
1421
  const errorText = await response.text();
1377
1422
  if (response.status === 413) {
@@ -1814,8 +1859,8 @@ var KVService = class extends BaseService {
1814
1859
  }
1815
1860
  /**
1816
1861
  * Map a failed `kv/sync` response. 410 and the retention 403 carry
1817
- * `{"error":{"code","reason"}}`; a revoked grant is a 401 whose text names
1818
- * `delegation-revoked` or `delegation-ancestor-revoked`. A body read that
1862
+ * `{"error":{"code","reason"}}`; a revoked grant is a 401 whose whole body
1863
+ * is the node's revocation error (see REVOKED_BODY). A body read that
1819
1864
  * fails because the request was cancelled or timed out rethrows, so the
1820
1865
  * caller sees `ABORTED`/`TIMEOUT` rather than a mapped status.
1821
1866
  */
@@ -1860,7 +1905,7 @@ var KVService = class extends BaseService {
1860
1905
  ));
1861
1906
  }
1862
1907
  if (response.status === 401) {
1863
- const revoked = /\bdelegation-ancestor-revoked\b/.test(errorText) ? ErrorCodes.AUTH_DELEGATION_ANCESTOR_REVOKED : /\bdelegation-revoked\b/.test(errorText) ? ErrorCodes.AUTH_DELEGATION_REVOKED : void 0;
1908
+ const revoked = revocationCodeOf(errorText);
1864
1909
  if (revoked !== void 0) {
1865
1910
  return err(serviceError(revoked, `${context}: 401 - ${errorText}`, "kv", { meta }));
1866
1911
  }
@@ -1940,7 +1985,7 @@ var KVService = class extends BaseService {
1940
1985
  );
1941
1986
  }
1942
1987
  if (response.status === 404) {
1943
- return this.classifyNotFound(response, key);
1988
+ return await this.classifyNotFound(response, key);
1944
1989
  }
1945
1990
  const errorText = await response.text();
1946
1991
  if (response.status === 412) {
@@ -2007,7 +2052,7 @@ var KVService = class extends BaseService {
2007
2052
  );
2008
2053
  }
2009
2054
  if (response.status === 404) {
2010
- return this.classifyNotFound(response, key);
2055
+ return await this.classifyNotFound(response, key);
2011
2056
  }
2012
2057
  const errorText = await response.text();
2013
2058
  return err(
@@ -2073,7 +2118,7 @@ var KVService = class extends BaseService {
2073
2118
  signal: request.signal
2074
2119
  });
2075
2120
  if (!response.ok) {
2076
- return this.createSignedReadUrlError(response, key, path);
2121
+ return await this.createSignedReadUrlError(response, key, path);
2077
2122
  }
2078
2123
  const signedUrl = this.normalizeSignedReadUrlResponse(
2079
2124
  await response.json()