@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.
package/dist/sql/index.js CHANGED
@@ -5,6 +5,10 @@ var ErrorCodes = {
5
5
  AUTH_EXPIRED: "AUTH_EXPIRED",
6
6
  AUTH_REQUIRED: "AUTH_REQUIRED",
7
7
  AUTH_UNAUTHORIZED: "AUTH_UNAUTHORIZED",
8
+ /** HTTP 401: the delegation the invocation cites was revoked. */
9
+ AUTH_DELEGATION_REVOKED: "AUTH_DELEGATION_REVOKED",
10
+ /** HTTP 401: a delegation the cited one rests on was revoked. */
11
+ AUTH_DELEGATION_ANCESTOR_REVOKED: "AUTH_DELEGATION_ANCESTOR_REVOKED",
8
12
  NETWORK_ERROR: "NETWORK_ERROR",
9
13
  TIMEOUT: "TIMEOUT",
10
14
  ABORTED: "ABORTED",
@@ -16,6 +20,10 @@ var ErrorCodes = {
16
20
  KV_PRECONDITION_FAILED: "KV_PRECONDITION_FAILED",
17
21
  KV_CONFLICT: "KV_CONFLICT",
18
22
  KV_RESPONSE_TOO_LARGE: "KV_RESPONSE_TOO_LARGE",
23
+ /** HTTP 410 from the change feed: discard sync state and restart without a cursor; `meta.reason` says why. */
24
+ KV_SYNC_RESET_REQUIRED: "KV_SYNC_RESET_REQUIRED",
25
+ /** HTTP 403 from the change feed: the retention grant was refused; `meta.reason` says why. */
26
+ KV_RETENTION_GRANT_REFUSED: "KV_RETENTION_GRANT_REFUSED",
19
27
  // SQL-specific errors
20
28
  SQL_ERROR: "SQL_ERROR",
21
29
  SQL_PERMISSION_DENIED: "SQL_PERMISSION_DENIED",
@@ -79,11 +87,31 @@ function authRequiredError(service) {
79
87
  service
80
88
  };
81
89
  }
82
- function timeoutError(service) {
90
+ var RequestTimeoutError = class extends Error {
91
+ constructor(timeoutMs) {
92
+ super(`Request timed out after ${timeoutMs}ms.`);
93
+ this.name = "TimeoutError";
94
+ this.timeoutMs = timeoutMs;
95
+ }
96
+ };
97
+ function isRequestTimeoutError(error) {
98
+ return error instanceof RequestTimeoutError || error instanceof Error && error.name === "TimeoutError" && typeof error.timeoutMs === "number";
99
+ }
100
+ function timeoutError(service, timeoutMs, cause) {
101
+ if (timeoutMs === void 0) {
102
+ return {
103
+ code: ErrorCodes.TIMEOUT,
104
+ message: "Request timed out.",
105
+ service,
106
+ ...cause === void 0 ? {} : { cause }
107
+ };
108
+ }
83
109
  return {
84
110
  code: ErrorCodes.TIMEOUT,
85
- message: "Request timed out.",
86
- service
111
+ message: `Request timed out after ${timeoutMs}ms.`,
112
+ service,
113
+ ...cause === void 0 ? {} : { cause },
114
+ meta: { timeoutMs }
87
115
  };
88
116
  }
89
117
  function abortedError(service) {
@@ -138,6 +166,9 @@ function storageRejectionError(service, status, meta, responseText) {
138
166
  }
139
167
  function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
140
168
  if (error instanceof Error) {
169
+ if (isRequestTimeoutError(error)) {
170
+ return timeoutError(service, error.timeoutMs, error);
171
+ }
141
172
  if (error.name === "AbortError") {
142
173
  return abortedError(service);
143
174
  }
@@ -159,6 +190,7 @@ function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
159
190
  }
160
191
 
161
192
  // src/base/BaseService.ts
193
+ var MAX_TIMER_DELAY_MS = 2147483647;
162
194
  var BaseService = class {
163
195
  constructor() {
164
196
  /**
@@ -339,6 +371,64 @@ var BaseService = class {
339
371
  }
340
372
  return controller.signal;
341
373
  }
374
+ /**
375
+ * Create the abort signal for one request, bounded by the request timeout.
376
+ *
377
+ * The signal aborts when the service signs out, the context aborts, the
378
+ * caller's `signal` aborts, or the timeout elapses, whichever comes first.
379
+ * A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which
380
+ * `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own
381
+ * reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).
382
+ *
383
+ * The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a
384
+ * positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and
385
+ * `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.
386
+ *
387
+ * Callers must call `dispose()` once the response body has been consumed,
388
+ * which clears the timer and removes the listeners this adds to the
389
+ * long-lived service and context signals.
390
+ *
391
+ * @param signal - The caller's abort signal for this request
392
+ * @param timeoutMs - Per-request timeout override in milliseconds
393
+ */
394
+ createRequestSignal(signal, timeoutMs) {
395
+ const controller = new AbortController();
396
+ const detachers = [];
397
+ let timer;
398
+ const dispose = () => {
399
+ if (timer !== void 0) {
400
+ clearTimeout(timer);
401
+ timer = void 0;
402
+ }
403
+ for (const detach of detachers.splice(0)) {
404
+ detach();
405
+ }
406
+ };
407
+ const abort = (reason) => {
408
+ dispose();
409
+ controller.abort(reason);
410
+ };
411
+ const requestSignal = { signal: controller.signal, dispose };
412
+ const parents = [
413
+ this.abortController.signal,
414
+ this.context?.abortSignal,
415
+ signal
416
+ ].filter((parent) => parent !== void 0);
417
+ for (const parent of parents) {
418
+ if (parent.aborted) {
419
+ abort(parent.reason);
420
+ return requestSignal;
421
+ }
422
+ const onAbort = () => abort(parent.reason);
423
+ parent.addEventListener("abort", onAbort, { once: true });
424
+ detachers.push(() => parent.removeEventListener("abort", onAbort));
425
+ }
426
+ const timeout = timeoutMs ?? this._config.timeout;
427
+ if (typeof timeout === "number" && timeout > 0 && timeout <= MAX_TIMER_DELAY_MS) {
428
+ timer = setTimeout(() => abort(new RequestTimeoutError(timeout)), timeout);
429
+ }
430
+ return requestSignal;
431
+ }
342
432
  /**
343
433
  * Wrap an operation with error handling and telemetry.
344
434
  *
@@ -527,6 +617,7 @@ var SQLService = class extends BaseService {
527
617
  if (!this.requireAuth()) {
528
618
  return err(authRequiredError("sql"));
529
619
  }
620
+ const request = this.createRequestSignal(options?.signal);
530
621
  try {
531
622
  const body = {
532
623
  action: "query",
@@ -540,7 +631,7 @@ var SQLService = class extends BaseService {
540
631
  dbName,
541
632
  action,
542
633
  body,
543
- options?.signal
634
+ request.signal
544
635
  );
545
636
  if (!response.ok) {
546
637
  return this.handleErrorResponse(response, "query", dbName, [action]);
@@ -549,6 +640,8 @@ var SQLService = class extends BaseService {
549
640
  return ok(data);
550
641
  } catch (error) {
551
642
  return err(wrapError("sql", error));
643
+ } finally {
644
+ request.dispose();
552
645
  }
553
646
  });
554
647
  }
@@ -557,6 +650,7 @@ var SQLService = class extends BaseService {
557
650
  if (!this.requireAuth()) {
558
651
  return err(authRequiredError("sql"));
559
652
  }
653
+ const request = this.createRequestSignal(options?.signal);
560
654
  try {
561
655
  const body = {
562
656
  action: "execute",
@@ -577,7 +671,7 @@ var SQLService = class extends BaseService {
577
671
  dbName,
578
672
  requestedActions,
579
673
  body,
580
- options?.signal
674
+ request.signal
581
675
  );
582
676
  if (!response.ok) {
583
677
  return this.handleErrorResponse(response, "execute", dbName, requestedActions);
@@ -586,6 +680,8 @@ var SQLService = class extends BaseService {
586
680
  return ok(data);
587
681
  } catch (error) {
588
682
  return err(wrapError("sql", error));
683
+ } finally {
684
+ request.dispose();
589
685
  }
590
686
  });
591
687
  }
@@ -594,13 +690,14 @@ var SQLService = class extends BaseService {
594
690
  if (!this.requireAuth()) {
595
691
  return err(authRequiredError("sql"));
596
692
  }
693
+ const request = this.createRequestSignal(options?.signal);
597
694
  try {
598
695
  const requestedActions = this.actionsForSqlBatch(statements);
599
696
  const response = await this.invokeSQL(
600
697
  dbName,
601
698
  requestedActions,
602
699
  { action: "batch", statements },
603
- options?.signal
700
+ request.signal
604
701
  );
605
702
  if (!response.ok) {
606
703
  return this.handleErrorResponse(response, "batch", dbName, requestedActions);
@@ -609,6 +706,8 @@ var SQLService = class extends BaseService {
609
706
  return ok(data);
610
707
  } catch (error) {
611
708
  return err(wrapError("sql", error));
709
+ } finally {
710
+ request.dispose();
612
711
  }
613
712
  });
614
713
  }
@@ -617,12 +716,13 @@ var SQLService = class extends BaseService {
617
716
  if (!this.requireAuth()) {
618
717
  return err(authRequiredError("sql"));
619
718
  }
719
+ const request = this.createRequestSignal(options?.signal);
620
720
  try {
621
721
  const response = await this.invokeSQL(
622
722
  dbName,
623
723
  SQLAction.WRITE,
624
724
  { action: "execute_statement", name, params: params ?? [] },
625
- options?.signal
725
+ request.signal
626
726
  );
627
727
  if (!response.ok) {
628
728
  return this.handleErrorResponse(response, "executeStatement", dbName, [SQLAction.WRITE]);
@@ -631,6 +731,8 @@ var SQLService = class extends BaseService {
631
731
  return ok(data);
632
732
  } catch (error) {
633
733
  return err(wrapError("sql", error));
734
+ } finally {
735
+ request.dispose();
634
736
  }
635
737
  });
636
738
  }
@@ -639,12 +741,13 @@ var SQLService = class extends BaseService {
639
741
  if (!this.requireAuth()) {
640
742
  return err(authRequiredError("sql"));
641
743
  }
744
+ const request = this.createRequestSignal(options?.signal);
642
745
  try {
643
746
  const response = await this.invokeSQL(
644
747
  dbName,
645
748
  SQLAction.READ,
646
749
  { action: "export" },
647
- options?.signal
750
+ request.signal
648
751
  );
649
752
  if (!response.ok) {
650
753
  return this.handleErrorResponse(response, "export", dbName, [SQLAction.READ]);
@@ -658,6 +761,8 @@ var SQLService = class extends BaseService {
658
761
  return ok(text);
659
762
  } catch (error) {
660
763
  return err(wrapError("sql", error));
764
+ } finally {
765
+ request.dispose();
661
766
  }
662
767
  });
663
768
  }
@@ -748,7 +853,7 @@ var SQLService = class extends BaseService {
748
853
  "Content-Type": "application/json"
749
854
  },
750
855
  body: JSON.stringify(body),
751
- signal: this.combineSignals(signal)
856
+ signal
752
857
  });
753
858
  }
754
859
  actionForSql(sql, fallback) {