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

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.
@@ -34,6 +34,10 @@ var ErrorCodes = {
34
34
  AUTH_EXPIRED: "AUTH_EXPIRED",
35
35
  AUTH_REQUIRED: "AUTH_REQUIRED",
36
36
  AUTH_UNAUTHORIZED: "AUTH_UNAUTHORIZED",
37
+ /** HTTP 401: the delegation the invocation cites was revoked. */
38
+ AUTH_DELEGATION_REVOKED: "AUTH_DELEGATION_REVOKED",
39
+ /** HTTP 401: a delegation the cited one rests on was revoked. */
40
+ AUTH_DELEGATION_ANCESTOR_REVOKED: "AUTH_DELEGATION_ANCESTOR_REVOKED",
37
41
  NETWORK_ERROR: "NETWORK_ERROR",
38
42
  TIMEOUT: "TIMEOUT",
39
43
  ABORTED: "ABORTED",
@@ -45,6 +49,10 @@ var ErrorCodes = {
45
49
  KV_PRECONDITION_FAILED: "KV_PRECONDITION_FAILED",
46
50
  KV_CONFLICT: "KV_CONFLICT",
47
51
  KV_RESPONSE_TOO_LARGE: "KV_RESPONSE_TOO_LARGE",
52
+ /** HTTP 410 from the change feed: discard sync state and restart without a cursor; `meta.reason` says why. */
53
+ KV_SYNC_RESET_REQUIRED: "KV_SYNC_RESET_REQUIRED",
54
+ /** HTTP 403 from the change feed: the retention grant was refused; `meta.reason` says why. */
55
+ KV_RETENTION_GRANT_REFUSED: "KV_RETENTION_GRANT_REFUSED",
48
56
  // SQL-specific errors
49
57
  SQL_ERROR: "SQL_ERROR",
50
58
  SQL_PERMISSION_DENIED: "SQL_PERMISSION_DENIED",
@@ -108,11 +116,31 @@ function authRequiredError(service) {
108
116
  service
109
117
  };
110
118
  }
111
- function timeoutError(service) {
119
+ var RequestTimeoutError = class extends Error {
120
+ constructor(timeoutMs) {
121
+ super(`Request timed out after ${timeoutMs}ms.`);
122
+ this.name = "TimeoutError";
123
+ this.timeoutMs = timeoutMs;
124
+ }
125
+ };
126
+ function isRequestTimeoutError(error) {
127
+ return error instanceof RequestTimeoutError || error instanceof Error && error.name === "TimeoutError" && typeof error.timeoutMs === "number";
128
+ }
129
+ function timeoutError(service, timeoutMs, cause) {
130
+ if (timeoutMs === void 0) {
131
+ return {
132
+ code: ErrorCodes.TIMEOUT,
133
+ message: "Request timed out.",
134
+ service,
135
+ ...cause === void 0 ? {} : { cause }
136
+ };
137
+ }
112
138
  return {
113
139
  code: ErrorCodes.TIMEOUT,
114
- message: "Request timed out.",
115
- service
140
+ message: `Request timed out after ${timeoutMs}ms.`,
141
+ service,
142
+ ...cause === void 0 ? {} : { cause },
143
+ meta: { timeoutMs }
116
144
  };
117
145
  }
118
146
  function abortedError(service) {
@@ -167,6 +195,9 @@ function storageRejectionError(service, status, meta, responseText) {
167
195
  }
168
196
  function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
169
197
  if (error instanceof Error) {
198
+ if (isRequestTimeoutError(error)) {
199
+ return timeoutError(service, error.timeoutMs, error);
200
+ }
170
201
  if (error.name === "AbortError") {
171
202
  return abortedError(service);
172
203
  }
@@ -188,6 +219,7 @@ function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
188
219
  }
189
220
 
190
221
  // src/base/BaseService.ts
222
+ var MAX_TIMER_DELAY_MS = 2147483647;
191
223
  var BaseService = class {
192
224
  constructor() {
193
225
  /**
@@ -368,6 +400,64 @@ var BaseService = class {
368
400
  }
369
401
  return controller.signal;
370
402
  }
403
+ /**
404
+ * Create the abort signal for one request, bounded by the request timeout.
405
+ *
406
+ * The signal aborts when the service signs out, the context aborts, the
407
+ * caller's `signal` aborts, or the timeout elapses, whichever comes first.
408
+ * A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which
409
+ * `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own
410
+ * reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).
411
+ *
412
+ * The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a
413
+ * positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and
414
+ * `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.
415
+ *
416
+ * Callers must call `dispose()` once the response body has been consumed,
417
+ * which clears the timer and removes the listeners this adds to the
418
+ * long-lived service and context signals.
419
+ *
420
+ * @param signal - The caller's abort signal for this request
421
+ * @param timeoutMs - Per-request timeout override in milliseconds
422
+ */
423
+ createRequestSignal(signal, timeoutMs) {
424
+ const controller = new AbortController();
425
+ const detachers = [];
426
+ let timer;
427
+ const dispose = () => {
428
+ if (timer !== void 0) {
429
+ clearTimeout(timer);
430
+ timer = void 0;
431
+ }
432
+ for (const detach of detachers.splice(0)) {
433
+ detach();
434
+ }
435
+ };
436
+ const abort = (reason) => {
437
+ dispose();
438
+ controller.abort(reason);
439
+ };
440
+ const requestSignal = { signal: controller.signal, dispose };
441
+ const parents = [
442
+ this.abortController.signal,
443
+ this.context?.abortSignal,
444
+ signal
445
+ ].filter((parent) => parent !== void 0);
446
+ for (const parent of parents) {
447
+ if (parent.aborted) {
448
+ abort(parent.reason);
449
+ return requestSignal;
450
+ }
451
+ const onAbort = () => abort(parent.reason);
452
+ parent.addEventListener("abort", onAbort, { once: true });
453
+ detachers.push(() => parent.removeEventListener("abort", onAbort));
454
+ }
455
+ const timeout = timeoutMs ?? this._config.timeout;
456
+ if (typeof timeout === "number" && timeout > 0 && timeout <= MAX_TIMER_DELAY_MS) {
457
+ timer = setTimeout(() => abort(new RequestTimeoutError(timeout)), timeout);
458
+ }
459
+ return requestSignal;
460
+ }
371
461
  /**
372
462
  * Wrap an operation with error handling and telemetry.
373
463
  *
@@ -556,6 +646,7 @@ var SQLService = class extends BaseService {
556
646
  if (!this.requireAuth()) {
557
647
  return err(authRequiredError("sql"));
558
648
  }
649
+ const request = this.createRequestSignal(options?.signal);
559
650
  try {
560
651
  const body = {
561
652
  action: "query",
@@ -569,7 +660,7 @@ var SQLService = class extends BaseService {
569
660
  dbName,
570
661
  action,
571
662
  body,
572
- options?.signal
663
+ request.signal
573
664
  );
574
665
  if (!response.ok) {
575
666
  return this.handleErrorResponse(response, "query", dbName, [action]);
@@ -578,6 +669,8 @@ var SQLService = class extends BaseService {
578
669
  return ok(data);
579
670
  } catch (error) {
580
671
  return err(wrapError("sql", error));
672
+ } finally {
673
+ request.dispose();
581
674
  }
582
675
  });
583
676
  }
@@ -586,6 +679,7 @@ var SQLService = class extends BaseService {
586
679
  if (!this.requireAuth()) {
587
680
  return err(authRequiredError("sql"));
588
681
  }
682
+ const request = this.createRequestSignal(options?.signal);
589
683
  try {
590
684
  const body = {
591
685
  action: "execute",
@@ -606,7 +700,7 @@ var SQLService = class extends BaseService {
606
700
  dbName,
607
701
  requestedActions,
608
702
  body,
609
- options?.signal
703
+ request.signal
610
704
  );
611
705
  if (!response.ok) {
612
706
  return this.handleErrorResponse(response, "execute", dbName, requestedActions);
@@ -615,6 +709,8 @@ var SQLService = class extends BaseService {
615
709
  return ok(data);
616
710
  } catch (error) {
617
711
  return err(wrapError("sql", error));
712
+ } finally {
713
+ request.dispose();
618
714
  }
619
715
  });
620
716
  }
@@ -623,13 +719,14 @@ var SQLService = class extends BaseService {
623
719
  if (!this.requireAuth()) {
624
720
  return err(authRequiredError("sql"));
625
721
  }
722
+ const request = this.createRequestSignal(options?.signal);
626
723
  try {
627
724
  const requestedActions = this.actionsForSqlBatch(statements);
628
725
  const response = await this.invokeSQL(
629
726
  dbName,
630
727
  requestedActions,
631
728
  { action: "batch", statements },
632
- options?.signal
729
+ request.signal
633
730
  );
634
731
  if (!response.ok) {
635
732
  return this.handleErrorResponse(response, "batch", dbName, requestedActions);
@@ -638,6 +735,8 @@ var SQLService = class extends BaseService {
638
735
  return ok(data);
639
736
  } catch (error) {
640
737
  return err(wrapError("sql", error));
738
+ } finally {
739
+ request.dispose();
641
740
  }
642
741
  });
643
742
  }
@@ -646,12 +745,13 @@ var SQLService = class extends BaseService {
646
745
  if (!this.requireAuth()) {
647
746
  return err(authRequiredError("sql"));
648
747
  }
748
+ const request = this.createRequestSignal(options?.signal);
649
749
  try {
650
750
  const response = await this.invokeSQL(
651
751
  dbName,
652
752
  SQLAction.WRITE,
653
753
  { action: "execute_statement", name, params: params ?? [] },
654
- options?.signal
754
+ request.signal
655
755
  );
656
756
  if (!response.ok) {
657
757
  return this.handleErrorResponse(response, "executeStatement", dbName, [SQLAction.WRITE]);
@@ -660,6 +760,8 @@ var SQLService = class extends BaseService {
660
760
  return ok(data);
661
761
  } catch (error) {
662
762
  return err(wrapError("sql", error));
763
+ } finally {
764
+ request.dispose();
663
765
  }
664
766
  });
665
767
  }
@@ -668,12 +770,13 @@ var SQLService = class extends BaseService {
668
770
  if (!this.requireAuth()) {
669
771
  return err(authRequiredError("sql"));
670
772
  }
773
+ const request = this.createRequestSignal(options?.signal);
671
774
  try {
672
775
  const response = await this.invokeSQL(
673
776
  dbName,
674
777
  SQLAction.READ,
675
778
  { action: "export" },
676
- options?.signal
779
+ request.signal
677
780
  );
678
781
  if (!response.ok) {
679
782
  return this.handleErrorResponse(response, "export", dbName, [SQLAction.READ]);
@@ -687,6 +790,8 @@ var SQLService = class extends BaseService {
687
790
  return ok(text);
688
791
  } catch (error) {
689
792
  return err(wrapError("sql", error));
793
+ } finally {
794
+ request.dispose();
690
795
  }
691
796
  });
692
797
  }
@@ -777,7 +882,7 @@ var SQLService = class extends BaseService {
777
882
  "Content-Type": "application/json"
778
883
  },
779
884
  body: JSON.stringify(body),
780
- signal: this.combineSignals(signal)
885
+ signal
781
886
  });
782
887
  }
783
888
  actionForSql(sql, fallback) {