@tinycloud/sdk-services 3.0.0 → 3.1.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.
@@ -850,7 +850,6 @@ declare class KVService extends BaseService implements IKVService {
850
850
  * Get the service configuration.
851
851
  */
852
852
  get config(): KVServiceConfig;
853
- private parseQuotaInfo;
854
853
  private handleQuotaErrorResponse;
855
854
  /**
856
855
  * Classify a KV 404 by reading the response body once.
@@ -919,6 +918,14 @@ declare class KVService extends BaseService implements IKVService {
919
918
  */
920
919
  private parseResponse;
921
920
  private createSignedReadUrlError;
921
+ /**
922
+ * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP
923
+ * status and the server text (`<context>: <status> - <text>`, the same
924
+ * shape as every other KV failure) so callers that rethrow only the message
925
+ * still see the status; `meta.status` carries it for typed callers.
926
+ * Resource/ability hints are parsed from the raw body, never the message.
927
+ */
928
+ private authorizationFailure;
922
929
  private normalizeSignedReadUrlResponse;
923
930
  /**
924
931
  * Get a value by key.
@@ -850,7 +850,6 @@ declare class KVService extends BaseService implements IKVService {
850
850
  * Get the service configuration.
851
851
  */
852
852
  get config(): KVServiceConfig;
853
- private parseQuotaInfo;
854
853
  private handleQuotaErrorResponse;
855
854
  /**
856
855
  * Classify a KV 404 by reading the response body once.
@@ -919,6 +918,14 @@ declare class KVService extends BaseService implements IKVService {
919
918
  */
920
919
  private parseResponse;
921
920
  private createSignedReadUrlError;
921
+ /**
922
+ * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP
923
+ * status and the server text (`<context>: <status> - <text>`, the same
924
+ * shape as every other KV failure) so callers that rethrow only the message
925
+ * still see the status; `meta.status` carries it for typed callers.
926
+ * Resource/ability hints are parsed from the raw body, never the message.
927
+ */
928
+ private authorizationFailure;
922
929
  private normalizeSignedReadUrlResponse;
923
930
  /**
924
931
  * Get a value by key.
package/dist/kv/index.js CHANGED
@@ -154,6 +154,20 @@ function storageLimitReachedError(service, message, meta) {
154
154
  meta
155
155
  };
156
156
  }
157
+ var STORAGE_FULL_MESSAGE = "TinyCloud storage is full, so this change was not saved. Reading still works. Free up space or upgrade your plan to save again.";
158
+ var STORAGE_WRITE_TOO_LARGE_MESSAGE = "This change is larger than the TinyCloud storage you have left, so it was not saved. Reading still works. Free up space or upgrade your plan to save it.";
159
+ function parseStorageQuotaBytes(responseText) {
160
+ const match = responseText.match(/Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i);
161
+ if (!match) return void 0;
162
+ return {
163
+ usedBytes: parseInt(match[1], 10),
164
+ limitBytes: parseInt(match[2], 10)
165
+ };
166
+ }
167
+ function storageRejectionError(service, status, meta, responseText) {
168
+ const quotaMeta = { ...meta, ...parseStorageQuotaBytes(responseText) };
169
+ return status === 402 ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta) : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);
170
+ }
157
171
  function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
158
172
  if (error instanceof Error) {
159
173
  if (error.name === "AbortError") {
@@ -544,47 +558,21 @@ var KVService = class extends BaseService {
544
558
  get config() {
545
559
  return this._config;
546
560
  }
547
- // Parses "Used: X bytes, Limit: Y bytes" from tinycloud-node error responses
548
- parseQuotaInfo(errorText) {
549
- const match = errorText.match(
550
- /Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i
551
- );
552
- if (match) {
553
- return {
554
- usedBytes: parseInt(match[1], 10),
555
- limitBytes: parseInt(match[2], 10)
556
- };
557
- }
558
- return void 0;
559
- }
560
561
  handleQuotaErrorResponse(response, errorText, key) {
561
- if (response.status === 402) {
562
- const quotaInfo = this.parseQuotaInfo(errorText);
563
- return err(
564
- storageQuotaExceededError(
565
- "kv",
566
- `Storage quota exceeded for key "${key}": ${errorText}`,
567
- {
568
- status: response.status,
569
- ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
570
- }
571
- )
572
- );
562
+ if (response.status !== 402 && response.status !== 413) {
563
+ return void 0;
573
564
  }
574
- if (response.status === 413) {
575
- const quotaInfo = this.parseQuotaInfo(errorText);
576
- return err(
577
- storageLimitReachedError(
578
- "kv",
579
- `Storage limit reached for key "${key}": ${errorText}`,
580
- {
581
- status: response.status,
582
- ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
583
- }
584
- )
585
- );
565
+ if (response.status === 413 && !parseStorageQuotaBytes(errorText)) {
566
+ return void 0;
586
567
  }
587
- return void 0;
568
+ return err(
569
+ storageRejectionError(
570
+ "kv",
571
+ response.status,
572
+ { status: response.status, statusText: response.statusText, key },
573
+ errorText
574
+ )
575
+ );
588
576
  }
589
577
  /**
590
578
  * Classify a KV 404 by reading the response body once.
@@ -862,12 +850,11 @@ var KVService = class extends BaseService {
862
850
  if (!response.ok) {
863
851
  const errorText = await response.text();
864
852
  if (response.status === 401 || response.status === 403) {
865
- const { resource, action: requiredAction } = parseAuthError(errorText);
866
- return err(authUnauthorizedError("kv", errorText, {
867
- status: response.status,
868
- ...requiredAction && { requiredAction },
869
- ...resource && { resource }
870
- }));
853
+ return this.authorizationFailure(
854
+ `Failed to batch read ${keys.length} key(s)`,
855
+ response,
856
+ errorText
857
+ );
871
858
  }
872
859
  if (response.status === 413) {
873
860
  return err(serviceError(
@@ -966,23 +953,39 @@ var KVService = class extends BaseService {
966
953
  } catch {
967
954
  }
968
955
  if (response.status === 401 || response.status === 403) {
969
- const { resource, action } = parseAuthError(errorText);
970
- return err(authUnauthorizedError("kv", errorText, {
971
- status: response.status,
972
- ...action && { requiredAction: action },
973
- ...resource && { resource }
974
- }));
956
+ return this.authorizationFailure(
957
+ `Failed to create signed read URL for key ${JSON.stringify(key)}`,
958
+ response,
959
+ errorText
960
+ );
975
961
  }
976
962
  const code = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
977
963
  return err(
978
964
  serviceError(
979
965
  code,
980
- `Failed to create signed read URL for key "${key}": ${response.status} - ${errorText}`,
966
+ `Failed to create signed read URL for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
981
967
  "kv",
982
968
  { meta: { status: response.status, statusText: response.statusText } }
983
969
  )
984
970
  );
985
971
  }
972
+ /**
973
+ * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP
974
+ * status and the server text (`<context>: <status> - <text>`, the same
975
+ * shape as every other KV failure) so callers that rethrow only the message
976
+ * still see the status; `meta.status` carries it for typed callers.
977
+ * Resource/ability hints are parsed from the raw body, never the message.
978
+ */
979
+ authorizationFailure(context, response, errorText, extraMeta = {}) {
980
+ const { resource, action } = parseAuthError(errorText);
981
+ const detail = errorText.trim().length > 0 ? errorText : response.statusText || "authorization failed";
982
+ return err(authUnauthorizedError("kv", `${context}: ${response.status} - ${detail}`, {
983
+ status: response.status,
984
+ ...action && { requiredAction: action },
985
+ ...resource && { resource },
986
+ ...extraMeta
987
+ }));
988
+ }
986
989
  normalizeSignedReadUrlResponse(data) {
987
990
  if (!data || typeof data !== "object") {
988
991
  return void 0;
@@ -1025,14 +1028,13 @@ var KVService = class extends BaseService {
1025
1028
  if (!response.ok) {
1026
1029
  if (response.status === 401 || response.status === 403) {
1027
1030
  const errorText2 = await response.text();
1028
- const { resource, action } = parseAuthError(errorText2);
1029
1031
  const permissionHint = parsePermissionHintFromErrorText(errorText2);
1030
- return err(authUnauthorizedError("kv", errorText2, {
1031
- status: response.status,
1032
- ...action && { requiredAction: action },
1033
- ...resource && { resource },
1034
- ...permissionHint === void 0 ? {} : { permissionHint }
1035
- }));
1032
+ return this.authorizationFailure(
1033
+ `Failed to get key ${JSON.stringify(key)}`,
1034
+ response,
1035
+ errorText2,
1036
+ permissionHint === void 0 ? {} : { permissionHint }
1037
+ );
1036
1038
  }
1037
1039
  if (response.status === 404) {
1038
1040
  return this.classifyNotFound(response, key);
@@ -1041,7 +1043,7 @@ var KVService = class extends BaseService {
1041
1043
  if (response.status === 413) {
1042
1044
  return err(serviceError(
1043
1045
  ErrorCodes.KV_RESPONSE_TOO_LARGE,
1044
- `KV value at key "${key}" exceeds the requested response limit`,
1046
+ `KV value at key ${JSON.stringify(key)} exceeds the requested response limit`,
1045
1047
  "kv",
1046
1048
  { meta: { status: response.status, statusText: response.statusText } }
1047
1049
  ));
@@ -1049,7 +1051,7 @@ var KVService = class extends BaseService {
1049
1051
  return err(
1050
1052
  serviceError(
1051
1053
  ErrorCodes.NETWORK_ERROR,
1052
- `Failed to get key "${key}": ${response.status} - ${errorText}`,
1054
+ `Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
1053
1055
  "kv",
1054
1056
  { meta: { status: response.status, statusText: response.statusText } }
1055
1057
  )
@@ -1105,21 +1107,18 @@ var KVService = class extends BaseService {
1105
1107
  }
1106
1108
  );
1107
1109
  if (response.status === 401 || response.status === 403) {
1108
- const errorText = await response.text();
1109
- const message = errorText.trim().length > 0 ? errorText : `Failed to put key "${key}": ${response.status} - ${response.statusText || "authorization failed"}`;
1110
- const { resource, action: requiredAction } = parseAuthError(errorText);
1111
- return err(authUnauthorizedError("kv", message, {
1112
- status: response.status,
1113
- ...requiredAction && { requiredAction },
1114
- ...resource && { resource }
1115
- }));
1110
+ return this.authorizationFailure(
1111
+ `Failed to put key ${JSON.stringify(key)}`,
1112
+ response,
1113
+ await response.text()
1114
+ );
1116
1115
  }
1117
1116
  if (!response.ok) {
1118
1117
  const errorText = await response.text();
1119
1118
  if (response.status === 412) {
1120
1119
  return err(serviceError(
1121
1120
  ErrorCodes.KV_PRECONDITION_FAILED,
1122
- `KV precondition failed for key "${key}"`,
1121
+ `KV precondition failed for key ${JSON.stringify(key)}`,
1123
1122
  "kv",
1124
1123
  { meta: { status: response.status, statusText: response.statusText } }
1125
1124
  ));
@@ -1127,7 +1126,7 @@ var KVService = class extends BaseService {
1127
1126
  if (response.status === 503 && (options?.ifMatch !== void 0 || options?.ifNoneMatch !== void 0)) {
1128
1127
  return err(serviceError(
1129
1128
  ErrorCodes.KV_CONFLICT,
1130
- `Concurrent KV update conflicted for key "${key}"`,
1129
+ `Concurrent KV update conflicted for key ${JSON.stringify(key)}`,
1131
1130
  "kv",
1132
1131
  { meta: { status: response.status, statusText: response.statusText } }
1133
1132
  ));
@@ -1143,7 +1142,7 @@ var KVService = class extends BaseService {
1143
1142
  return err(
1144
1143
  serviceError(
1145
1144
  ErrorCodes.KV_WRITE_FAILED,
1146
- `Failed to put key "${key}": ${response.status} - ${errorText}`,
1145
+ `Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
1147
1146
  "kv",
1148
1147
  { meta: { status: response.status, statusText: response.statusText } }
1149
1148
  )
@@ -1237,12 +1236,11 @@ var KVService = class extends BaseService {
1237
1236
  );
1238
1237
  }
1239
1238
  if (response.status === 401 || response.status === 403) {
1240
- const { resource, action } = parseAuthError(errorText);
1241
- return err(authUnauthorizedError("kv", errorText, {
1242
- status: response.status,
1243
- ...action && { requiredAction: action },
1244
- ...resource && { resource }
1245
- }));
1239
+ return this.authorizationFailure(
1240
+ `Failed to batch put ${items.length} key(s)`,
1241
+ response,
1242
+ errorText
1243
+ );
1246
1244
  }
1247
1245
  const quotaError = this.handleQuotaErrorResponse(
1248
1246
  response,
@@ -1363,13 +1361,11 @@ var KVService = class extends BaseService {
1363
1361
  );
1364
1362
  if (!response.ok) {
1365
1363
  if (response.status === 401) {
1366
- const errorText2 = await response.text();
1367
- const { resource, action } = parseAuthError(errorText2);
1368
- return err(authUnauthorizedError("kv", errorText2, {
1369
- status: response.status,
1370
- ...action && { requiredAction: action },
1371
- ...resource && { resource }
1372
- }));
1364
+ return this.authorizationFailure(
1365
+ "Failed to list keys",
1366
+ response,
1367
+ await response.text()
1368
+ );
1373
1369
  }
1374
1370
  const errorText = await response.text();
1375
1371
  return err(
@@ -1418,13 +1414,11 @@ var KVService = class extends BaseService {
1418
1414
  );
1419
1415
  if (!response.ok) {
1420
1416
  if (response.status === 401) {
1421
- const errorText2 = await response.text();
1422
- const { resource, action } = parseAuthError(errorText2);
1423
- return err(authUnauthorizedError("kv", errorText2, {
1424
- status: response.status,
1425
- ...action && { requiredAction: action },
1426
- ...resource && { resource }
1427
- }));
1417
+ return this.authorizationFailure(
1418
+ `Failed to delete key ${JSON.stringify(key)}`,
1419
+ response,
1420
+ await response.text()
1421
+ );
1428
1422
  }
1429
1423
  if (response.status === 404) {
1430
1424
  return this.classifyNotFound(response, key);
@@ -1433,7 +1427,7 @@ var KVService = class extends BaseService {
1433
1427
  if (response.status === 412) {
1434
1428
  return err(serviceError(
1435
1429
  ErrorCodes.KV_PRECONDITION_FAILED,
1436
- `KV precondition failed for key "${key}"`,
1430
+ `KV precondition failed for key ${JSON.stringify(key)}`,
1437
1431
  "kv",
1438
1432
  { meta: { status: response.status, statusText: response.statusText } }
1439
1433
  ));
@@ -1441,7 +1435,7 @@ var KVService = class extends BaseService {
1441
1435
  if (response.status === 503 && options?.ifMatch !== void 0) {
1442
1436
  return err(serviceError(
1443
1437
  ErrorCodes.KV_CONFLICT,
1444
- `Concurrent KV delete conflicted for key "${key}"`,
1438
+ `Concurrent KV delete conflicted for key ${JSON.stringify(key)}`,
1445
1439
  "kv",
1446
1440
  { meta: { status: response.status, statusText: response.statusText } }
1447
1441
  ));
@@ -1449,7 +1443,7 @@ var KVService = class extends BaseService {
1449
1443
  return err(
1450
1444
  serviceError(
1451
1445
  ErrorCodes.NETWORK_ERROR,
1452
- `Failed to delete key "${key}": ${response.status} - ${errorText}`,
1446
+ `Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
1453
1447
  "kv",
1454
1448
  { meta: { status: response.status, statusText: response.statusText } }
1455
1449
  )
@@ -1482,13 +1476,11 @@ var KVService = class extends BaseService {
1482
1476
  );
1483
1477
  if (!response.ok) {
1484
1478
  if (response.status === 401) {
1485
- const errorText2 = await response.text();
1486
- const { resource, action } = parseAuthError(errorText2);
1487
- return err(authUnauthorizedError("kv", errorText2, {
1488
- status: response.status,
1489
- ...action && { requiredAction: action },
1490
- ...resource && { resource }
1491
- }));
1479
+ return this.authorizationFailure(
1480
+ `Failed to get metadata for key ${JSON.stringify(key)}`,
1481
+ response,
1482
+ await response.text()
1483
+ );
1492
1484
  }
1493
1485
  if (response.status === 404) {
1494
1486
  return this.classifyNotFound(response, key);
@@ -1497,7 +1489,7 @@ var KVService = class extends BaseService {
1497
1489
  return err(
1498
1490
  serviceError(
1499
1491
  ErrorCodes.NETWORK_ERROR,
1500
- `Failed to get metadata for key "${key}": ${response.status} - ${errorText}`,
1492
+ `Failed to get metadata for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
1501
1493
  "kv",
1502
1494
  { meta: { status: response.status, statusText: response.statusText } }
1503
1495
  )