@tinycloud/sdk-services 3.0.0 → 3.1.0-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/index.cjs CHANGED
@@ -73,6 +73,7 @@ __export(src_exports, {
73
73
  authExpiredError: () => authExpiredError,
74
74
  authRequiredError: () => authRequiredError,
75
75
  authUnauthorizedError: () => authUnauthorizedError,
76
+ authorizationVerdictOf: () => authorizationVerdictOf,
76
77
  base64Decode: () => base64Decode2,
77
78
  base64Encode: () => base64Encode2,
78
79
  buildCanonicalDecryptRequest: () => buildCanonicalDecryptRequest,
@@ -1135,6 +1136,37 @@ function parseAuthError(responseText) {
1135
1136
  function authUnauthorizedError(service, message, meta) {
1136
1137
  return serviceError(ErrorCodes.AUTH_UNAUTHORIZED, message, service, { meta });
1137
1138
  }
1139
+ var MAX_CAUSE_DEPTH = 8;
1140
+ function errorStatusOf(node) {
1141
+ const meta = node.meta;
1142
+ const candidates = [
1143
+ node.status,
1144
+ node.statusCode,
1145
+ typeof meta === "object" && meta !== null ? meta.status : void 0
1146
+ ];
1147
+ for (const candidate of candidates) {
1148
+ if (typeof candidate === "number" && Number.isInteger(candidate) && candidate >= 400 && candidate <= 599) {
1149
+ return candidate;
1150
+ }
1151
+ }
1152
+ return void 0;
1153
+ }
1154
+ function authorizationVerdictOf(error) {
1155
+ const seen = /* @__PURE__ */ new Set();
1156
+ let sawUnauthorizedCode = false;
1157
+ let node = error;
1158
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH && typeof node === "object" && node !== null && !seen.has(node); depth += 1) {
1159
+ seen.add(node);
1160
+ const record = node;
1161
+ const status = errorStatusOf(record);
1162
+ if (status === 401) return "unauthenticated";
1163
+ if (status === 403) return "forbidden";
1164
+ if (status !== void 0) return "other";
1165
+ if (record.code === ErrorCodes.AUTH_UNAUTHORIZED) sawUnauthorizedCode = true;
1166
+ node = record.cause;
1167
+ }
1168
+ return sawUnauthorizedCode ? "forbidden" : void 0;
1169
+ }
1138
1170
  function storageQuotaExceededError(service, message, meta) {
1139
1171
  return {
1140
1172
  code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,
@@ -1563,7 +1595,7 @@ var KVService = class extends BaseService {
1563
1595
  return err(
1564
1596
  storageQuotaExceededError(
1565
1597
  "kv",
1566
- `Storage quota exceeded for key "${key}": ${errorText}`,
1598
+ `Storage quota exceeded for key ${JSON.stringify(key)}: ${errorText}`,
1567
1599
  {
1568
1600
  status: response.status,
1569
1601
  ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
@@ -1576,7 +1608,7 @@ var KVService = class extends BaseService {
1576
1608
  return err(
1577
1609
  storageLimitReachedError(
1578
1610
  "kv",
1579
- `Storage limit reached for key "${key}": ${errorText}`,
1611
+ `Storage limit reached for key ${JSON.stringify(key)}: ${errorText}`,
1580
1612
  {
1581
1613
  status: response.status,
1582
1614
  ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
@@ -1862,12 +1894,11 @@ var KVService = class extends BaseService {
1862
1894
  if (!response.ok) {
1863
1895
  const errorText = await response.text();
1864
1896
  if (response.status === 401 || response.status === 403) {
1865
- const { resource, action: requiredAction } = parseAuthError(errorText);
1866
- return err(authUnauthorizedError("kv", errorText, {
1867
- status: response.status,
1868
- ...requiredAction && { requiredAction },
1869
- ...resource && { resource }
1870
- }));
1897
+ return this.authorizationFailure(
1898
+ `Failed to batch read ${keys.length} key(s)`,
1899
+ response,
1900
+ errorText
1901
+ );
1871
1902
  }
1872
1903
  if (response.status === 413) {
1873
1904
  return err(serviceError(
@@ -1966,23 +1997,39 @@ var KVService = class extends BaseService {
1966
1997
  } catch {
1967
1998
  }
1968
1999
  if (response.status === 401 || response.status === 403) {
1969
- const { resource, action } = parseAuthError(errorText);
1970
- return err(authUnauthorizedError("kv", errorText, {
1971
- status: response.status,
1972
- ...action && { requiredAction: action },
1973
- ...resource && { resource }
1974
- }));
2000
+ return this.authorizationFailure(
2001
+ `Failed to create signed read URL for key ${JSON.stringify(key)}`,
2002
+ response,
2003
+ errorText
2004
+ );
1975
2005
  }
1976
2006
  const code = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
1977
2007
  return err(
1978
2008
  serviceError(
1979
2009
  code,
1980
- `Failed to create signed read URL for key "${key}": ${response.status} - ${errorText}`,
2010
+ `Failed to create signed read URL for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
1981
2011
  "kv",
1982
2012
  { meta: { status: response.status, statusText: response.statusText } }
1983
2013
  )
1984
2014
  );
1985
2015
  }
2016
+ /**
2017
+ * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP
2018
+ * status and the server text (`<context>: <status> - <text>`, the same
2019
+ * shape as every other KV failure) so callers that rethrow only the message
2020
+ * still see the status; `meta.status` carries it for typed callers.
2021
+ * Resource/ability hints are parsed from the raw body, never the message.
2022
+ */
2023
+ authorizationFailure(context, response, errorText, extraMeta = {}) {
2024
+ const { resource, action } = parseAuthError(errorText);
2025
+ const detail = errorText.trim().length > 0 ? errorText : response.statusText || "authorization failed";
2026
+ return err(authUnauthorizedError("kv", `${context}: ${response.status} - ${detail}`, {
2027
+ status: response.status,
2028
+ ...action && { requiredAction: action },
2029
+ ...resource && { resource },
2030
+ ...extraMeta
2031
+ }));
2032
+ }
1986
2033
  normalizeSignedReadUrlResponse(data) {
1987
2034
  if (!data || typeof data !== "object") {
1988
2035
  return void 0;
@@ -2025,14 +2072,13 @@ var KVService = class extends BaseService {
2025
2072
  if (!response.ok) {
2026
2073
  if (response.status === 401 || response.status === 403) {
2027
2074
  const errorText2 = await response.text();
2028
- const { resource, action } = parseAuthError(errorText2);
2029
2075
  const permissionHint = parsePermissionHintFromErrorText(errorText2);
2030
- return err(authUnauthorizedError("kv", errorText2, {
2031
- status: response.status,
2032
- ...action && { requiredAction: action },
2033
- ...resource && { resource },
2034
- ...permissionHint === void 0 ? {} : { permissionHint }
2035
- }));
2076
+ return this.authorizationFailure(
2077
+ `Failed to get key ${JSON.stringify(key)}`,
2078
+ response,
2079
+ errorText2,
2080
+ permissionHint === void 0 ? {} : { permissionHint }
2081
+ );
2036
2082
  }
2037
2083
  if (response.status === 404) {
2038
2084
  return this.classifyNotFound(response, key);
@@ -2041,7 +2087,7 @@ var KVService = class extends BaseService {
2041
2087
  if (response.status === 413) {
2042
2088
  return err(serviceError(
2043
2089
  ErrorCodes.KV_RESPONSE_TOO_LARGE,
2044
- `KV value at key "${key}" exceeds the requested response limit`,
2090
+ `KV value at key ${JSON.stringify(key)} exceeds the requested response limit`,
2045
2091
  "kv",
2046
2092
  { meta: { status: response.status, statusText: response.statusText } }
2047
2093
  ));
@@ -2049,7 +2095,7 @@ var KVService = class extends BaseService {
2049
2095
  return err(
2050
2096
  serviceError(
2051
2097
  ErrorCodes.NETWORK_ERROR,
2052
- `Failed to get key "${key}": ${response.status} - ${errorText}`,
2098
+ `Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2053
2099
  "kv",
2054
2100
  { meta: { status: response.status, statusText: response.statusText } }
2055
2101
  )
@@ -2105,21 +2151,18 @@ var KVService = class extends BaseService {
2105
2151
  }
2106
2152
  );
2107
2153
  if (response.status === 401 || response.status === 403) {
2108
- const errorText = await response.text();
2109
- const message = errorText.trim().length > 0 ? errorText : `Failed to put key "${key}": ${response.status} - ${response.statusText || "authorization failed"}`;
2110
- const { resource, action: requiredAction } = parseAuthError(errorText);
2111
- return err(authUnauthorizedError("kv", message, {
2112
- status: response.status,
2113
- ...requiredAction && { requiredAction },
2114
- ...resource && { resource }
2115
- }));
2154
+ return this.authorizationFailure(
2155
+ `Failed to put key ${JSON.stringify(key)}`,
2156
+ response,
2157
+ await response.text()
2158
+ );
2116
2159
  }
2117
2160
  if (!response.ok) {
2118
2161
  const errorText = await response.text();
2119
2162
  if (response.status === 412) {
2120
2163
  return err(serviceError(
2121
2164
  ErrorCodes.KV_PRECONDITION_FAILED,
2122
- `KV precondition failed for key "${key}"`,
2165
+ `KV precondition failed for key ${JSON.stringify(key)}`,
2123
2166
  "kv",
2124
2167
  { meta: { status: response.status, statusText: response.statusText } }
2125
2168
  ));
@@ -2127,7 +2170,7 @@ var KVService = class extends BaseService {
2127
2170
  if (response.status === 503 && (options?.ifMatch !== void 0 || options?.ifNoneMatch !== void 0)) {
2128
2171
  return err(serviceError(
2129
2172
  ErrorCodes.KV_CONFLICT,
2130
- `Concurrent KV update conflicted for key "${key}"`,
2173
+ `Concurrent KV update conflicted for key ${JSON.stringify(key)}`,
2131
2174
  "kv",
2132
2175
  { meta: { status: response.status, statusText: response.statusText } }
2133
2176
  ));
@@ -2143,7 +2186,7 @@ var KVService = class extends BaseService {
2143
2186
  return err(
2144
2187
  serviceError(
2145
2188
  ErrorCodes.KV_WRITE_FAILED,
2146
- `Failed to put key "${key}": ${response.status} - ${errorText}`,
2189
+ `Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2147
2190
  "kv",
2148
2191
  { meta: { status: response.status, statusText: response.statusText } }
2149
2192
  )
@@ -2237,12 +2280,11 @@ var KVService = class extends BaseService {
2237
2280
  );
2238
2281
  }
2239
2282
  if (response.status === 401 || response.status === 403) {
2240
- const { resource, action } = parseAuthError(errorText);
2241
- return err(authUnauthorizedError("kv", errorText, {
2242
- status: response.status,
2243
- ...action && { requiredAction: action },
2244
- ...resource && { resource }
2245
- }));
2283
+ return this.authorizationFailure(
2284
+ `Failed to batch put ${items.length} key(s)`,
2285
+ response,
2286
+ errorText
2287
+ );
2246
2288
  }
2247
2289
  const quotaError = this.handleQuotaErrorResponse(
2248
2290
  response,
@@ -2363,13 +2405,11 @@ var KVService = class extends BaseService {
2363
2405
  );
2364
2406
  if (!response.ok) {
2365
2407
  if (response.status === 401) {
2366
- const errorText2 = await response.text();
2367
- const { resource, action } = parseAuthError(errorText2);
2368
- return err(authUnauthorizedError("kv", errorText2, {
2369
- status: response.status,
2370
- ...action && { requiredAction: action },
2371
- ...resource && { resource }
2372
- }));
2408
+ return this.authorizationFailure(
2409
+ "Failed to list keys",
2410
+ response,
2411
+ await response.text()
2412
+ );
2373
2413
  }
2374
2414
  const errorText = await response.text();
2375
2415
  return err(
@@ -2418,13 +2458,11 @@ var KVService = class extends BaseService {
2418
2458
  );
2419
2459
  if (!response.ok) {
2420
2460
  if (response.status === 401) {
2421
- const errorText2 = await response.text();
2422
- const { resource, action } = parseAuthError(errorText2);
2423
- return err(authUnauthorizedError("kv", errorText2, {
2424
- status: response.status,
2425
- ...action && { requiredAction: action },
2426
- ...resource && { resource }
2427
- }));
2461
+ return this.authorizationFailure(
2462
+ `Failed to delete key ${JSON.stringify(key)}`,
2463
+ response,
2464
+ await response.text()
2465
+ );
2428
2466
  }
2429
2467
  if (response.status === 404) {
2430
2468
  return this.classifyNotFound(response, key);
@@ -2433,7 +2471,7 @@ var KVService = class extends BaseService {
2433
2471
  if (response.status === 412) {
2434
2472
  return err(serviceError(
2435
2473
  ErrorCodes.KV_PRECONDITION_FAILED,
2436
- `KV precondition failed for key "${key}"`,
2474
+ `KV precondition failed for key ${JSON.stringify(key)}`,
2437
2475
  "kv",
2438
2476
  { meta: { status: response.status, statusText: response.statusText } }
2439
2477
  ));
@@ -2441,7 +2479,7 @@ var KVService = class extends BaseService {
2441
2479
  if (response.status === 503 && options?.ifMatch !== void 0) {
2442
2480
  return err(serviceError(
2443
2481
  ErrorCodes.KV_CONFLICT,
2444
- `Concurrent KV delete conflicted for key "${key}"`,
2482
+ `Concurrent KV delete conflicted for key ${JSON.stringify(key)}`,
2445
2483
  "kv",
2446
2484
  { meta: { status: response.status, statusText: response.statusText } }
2447
2485
  ));
@@ -2449,7 +2487,7 @@ var KVService = class extends BaseService {
2449
2487
  return err(
2450
2488
  serviceError(
2451
2489
  ErrorCodes.NETWORK_ERROR,
2452
- `Failed to delete key "${key}": ${response.status} - ${errorText}`,
2490
+ `Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2453
2491
  "kv",
2454
2492
  { meta: { status: response.status, statusText: response.statusText } }
2455
2493
  )
@@ -2482,13 +2520,11 @@ var KVService = class extends BaseService {
2482
2520
  );
2483
2521
  if (!response.ok) {
2484
2522
  if (response.status === 401) {
2485
- const errorText2 = await response.text();
2486
- const { resource, action } = parseAuthError(errorText2);
2487
- return err(authUnauthorizedError("kv", errorText2, {
2488
- status: response.status,
2489
- ...action && { requiredAction: action },
2490
- ...resource && { resource }
2491
- }));
2523
+ return this.authorizationFailure(
2524
+ `Failed to get metadata for key ${JSON.stringify(key)}`,
2525
+ response,
2526
+ await response.text()
2527
+ );
2492
2528
  }
2493
2529
  if (response.status === 404) {
2494
2530
  return this.classifyNotFound(response, key);
@@ -2497,7 +2533,7 @@ var KVService = class extends BaseService {
2497
2533
  return err(
2498
2534
  serviceError(
2499
2535
  ErrorCodes.NETWORK_ERROR,
2500
- `Failed to get metadata for key "${key}": ${response.status} - ${errorText}`,
2536
+ `Failed to get metadata for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2501
2537
  "kv",
2502
2538
  { meta: { status: response.status, statusText: response.statusText } }
2503
2539
  )
@@ -3979,10 +4015,14 @@ async function responseError(service, message, response) {
3979
4015
  }
3980
4016
  } catch {
3981
4017
  }
3982
- return wrapError(
4018
+ const error = wrapError(
3983
4019
  service,
3984
4020
  new Error(`${message}: ${response.status} ${detail}`)
3985
4021
  );
4022
+ return {
4023
+ ...error,
4024
+ meta: { ...error.meta, status: response.status, statusText: response.statusText }
4025
+ };
3986
4026
  }
3987
4027
  function isAbortError(error) {
3988
4028
  return error instanceof DOMException && error.name === "AbortError";
@@ -7034,6 +7074,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
7034
7074
  authExpiredError,
7035
7075
  authRequiredError,
7036
7076
  authUnauthorizedError,
7077
+ authorizationVerdictOf,
7037
7078
  base64Decode,
7038
7079
  base64Encode,
7039
7080
  buildCanonicalDecryptRequest,