@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.
package/dist/kv/index.cjs CHANGED
@@ -183,6 +183,20 @@ function storageLimitReachedError(service, message, meta) {
183
183
  meta
184
184
  };
185
185
  }
186
+ 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.";
187
+ 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.";
188
+ function parseStorageQuotaBytes(responseText) {
189
+ const match = responseText.match(/Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i);
190
+ if (!match) return void 0;
191
+ return {
192
+ usedBytes: parseInt(match[1], 10),
193
+ limitBytes: parseInt(match[2], 10)
194
+ };
195
+ }
196
+ function storageRejectionError(service, status, meta, responseText) {
197
+ const quotaMeta = { ...meta, ...parseStorageQuotaBytes(responseText) };
198
+ return status === 402 ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta) : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);
199
+ }
186
200
  function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
187
201
  if (error instanceof Error) {
188
202
  if (error.name === "AbortError") {
@@ -573,47 +587,21 @@ var KVService = class extends BaseService {
573
587
  get config() {
574
588
  return this._config;
575
589
  }
576
- // Parses "Used: X bytes, Limit: Y bytes" from tinycloud-node error responses
577
- parseQuotaInfo(errorText) {
578
- const match = errorText.match(
579
- /Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i
580
- );
581
- if (match) {
582
- return {
583
- usedBytes: parseInt(match[1], 10),
584
- limitBytes: parseInt(match[2], 10)
585
- };
586
- }
587
- return void 0;
588
- }
589
590
  handleQuotaErrorResponse(response, errorText, key) {
590
- if (response.status === 402) {
591
- const quotaInfo = this.parseQuotaInfo(errorText);
592
- return err(
593
- storageQuotaExceededError(
594
- "kv",
595
- `Storage quota exceeded for key "${key}": ${errorText}`,
596
- {
597
- status: response.status,
598
- ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
599
- }
600
- )
601
- );
591
+ if (response.status !== 402 && response.status !== 413) {
592
+ return void 0;
602
593
  }
603
- if (response.status === 413) {
604
- const quotaInfo = this.parseQuotaInfo(errorText);
605
- return err(
606
- storageLimitReachedError(
607
- "kv",
608
- `Storage limit reached for key "${key}": ${errorText}`,
609
- {
610
- status: response.status,
611
- ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
612
- }
613
- )
614
- );
594
+ if (response.status === 413 && !parseStorageQuotaBytes(errorText)) {
595
+ return void 0;
615
596
  }
616
- return void 0;
597
+ return err(
598
+ storageRejectionError(
599
+ "kv",
600
+ response.status,
601
+ { status: response.status, statusText: response.statusText, key },
602
+ errorText
603
+ )
604
+ );
617
605
  }
618
606
  /**
619
607
  * Classify a KV 404 by reading the response body once.
@@ -891,12 +879,11 @@ var KVService = class extends BaseService {
891
879
  if (!response.ok) {
892
880
  const errorText = await response.text();
893
881
  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
- }));
882
+ return this.authorizationFailure(
883
+ `Failed to batch read ${keys.length} key(s)`,
884
+ response,
885
+ errorText
886
+ );
900
887
  }
901
888
  if (response.status === 413) {
902
889
  return err(serviceError(
@@ -995,23 +982,39 @@ var KVService = class extends BaseService {
995
982
  } catch {
996
983
  }
997
984
  if (response.status === 401 || response.status === 403) {
998
- const { resource, action } = parseAuthError(errorText);
999
- return err(authUnauthorizedError("kv", errorText, {
1000
- status: response.status,
1001
- ...action && { requiredAction: action },
1002
- ...resource && { resource }
1003
- }));
985
+ return this.authorizationFailure(
986
+ `Failed to create signed read URL for key ${JSON.stringify(key)}`,
987
+ response,
988
+ errorText
989
+ );
1004
990
  }
1005
991
  const code = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
1006
992
  return err(
1007
993
  serviceError(
1008
994
  code,
1009
- `Failed to create signed read URL for key "${key}": ${response.status} - ${errorText}`,
995
+ `Failed to create signed read URL for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
1010
996
  "kv",
1011
997
  { meta: { status: response.status, statusText: response.statusText } }
1012
998
  )
1013
999
  );
1014
1000
  }
1001
+ /**
1002
+ * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP
1003
+ * status and the server text (`<context>: <status> - <text>`, the same
1004
+ * shape as every other KV failure) so callers that rethrow only the message
1005
+ * still see the status; `meta.status` carries it for typed callers.
1006
+ * Resource/ability hints are parsed from the raw body, never the message.
1007
+ */
1008
+ authorizationFailure(context, response, errorText, extraMeta = {}) {
1009
+ const { resource, action } = parseAuthError(errorText);
1010
+ const detail = errorText.trim().length > 0 ? errorText : response.statusText || "authorization failed";
1011
+ return err(authUnauthorizedError("kv", `${context}: ${response.status} - ${detail}`, {
1012
+ status: response.status,
1013
+ ...action && { requiredAction: action },
1014
+ ...resource && { resource },
1015
+ ...extraMeta
1016
+ }));
1017
+ }
1015
1018
  normalizeSignedReadUrlResponse(data) {
1016
1019
  if (!data || typeof data !== "object") {
1017
1020
  return void 0;
@@ -1054,14 +1057,13 @@ var KVService = class extends BaseService {
1054
1057
  if (!response.ok) {
1055
1058
  if (response.status === 401 || response.status === 403) {
1056
1059
  const errorText2 = await response.text();
1057
- const { resource, action } = parseAuthError(errorText2);
1058
1060
  const permissionHint = parsePermissionHintFromErrorText(errorText2);
1059
- return err(authUnauthorizedError("kv", errorText2, {
1060
- status: response.status,
1061
- ...action && { requiredAction: action },
1062
- ...resource && { resource },
1063
- ...permissionHint === void 0 ? {} : { permissionHint }
1064
- }));
1061
+ return this.authorizationFailure(
1062
+ `Failed to get key ${JSON.stringify(key)}`,
1063
+ response,
1064
+ errorText2,
1065
+ permissionHint === void 0 ? {} : { permissionHint }
1066
+ );
1065
1067
  }
1066
1068
  if (response.status === 404) {
1067
1069
  return this.classifyNotFound(response, key);
@@ -1070,7 +1072,7 @@ var KVService = class extends BaseService {
1070
1072
  if (response.status === 413) {
1071
1073
  return err(serviceError(
1072
1074
  ErrorCodes.KV_RESPONSE_TOO_LARGE,
1073
- `KV value at key "${key}" exceeds the requested response limit`,
1075
+ `KV value at key ${JSON.stringify(key)} exceeds the requested response limit`,
1074
1076
  "kv",
1075
1077
  { meta: { status: response.status, statusText: response.statusText } }
1076
1078
  ));
@@ -1078,7 +1080,7 @@ var KVService = class extends BaseService {
1078
1080
  return err(
1079
1081
  serviceError(
1080
1082
  ErrorCodes.NETWORK_ERROR,
1081
- `Failed to get key "${key}": ${response.status} - ${errorText}`,
1083
+ `Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
1082
1084
  "kv",
1083
1085
  { meta: { status: response.status, statusText: response.statusText } }
1084
1086
  )
@@ -1134,21 +1136,18 @@ var KVService = class extends BaseService {
1134
1136
  }
1135
1137
  );
1136
1138
  if (response.status === 401 || response.status === 403) {
1137
- const errorText = await response.text();
1138
- const message = errorText.trim().length > 0 ? errorText : `Failed to put key "${key}": ${response.status} - ${response.statusText || "authorization failed"}`;
1139
- const { resource, action: requiredAction } = parseAuthError(errorText);
1140
- return err(authUnauthorizedError("kv", message, {
1141
- status: response.status,
1142
- ...requiredAction && { requiredAction },
1143
- ...resource && { resource }
1144
- }));
1139
+ return this.authorizationFailure(
1140
+ `Failed to put key ${JSON.stringify(key)}`,
1141
+ response,
1142
+ await response.text()
1143
+ );
1145
1144
  }
1146
1145
  if (!response.ok) {
1147
1146
  const errorText = await response.text();
1148
1147
  if (response.status === 412) {
1149
1148
  return err(serviceError(
1150
1149
  ErrorCodes.KV_PRECONDITION_FAILED,
1151
- `KV precondition failed for key "${key}"`,
1150
+ `KV precondition failed for key ${JSON.stringify(key)}`,
1152
1151
  "kv",
1153
1152
  { meta: { status: response.status, statusText: response.statusText } }
1154
1153
  ));
@@ -1156,7 +1155,7 @@ var KVService = class extends BaseService {
1156
1155
  if (response.status === 503 && (options?.ifMatch !== void 0 || options?.ifNoneMatch !== void 0)) {
1157
1156
  return err(serviceError(
1158
1157
  ErrorCodes.KV_CONFLICT,
1159
- `Concurrent KV update conflicted for key "${key}"`,
1158
+ `Concurrent KV update conflicted for key ${JSON.stringify(key)}`,
1160
1159
  "kv",
1161
1160
  { meta: { status: response.status, statusText: response.statusText } }
1162
1161
  ));
@@ -1172,7 +1171,7 @@ var KVService = class extends BaseService {
1172
1171
  return err(
1173
1172
  serviceError(
1174
1173
  ErrorCodes.KV_WRITE_FAILED,
1175
- `Failed to put key "${key}": ${response.status} - ${errorText}`,
1174
+ `Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
1176
1175
  "kv",
1177
1176
  { meta: { status: response.status, statusText: response.statusText } }
1178
1177
  )
@@ -1266,12 +1265,11 @@ var KVService = class extends BaseService {
1266
1265
  );
1267
1266
  }
1268
1267
  if (response.status === 401 || response.status === 403) {
1269
- const { resource, action } = parseAuthError(errorText);
1270
- return err(authUnauthorizedError("kv", errorText, {
1271
- status: response.status,
1272
- ...action && { requiredAction: action },
1273
- ...resource && { resource }
1274
- }));
1268
+ return this.authorizationFailure(
1269
+ `Failed to batch put ${items.length} key(s)`,
1270
+ response,
1271
+ errorText
1272
+ );
1275
1273
  }
1276
1274
  const quotaError = this.handleQuotaErrorResponse(
1277
1275
  response,
@@ -1392,13 +1390,11 @@ var KVService = class extends BaseService {
1392
1390
  );
1393
1391
  if (!response.ok) {
1394
1392
  if (response.status === 401) {
1395
- const errorText2 = await response.text();
1396
- const { resource, action } = parseAuthError(errorText2);
1397
- return err(authUnauthorizedError("kv", errorText2, {
1398
- status: response.status,
1399
- ...action && { requiredAction: action },
1400
- ...resource && { resource }
1401
- }));
1393
+ return this.authorizationFailure(
1394
+ "Failed to list keys",
1395
+ response,
1396
+ await response.text()
1397
+ );
1402
1398
  }
1403
1399
  const errorText = await response.text();
1404
1400
  return err(
@@ -1447,13 +1443,11 @@ var KVService = class extends BaseService {
1447
1443
  );
1448
1444
  if (!response.ok) {
1449
1445
  if (response.status === 401) {
1450
- const errorText2 = await response.text();
1451
- const { resource, action } = parseAuthError(errorText2);
1452
- return err(authUnauthorizedError("kv", errorText2, {
1453
- status: response.status,
1454
- ...action && { requiredAction: action },
1455
- ...resource && { resource }
1456
- }));
1446
+ return this.authorizationFailure(
1447
+ `Failed to delete key ${JSON.stringify(key)}`,
1448
+ response,
1449
+ await response.text()
1450
+ );
1457
1451
  }
1458
1452
  if (response.status === 404) {
1459
1453
  return this.classifyNotFound(response, key);
@@ -1462,7 +1456,7 @@ var KVService = class extends BaseService {
1462
1456
  if (response.status === 412) {
1463
1457
  return err(serviceError(
1464
1458
  ErrorCodes.KV_PRECONDITION_FAILED,
1465
- `KV precondition failed for key "${key}"`,
1459
+ `KV precondition failed for key ${JSON.stringify(key)}`,
1466
1460
  "kv",
1467
1461
  { meta: { status: response.status, statusText: response.statusText } }
1468
1462
  ));
@@ -1470,7 +1464,7 @@ var KVService = class extends BaseService {
1470
1464
  if (response.status === 503 && options?.ifMatch !== void 0) {
1471
1465
  return err(serviceError(
1472
1466
  ErrorCodes.KV_CONFLICT,
1473
- `Concurrent KV delete conflicted for key "${key}"`,
1467
+ `Concurrent KV delete conflicted for key ${JSON.stringify(key)}`,
1474
1468
  "kv",
1475
1469
  { meta: { status: response.status, statusText: response.statusText } }
1476
1470
  ));
@@ -1478,7 +1472,7 @@ var KVService = class extends BaseService {
1478
1472
  return err(
1479
1473
  serviceError(
1480
1474
  ErrorCodes.NETWORK_ERROR,
1481
- `Failed to delete key "${key}": ${response.status} - ${errorText}`,
1475
+ `Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
1482
1476
  "kv",
1483
1477
  { meta: { status: response.status, statusText: response.statusText } }
1484
1478
  )
@@ -1511,13 +1505,11 @@ var KVService = class extends BaseService {
1511
1505
  );
1512
1506
  if (!response.ok) {
1513
1507
  if (response.status === 401) {
1514
- const errorText2 = await response.text();
1515
- const { resource, action } = parseAuthError(errorText2);
1516
- return err(authUnauthorizedError("kv", errorText2, {
1517
- status: response.status,
1518
- ...action && { requiredAction: action },
1519
- ...resource && { resource }
1520
- }));
1508
+ return this.authorizationFailure(
1509
+ `Failed to get metadata for key ${JSON.stringify(key)}`,
1510
+ response,
1511
+ await response.text()
1512
+ );
1521
1513
  }
1522
1514
  if (response.status === 404) {
1523
1515
  return this.classifyNotFound(response, key);
@@ -1526,7 +1518,7 @@ var KVService = class extends BaseService {
1526
1518
  return err(
1527
1519
  serviceError(
1528
1520
  ErrorCodes.NETWORK_ERROR,
1529
- `Failed to get metadata for key "${key}": ${response.status} - ${errorText}`,
1521
+ `Failed to get metadata for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
1530
1522
  "kv",
1531
1523
  { meta: { status: response.status, statusText: response.statusText } }
1532
1524
  )