@tinycloud/sdk-services 3.1.0-beta.6 → 3.1.0-beta.7

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.
@@ -1,4 +1,4 @@
1
- import { B as BaseService } from '../BaseService-BhpVpA9b.cjs';
1
+ import { B as BaseService } from '../BaseService-C8zRy0ww.cjs';
2
2
  import { R as Result, I as IService } from '../types-qwMmB7qB.cjs';
3
3
 
4
4
  /**
@@ -23,8 +23,11 @@ interface KVServiceConfig {
23
23
  */
24
24
  prefix?: string;
25
25
  /**
26
- * Default timeout in milliseconds for KV operations.
27
- * Overrides the context-level timeout if set.
26
+ * Default timeout in milliseconds for each KV request, including reading
27
+ * the response body. When it elapses the request is aborted and the
28
+ * operation returns an `ErrorCodes.TIMEOUT` error. A per-call `timeout`
29
+ * option overrides it. Unset (the default), `0`, or `Infinity` means no
30
+ * timeout.
28
31
  */
29
32
  timeout?: number;
30
33
  /** Allow additional config properties */
@@ -55,7 +58,8 @@ interface KVGetOptions {
55
58
  */
56
59
  maxResponseBytes?: number;
57
60
  /**
58
- * Custom timeout for this operation in milliseconds.
61
+ * Custom timeout for this operation in milliseconds. Overrides
62
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
59
63
  */
60
64
  timeout?: number;
61
65
  /**
@@ -85,7 +89,8 @@ interface KVPutOptions {
85
89
  /** Only write when the key does not exist. */
86
90
  ifNoneMatch?: "*";
87
91
  /**
88
- * Custom timeout for this operation in milliseconds.
92
+ * Custom timeout for this operation in milliseconds. Overrides
93
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
89
94
  */
90
95
  timeout?: number;
91
96
  /**
@@ -123,7 +128,8 @@ interface KVBatchPutOptions {
123
128
  */
124
129
  prefix?: string;
125
130
  /**
126
- * Custom timeout for this operation in milliseconds.
131
+ * Custom timeout for this operation in milliseconds. Overrides
132
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
127
133
  */
128
134
  timeout?: number;
129
135
  /**
@@ -182,7 +188,8 @@ interface KVListOptions {
182
188
  /** Opaque scope-bound cursor returned by a previous bounded list. */
183
189
  cursor?: string;
184
190
  /**
185
- * Custom timeout for this operation in milliseconds.
191
+ * Custom timeout for this operation in milliseconds. Overrides
192
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
186
193
  */
187
194
  timeout?: number;
188
195
  /**
@@ -201,7 +208,8 @@ interface KVDeleteOptions {
201
208
  /** Only delete when the current object has this strong ETag. */
202
209
  ifMatch?: string;
203
210
  /**
204
- * Custom timeout for this operation in milliseconds.
211
+ * Custom timeout for this operation in milliseconds. Overrides
212
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
205
213
  */
206
214
  timeout?: number;
207
215
  /**
@@ -218,7 +226,8 @@ interface KVHeadOptions {
218
226
  */
219
227
  prefix?: string;
220
228
  /**
221
- * Custom timeout for this operation in milliseconds.
229
+ * Custom timeout for this operation in milliseconds. Overrides
230
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
222
231
  */
223
232
  timeout?: number;
224
233
  /**
@@ -259,7 +268,8 @@ interface KVCreateSignedReadUrlOptions {
259
268
  */
260
269
  etag?: string;
261
270
  /**
262
- * Custom timeout for this operation in milliseconds.
271
+ * Custom timeout for this operation in milliseconds. Overrides
272
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
263
273
  */
264
274
  timeout?: number;
265
275
  /**
@@ -882,7 +892,7 @@ declare class KVService extends BaseService implements IKVService {
882
892
  * @param path - Resource path
883
893
  * @param action - KV action
884
894
  * @param body - Optional request body
885
- * @param signal - Optional abort signal
895
+ * @param signal - Request signal from createRequestSignal()
886
896
  * @returns Fetch response
887
897
  */
888
898
  private invokeOperation;
@@ -1,4 +1,4 @@
1
- import { B as BaseService } from '../BaseService-CuV-ZVPs.js';
1
+ import { B as BaseService } from '../BaseService-DU-lE-Qx.js';
2
2
  import { R as Result, I as IService } from '../types-qwMmB7qB.js';
3
3
 
4
4
  /**
@@ -23,8 +23,11 @@ interface KVServiceConfig {
23
23
  */
24
24
  prefix?: string;
25
25
  /**
26
- * Default timeout in milliseconds for KV operations.
27
- * Overrides the context-level timeout if set.
26
+ * Default timeout in milliseconds for each KV request, including reading
27
+ * the response body. When it elapses the request is aborted and the
28
+ * operation returns an `ErrorCodes.TIMEOUT` error. A per-call `timeout`
29
+ * option overrides it. Unset (the default), `0`, or `Infinity` means no
30
+ * timeout.
28
31
  */
29
32
  timeout?: number;
30
33
  /** Allow additional config properties */
@@ -55,7 +58,8 @@ interface KVGetOptions {
55
58
  */
56
59
  maxResponseBytes?: number;
57
60
  /**
58
- * Custom timeout for this operation in milliseconds.
61
+ * Custom timeout for this operation in milliseconds. Overrides
62
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
59
63
  */
60
64
  timeout?: number;
61
65
  /**
@@ -85,7 +89,8 @@ interface KVPutOptions {
85
89
  /** Only write when the key does not exist. */
86
90
  ifNoneMatch?: "*";
87
91
  /**
88
- * Custom timeout for this operation in milliseconds.
92
+ * Custom timeout for this operation in milliseconds. Overrides
93
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
89
94
  */
90
95
  timeout?: number;
91
96
  /**
@@ -123,7 +128,8 @@ interface KVBatchPutOptions {
123
128
  */
124
129
  prefix?: string;
125
130
  /**
126
- * Custom timeout for this operation in milliseconds.
131
+ * Custom timeout for this operation in milliseconds. Overrides
132
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
127
133
  */
128
134
  timeout?: number;
129
135
  /**
@@ -182,7 +188,8 @@ interface KVListOptions {
182
188
  /** Opaque scope-bound cursor returned by a previous bounded list. */
183
189
  cursor?: string;
184
190
  /**
185
- * Custom timeout for this operation in milliseconds.
191
+ * Custom timeout for this operation in milliseconds. Overrides
192
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
186
193
  */
187
194
  timeout?: number;
188
195
  /**
@@ -201,7 +208,8 @@ interface KVDeleteOptions {
201
208
  /** Only delete when the current object has this strong ETag. */
202
209
  ifMatch?: string;
203
210
  /**
204
- * Custom timeout for this operation in milliseconds.
211
+ * Custom timeout for this operation in milliseconds. Overrides
212
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
205
213
  */
206
214
  timeout?: number;
207
215
  /**
@@ -218,7 +226,8 @@ interface KVHeadOptions {
218
226
  */
219
227
  prefix?: string;
220
228
  /**
221
- * Custom timeout for this operation in milliseconds.
229
+ * Custom timeout for this operation in milliseconds. Overrides
230
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
222
231
  */
223
232
  timeout?: number;
224
233
  /**
@@ -259,7 +268,8 @@ interface KVCreateSignedReadUrlOptions {
259
268
  */
260
269
  etag?: string;
261
270
  /**
262
- * Custom timeout for this operation in milliseconds.
271
+ * Custom timeout for this operation in milliseconds. Overrides
272
+ * `KVServiceConfig.timeout`; `0` disables a configured timeout.
263
273
  */
264
274
  timeout?: number;
265
275
  /**
@@ -882,7 +892,7 @@ declare class KVService extends BaseService implements IKVService {
882
892
  * @param path - Resource path
883
893
  * @param action - KV action
884
894
  * @param body - Optional request body
885
- * @param signal - Optional abort signal
895
+ * @param signal - Request signal from createRequestSignal()
886
896
  * @returns Fetch response
887
897
  */
888
898
  private invokeOperation;
package/dist/kv/index.js CHANGED
@@ -163,11 +163,31 @@ function authRequiredError(service) {
163
163
  service
164
164
  };
165
165
  }
166
- function timeoutError(service) {
166
+ var RequestTimeoutError = class extends Error {
167
+ constructor(timeoutMs) {
168
+ super(`Request timed out after ${timeoutMs}ms.`);
169
+ this.name = "TimeoutError";
170
+ this.timeoutMs = timeoutMs;
171
+ }
172
+ };
173
+ function isRequestTimeoutError(error) {
174
+ return error instanceof RequestTimeoutError || error instanceof Error && error.name === "TimeoutError" && typeof error.timeoutMs === "number";
175
+ }
176
+ function timeoutError(service, timeoutMs, cause) {
177
+ if (timeoutMs === void 0) {
178
+ return {
179
+ code: ErrorCodes.TIMEOUT,
180
+ message: "Request timed out.",
181
+ service,
182
+ ...cause === void 0 ? {} : { cause }
183
+ };
184
+ }
167
185
  return {
168
186
  code: ErrorCodes.TIMEOUT,
169
- message: "Request timed out.",
170
- service
187
+ message: `Request timed out after ${timeoutMs}ms.`,
188
+ service,
189
+ ...cause === void 0 ? {} : { cause },
190
+ meta: { timeoutMs }
171
191
  };
172
192
  }
173
193
  function abortedError(service) {
@@ -314,6 +334,9 @@ function storageRejectionError(service, status, meta, responseText) {
314
334
  }
315
335
  function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
316
336
  if (error instanceof Error) {
337
+ if (isRequestTimeoutError(error)) {
338
+ return timeoutError(service, error.timeoutMs, error);
339
+ }
317
340
  if (error.name === "AbortError") {
318
341
  return abortedError(service);
319
342
  }
@@ -335,6 +358,7 @@ function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
335
358
  }
336
359
 
337
360
  // src/base/BaseService.ts
361
+ var MAX_TIMER_DELAY_MS = 2147483647;
338
362
  var BaseService = class {
339
363
  constructor() {
340
364
  /**
@@ -515,6 +539,64 @@ var BaseService = class {
515
539
  }
516
540
  return controller.signal;
517
541
  }
542
+ /**
543
+ * Create the abort signal for one request, bounded by the request timeout.
544
+ *
545
+ * The signal aborts when the service signs out, the context aborts, the
546
+ * caller's `signal` aborts, or the timeout elapses, whichever comes first.
547
+ * A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which
548
+ * `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own
549
+ * reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).
550
+ *
551
+ * The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a
552
+ * positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and
553
+ * `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.
554
+ *
555
+ * Callers must call `dispose()` once the response body has been consumed,
556
+ * which clears the timer and removes the listeners this adds to the
557
+ * long-lived service and context signals.
558
+ *
559
+ * @param signal - The caller's abort signal for this request
560
+ * @param timeoutMs - Per-request timeout override in milliseconds
561
+ */
562
+ createRequestSignal(signal, timeoutMs) {
563
+ const controller = new AbortController();
564
+ const detachers = [];
565
+ let timer;
566
+ const dispose = () => {
567
+ if (timer !== void 0) {
568
+ clearTimeout(timer);
569
+ timer = void 0;
570
+ }
571
+ for (const detach of detachers.splice(0)) {
572
+ detach();
573
+ }
574
+ };
575
+ const abort = (reason) => {
576
+ dispose();
577
+ controller.abort(reason);
578
+ };
579
+ const requestSignal = { signal: controller.signal, dispose };
580
+ const parents = [
581
+ this.abortController.signal,
582
+ this.context?.abortSignal,
583
+ signal
584
+ ].filter((parent) => parent !== void 0);
585
+ for (const parent of parents) {
586
+ if (parent.aborted) {
587
+ abort(parent.reason);
588
+ return requestSignal;
589
+ }
590
+ const onAbort = () => abort(parent.reason);
591
+ parent.addEventListener("abort", onAbort, { once: true });
592
+ detachers.push(() => parent.removeEventListener("abort", onAbort));
593
+ }
594
+ const timeout = timeoutMs ?? this._config.timeout;
595
+ if (typeof timeout === "number" && timeout > 0 && timeout <= MAX_TIMER_DELAY_MS) {
596
+ timer = setTimeout(() => abort(new RequestTimeoutError(timeout)), timeout);
597
+ }
598
+ return requestSignal;
599
+ }
518
600
  /**
519
601
  * Wrap an operation with error handling and telemetry.
520
602
  *
@@ -775,7 +857,7 @@ var KVService = class extends BaseService {
775
857
  * @param path - Resource path
776
858
  * @param action - KV action
777
859
  * @param body - Optional request body
778
- * @param signal - Optional abort signal
860
+ * @param signal - Request signal from createRequestSignal()
779
861
  * @returns Fetch response
780
862
  */
781
863
  async invokeOperation(path, action, body, signal, extraHeaders) {
@@ -791,7 +873,7 @@ var KVService = class extends BaseService {
791
873
  method: "POST",
792
874
  headers: requestHeaders,
793
875
  body,
794
- signal: this.combineSignals(signal)
876
+ signal
795
877
  });
796
878
  }
797
879
  /**
@@ -968,6 +1050,7 @@ var KVService = class extends BaseService {
968
1050
  "kv"
969
1051
  ));
970
1052
  }
1053
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
971
1054
  try {
972
1055
  const session = this.context.session;
973
1056
  const invocationHeaders = this.context.invokeAny(
@@ -989,7 +1072,7 @@ var KVService = class extends BaseService {
989
1072
  const response = await this.context.fetch(`${this.host}/invoke`, {
990
1073
  method: "POST",
991
1074
  headers,
992
- signal: this.combineSignals(options?.signal)
1075
+ signal: request.signal
993
1076
  });
994
1077
  if (!response.ok) {
995
1078
  const errorText = response.status === 401 || response.status === 403 ? await this.readAuthorizationText(response) : await response.text();
@@ -1054,6 +1137,8 @@ var KVService = class extends BaseService {
1054
1137
  return ok({ results, count: results.length });
1055
1138
  } catch (error) {
1056
1139
  return err(wrapError("kv", error));
1140
+ } finally {
1141
+ request.dispose();
1057
1142
  }
1058
1143
  }
1059
1144
  /**
@@ -1186,12 +1271,13 @@ var KVService = class extends BaseService {
1186
1271
  ));
1187
1272
  }
1188
1273
  const path = this.getFullPath(key, options?.prefix);
1274
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
1189
1275
  try {
1190
1276
  const response = await this.invokeOperation(
1191
1277
  path,
1192
1278
  KVAction.GET,
1193
1279
  void 0,
1194
- options?.signal,
1280
+ request.signal,
1195
1281
  options?.maxResponseBytes === void 0 ? void 0 : { "x-tinycloud-max-response-bytes": String(options.maxResponseBytes) }
1196
1282
  );
1197
1283
  if (!response.ok) {
@@ -1248,6 +1334,8 @@ var KVService = class extends BaseService {
1248
1334
  });
1249
1335
  } catch (error) {
1250
1336
  return err(wrapError("kv", error));
1337
+ } finally {
1338
+ request.dispose();
1251
1339
  }
1252
1340
  });
1253
1341
  }
@@ -1275,12 +1363,13 @@ var KVService = class extends BaseService {
1275
1363
  }
1276
1364
  const path = this.getFullPath(key, options?.prefix);
1277
1365
  const body = this.serializePutValue(value, options?.contentType);
1366
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
1278
1367
  try {
1279
1368
  const response = await this.invokeOperation(
1280
1369
  path,
1281
1370
  KVAction.PUT,
1282
1371
  body,
1283
- options?.signal,
1372
+ request.signal,
1284
1373
  {
1285
1374
  ...options?.ifMatch === void 0 ? {} : { "if-match": options.ifMatch },
1286
1375
  ...options?.ifNoneMatch === void 0 ? {} : { "if-none-match": options.ifNoneMatch }
@@ -1336,6 +1425,8 @@ var KVService = class extends BaseService {
1336
1425
  });
1337
1426
  } catch (error) {
1338
1427
  return err(wrapError("kv", error));
1428
+ } finally {
1429
+ request.dispose();
1339
1430
  }
1340
1431
  });
1341
1432
  }
@@ -1375,6 +1466,7 @@ var KVService = class extends BaseService {
1375
1466
  seen.add(path);
1376
1467
  }
1377
1468
  let requestMayHaveDispatched = false;
1469
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
1378
1470
  try {
1379
1471
  const body = new FormData();
1380
1472
  for (let index = 0; index < items.length; index++) {
@@ -1397,7 +1489,7 @@ var KVService = class extends BaseService {
1397
1489
  method: "POST",
1398
1490
  headers,
1399
1491
  body,
1400
- signal: this.combineSignals(options?.signal)
1492
+ signal: request.signal
1401
1493
  };
1402
1494
  const fetchFn = this.context.fetch;
1403
1495
  requestMayHaveDispatched = true;
@@ -1448,14 +1540,16 @@ var KVService = class extends BaseService {
1448
1540
  rawBody = await response.json();
1449
1541
  } catch (jsonError) {
1450
1542
  const cause = jsonError instanceof Error ? jsonError : new Error(String(jsonError));
1543
+ const timedOut = isRequestTimeoutError(cause);
1451
1544
  return err(
1452
1545
  serviceError(
1453
- ErrorCodes.NETWORK_ERROR,
1454
- `KV batchPut response was not valid JSON: ${cause.message}`,
1546
+ timedOut ? ErrorCodes.TIMEOUT : ErrorCodes.NETWORK_ERROR,
1547
+ timedOut ? `KV batchPut response body did not arrive in time: ${cause.message}` : `KV batchPut response was not valid JSON: ${cause.message}`,
1455
1548
  "kv",
1456
1549
  {
1457
1550
  cause,
1458
1551
  meta: {
1552
+ ...timedOut ? { timeoutMs: cause.timeoutMs } : {},
1459
1553
  requestMayHaveDispatched: true,
1460
1554
  responseReceived: true,
1461
1555
  status: response.status,
@@ -1510,6 +1604,8 @@ var KVService = class extends BaseService {
1510
1604
  ...wrapped,
1511
1605
  meta: { ...wrapped.meta, requestMayHaveDispatched }
1512
1606
  });
1607
+ } finally {
1608
+ request.dispose();
1513
1609
  }
1514
1610
  });
1515
1611
  }
@@ -1532,12 +1628,13 @@ var KVService = class extends BaseService {
1532
1628
  if (options?.path) {
1533
1629
  listPath = listPath ? `${listPath}/${options.path}` : options.path;
1534
1630
  }
1631
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
1535
1632
  try {
1536
1633
  const response = await this.invokeOperation(
1537
1634
  listPath,
1538
1635
  KVAction.LIST,
1539
1636
  void 0,
1540
- options?.signal,
1637
+ request.signal,
1541
1638
  options?.limit === void 0 && options?.cursor === void 0 ? void 0 : {
1542
1639
  ...options?.limit === void 0 ? {} : { "x-tinycloud-limit": String(options.limit) },
1543
1640
  ...options?.cursor === void 0 ? {} : { "x-tinycloud-cursor": options.cursor }
@@ -1578,6 +1675,8 @@ var KVService = class extends BaseService {
1578
1675
  });
1579
1676
  } catch (error) {
1580
1677
  return err(wrapError("kv", error));
1678
+ } finally {
1679
+ request.dispose();
1581
1680
  }
1582
1681
  });
1583
1682
  }
@@ -1590,12 +1689,13 @@ var KVService = class extends BaseService {
1590
1689
  return err(authRequiredError("kv"));
1591
1690
  }
1592
1691
  const path = this.getFullPath(key, options?.prefix);
1692
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
1593
1693
  try {
1594
1694
  const response = await this.invokeOperation(
1595
1695
  path,
1596
1696
  KVAction.DELETE,
1597
1697
  void 0,
1598
- options?.signal,
1698
+ request.signal,
1599
1699
  options?.ifMatch === void 0 ? void 0 : { "if-match": options.ifMatch }
1600
1700
  );
1601
1701
  if (!response.ok) {
@@ -1643,6 +1743,8 @@ var KVService = class extends BaseService {
1643
1743
  });
1644
1744
  } catch (error) {
1645
1745
  return err(wrapError("kv", error));
1746
+ } finally {
1747
+ request.dispose();
1646
1748
  }
1647
1749
  });
1648
1750
  }
@@ -1655,12 +1757,13 @@ var KVService = class extends BaseService {
1655
1757
  return err(authRequiredError("kv"));
1656
1758
  }
1657
1759
  const path = this.getFullPath(key, options?.prefix);
1760
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
1658
1761
  try {
1659
1762
  const response = await this.invokeOperation(
1660
1763
  path,
1661
1764
  KVAction.HEAD,
1662
1765
  void 0,
1663
- options?.signal
1766
+ request.signal
1664
1767
  );
1665
1768
  if (!response.ok) {
1666
1769
  if (response.status === 401 || response.status === 403) {
@@ -1691,6 +1794,8 @@ var KVService = class extends BaseService {
1691
1794
  });
1692
1795
  } catch (error) {
1693
1796
  return err(wrapError("kv", error));
1797
+ } finally {
1798
+ request.dispose();
1694
1799
  }
1695
1800
  });
1696
1801
  }
@@ -1728,12 +1833,13 @@ var KVService = class extends BaseService {
1728
1833
  if (options?.etag !== void 0) {
1729
1834
  body.etag = options.etag;
1730
1835
  }
1836
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
1731
1837
  try {
1732
1838
  const response = await this.context.fetch(`${this.host}/signed/kv`, {
1733
1839
  method: "POST",
1734
1840
  headers: this.withJsonContentType(headers),
1735
1841
  body: JSON.stringify(body),
1736
- signal: this.combineSignals(options?.signal)
1842
+ signal: request.signal
1737
1843
  });
1738
1844
  if (!response.ok) {
1739
1845
  return this.createSignedReadUrlError(response, key, path);
@@ -1753,6 +1859,8 @@ var KVService = class extends BaseService {
1753
1859
  return ok(signedUrl);
1754
1860
  } catch (error) {
1755
1861
  return err(wrapError("kv", error));
1862
+ } finally {
1863
+ request.dispose();
1756
1864
  }
1757
1865
  });
1758
1866
  }