@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/index.cjs CHANGED
@@ -55,6 +55,8 @@ __export(src_exports, {
55
55
  SQLAction: () => SQLAction,
56
56
  SQLMigrations: () => SQLMigrations,
57
57
  SQLService: () => SQLService,
58
+ STORAGE_FULL_MESSAGE: () => STORAGE_FULL_MESSAGE,
59
+ STORAGE_WRITE_TOO_LARGE_MESSAGE: () => STORAGE_WRITE_TOO_LARGE_MESSAGE,
58
60
  SecretsService: () => SecretsService,
59
61
  ServiceContext: () => ServiceContext,
60
62
  ServiceErrorEventSchema: () => ServiceErrorEventSchema,
@@ -73,6 +75,7 @@ __export(src_exports, {
73
75
  authExpiredError: () => authExpiredError,
74
76
  authRequiredError: () => authRequiredError,
75
77
  authUnauthorizedError: () => authUnauthorizedError,
78
+ authorizationVerdictOf: () => authorizationVerdictOf,
76
79
  base64Decode: () => base64Decode2,
77
80
  base64Encode: () => base64Encode2,
78
81
  buildCanonicalDecryptRequest: () => buildCanonicalDecryptRequest,
@@ -106,6 +109,7 @@ __export(src_exports, {
106
109
  hexEncode: () => hexEncode2,
107
110
  installTinyCloudDebugGlobals: () => installTinyCloudDebugGlobals,
108
111
  isNetworkId: () => isNetworkId,
112
+ isStorageFullError: () => isStorageFullError,
109
113
  networkDiscoveryKey: () => networkDiscoveryKey,
110
114
  networkError: () => networkError,
111
115
  notFoundError: () => notFoundError,
@@ -116,12 +120,14 @@ __export(src_exports, {
116
120
  parsePermissionHint: () => parsePermissionHint,
117
121
  parsePermissionHintFromErrorText: () => parsePermissionHintFromErrorText,
118
122
  parseSecretCatalogKey: () => parseSecretCatalogKey,
123
+ parseStorageQuotaBytes: () => parseStorageQuotaBytes,
119
124
  permissionDeniedError: () => permissionDeniedError,
120
125
  resolveSecretListPrefix: () => resolveSecretListPrefix,
121
126
  resolveSecretPath: () => resolveSecretPath,
122
127
  serviceError: () => serviceError,
123
128
  storageLimitReachedError: () => storageLimitReachedError,
124
129
  storageQuotaExceededError: () => storageQuotaExceededError,
130
+ storageRejectionError: () => storageRejectionError,
125
131
  timeoutError: () => timeoutError,
126
132
  tinyCloudDebugLogger: () => tinyCloudDebugLogger,
127
133
  utf8Decode: () => utf8Decode,
@@ -1135,6 +1141,37 @@ function parseAuthError(responseText) {
1135
1141
  function authUnauthorizedError(service, message, meta) {
1136
1142
  return serviceError(ErrorCodes.AUTH_UNAUTHORIZED, message, service, { meta });
1137
1143
  }
1144
+ var MAX_CAUSE_DEPTH = 8;
1145
+ function errorStatusOf(node) {
1146
+ const meta = node.meta;
1147
+ const candidates = [
1148
+ node.status,
1149
+ node.statusCode,
1150
+ typeof meta === "object" && meta !== null ? meta.status : void 0
1151
+ ];
1152
+ for (const candidate of candidates) {
1153
+ if (typeof candidate === "number" && Number.isInteger(candidate) && candidate >= 400 && candidate <= 599) {
1154
+ return candidate;
1155
+ }
1156
+ }
1157
+ return void 0;
1158
+ }
1159
+ function authorizationVerdictOf(error) {
1160
+ const seen = /* @__PURE__ */ new Set();
1161
+ let sawUnauthorizedCode = false;
1162
+ let node = error;
1163
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH && typeof node === "object" && node !== null && !seen.has(node); depth += 1) {
1164
+ seen.add(node);
1165
+ const record = node;
1166
+ const status = errorStatusOf(record);
1167
+ if (status === 401) return "unauthenticated";
1168
+ if (status === 403) return "forbidden";
1169
+ if (status !== void 0) return "other";
1170
+ if (record.code === ErrorCodes.AUTH_UNAUTHORIZED) sawUnauthorizedCode = true;
1171
+ node = record.cause;
1172
+ }
1173
+ return sawUnauthorizedCode ? "forbidden" : void 0;
1174
+ }
1138
1175
  function storageQuotaExceededError(service, message, meta) {
1139
1176
  return {
1140
1177
  code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,
@@ -1151,6 +1188,23 @@ function storageLimitReachedError(service, message, meta) {
1151
1188
  meta
1152
1189
  };
1153
1190
  }
1191
+ 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.";
1192
+ 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.";
1193
+ function parseStorageQuotaBytes(responseText) {
1194
+ const match = responseText.match(/Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i);
1195
+ if (!match) return void 0;
1196
+ return {
1197
+ usedBytes: parseInt(match[1], 10),
1198
+ limitBytes: parseInt(match[2], 10)
1199
+ };
1200
+ }
1201
+ function storageRejectionError(service, status, meta, responseText) {
1202
+ const quotaMeta = { ...meta, ...parseStorageQuotaBytes(responseText) };
1203
+ return status === 402 ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta) : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);
1204
+ }
1205
+ function isStorageFullError(error) {
1206
+ return error?.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED || error?.code === ErrorCodes.STORAGE_LIMIT_REACHED;
1207
+ }
1154
1208
  function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
1155
1209
  if (error instanceof Error) {
1156
1210
  if (error.name === "AbortError") {
@@ -1544,47 +1598,21 @@ var KVService = class extends BaseService {
1544
1598
  get config() {
1545
1599
  return this._config;
1546
1600
  }
1547
- // Parses "Used: X bytes, Limit: Y bytes" from tinycloud-node error responses
1548
- parseQuotaInfo(errorText) {
1549
- const match = errorText.match(
1550
- /Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i
1551
- );
1552
- if (match) {
1553
- return {
1554
- usedBytes: parseInt(match[1], 10),
1555
- limitBytes: parseInt(match[2], 10)
1556
- };
1557
- }
1558
- return void 0;
1559
- }
1560
1601
  handleQuotaErrorResponse(response, errorText, key) {
1561
- if (response.status === 402) {
1562
- const quotaInfo = this.parseQuotaInfo(errorText);
1563
- return err(
1564
- storageQuotaExceededError(
1565
- "kv",
1566
- `Storage quota exceeded for key "${key}": ${errorText}`,
1567
- {
1568
- status: response.status,
1569
- ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
1570
- }
1571
- )
1572
- );
1602
+ if (response.status !== 402 && response.status !== 413) {
1603
+ return void 0;
1573
1604
  }
1574
- if (response.status === 413) {
1575
- const quotaInfo = this.parseQuotaInfo(errorText);
1576
- return err(
1577
- storageLimitReachedError(
1578
- "kv",
1579
- `Storage limit reached for key "${key}": ${errorText}`,
1580
- {
1581
- status: response.status,
1582
- ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
1583
- }
1584
- )
1585
- );
1605
+ if (response.status === 413 && !parseStorageQuotaBytes(errorText)) {
1606
+ return void 0;
1586
1607
  }
1587
- return void 0;
1608
+ return err(
1609
+ storageRejectionError(
1610
+ "kv",
1611
+ response.status,
1612
+ { status: response.status, statusText: response.statusText, key },
1613
+ errorText
1614
+ )
1615
+ );
1588
1616
  }
1589
1617
  /**
1590
1618
  * Classify a KV 404 by reading the response body once.
@@ -1862,12 +1890,11 @@ var KVService = class extends BaseService {
1862
1890
  if (!response.ok) {
1863
1891
  const errorText = await response.text();
1864
1892
  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
- }));
1893
+ return this.authorizationFailure(
1894
+ `Failed to batch read ${keys.length} key(s)`,
1895
+ response,
1896
+ errorText
1897
+ );
1871
1898
  }
1872
1899
  if (response.status === 413) {
1873
1900
  return err(serviceError(
@@ -1966,23 +1993,39 @@ var KVService = class extends BaseService {
1966
1993
  } catch {
1967
1994
  }
1968
1995
  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
- }));
1996
+ return this.authorizationFailure(
1997
+ `Failed to create signed read URL for key ${JSON.stringify(key)}`,
1998
+ response,
1999
+ errorText
2000
+ );
1975
2001
  }
1976
2002
  const code = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
1977
2003
  return err(
1978
2004
  serviceError(
1979
2005
  code,
1980
- `Failed to create signed read URL for key "${key}": ${response.status} - ${errorText}`,
2006
+ `Failed to create signed read URL for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
1981
2007
  "kv",
1982
2008
  { meta: { status: response.status, statusText: response.statusText } }
1983
2009
  )
1984
2010
  );
1985
2011
  }
2012
+ /**
2013
+ * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP
2014
+ * status and the server text (`<context>: <status> - <text>`, the same
2015
+ * shape as every other KV failure) so callers that rethrow only the message
2016
+ * still see the status; `meta.status` carries it for typed callers.
2017
+ * Resource/ability hints are parsed from the raw body, never the message.
2018
+ */
2019
+ authorizationFailure(context, response, errorText, extraMeta = {}) {
2020
+ const { resource, action } = parseAuthError(errorText);
2021
+ const detail = errorText.trim().length > 0 ? errorText : response.statusText || "authorization failed";
2022
+ return err(authUnauthorizedError("kv", `${context}: ${response.status} - ${detail}`, {
2023
+ status: response.status,
2024
+ ...action && { requiredAction: action },
2025
+ ...resource && { resource },
2026
+ ...extraMeta
2027
+ }));
2028
+ }
1986
2029
  normalizeSignedReadUrlResponse(data) {
1987
2030
  if (!data || typeof data !== "object") {
1988
2031
  return void 0;
@@ -2025,14 +2068,13 @@ var KVService = class extends BaseService {
2025
2068
  if (!response.ok) {
2026
2069
  if (response.status === 401 || response.status === 403) {
2027
2070
  const errorText2 = await response.text();
2028
- const { resource, action } = parseAuthError(errorText2);
2029
2071
  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
- }));
2072
+ return this.authorizationFailure(
2073
+ `Failed to get key ${JSON.stringify(key)}`,
2074
+ response,
2075
+ errorText2,
2076
+ permissionHint === void 0 ? {} : { permissionHint }
2077
+ );
2036
2078
  }
2037
2079
  if (response.status === 404) {
2038
2080
  return this.classifyNotFound(response, key);
@@ -2041,7 +2083,7 @@ var KVService = class extends BaseService {
2041
2083
  if (response.status === 413) {
2042
2084
  return err(serviceError(
2043
2085
  ErrorCodes.KV_RESPONSE_TOO_LARGE,
2044
- `KV value at key "${key}" exceeds the requested response limit`,
2086
+ `KV value at key ${JSON.stringify(key)} exceeds the requested response limit`,
2045
2087
  "kv",
2046
2088
  { meta: { status: response.status, statusText: response.statusText } }
2047
2089
  ));
@@ -2049,7 +2091,7 @@ var KVService = class extends BaseService {
2049
2091
  return err(
2050
2092
  serviceError(
2051
2093
  ErrorCodes.NETWORK_ERROR,
2052
- `Failed to get key "${key}": ${response.status} - ${errorText}`,
2094
+ `Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2053
2095
  "kv",
2054
2096
  { meta: { status: response.status, statusText: response.statusText } }
2055
2097
  )
@@ -2105,21 +2147,18 @@ var KVService = class extends BaseService {
2105
2147
  }
2106
2148
  );
2107
2149
  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
- }));
2150
+ return this.authorizationFailure(
2151
+ `Failed to put key ${JSON.stringify(key)}`,
2152
+ response,
2153
+ await response.text()
2154
+ );
2116
2155
  }
2117
2156
  if (!response.ok) {
2118
2157
  const errorText = await response.text();
2119
2158
  if (response.status === 412) {
2120
2159
  return err(serviceError(
2121
2160
  ErrorCodes.KV_PRECONDITION_FAILED,
2122
- `KV precondition failed for key "${key}"`,
2161
+ `KV precondition failed for key ${JSON.stringify(key)}`,
2123
2162
  "kv",
2124
2163
  { meta: { status: response.status, statusText: response.statusText } }
2125
2164
  ));
@@ -2127,7 +2166,7 @@ var KVService = class extends BaseService {
2127
2166
  if (response.status === 503 && (options?.ifMatch !== void 0 || options?.ifNoneMatch !== void 0)) {
2128
2167
  return err(serviceError(
2129
2168
  ErrorCodes.KV_CONFLICT,
2130
- `Concurrent KV update conflicted for key "${key}"`,
2169
+ `Concurrent KV update conflicted for key ${JSON.stringify(key)}`,
2131
2170
  "kv",
2132
2171
  { meta: { status: response.status, statusText: response.statusText } }
2133
2172
  ));
@@ -2143,7 +2182,7 @@ var KVService = class extends BaseService {
2143
2182
  return err(
2144
2183
  serviceError(
2145
2184
  ErrorCodes.KV_WRITE_FAILED,
2146
- `Failed to put key "${key}": ${response.status} - ${errorText}`,
2185
+ `Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2147
2186
  "kv",
2148
2187
  { meta: { status: response.status, statusText: response.statusText } }
2149
2188
  )
@@ -2237,12 +2276,11 @@ var KVService = class extends BaseService {
2237
2276
  );
2238
2277
  }
2239
2278
  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
- }));
2279
+ return this.authorizationFailure(
2280
+ `Failed to batch put ${items.length} key(s)`,
2281
+ response,
2282
+ errorText
2283
+ );
2246
2284
  }
2247
2285
  const quotaError = this.handleQuotaErrorResponse(
2248
2286
  response,
@@ -2363,13 +2401,11 @@ var KVService = class extends BaseService {
2363
2401
  );
2364
2402
  if (!response.ok) {
2365
2403
  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
- }));
2404
+ return this.authorizationFailure(
2405
+ "Failed to list keys",
2406
+ response,
2407
+ await response.text()
2408
+ );
2373
2409
  }
2374
2410
  const errorText = await response.text();
2375
2411
  return err(
@@ -2418,13 +2454,11 @@ var KVService = class extends BaseService {
2418
2454
  );
2419
2455
  if (!response.ok) {
2420
2456
  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
- }));
2457
+ return this.authorizationFailure(
2458
+ `Failed to delete key ${JSON.stringify(key)}`,
2459
+ response,
2460
+ await response.text()
2461
+ );
2428
2462
  }
2429
2463
  if (response.status === 404) {
2430
2464
  return this.classifyNotFound(response, key);
@@ -2433,7 +2467,7 @@ var KVService = class extends BaseService {
2433
2467
  if (response.status === 412) {
2434
2468
  return err(serviceError(
2435
2469
  ErrorCodes.KV_PRECONDITION_FAILED,
2436
- `KV precondition failed for key "${key}"`,
2470
+ `KV precondition failed for key ${JSON.stringify(key)}`,
2437
2471
  "kv",
2438
2472
  { meta: { status: response.status, statusText: response.statusText } }
2439
2473
  ));
@@ -2441,7 +2475,7 @@ var KVService = class extends BaseService {
2441
2475
  if (response.status === 503 && options?.ifMatch !== void 0) {
2442
2476
  return err(serviceError(
2443
2477
  ErrorCodes.KV_CONFLICT,
2444
- `Concurrent KV delete conflicted for key "${key}"`,
2478
+ `Concurrent KV delete conflicted for key ${JSON.stringify(key)}`,
2445
2479
  "kv",
2446
2480
  { meta: { status: response.status, statusText: response.statusText } }
2447
2481
  ));
@@ -2449,7 +2483,7 @@ var KVService = class extends BaseService {
2449
2483
  return err(
2450
2484
  serviceError(
2451
2485
  ErrorCodes.NETWORK_ERROR,
2452
- `Failed to delete key "${key}": ${response.status} - ${errorText}`,
2486
+ `Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2453
2487
  "kv",
2454
2488
  { meta: { status: response.status, statusText: response.statusText } }
2455
2489
  )
@@ -2482,13 +2516,11 @@ var KVService = class extends BaseService {
2482
2516
  );
2483
2517
  if (!response.ok) {
2484
2518
  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
- }));
2519
+ return this.authorizationFailure(
2520
+ `Failed to get metadata for key ${JSON.stringify(key)}`,
2521
+ response,
2522
+ await response.text()
2523
+ );
2492
2524
  }
2493
2525
  if (response.status === 404) {
2494
2526
  return this.classifyNotFound(response, key);
@@ -2497,7 +2529,7 @@ var KVService = class extends BaseService {
2497
2529
  return err(
2498
2530
  serviceError(
2499
2531
  ErrorCodes.NETWORK_ERROR,
2500
- `Failed to get metadata for key "${key}": ${response.status} - ${errorText}`,
2532
+ `Failed to get metadata for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2501
2533
  "kv",
2502
2534
  { meta: { status: response.status, statusText: response.statusText } }
2503
2535
  )
@@ -2917,21 +2949,24 @@ var SQLService = class extends BaseService {
2917
2949
  }
2918
2950
  async applyMigrationsOnDbUnlocked(dbName, options) {
2919
2951
  return this.withTelemetry("migrations.apply", dbName, async () => {
2920
- const created = await this.ensureMigrationsTable(dbName, options.signal);
2921
- if (!created.ok) return created;
2922
2952
  const listed = await this.queryOnDb(
2923
2953
  dbName,
2924
2954
  `SELECT id FROM ${MIGRATIONS_TABLE} WHERE namespace = ? ORDER BY applied_at, id`,
2925
2955
  [options.namespace],
2926
2956
  { signal: options.signal }
2927
2957
  );
2928
- if (!listed.ok) return listed;
2958
+ const tableMissing = !listed.ok && (listed.error.code === ErrorCodes.SQL_DATABASE_NOT_FOUND || isMissingTableError(listed.error.message));
2959
+ if (!listed.ok && !tableMissing) return listed;
2929
2960
  const appliedIds = new Set(
2930
- listed.data.rows.map((row) => rowValue(row, 0)).filter((id) => typeof id === "string")
2961
+ (listed.ok ? listed.data.rows : []).map((row) => rowValue(row, 0)).filter((id) => typeof id === "string")
2931
2962
  );
2932
2963
  const skipped = options.migrations.filter((migration) => appliedIds.has(migration.id)).map((migration) => migration.id);
2933
2964
  const pending = options.migrations.filter((migration) => !appliedIds.has(migration.id));
2934
2965
  const applied = [];
2966
+ if (pending.length > 0 && tableMissing) {
2967
+ const created = await this.ensureMigrationsTable(dbName, options.signal);
2968
+ if (!created.ok) return created;
2969
+ }
2935
2970
  for (const migration of pending) {
2936
2971
  const result = await this.applyOneMigration(dbName, options.namespace, migration, options.signal);
2937
2972
  if (!result.ok) return result;
@@ -3034,6 +3069,10 @@ var SQLService = class extends BaseService {
3034
3069
  }
3035
3070
  async handleErrorResponse(response, operation) {
3036
3071
  const errorText = await response.text();
3072
+ const meta = responseErrorMeta(response.status, response.statusText, errorText);
3073
+ if (response.status === 402) {
3074
+ return err(storageRejectionError("sql", 402, meta, errorText));
3075
+ }
3037
3076
  const errorBody = parseServiceErrorBody(errorText);
3038
3077
  const errorCode = this.mapHttpStatusToErrorCode(
3039
3078
  response.status,
@@ -3046,7 +3085,6 @@ var SQLService = class extends BaseService {
3046
3085
  errorText,
3047
3086
  errorBody
3048
3087
  );
3049
- const meta = responseErrorMeta(response.status, response.statusText, errorText);
3050
3088
  if (response.status === 401) {
3051
3089
  const { resource, action } = parseAuthError(errorText);
3052
3090
  if (action) meta.requiredAction = action;
@@ -3147,6 +3185,9 @@ function serializeSqlValues(values) {
3147
3185
  (value) => value instanceof Uint8Array ? Array.from(value) : value
3148
3186
  );
3149
3187
  }
3188
+ function isMissingTableError(message) {
3189
+ return /no such table/i.test(message);
3190
+ }
3150
3191
  function migrationKey(namespace, id) {
3151
3192
  return `${encodeURIComponent(namespace)}:${encodeURIComponent(id)}`;
3152
3193
  }
@@ -3468,6 +3509,10 @@ var DuckDbService = class extends BaseService {
3468
3509
  }
3469
3510
  async handleErrorResponse(response, operation) {
3470
3511
  const errorText = await response.text();
3512
+ const meta = responseErrorMeta(response.status, response.statusText, errorText);
3513
+ if (response.status === 402) {
3514
+ return err(storageRejectionError("duckdb", 402, meta, errorText));
3515
+ }
3471
3516
  const errorBody = parseServiceErrorBody(errorText);
3472
3517
  const errorCode = this.mapHttpStatusToErrorCode(
3473
3518
  response.status,
@@ -3480,7 +3525,6 @@ var DuckDbService = class extends BaseService {
3480
3525
  errorText,
3481
3526
  errorBody
3482
3527
  );
3483
- const meta = responseErrorMeta(response.status, response.statusText, errorText);
3484
3528
  if (response.status === 401) {
3485
3529
  const { resource, action } = parseAuthError(errorText);
3486
3530
  if (action) meta.requiredAction = action;
@@ -3979,10 +4023,14 @@ async function responseError(service, message, response) {
3979
4023
  }
3980
4024
  } catch {
3981
4025
  }
3982
- return wrapError(
4026
+ const error = wrapError(
3983
4027
  service,
3984
4028
  new Error(`${message}: ${response.status} ${detail}`)
3985
4029
  );
4030
+ return {
4031
+ ...error,
4032
+ meta: { ...error.meta, status: response.status, statusText: response.statusText }
4033
+ };
3986
4034
  }
3987
4035
  function isAbortError(error) {
3988
4036
  return error instanceof DOMException && error.name === "AbortError";
@@ -4448,6 +4496,9 @@ function defaultVaultMessage(input) {
4448
4496
  return input.message ?? `Public key not found for ${input.did}`;
4449
4497
  case "STORAGE_ERROR":
4450
4498
  return input.message ?? input.cause.message;
4499
+ case "STORAGE_QUOTA_EXCEEDED":
4500
+ case "STORAGE_LIMIT_REACHED":
4501
+ return input.message;
4451
4502
  }
4452
4503
  }
4453
4504
  function vaultError(input) {
@@ -4458,6 +4509,15 @@ function vaultError(input) {
4458
4509
  };
4459
4510
  return { ok: false, error };
4460
4511
  }
4512
+ function kvWriteError(context, error) {
4513
+ if (error.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED || error.code === ErrorCodes.STORAGE_LIMIT_REACHED) {
4514
+ return vaultError({ code: error.code, message: error.message, meta: error.meta });
4515
+ }
4516
+ return vaultError({
4517
+ code: "STORAGE_ERROR",
4518
+ cause: new Error(`${context}: ${error.message}`)
4519
+ });
4520
+ }
4461
4521
  var DataVaultService = class extends BaseService {
4462
4522
  /**
4463
4523
  * Create a new DataVaultService instance.
@@ -4740,12 +4800,7 @@ var DataVaultService = class extends BaseService {
4740
4800
  JSON.stringify(envelopeResult.data)
4741
4801
  );
4742
4802
  if (!valuePutResult.ok) {
4743
- return vaultError({
4744
- code: "STORAGE_ERROR",
4745
- cause: new Error(
4746
- `Failed to store encrypted value: ${valuePutResult.error.message}`
4747
- )
4748
- });
4803
+ return kvWriteError("Failed to store encrypted value", valuePutResult.error);
4749
4804
  }
4750
4805
  return { ok: true, data: void 0 };
4751
4806
  } catch (error) {
@@ -4963,12 +5018,7 @@ var DataVaultService = class extends BaseService {
4963
5018
  keyPayload
4964
5019
  );
4965
5020
  if (!keyPutResult.ok) {
4966
- return vaultError({
4967
- code: "STORAGE_ERROR",
4968
- cause: new Error(
4969
- `Failed to store key blob: ${keyPutResult.error.message}`
4970
- )
4971
- });
5021
+ return kvWriteError("Failed to store key blob", keyPutResult.error);
4972
5022
  }
4973
5023
  const valuePayload = JSON.stringify({
4974
5024
  data: base64Encode(encrypted),
@@ -4979,12 +5029,7 @@ var DataVaultService = class extends BaseService {
4979
5029
  valuePayload
4980
5030
  );
4981
5031
  if (!valuePutResult.ok) {
4982
- return vaultError({
4983
- code: "STORAGE_ERROR",
4984
- cause: new Error(
4985
- `Failed to store encrypted value: ${valuePutResult.error.message}`
4986
- )
4987
- });
5032
+ return kvWriteError("Failed to store encrypted value", valuePutResult.error);
4988
5033
  }
4989
5034
  return ok(void 0);
4990
5035
  } catch (error) {
@@ -5315,12 +5360,7 @@ var DataVaultService = class extends BaseService {
5315
5360
  grantPayload
5316
5361
  );
5317
5362
  if (!grantPutResult.ok) {
5318
- return vaultError({
5319
- code: "STORAGE_ERROR",
5320
- cause: new Error(
5321
- `Failed to store grant: ${grantPutResult.error.message}`
5322
- )
5323
- });
5363
+ return kvWriteError("Failed to store grant", grantPutResult.error);
5324
5364
  }
5325
5365
  return ok(void 0);
5326
5366
  } catch (error) {
@@ -5665,12 +5705,7 @@ var DataVaultService = class extends BaseService {
5665
5705
  keyPayload
5666
5706
  );
5667
5707
  if (!keyPutResult.ok) {
5668
- return vaultError({
5669
- code: "STORAGE_ERROR",
5670
- cause: new Error(
5671
- `Failed to store rotated key blob: ${keyPutResult.error.message}`
5672
- )
5673
- });
5708
+ return kvWriteError("Failed to store rotated key blob", keyPutResult.error);
5674
5709
  }
5675
5710
  const valuePayload = JSON.stringify({
5676
5711
  data: base64Encode(encrypted),
@@ -5681,12 +5716,7 @@ var DataVaultService = class extends BaseService {
5681
5716
  valuePayload
5682
5717
  );
5683
5718
  if (!valuePutResult.ok) {
5684
- return vaultError({
5685
- code: "STORAGE_ERROR",
5686
- cause: new Error(
5687
- `Failed to store re-encrypted value: ${valuePutResult.error.message}`
5688
- )
5689
- });
5719
+ return kvWriteError("Failed to store re-encrypted value", valuePutResult.error);
5690
5720
  }
5691
5721
  for (const did of remainingGrantees) {
5692
5722
  const grantResult = await this.reencrypt(key, did);
@@ -7016,6 +7046,8 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
7016
7046
  SQLAction,
7017
7047
  SQLMigrations,
7018
7048
  SQLService,
7049
+ STORAGE_FULL_MESSAGE,
7050
+ STORAGE_WRITE_TOO_LARGE_MESSAGE,
7019
7051
  SecretsService,
7020
7052
  ServiceContext,
7021
7053
  ServiceErrorEventSchema,
@@ -7034,6 +7066,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
7034
7066
  authExpiredError,
7035
7067
  authRequiredError,
7036
7068
  authUnauthorizedError,
7069
+ authorizationVerdictOf,
7037
7070
  base64Decode,
7038
7071
  base64Encode,
7039
7072
  buildCanonicalDecryptRequest,
@@ -7067,6 +7100,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
7067
7100
  hexEncode,
7068
7101
  installTinyCloudDebugGlobals,
7069
7102
  isNetworkId,
7103
+ isStorageFullError,
7070
7104
  networkDiscoveryKey,
7071
7105
  networkError,
7072
7106
  notFoundError,
@@ -7077,12 +7111,14 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
7077
7111
  parsePermissionHint,
7078
7112
  parsePermissionHintFromErrorText,
7079
7113
  parseSecretCatalogKey,
7114
+ parseStorageQuotaBytes,
7080
7115
  permissionDeniedError,
7081
7116
  resolveSecretListPrefix,
7082
7117
  resolveSecretPath,
7083
7118
  serviceError,
7084
7119
  storageLimitReachedError,
7085
7120
  storageQuotaExceededError,
7121
+ storageRejectionError,
7086
7122
  timeoutError,
7087
7123
  tinyCloudDebugLogger,
7088
7124
  utf8Decode,