@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.cjs
CHANGED
|
@@ -100,6 +100,7 @@ function serviceError(code, message, service, options) {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// src/errors.ts
|
|
103
|
+
var import_bootstrap = require("@tinycloud/bootstrap");
|
|
103
104
|
function authRequiredError(service) {
|
|
104
105
|
return {
|
|
105
106
|
code: ErrorCodes.AUTH_REQUIRED,
|
|
@@ -107,11 +108,31 @@ function authRequiredError(service) {
|
|
|
107
108
|
service
|
|
108
109
|
};
|
|
109
110
|
}
|
|
110
|
-
|
|
111
|
+
var RequestTimeoutError = class extends Error {
|
|
112
|
+
constructor(timeoutMs) {
|
|
113
|
+
super(`Request timed out after ${timeoutMs}ms.`);
|
|
114
|
+
this.name = "TimeoutError";
|
|
115
|
+
this.timeoutMs = timeoutMs;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
function isRequestTimeoutError(error) {
|
|
119
|
+
return error instanceof RequestTimeoutError || error instanceof Error && error.name === "TimeoutError" && typeof error.timeoutMs === "number";
|
|
120
|
+
}
|
|
121
|
+
function timeoutError(service, timeoutMs, cause) {
|
|
122
|
+
if (timeoutMs === void 0) {
|
|
123
|
+
return {
|
|
124
|
+
code: ErrorCodes.TIMEOUT,
|
|
125
|
+
message: "Request timed out.",
|
|
126
|
+
service,
|
|
127
|
+
...cause === void 0 ? {} : { cause }
|
|
128
|
+
};
|
|
129
|
+
}
|
|
111
130
|
return {
|
|
112
131
|
code: ErrorCodes.TIMEOUT,
|
|
113
|
-
message:
|
|
114
|
-
service
|
|
132
|
+
message: `Request timed out after ${timeoutMs}ms.`,
|
|
133
|
+
service,
|
|
134
|
+
...cause === void 0 ? {} : { cause },
|
|
135
|
+
meta: { timeoutMs }
|
|
115
136
|
};
|
|
116
137
|
}
|
|
117
138
|
function abortedError(service) {
|
|
@@ -128,6 +149,12 @@ function parseAuthError(responseText) {
|
|
|
128
149
|
}
|
|
129
150
|
return {};
|
|
130
151
|
}
|
|
152
|
+
var grantableActions = new Set(
|
|
153
|
+
import_bootstrap.CAPABILITY_REGISTRY.map(({ urn }) => urn).filter((urn) => !urn.endsWith("/*"))
|
|
154
|
+
);
|
|
155
|
+
var capabilityServices = new Set(
|
|
156
|
+
import_bootstrap.CAPABILITY_REGISTRY.map(({ service }) => service.slice("tinycloud.".length))
|
|
157
|
+
);
|
|
131
158
|
function storageQuotaExceededError(service, message, meta) {
|
|
132
159
|
return {
|
|
133
160
|
code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,
|
|
@@ -160,6 +187,9 @@ function storageRejectionError(service, status, meta, responseText) {
|
|
|
160
187
|
}
|
|
161
188
|
function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
|
|
162
189
|
if (error instanceof Error) {
|
|
190
|
+
if (isRequestTimeoutError(error)) {
|
|
191
|
+
return timeoutError(service, error.timeoutMs, error);
|
|
192
|
+
}
|
|
163
193
|
if (error.name === "AbortError") {
|
|
164
194
|
return abortedError(service);
|
|
165
195
|
}
|
|
@@ -181,6 +211,7 @@ function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
|
|
|
181
211
|
}
|
|
182
212
|
|
|
183
213
|
// src/base/BaseService.ts
|
|
214
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
184
215
|
var BaseService = class {
|
|
185
216
|
constructor() {
|
|
186
217
|
/**
|
|
@@ -361,6 +392,64 @@ var BaseService = class {
|
|
|
361
392
|
}
|
|
362
393
|
return controller.signal;
|
|
363
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* Create the abort signal for one request, bounded by the request timeout.
|
|
397
|
+
*
|
|
398
|
+
* The signal aborts when the service signs out, the context aborts, the
|
|
399
|
+
* caller's `signal` aborts, or the timeout elapses, whichever comes first.
|
|
400
|
+
* A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which
|
|
401
|
+
* `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own
|
|
402
|
+
* reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).
|
|
403
|
+
*
|
|
404
|
+
* The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a
|
|
405
|
+
* positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and
|
|
406
|
+
* `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.
|
|
407
|
+
*
|
|
408
|
+
* Callers must call `dispose()` once the response body has been consumed,
|
|
409
|
+
* which clears the timer and removes the listeners this adds to the
|
|
410
|
+
* long-lived service and context signals.
|
|
411
|
+
*
|
|
412
|
+
* @param signal - The caller's abort signal for this request
|
|
413
|
+
* @param timeoutMs - Per-request timeout override in milliseconds
|
|
414
|
+
*/
|
|
415
|
+
createRequestSignal(signal, timeoutMs) {
|
|
416
|
+
const controller = new AbortController();
|
|
417
|
+
const detachers = [];
|
|
418
|
+
let timer;
|
|
419
|
+
const dispose = () => {
|
|
420
|
+
if (timer !== void 0) {
|
|
421
|
+
clearTimeout(timer);
|
|
422
|
+
timer = void 0;
|
|
423
|
+
}
|
|
424
|
+
for (const detach of detachers.splice(0)) {
|
|
425
|
+
detach();
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
const abort = (reason) => {
|
|
429
|
+
dispose();
|
|
430
|
+
controller.abort(reason);
|
|
431
|
+
};
|
|
432
|
+
const requestSignal = { signal: controller.signal, dispose };
|
|
433
|
+
const parents = [
|
|
434
|
+
this.abortController.signal,
|
|
435
|
+
this.context?.abortSignal,
|
|
436
|
+
signal
|
|
437
|
+
].filter((parent) => parent !== void 0);
|
|
438
|
+
for (const parent of parents) {
|
|
439
|
+
if (parent.aborted) {
|
|
440
|
+
abort(parent.reason);
|
|
441
|
+
return requestSignal;
|
|
442
|
+
}
|
|
443
|
+
const onAbort = () => abort(parent.reason);
|
|
444
|
+
parent.addEventListener("abort", onAbort, { once: true });
|
|
445
|
+
detachers.push(() => parent.removeEventListener("abort", onAbort));
|
|
446
|
+
}
|
|
447
|
+
const timeout = timeoutMs ?? this._config.timeout;
|
|
448
|
+
if (typeof timeout === "number" && timeout > 0 && timeout <= MAX_TIMER_DELAY_MS) {
|
|
449
|
+
timer = setTimeout(() => abort(new RequestTimeoutError(timeout)), timeout);
|
|
450
|
+
}
|
|
451
|
+
return requestSignal;
|
|
452
|
+
}
|
|
364
453
|
/**
|
|
365
454
|
* Wrap an operation with error handling and telemetry.
|
|
366
455
|
*
|
|
@@ -462,16 +551,16 @@ var DatabaseHandle = class {
|
|
|
462
551
|
};
|
|
463
552
|
|
|
464
553
|
// src/sql/types.ts
|
|
465
|
-
var
|
|
554
|
+
var import_bootstrap2 = require("@tinycloud/bootstrap");
|
|
466
555
|
var SQLAction = {
|
|
467
|
-
READ:
|
|
468
|
-
WRITE:
|
|
469
|
-
SCHEMA:
|
|
470
|
-
ADMIN:
|
|
471
|
-
SELECT:
|
|
472
|
-
EXECUTE:
|
|
473
|
-
EXPORT:
|
|
474
|
-
ALL:
|
|
556
|
+
READ: import_bootstrap2.SQL.READ,
|
|
557
|
+
WRITE: import_bootstrap2.SQL.WRITE,
|
|
558
|
+
SCHEMA: import_bootstrap2.SQL.SCHEMA,
|
|
559
|
+
ADMIN: import_bootstrap2.SQL.ADMIN,
|
|
560
|
+
SELECT: import_bootstrap2.SQL.SELECT,
|
|
561
|
+
EXECUTE: import_bootstrap2.SQL.EXECUTE,
|
|
562
|
+
EXPORT: import_bootstrap2.SQL.EXPORT,
|
|
563
|
+
ALL: import_bootstrap2.SQL.ALL
|
|
475
564
|
};
|
|
476
565
|
|
|
477
566
|
// src/sql/SQLService.ts
|
|
@@ -549,6 +638,7 @@ var SQLService = class extends BaseService {
|
|
|
549
638
|
if (!this.requireAuth()) {
|
|
550
639
|
return err(authRequiredError("sql"));
|
|
551
640
|
}
|
|
641
|
+
const request = this.createRequestSignal(options?.signal);
|
|
552
642
|
try {
|
|
553
643
|
const body = {
|
|
554
644
|
action: "query",
|
|
@@ -557,19 +647,22 @@ var SQLService = class extends BaseService {
|
|
|
557
647
|
};
|
|
558
648
|
if (options?.maxRows !== void 0) body.maxRows = options.maxRows;
|
|
559
649
|
if (options?.maxBytes !== void 0) body.maxBytes = options.maxBytes;
|
|
650
|
+
const action = this.actionForSql(sql, SQLAction.READ);
|
|
560
651
|
const response = await this.invokeSQL(
|
|
561
652
|
dbName,
|
|
562
|
-
|
|
653
|
+
action,
|
|
563
654
|
body,
|
|
564
|
-
|
|
655
|
+
request.signal
|
|
565
656
|
);
|
|
566
657
|
if (!response.ok) {
|
|
567
|
-
return this.handleErrorResponse(response, "query");
|
|
658
|
+
return this.handleErrorResponse(response, "query", dbName, [action]);
|
|
568
659
|
}
|
|
569
660
|
const data = await response.json();
|
|
570
661
|
return ok(data);
|
|
571
662
|
} catch (error) {
|
|
572
663
|
return err(wrapError("sql", error));
|
|
664
|
+
} finally {
|
|
665
|
+
request.dispose();
|
|
573
666
|
}
|
|
574
667
|
});
|
|
575
668
|
}
|
|
@@ -578,6 +671,7 @@ var SQLService = class extends BaseService {
|
|
|
578
671
|
if (!this.requireAuth()) {
|
|
579
672
|
return err(authRequiredError("sql"));
|
|
580
673
|
}
|
|
674
|
+
const request = this.createRequestSignal(options?.signal);
|
|
581
675
|
try {
|
|
582
676
|
const body = {
|
|
583
677
|
action: "execute",
|
|
@@ -593,19 +687,22 @@ var SQLService = class extends BaseService {
|
|
|
593
687
|
(statement) => this.actionForSql(statement, SQLAction.SCHEMA)
|
|
594
688
|
)
|
|
595
689
|
];
|
|
690
|
+
const requestedActions = this.dedupeActions(actions);
|
|
596
691
|
const response = await this.invokeSQL(
|
|
597
692
|
dbName,
|
|
598
|
-
|
|
693
|
+
requestedActions,
|
|
599
694
|
body,
|
|
600
|
-
|
|
695
|
+
request.signal
|
|
601
696
|
);
|
|
602
697
|
if (!response.ok) {
|
|
603
|
-
return this.handleErrorResponse(response, "execute");
|
|
698
|
+
return this.handleErrorResponse(response, "execute", dbName, requestedActions);
|
|
604
699
|
}
|
|
605
700
|
const data = await response.json();
|
|
606
701
|
return ok(data);
|
|
607
702
|
} catch (error) {
|
|
608
703
|
return err(wrapError("sql", error));
|
|
704
|
+
} finally {
|
|
705
|
+
request.dispose();
|
|
609
706
|
}
|
|
610
707
|
});
|
|
611
708
|
}
|
|
@@ -614,20 +711,24 @@ var SQLService = class extends BaseService {
|
|
|
614
711
|
if (!this.requireAuth()) {
|
|
615
712
|
return err(authRequiredError("sql"));
|
|
616
713
|
}
|
|
714
|
+
const request = this.createRequestSignal(options?.signal);
|
|
617
715
|
try {
|
|
716
|
+
const requestedActions = this.actionsForSqlBatch(statements);
|
|
618
717
|
const response = await this.invokeSQL(
|
|
619
718
|
dbName,
|
|
620
|
-
|
|
719
|
+
requestedActions,
|
|
621
720
|
{ action: "batch", statements },
|
|
622
|
-
|
|
721
|
+
request.signal
|
|
623
722
|
);
|
|
624
723
|
if (!response.ok) {
|
|
625
|
-
return this.handleErrorResponse(response, "batch");
|
|
724
|
+
return this.handleErrorResponse(response, "batch", dbName, requestedActions);
|
|
626
725
|
}
|
|
627
726
|
const data = await response.json();
|
|
628
727
|
return ok(data);
|
|
629
728
|
} catch (error) {
|
|
630
729
|
return err(wrapError("sql", error));
|
|
730
|
+
} finally {
|
|
731
|
+
request.dispose();
|
|
631
732
|
}
|
|
632
733
|
});
|
|
633
734
|
}
|
|
@@ -636,20 +737,23 @@ var SQLService = class extends BaseService {
|
|
|
636
737
|
if (!this.requireAuth()) {
|
|
637
738
|
return err(authRequiredError("sql"));
|
|
638
739
|
}
|
|
740
|
+
const request = this.createRequestSignal(options?.signal);
|
|
639
741
|
try {
|
|
640
742
|
const response = await this.invokeSQL(
|
|
641
743
|
dbName,
|
|
642
744
|
SQLAction.WRITE,
|
|
643
745
|
{ action: "execute_statement", name, params: params ?? [] },
|
|
644
|
-
|
|
746
|
+
request.signal
|
|
645
747
|
);
|
|
646
748
|
if (!response.ok) {
|
|
647
|
-
return this.handleErrorResponse(response, "executeStatement");
|
|
749
|
+
return this.handleErrorResponse(response, "executeStatement", dbName, [SQLAction.WRITE]);
|
|
648
750
|
}
|
|
649
751
|
const data = await response.json();
|
|
650
752
|
return ok(data);
|
|
651
753
|
} catch (error) {
|
|
652
754
|
return err(wrapError("sql", error));
|
|
755
|
+
} finally {
|
|
756
|
+
request.dispose();
|
|
653
757
|
}
|
|
654
758
|
});
|
|
655
759
|
}
|
|
@@ -658,15 +762,16 @@ var SQLService = class extends BaseService {
|
|
|
658
762
|
if (!this.requireAuth()) {
|
|
659
763
|
return err(authRequiredError("sql"));
|
|
660
764
|
}
|
|
765
|
+
const request = this.createRequestSignal(options?.signal);
|
|
661
766
|
try {
|
|
662
767
|
const response = await this.invokeSQL(
|
|
663
768
|
dbName,
|
|
664
769
|
SQLAction.READ,
|
|
665
770
|
{ action: "export" },
|
|
666
|
-
|
|
771
|
+
request.signal
|
|
667
772
|
);
|
|
668
773
|
if (!response.ok) {
|
|
669
|
-
return this.handleErrorResponse(response, "export");
|
|
774
|
+
return this.handleErrorResponse(response, "export", dbName, [SQLAction.READ]);
|
|
670
775
|
}
|
|
671
776
|
const resp = response;
|
|
672
777
|
if (typeof resp.blob === "function") {
|
|
@@ -677,6 +782,8 @@ var SQLService = class extends BaseService {
|
|
|
677
782
|
return ok(text);
|
|
678
783
|
} catch (error) {
|
|
679
784
|
return err(wrapError("sql", error));
|
|
785
|
+
} finally {
|
|
786
|
+
request.dispose();
|
|
680
787
|
}
|
|
681
788
|
});
|
|
682
789
|
}
|
|
@@ -767,7 +874,7 @@ var SQLService = class extends BaseService {
|
|
|
767
874
|
"Content-Type": "application/json"
|
|
768
875
|
},
|
|
769
876
|
body: JSON.stringify(body),
|
|
770
|
-
signal
|
|
877
|
+
signal
|
|
771
878
|
});
|
|
772
879
|
}
|
|
773
880
|
actionForSql(sql, fallback) {
|
|
@@ -800,7 +907,7 @@ var SQLService = class extends BaseService {
|
|
|
800
907
|
}))
|
|
801
908
|
);
|
|
802
909
|
}
|
|
803
|
-
async handleErrorResponse(response, operation) {
|
|
910
|
+
async handleErrorResponse(response, operation, dbName, requestedActions) {
|
|
804
911
|
const errorText = await response.text();
|
|
805
912
|
const meta = responseErrorMeta(response.status, response.statusText, errorText);
|
|
806
913
|
if (response.status === 402) {
|
|
@@ -819,9 +926,11 @@ var SQLService = class extends BaseService {
|
|
|
819
926
|
errorBody
|
|
820
927
|
);
|
|
821
928
|
if (response.status === 401) {
|
|
822
|
-
const {
|
|
823
|
-
if (action
|
|
824
|
-
|
|
929
|
+
const { action } = parseAuthError(errorText);
|
|
930
|
+
if (action && requestedActions.includes(action)) {
|
|
931
|
+
meta.requiredAction = action;
|
|
932
|
+
meta.resource = `${this.context.session.spaceId}/sql/${dbName}`;
|
|
933
|
+
}
|
|
825
934
|
}
|
|
826
935
|
return err(
|
|
827
936
|
serviceError(errorCode, message, "sql", { meta })
|