@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.
@@ -1,4 +1,55 @@
1
- import { I as IService, a as IServiceContext, S as ServiceSession, b as ServiceError, R as Result } from './types-qwMmB7qB.cjs';
1
+ import { I as IService, a as IServiceContext, S as ServiceSession, b as ServiceError, R as Result } from './types-CVwwxqXF.js';
2
+
3
+ /**
4
+ * Base Service Types
5
+ *
6
+ * Types specific to the base service infrastructure.
7
+ */
8
+
9
+ /**
10
+ * Service constructor type for registration.
11
+ * Used by the SDK to instantiate services.
12
+ */
13
+ interface ServiceConstructor<TConfig = Record<string, unknown>, TService extends IService = IService> {
14
+ /** Service identifier used for registration */
15
+ readonly serviceName: string;
16
+ /** Create a new instance of the service */
17
+ new (config?: TConfig): TService;
18
+ }
19
+ /**
20
+ * Service registration entry.
21
+ */
22
+ interface ServiceRegistration {
23
+ /** The service class constructor */
24
+ constructor: ServiceConstructor;
25
+ /** Configuration for this service instance */
26
+ config?: Record<string, unknown>;
27
+ }
28
+ /**
29
+ * Options for base service operations.
30
+ */
31
+ interface BaseServiceOptions {
32
+ /** Override the default timeout for this operation */
33
+ timeout?: number;
34
+ /** Custom abort signal for this operation */
35
+ signal?: AbortSignal;
36
+ }
37
+ /**
38
+ * A per-request abort signal and the cleanup that releases it.
39
+ * Created by `BaseService.createRequestSignal()`.
40
+ */
41
+ interface RequestSignal {
42
+ /**
43
+ * Aborts on service sign-out, context abort, the caller's signal, or when
44
+ * the request timeout elapses (reason: a `TimeoutError`).
45
+ */
46
+ readonly signal: AbortSignal;
47
+ /**
48
+ * Clear the timeout and detach from the parent signals. Call once the
49
+ * response has been consumed. Idempotent.
50
+ */
51
+ dispose(): void;
52
+ }
2
53
 
3
54
  /**
4
55
  * BaseService - Abstract base class for all TinyCloud services.
@@ -144,6 +195,27 @@ declare abstract class BaseService implements IService {
144
195
  * @returns A combined abort signal
145
196
  */
146
197
  protected combineSignals(...signals: (AbortSignal | undefined)[]): AbortSignal;
198
+ /**
199
+ * Create the abort signal for one request, bounded by the request timeout.
200
+ *
201
+ * The signal aborts when the service signs out, the context aborts, the
202
+ * caller's `signal` aborts, or the timeout elapses, whichever comes first.
203
+ * A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which
204
+ * `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own
205
+ * reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).
206
+ *
207
+ * The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a
208
+ * positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and
209
+ * `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.
210
+ *
211
+ * Callers must call `dispose()` once the response body has been consumed,
212
+ * which clears the timer and removes the listeners this adds to the
213
+ * long-lived service and context signals.
214
+ *
215
+ * @param signal - The caller's abort signal for this request
216
+ * @param timeoutMs - Per-request timeout override in milliseconds
217
+ */
218
+ protected createRequestSignal(signal?: AbortSignal, timeoutMs?: number): RequestSignal;
147
219
  /**
148
220
  * Wrap an operation with error handling and telemetry.
149
221
  *
@@ -155,4 +227,4 @@ declare abstract class BaseService implements IService {
155
227
  protected withTelemetry<T>(action: string, key: string | undefined, operation: () => Promise<Result<T>>): Promise<Result<T>>;
156
228
  }
157
229
 
158
- export { BaseService as B };
230
+ export { BaseService as B, type RequestSignal as R, type ServiceConstructor as S, type BaseServiceOptions as a, type ServiceRegistration as b };
@@ -1,4 +1,55 @@
1
- import { I as IService, a as IServiceContext, S as ServiceSession, b as ServiceError, R as Result } from './types-qwMmB7qB.js';
1
+ import { I as IService, a as IServiceContext, S as ServiceSession, b as ServiceError, R as Result } from './types-CVwwxqXF.cjs';
2
+
3
+ /**
4
+ * Base Service Types
5
+ *
6
+ * Types specific to the base service infrastructure.
7
+ */
8
+
9
+ /**
10
+ * Service constructor type for registration.
11
+ * Used by the SDK to instantiate services.
12
+ */
13
+ interface ServiceConstructor<TConfig = Record<string, unknown>, TService extends IService = IService> {
14
+ /** Service identifier used for registration */
15
+ readonly serviceName: string;
16
+ /** Create a new instance of the service */
17
+ new (config?: TConfig): TService;
18
+ }
19
+ /**
20
+ * Service registration entry.
21
+ */
22
+ interface ServiceRegistration {
23
+ /** The service class constructor */
24
+ constructor: ServiceConstructor;
25
+ /** Configuration for this service instance */
26
+ config?: Record<string, unknown>;
27
+ }
28
+ /**
29
+ * Options for base service operations.
30
+ */
31
+ interface BaseServiceOptions {
32
+ /** Override the default timeout for this operation */
33
+ timeout?: number;
34
+ /** Custom abort signal for this operation */
35
+ signal?: AbortSignal;
36
+ }
37
+ /**
38
+ * A per-request abort signal and the cleanup that releases it.
39
+ * Created by `BaseService.createRequestSignal()`.
40
+ */
41
+ interface RequestSignal {
42
+ /**
43
+ * Aborts on service sign-out, context abort, the caller's signal, or when
44
+ * the request timeout elapses (reason: a `TimeoutError`).
45
+ */
46
+ readonly signal: AbortSignal;
47
+ /**
48
+ * Clear the timeout and detach from the parent signals. Call once the
49
+ * response has been consumed. Idempotent.
50
+ */
51
+ dispose(): void;
52
+ }
2
53
 
3
54
  /**
4
55
  * BaseService - Abstract base class for all TinyCloud services.
@@ -144,6 +195,27 @@ declare abstract class BaseService implements IService {
144
195
  * @returns A combined abort signal
145
196
  */
146
197
  protected combineSignals(...signals: (AbortSignal | undefined)[]): AbortSignal;
198
+ /**
199
+ * Create the abort signal for one request, bounded by the request timeout.
200
+ *
201
+ * The signal aborts when the service signs out, the context aborts, the
202
+ * caller's `signal` aborts, or the timeout elapses, whichever comes first.
203
+ * A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which
204
+ * `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own
205
+ * reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).
206
+ *
207
+ * The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a
208
+ * positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and
209
+ * `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.
210
+ *
211
+ * Callers must call `dispose()` once the response body has been consumed,
212
+ * which clears the timer and removes the listeners this adds to the
213
+ * long-lived service and context signals.
214
+ *
215
+ * @param signal - The caller's abort signal for this request
216
+ * @param timeoutMs - Per-request timeout override in milliseconds
217
+ */
218
+ protected createRequestSignal(signal?: AbortSignal, timeoutMs?: number): RequestSignal;
147
219
  /**
148
220
  * Wrap an operation with error handling and telemetry.
149
221
  *
@@ -155,4 +227,4 @@ declare abstract class BaseService implements IService {
155
227
  protected withTelemetry<T>(action: string, key: string | undefined, operation: () => Promise<Result<T>>): Promise<Result<T>>;
156
228
  }
157
229
 
158
- export { BaseService as B };
230
+ export { BaseService as B, type RequestSignal as R, type ServiceConstructor as S, type BaseServiceOptions as a, type ServiceRegistration as b };
@@ -71,6 +71,10 @@ var ErrorCodes = {
71
71
  AUTH_EXPIRED: "AUTH_EXPIRED",
72
72
  AUTH_REQUIRED: "AUTH_REQUIRED",
73
73
  AUTH_UNAUTHORIZED: "AUTH_UNAUTHORIZED",
74
+ /** HTTP 401: the delegation the invocation cites was revoked. */
75
+ AUTH_DELEGATION_REVOKED: "AUTH_DELEGATION_REVOKED",
76
+ /** HTTP 401: a delegation the cited one rests on was revoked. */
77
+ AUTH_DELEGATION_ANCESTOR_REVOKED: "AUTH_DELEGATION_ANCESTOR_REVOKED",
74
78
  NETWORK_ERROR: "NETWORK_ERROR",
75
79
  TIMEOUT: "TIMEOUT",
76
80
  ABORTED: "ABORTED",
@@ -82,6 +86,10 @@ var ErrorCodes = {
82
86
  KV_PRECONDITION_FAILED: "KV_PRECONDITION_FAILED",
83
87
  KV_CONFLICT: "KV_CONFLICT",
84
88
  KV_RESPONSE_TOO_LARGE: "KV_RESPONSE_TOO_LARGE",
89
+ /** HTTP 410 from the change feed: discard sync state and restart without a cursor; `meta.reason` says why. */
90
+ KV_SYNC_RESET_REQUIRED: "KV_SYNC_RESET_REQUIRED",
91
+ /** HTTP 403 from the change feed: the retention grant was refused; `meta.reason` says why. */
92
+ KV_RETENTION_GRANT_REFUSED: "KV_RETENTION_GRANT_REFUSED",
85
93
  // SQL-specific errors
86
94
  SQL_ERROR: "SQL_ERROR",
87
95
  SQL_PERMISSION_DENIED: "SQL_PERMISSION_DENIED",
@@ -225,11 +233,31 @@ var ENCRYPTION_NETWORK_URN_PREFIX = URN_PREFIX;
225
233
  var NETWORK_NAME_PATTERN = NETWORK_NAME_RE;
226
234
 
227
235
  // src/errors.ts
228
- function timeoutError(service) {
236
+ var RequestTimeoutError = class extends Error {
237
+ constructor(timeoutMs) {
238
+ super(`Request timed out after ${timeoutMs}ms.`);
239
+ this.name = "TimeoutError";
240
+ this.timeoutMs = timeoutMs;
241
+ }
242
+ };
243
+ function isRequestTimeoutError(error) {
244
+ return error instanceof RequestTimeoutError || error instanceof Error && error.name === "TimeoutError" && typeof error.timeoutMs === "number";
245
+ }
246
+ function timeoutError(service, timeoutMs, cause) {
247
+ if (timeoutMs === void 0) {
248
+ return {
249
+ code: ErrorCodes.TIMEOUT,
250
+ message: "Request timed out.",
251
+ service,
252
+ ...cause === void 0 ? {} : { cause }
253
+ };
254
+ }
229
255
  return {
230
256
  code: ErrorCodes.TIMEOUT,
231
- message: "Request timed out.",
232
- service
257
+ message: `Request timed out after ${timeoutMs}ms.`,
258
+ service,
259
+ ...cause === void 0 ? {} : { cause },
260
+ meta: { timeoutMs }
233
261
  };
234
262
  }
235
263
  function abortedError(service) {
@@ -247,6 +275,9 @@ var capabilityServices = new Set(
247
275
  );
248
276
  function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
249
277
  if (error instanceof Error) {
278
+ if (isRequestTimeoutError(error)) {
279
+ return timeoutError(service, error.timeoutMs, error);
280
+ }
250
281
  if (error.name === "AbortError") {
251
282
  return abortedError(service);
252
283
  }
@@ -268,6 +299,7 @@ function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
268
299
  }
269
300
 
270
301
  // src/base/BaseService.ts
302
+ var MAX_TIMER_DELAY_MS = 2147483647;
271
303
  var BaseService = class {
272
304
  constructor() {
273
305
  /**
@@ -448,6 +480,64 @@ var BaseService = class {
448
480
  }
449
481
  return controller.signal;
450
482
  }
483
+ /**
484
+ * Create the abort signal for one request, bounded by the request timeout.
485
+ *
486
+ * The signal aborts when the service signs out, the context aborts, the
487
+ * caller's `signal` aborts, or the timeout elapses, whichever comes first.
488
+ * A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which
489
+ * `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own
490
+ * reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).
491
+ *
492
+ * The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a
493
+ * positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and
494
+ * `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.
495
+ *
496
+ * Callers must call `dispose()` once the response body has been consumed,
497
+ * which clears the timer and removes the listeners this adds to the
498
+ * long-lived service and context signals.
499
+ *
500
+ * @param signal - The caller's abort signal for this request
501
+ * @param timeoutMs - Per-request timeout override in milliseconds
502
+ */
503
+ createRequestSignal(signal, timeoutMs) {
504
+ const controller = new AbortController();
505
+ const detachers = [];
506
+ let timer;
507
+ const dispose = () => {
508
+ if (timer !== void 0) {
509
+ clearTimeout(timer);
510
+ timer = void 0;
511
+ }
512
+ for (const detach of detachers.splice(0)) {
513
+ detach();
514
+ }
515
+ };
516
+ const abort = (reason) => {
517
+ dispose();
518
+ controller.abort(reason);
519
+ };
520
+ const requestSignal = { signal: controller.signal, dispose };
521
+ const parents = [
522
+ this.abortController.signal,
523
+ this.context?.abortSignal,
524
+ signal
525
+ ].filter((parent) => parent !== void 0);
526
+ for (const parent of parents) {
527
+ if (parent.aborted) {
528
+ abort(parent.reason);
529
+ return requestSignal;
530
+ }
531
+ const onAbort = () => abort(parent.reason);
532
+ parent.addEventListener("abort", onAbort, { once: true });
533
+ detachers.push(() => parent.removeEventListener("abort", onAbort));
534
+ }
535
+ const timeout = timeoutMs ?? this._config.timeout;
536
+ if (typeof timeout === "number" && timeout > 0 && timeout <= MAX_TIMER_DELAY_MS) {
537
+ timer = setTimeout(() => abort(new RequestTimeoutError(timeout)), timeout);
538
+ }
539
+ return requestSignal;
540
+ }
451
541
  /**
452
542
  * Wrap an operation with error handling and telemetry.
453
543
  *