@tinycloud/sdk-services 3.1.0-beta.2 → 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.
- package/dist/{BaseService-BhpVpA9b.d.cts → BaseService-C8zRy0ww.d.cts} +73 -1
- package/dist/{BaseService-CuV-ZVPs.d.ts → BaseService-DU-lE-Qx.d.ts} +73 -1
- package/dist/encryption/index.cjs +191 -100
- package/dist/encryption/index.cjs.map +1 -1
- package/dist/encryption/index.d.cts +1 -1
- package/dist/encryption/index.d.ts +1 -1
- package/dist/encryption/index.js +191 -100
- package/dist/encryption/index.js.map +1 -1
- package/dist/index.cjs +446 -177
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -39
- package/dist/index.d.ts +29 -39
- package/dist/index.js +426 -159
- package/dist/index.js.map +1 -1
- package/dist/kv/index.cjs +333 -35
- package/dist/kv/index.cjs.map +1 -1
- package/dist/kv/index.d.cts +26 -12
- package/dist/kv/index.d.ts +26 -12
- package/dist/kv/index.js +333 -35
- package/dist/kv/index.js.map +1 -1
- package/dist/sql/index.cjs +139 -30
- package/dist/sql/index.cjs.map +1 -1
- package/dist/sql/index.d.cts +5 -2
- package/dist/sql/index.d.ts +5 -2
- package/dist/sql/index.js +130 -21
- package/dist/sql/index.js.map +1 -1
- package/package.json +1 -1
package/dist/sql/index.js
CHANGED
|
@@ -71,6 +71,7 @@ function serviceError(code, message, service, options) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// src/errors.ts
|
|
74
|
+
import { CAPABILITY_REGISTRY } from "@tinycloud/bootstrap";
|
|
74
75
|
function authRequiredError(service) {
|
|
75
76
|
return {
|
|
76
77
|
code: ErrorCodes.AUTH_REQUIRED,
|
|
@@ -78,11 +79,31 @@ function authRequiredError(service) {
|
|
|
78
79
|
service
|
|
79
80
|
};
|
|
80
81
|
}
|
|
81
|
-
|
|
82
|
+
var RequestTimeoutError = class extends Error {
|
|
83
|
+
constructor(timeoutMs) {
|
|
84
|
+
super(`Request timed out after ${timeoutMs}ms.`);
|
|
85
|
+
this.name = "TimeoutError";
|
|
86
|
+
this.timeoutMs = timeoutMs;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
function isRequestTimeoutError(error) {
|
|
90
|
+
return error instanceof RequestTimeoutError || error instanceof Error && error.name === "TimeoutError" && typeof error.timeoutMs === "number";
|
|
91
|
+
}
|
|
92
|
+
function timeoutError(service, timeoutMs, cause) {
|
|
93
|
+
if (timeoutMs === void 0) {
|
|
94
|
+
return {
|
|
95
|
+
code: ErrorCodes.TIMEOUT,
|
|
96
|
+
message: "Request timed out.",
|
|
97
|
+
service,
|
|
98
|
+
...cause === void 0 ? {} : { cause }
|
|
99
|
+
};
|
|
100
|
+
}
|
|
82
101
|
return {
|
|
83
102
|
code: ErrorCodes.TIMEOUT,
|
|
84
|
-
message:
|
|
85
|
-
service
|
|
103
|
+
message: `Request timed out after ${timeoutMs}ms.`,
|
|
104
|
+
service,
|
|
105
|
+
...cause === void 0 ? {} : { cause },
|
|
106
|
+
meta: { timeoutMs }
|
|
86
107
|
};
|
|
87
108
|
}
|
|
88
109
|
function abortedError(service) {
|
|
@@ -99,6 +120,12 @@ function parseAuthError(responseText) {
|
|
|
99
120
|
}
|
|
100
121
|
return {};
|
|
101
122
|
}
|
|
123
|
+
var grantableActions = new Set(
|
|
124
|
+
CAPABILITY_REGISTRY.map(({ urn }) => urn).filter((urn) => !urn.endsWith("/*"))
|
|
125
|
+
);
|
|
126
|
+
var capabilityServices = new Set(
|
|
127
|
+
CAPABILITY_REGISTRY.map(({ service }) => service.slice("tinycloud.".length))
|
|
128
|
+
);
|
|
102
129
|
function storageQuotaExceededError(service, message, meta) {
|
|
103
130
|
return {
|
|
104
131
|
code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,
|
|
@@ -131,6 +158,9 @@ function storageRejectionError(service, status, meta, responseText) {
|
|
|
131
158
|
}
|
|
132
159
|
function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
|
|
133
160
|
if (error instanceof Error) {
|
|
161
|
+
if (isRequestTimeoutError(error)) {
|
|
162
|
+
return timeoutError(service, error.timeoutMs, error);
|
|
163
|
+
}
|
|
134
164
|
if (error.name === "AbortError") {
|
|
135
165
|
return abortedError(service);
|
|
136
166
|
}
|
|
@@ -152,6 +182,7 @@ function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
|
|
|
152
182
|
}
|
|
153
183
|
|
|
154
184
|
// src/base/BaseService.ts
|
|
185
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
155
186
|
var BaseService = class {
|
|
156
187
|
constructor() {
|
|
157
188
|
/**
|
|
@@ -332,6 +363,64 @@ var BaseService = class {
|
|
|
332
363
|
}
|
|
333
364
|
return controller.signal;
|
|
334
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Create the abort signal for one request, bounded by the request timeout.
|
|
368
|
+
*
|
|
369
|
+
* The signal aborts when the service signs out, the context aborts, the
|
|
370
|
+
* caller's `signal` aborts, or the timeout elapses, whichever comes first.
|
|
371
|
+
* A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which
|
|
372
|
+
* `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own
|
|
373
|
+
* reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).
|
|
374
|
+
*
|
|
375
|
+
* The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a
|
|
376
|
+
* positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and
|
|
377
|
+
* `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.
|
|
378
|
+
*
|
|
379
|
+
* Callers must call `dispose()` once the response body has been consumed,
|
|
380
|
+
* which clears the timer and removes the listeners this adds to the
|
|
381
|
+
* long-lived service and context signals.
|
|
382
|
+
*
|
|
383
|
+
* @param signal - The caller's abort signal for this request
|
|
384
|
+
* @param timeoutMs - Per-request timeout override in milliseconds
|
|
385
|
+
*/
|
|
386
|
+
createRequestSignal(signal, timeoutMs) {
|
|
387
|
+
const controller = new AbortController();
|
|
388
|
+
const detachers = [];
|
|
389
|
+
let timer;
|
|
390
|
+
const dispose = () => {
|
|
391
|
+
if (timer !== void 0) {
|
|
392
|
+
clearTimeout(timer);
|
|
393
|
+
timer = void 0;
|
|
394
|
+
}
|
|
395
|
+
for (const detach of detachers.splice(0)) {
|
|
396
|
+
detach();
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
const abort = (reason) => {
|
|
400
|
+
dispose();
|
|
401
|
+
controller.abort(reason);
|
|
402
|
+
};
|
|
403
|
+
const requestSignal = { signal: controller.signal, dispose };
|
|
404
|
+
const parents = [
|
|
405
|
+
this.abortController.signal,
|
|
406
|
+
this.context?.abortSignal,
|
|
407
|
+
signal
|
|
408
|
+
].filter((parent) => parent !== void 0);
|
|
409
|
+
for (const parent of parents) {
|
|
410
|
+
if (parent.aborted) {
|
|
411
|
+
abort(parent.reason);
|
|
412
|
+
return requestSignal;
|
|
413
|
+
}
|
|
414
|
+
const onAbort = () => abort(parent.reason);
|
|
415
|
+
parent.addEventListener("abort", onAbort, { once: true });
|
|
416
|
+
detachers.push(() => parent.removeEventListener("abort", onAbort));
|
|
417
|
+
}
|
|
418
|
+
const timeout = timeoutMs ?? this._config.timeout;
|
|
419
|
+
if (typeof timeout === "number" && timeout > 0 && timeout <= MAX_TIMER_DELAY_MS) {
|
|
420
|
+
timer = setTimeout(() => abort(new RequestTimeoutError(timeout)), timeout);
|
|
421
|
+
}
|
|
422
|
+
return requestSignal;
|
|
423
|
+
}
|
|
335
424
|
/**
|
|
336
425
|
* Wrap an operation with error handling and telemetry.
|
|
337
426
|
*
|
|
@@ -520,6 +609,7 @@ var SQLService = class extends BaseService {
|
|
|
520
609
|
if (!this.requireAuth()) {
|
|
521
610
|
return err(authRequiredError("sql"));
|
|
522
611
|
}
|
|
612
|
+
const request = this.createRequestSignal(options?.signal);
|
|
523
613
|
try {
|
|
524
614
|
const body = {
|
|
525
615
|
action: "query",
|
|
@@ -528,19 +618,22 @@ var SQLService = class extends BaseService {
|
|
|
528
618
|
};
|
|
529
619
|
if (options?.maxRows !== void 0) body.maxRows = options.maxRows;
|
|
530
620
|
if (options?.maxBytes !== void 0) body.maxBytes = options.maxBytes;
|
|
621
|
+
const action = this.actionForSql(sql, SQLAction.READ);
|
|
531
622
|
const response = await this.invokeSQL(
|
|
532
623
|
dbName,
|
|
533
|
-
|
|
624
|
+
action,
|
|
534
625
|
body,
|
|
535
|
-
|
|
626
|
+
request.signal
|
|
536
627
|
);
|
|
537
628
|
if (!response.ok) {
|
|
538
|
-
return this.handleErrorResponse(response, "query");
|
|
629
|
+
return this.handleErrorResponse(response, "query", dbName, [action]);
|
|
539
630
|
}
|
|
540
631
|
const data = await response.json();
|
|
541
632
|
return ok(data);
|
|
542
633
|
} catch (error) {
|
|
543
634
|
return err(wrapError("sql", error));
|
|
635
|
+
} finally {
|
|
636
|
+
request.dispose();
|
|
544
637
|
}
|
|
545
638
|
});
|
|
546
639
|
}
|
|
@@ -549,6 +642,7 @@ var SQLService = class extends BaseService {
|
|
|
549
642
|
if (!this.requireAuth()) {
|
|
550
643
|
return err(authRequiredError("sql"));
|
|
551
644
|
}
|
|
645
|
+
const request = this.createRequestSignal(options?.signal);
|
|
552
646
|
try {
|
|
553
647
|
const body = {
|
|
554
648
|
action: "execute",
|
|
@@ -564,19 +658,22 @@ var SQLService = class extends BaseService {
|
|
|
564
658
|
(statement) => this.actionForSql(statement, SQLAction.SCHEMA)
|
|
565
659
|
)
|
|
566
660
|
];
|
|
661
|
+
const requestedActions = this.dedupeActions(actions);
|
|
567
662
|
const response = await this.invokeSQL(
|
|
568
663
|
dbName,
|
|
569
|
-
|
|
664
|
+
requestedActions,
|
|
570
665
|
body,
|
|
571
|
-
|
|
666
|
+
request.signal
|
|
572
667
|
);
|
|
573
668
|
if (!response.ok) {
|
|
574
|
-
return this.handleErrorResponse(response, "execute");
|
|
669
|
+
return this.handleErrorResponse(response, "execute", dbName, requestedActions);
|
|
575
670
|
}
|
|
576
671
|
const data = await response.json();
|
|
577
672
|
return ok(data);
|
|
578
673
|
} catch (error) {
|
|
579
674
|
return err(wrapError("sql", error));
|
|
675
|
+
} finally {
|
|
676
|
+
request.dispose();
|
|
580
677
|
}
|
|
581
678
|
});
|
|
582
679
|
}
|
|
@@ -585,20 +682,24 @@ var SQLService = class extends BaseService {
|
|
|
585
682
|
if (!this.requireAuth()) {
|
|
586
683
|
return err(authRequiredError("sql"));
|
|
587
684
|
}
|
|
685
|
+
const request = this.createRequestSignal(options?.signal);
|
|
588
686
|
try {
|
|
687
|
+
const requestedActions = this.actionsForSqlBatch(statements);
|
|
589
688
|
const response = await this.invokeSQL(
|
|
590
689
|
dbName,
|
|
591
|
-
|
|
690
|
+
requestedActions,
|
|
592
691
|
{ action: "batch", statements },
|
|
593
|
-
|
|
692
|
+
request.signal
|
|
594
693
|
);
|
|
595
694
|
if (!response.ok) {
|
|
596
|
-
return this.handleErrorResponse(response, "batch");
|
|
695
|
+
return this.handleErrorResponse(response, "batch", dbName, requestedActions);
|
|
597
696
|
}
|
|
598
697
|
const data = await response.json();
|
|
599
698
|
return ok(data);
|
|
600
699
|
} catch (error) {
|
|
601
700
|
return err(wrapError("sql", error));
|
|
701
|
+
} finally {
|
|
702
|
+
request.dispose();
|
|
602
703
|
}
|
|
603
704
|
});
|
|
604
705
|
}
|
|
@@ -607,20 +708,23 @@ var SQLService = class extends BaseService {
|
|
|
607
708
|
if (!this.requireAuth()) {
|
|
608
709
|
return err(authRequiredError("sql"));
|
|
609
710
|
}
|
|
711
|
+
const request = this.createRequestSignal(options?.signal);
|
|
610
712
|
try {
|
|
611
713
|
const response = await this.invokeSQL(
|
|
612
714
|
dbName,
|
|
613
715
|
SQLAction.WRITE,
|
|
614
716
|
{ action: "execute_statement", name, params: params ?? [] },
|
|
615
|
-
|
|
717
|
+
request.signal
|
|
616
718
|
);
|
|
617
719
|
if (!response.ok) {
|
|
618
|
-
return this.handleErrorResponse(response, "executeStatement");
|
|
720
|
+
return this.handleErrorResponse(response, "executeStatement", dbName, [SQLAction.WRITE]);
|
|
619
721
|
}
|
|
620
722
|
const data = await response.json();
|
|
621
723
|
return ok(data);
|
|
622
724
|
} catch (error) {
|
|
623
725
|
return err(wrapError("sql", error));
|
|
726
|
+
} finally {
|
|
727
|
+
request.dispose();
|
|
624
728
|
}
|
|
625
729
|
});
|
|
626
730
|
}
|
|
@@ -629,15 +733,16 @@ var SQLService = class extends BaseService {
|
|
|
629
733
|
if (!this.requireAuth()) {
|
|
630
734
|
return err(authRequiredError("sql"));
|
|
631
735
|
}
|
|
736
|
+
const request = this.createRequestSignal(options?.signal);
|
|
632
737
|
try {
|
|
633
738
|
const response = await this.invokeSQL(
|
|
634
739
|
dbName,
|
|
635
740
|
SQLAction.READ,
|
|
636
741
|
{ action: "export" },
|
|
637
|
-
|
|
742
|
+
request.signal
|
|
638
743
|
);
|
|
639
744
|
if (!response.ok) {
|
|
640
|
-
return this.handleErrorResponse(response, "export");
|
|
745
|
+
return this.handleErrorResponse(response, "export", dbName, [SQLAction.READ]);
|
|
641
746
|
}
|
|
642
747
|
const resp = response;
|
|
643
748
|
if (typeof resp.blob === "function") {
|
|
@@ -648,6 +753,8 @@ var SQLService = class extends BaseService {
|
|
|
648
753
|
return ok(text);
|
|
649
754
|
} catch (error) {
|
|
650
755
|
return err(wrapError("sql", error));
|
|
756
|
+
} finally {
|
|
757
|
+
request.dispose();
|
|
651
758
|
}
|
|
652
759
|
});
|
|
653
760
|
}
|
|
@@ -738,7 +845,7 @@ var SQLService = class extends BaseService {
|
|
|
738
845
|
"Content-Type": "application/json"
|
|
739
846
|
},
|
|
740
847
|
body: JSON.stringify(body),
|
|
741
|
-
signal
|
|
848
|
+
signal
|
|
742
849
|
});
|
|
743
850
|
}
|
|
744
851
|
actionForSql(sql, fallback) {
|
|
@@ -771,7 +878,7 @@ var SQLService = class extends BaseService {
|
|
|
771
878
|
}))
|
|
772
879
|
);
|
|
773
880
|
}
|
|
774
|
-
async handleErrorResponse(response, operation) {
|
|
881
|
+
async handleErrorResponse(response, operation, dbName, requestedActions) {
|
|
775
882
|
const errorText = await response.text();
|
|
776
883
|
const meta = responseErrorMeta(response.status, response.statusText, errorText);
|
|
777
884
|
if (response.status === 402) {
|
|
@@ -790,9 +897,11 @@ var SQLService = class extends BaseService {
|
|
|
790
897
|
errorBody
|
|
791
898
|
);
|
|
792
899
|
if (response.status === 401) {
|
|
793
|
-
const {
|
|
794
|
-
if (action
|
|
795
|
-
|
|
900
|
+
const { action } = parseAuthError(errorText);
|
|
901
|
+
if (action && requestedActions.includes(action)) {
|
|
902
|
+
meta.requiredAction = action;
|
|
903
|
+
meta.resource = `${this.context.session.spaceId}/sql/${dbName}`;
|
|
904
|
+
}
|
|
796
905
|
}
|
|
797
906
|
return err(
|
|
798
907
|
serviceError(errorCode, message, "sql", { meta })
|