@tinycloud/operations 0.3.4-beta.0 → 0.3.4-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/artifacts.js CHANGED
@@ -1636,7 +1636,7 @@ var KVService = class extends BaseService {
1636
1636
  return err(
1637
1637
  storageQuotaExceededError(
1638
1638
  "kv",
1639
- `Storage quota exceeded for key "${key}": ${errorText}`,
1639
+ `Storage quota exceeded for key ${JSON.stringify(key)}: ${errorText}`,
1640
1640
  {
1641
1641
  status: response.status,
1642
1642
  ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
@@ -1649,7 +1649,7 @@ var KVService = class extends BaseService {
1649
1649
  return err(
1650
1650
  storageLimitReachedError(
1651
1651
  "kv",
1652
- `Storage limit reached for key "${key}": ${errorText}`,
1652
+ `Storage limit reached for key ${JSON.stringify(key)}: ${errorText}`,
1653
1653
  {
1654
1654
  status: response.status,
1655
1655
  ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
@@ -1935,12 +1935,11 @@ var KVService = class extends BaseService {
1935
1935
  if (!response.ok) {
1936
1936
  const errorText = await response.text();
1937
1937
  if (response.status === 401 || response.status === 403) {
1938
- const { resource, action: requiredAction } = parseAuthError(errorText);
1939
- return err(authUnauthorizedError("kv", errorText, {
1940
- status: response.status,
1941
- ...requiredAction && { requiredAction },
1942
- ...resource && { resource }
1943
- }));
1938
+ return this.authorizationFailure(
1939
+ `Failed to batch read ${keys.length} key(s)`,
1940
+ response,
1941
+ errorText
1942
+ );
1944
1943
  }
1945
1944
  if (response.status === 413) {
1946
1945
  return err(serviceError(
@@ -2039,23 +2038,39 @@ var KVService = class extends BaseService {
2039
2038
  } catch {
2040
2039
  }
2041
2040
  if (response.status === 401 || response.status === 403) {
2042
- const { resource, action } = parseAuthError(errorText);
2043
- return err(authUnauthorizedError("kv", errorText, {
2044
- status: response.status,
2045
- ...action && { requiredAction: action },
2046
- ...resource && { resource }
2047
- }));
2041
+ return this.authorizationFailure(
2042
+ `Failed to create signed read URL for key ${JSON.stringify(key)}`,
2043
+ response,
2044
+ errorText
2045
+ );
2048
2046
  }
2049
2047
  const code = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
2050
2048
  return err(
2051
2049
  serviceError(
2052
2050
  code,
2053
- `Failed to create signed read URL for key "${key}": ${response.status} - ${errorText}`,
2051
+ `Failed to create signed read URL for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2054
2052
  "kv",
2055
2053
  { meta: { status: response.status, statusText: response.statusText } }
2056
2054
  )
2057
2055
  );
2058
2056
  }
2057
+ /**
2058
+ * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP
2059
+ * status and the server text (`<context>: <status> - <text>`, the same
2060
+ * shape as every other KV failure) so callers that rethrow only the message
2061
+ * still see the status; `meta.status` carries it for typed callers.
2062
+ * Resource/ability hints are parsed from the raw body, never the message.
2063
+ */
2064
+ authorizationFailure(context, response, errorText, extraMeta = {}) {
2065
+ const { resource, action } = parseAuthError(errorText);
2066
+ const detail = errorText.trim().length > 0 ? errorText : response.statusText || "authorization failed";
2067
+ return err(authUnauthorizedError("kv", `${context}: ${response.status} - ${detail}`, {
2068
+ status: response.status,
2069
+ ...action && { requiredAction: action },
2070
+ ...resource && { resource },
2071
+ ...extraMeta
2072
+ }));
2073
+ }
2059
2074
  normalizeSignedReadUrlResponse(data) {
2060
2075
  if (!data || typeof data !== "object") {
2061
2076
  return void 0;
@@ -2098,14 +2113,13 @@ var KVService = class extends BaseService {
2098
2113
  if (!response.ok) {
2099
2114
  if (response.status === 401 || response.status === 403) {
2100
2115
  const errorText2 = await response.text();
2101
- const { resource, action } = parseAuthError(errorText2);
2102
2116
  const permissionHint = parsePermissionHintFromErrorText(errorText2);
2103
- return err(authUnauthorizedError("kv", errorText2, {
2104
- status: response.status,
2105
- ...action && { requiredAction: action },
2106
- ...resource && { resource },
2107
- ...permissionHint === void 0 ? {} : { permissionHint }
2108
- }));
2117
+ return this.authorizationFailure(
2118
+ `Failed to get key ${JSON.stringify(key)}`,
2119
+ response,
2120
+ errorText2,
2121
+ permissionHint === void 0 ? {} : { permissionHint }
2122
+ );
2109
2123
  }
2110
2124
  if (response.status === 404) {
2111
2125
  return this.classifyNotFound(response, key);
@@ -2114,7 +2128,7 @@ var KVService = class extends BaseService {
2114
2128
  if (response.status === 413) {
2115
2129
  return err(serviceError(
2116
2130
  ErrorCodes.KV_RESPONSE_TOO_LARGE,
2117
- `KV value at key "${key}" exceeds the requested response limit`,
2131
+ `KV value at key ${JSON.stringify(key)} exceeds the requested response limit`,
2118
2132
  "kv",
2119
2133
  { meta: { status: response.status, statusText: response.statusText } }
2120
2134
  ));
@@ -2122,7 +2136,7 @@ var KVService = class extends BaseService {
2122
2136
  return err(
2123
2137
  serviceError(
2124
2138
  ErrorCodes.NETWORK_ERROR,
2125
- `Failed to get key "${key}": ${response.status} - ${errorText}`,
2139
+ `Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2126
2140
  "kv",
2127
2141
  { meta: { status: response.status, statusText: response.statusText } }
2128
2142
  )
@@ -2178,21 +2192,18 @@ var KVService = class extends BaseService {
2178
2192
  }
2179
2193
  );
2180
2194
  if (response.status === 401 || response.status === 403) {
2181
- const errorText = await response.text();
2182
- const message = errorText.trim().length > 0 ? errorText : `Failed to put key "${key}": ${response.status} - ${response.statusText || "authorization failed"}`;
2183
- const { resource, action: requiredAction } = parseAuthError(errorText);
2184
- return err(authUnauthorizedError("kv", message, {
2185
- status: response.status,
2186
- ...requiredAction && { requiredAction },
2187
- ...resource && { resource }
2188
- }));
2195
+ return this.authorizationFailure(
2196
+ `Failed to put key ${JSON.stringify(key)}`,
2197
+ response,
2198
+ await response.text()
2199
+ );
2189
2200
  }
2190
2201
  if (!response.ok) {
2191
2202
  const errorText = await response.text();
2192
2203
  if (response.status === 412) {
2193
2204
  return err(serviceError(
2194
2205
  ErrorCodes.KV_PRECONDITION_FAILED,
2195
- `KV precondition failed for key "${key}"`,
2206
+ `KV precondition failed for key ${JSON.stringify(key)}`,
2196
2207
  "kv",
2197
2208
  { meta: { status: response.status, statusText: response.statusText } }
2198
2209
  ));
@@ -2200,7 +2211,7 @@ var KVService = class extends BaseService {
2200
2211
  if (response.status === 503 && (options?.ifMatch !== void 0 || options?.ifNoneMatch !== void 0)) {
2201
2212
  return err(serviceError(
2202
2213
  ErrorCodes.KV_CONFLICT,
2203
- `Concurrent KV update conflicted for key "${key}"`,
2214
+ `Concurrent KV update conflicted for key ${JSON.stringify(key)}`,
2204
2215
  "kv",
2205
2216
  { meta: { status: response.status, statusText: response.statusText } }
2206
2217
  ));
@@ -2216,7 +2227,7 @@ var KVService = class extends BaseService {
2216
2227
  return err(
2217
2228
  serviceError(
2218
2229
  ErrorCodes.KV_WRITE_FAILED,
2219
- `Failed to put key "${key}": ${response.status} - ${errorText}`,
2230
+ `Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2220
2231
  "kv",
2221
2232
  { meta: { status: response.status, statusText: response.statusText } }
2222
2233
  )
@@ -2310,12 +2321,11 @@ var KVService = class extends BaseService {
2310
2321
  );
2311
2322
  }
2312
2323
  if (response.status === 401 || response.status === 403) {
2313
- const { resource, action } = parseAuthError(errorText);
2314
- return err(authUnauthorizedError("kv", errorText, {
2315
- status: response.status,
2316
- ...action && { requiredAction: action },
2317
- ...resource && { resource }
2318
- }));
2324
+ return this.authorizationFailure(
2325
+ `Failed to batch put ${items.length} key(s)`,
2326
+ response,
2327
+ errorText
2328
+ );
2319
2329
  }
2320
2330
  const quotaError = this.handleQuotaErrorResponse(
2321
2331
  response,
@@ -2436,13 +2446,11 @@ var KVService = class extends BaseService {
2436
2446
  );
2437
2447
  if (!response.ok) {
2438
2448
  if (response.status === 401) {
2439
- const errorText2 = await response.text();
2440
- const { resource, action } = parseAuthError(errorText2);
2441
- return err(authUnauthorizedError("kv", errorText2, {
2442
- status: response.status,
2443
- ...action && { requiredAction: action },
2444
- ...resource && { resource }
2445
- }));
2449
+ return this.authorizationFailure(
2450
+ "Failed to list keys",
2451
+ response,
2452
+ await response.text()
2453
+ );
2446
2454
  }
2447
2455
  const errorText = await response.text();
2448
2456
  return err(
@@ -2491,13 +2499,11 @@ var KVService = class extends BaseService {
2491
2499
  );
2492
2500
  if (!response.ok) {
2493
2501
  if (response.status === 401) {
2494
- const errorText2 = await response.text();
2495
- const { resource, action } = parseAuthError(errorText2);
2496
- return err(authUnauthorizedError("kv", errorText2, {
2497
- status: response.status,
2498
- ...action && { requiredAction: action },
2499
- ...resource && { resource }
2500
- }));
2502
+ return this.authorizationFailure(
2503
+ `Failed to delete key ${JSON.stringify(key)}`,
2504
+ response,
2505
+ await response.text()
2506
+ );
2501
2507
  }
2502
2508
  if (response.status === 404) {
2503
2509
  return this.classifyNotFound(response, key);
@@ -2506,7 +2512,7 @@ var KVService = class extends BaseService {
2506
2512
  if (response.status === 412) {
2507
2513
  return err(serviceError(
2508
2514
  ErrorCodes.KV_PRECONDITION_FAILED,
2509
- `KV precondition failed for key "${key}"`,
2515
+ `KV precondition failed for key ${JSON.stringify(key)}`,
2510
2516
  "kv",
2511
2517
  { meta: { status: response.status, statusText: response.statusText } }
2512
2518
  ));
@@ -2514,7 +2520,7 @@ var KVService = class extends BaseService {
2514
2520
  if (response.status === 503 && options?.ifMatch !== void 0) {
2515
2521
  return err(serviceError(
2516
2522
  ErrorCodes.KV_CONFLICT,
2517
- `Concurrent KV delete conflicted for key "${key}"`,
2523
+ `Concurrent KV delete conflicted for key ${JSON.stringify(key)}`,
2518
2524
  "kv",
2519
2525
  { meta: { status: response.status, statusText: response.statusText } }
2520
2526
  ));
@@ -2522,7 +2528,7 @@ var KVService = class extends BaseService {
2522
2528
  return err(
2523
2529
  serviceError(
2524
2530
  ErrorCodes.NETWORK_ERROR,
2525
- `Failed to delete key "${key}": ${response.status} - ${errorText}`,
2531
+ `Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2526
2532
  "kv",
2527
2533
  { meta: { status: response.status, statusText: response.statusText } }
2528
2534
  )
@@ -2555,13 +2561,11 @@ var KVService = class extends BaseService {
2555
2561
  );
2556
2562
  if (!response.ok) {
2557
2563
  if (response.status === 401) {
2558
- const errorText2 = await response.text();
2559
- const { resource, action } = parseAuthError(errorText2);
2560
- return err(authUnauthorizedError("kv", errorText2, {
2561
- status: response.status,
2562
- ...action && { requiredAction: action },
2563
- ...resource && { resource }
2564
- }));
2564
+ return this.authorizationFailure(
2565
+ `Failed to get metadata for key ${JSON.stringify(key)}`,
2566
+ response,
2567
+ await response.text()
2568
+ );
2565
2569
  }
2566
2570
  if (response.status === 404) {
2567
2571
  return this.classifyNotFound(response, key);
@@ -2570,7 +2574,7 @@ var KVService = class extends BaseService {
2570
2574
  return err(
2571
2575
  serviceError(
2572
2576
  ErrorCodes.NETWORK_ERROR,
2573
- `Failed to get metadata for key "${key}": ${response.status} - ${errorText}`,
2577
+ `Failed to get metadata for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
2574
2578
  "kv",
2575
2579
  { meta: { status: response.status, statusText: response.statusText } }
2576
2580
  )
@@ -4029,10 +4033,14 @@ async function responseError(service, message, response) {
4029
4033
  }
4030
4034
  } catch {
4031
4035
  }
4032
- return wrapError(
4036
+ const error = wrapError(
4033
4037
  service,
4034
4038
  new Error(`${message}: ${response.status} ${detail}`)
4035
4039
  );
4040
+ return {
4041
+ ...error,
4042
+ meta: { ...error.meta, status: response.status, statusText: response.statusText }
4043
+ };
4036
4044
  }
4037
4045
  function isAbortError(error) {
4038
4046
  return error instanceof DOMException && error.name === "AbortError";
@@ -7066,9 +7074,12 @@ async function acquireProfileLock(profile, options) {
7066
7074
  const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_LOCK_MS;
7067
7075
  const startedAt = Date.now();
7068
7076
  const lockPath = profileLockPath(profile);
7069
- await mkdir(profilePath(profile), { recursive: true, mode: PRIVATE_DIR_MODE });
7070
- for (const directory of [tinycloudHomePath(), profilesPath(), profilePath(profile)]) {
7071
- await chmod(directory, PRIVATE_DIR_MODE);
7077
+ const directory = profilePath(profile);
7078
+ await mkdir(directory, { recursive: true, mode: PRIVATE_DIR_MODE });
7079
+ for (const path of [tinycloudHomePath(), profilesPath(), directory]) {
7080
+ await chmod(path, PRIVATE_DIR_MODE).catch((error) => {
7081
+ if (path !== directory || !isErrno(error, "ENOENT")) throw error;
7082
+ });
7072
7083
  }
7073
7084
  while (true) {
7074
7085
  const token = randomUUID();
@@ -7091,7 +7102,9 @@ async function publishProfileLock(profile, lockPath, token) {
7091
7102
  await mkdir(lockPath, { mode: PRIVATE_DIR_MODE });
7092
7103
  } catch (error) {
7093
7104
  if (isErrno(error, "EEXIST")) return false;
7094
- throw error;
7105
+ if (!isErrno(error, "ENOENT")) throw error;
7106
+ await mkdir(profilePath(profile), { recursive: true, mode: PRIVATE_DIR_MODE });
7107
+ return false;
7095
7108
  }
7096
7109
  await waitForTestBarrier(TEST_LOCK_PUBLISH_BARRIER_DIR, profile);
7097
7110
  const staged = join(dirname(lockPath), `.lock-owner-${token}.tmp`);