@tinycloud/sdk-services 3.1.0-beta.1 → 3.1.0-beta.11
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-B_UKhVht.d.ts} +74 -2
- package/dist/{BaseService-CuV-ZVPs.d.ts → BaseService-DWbPbov7.d.cts} +74 -2
- package/dist/encryption/index.cjs +199 -100
- package/dist/encryption/index.cjs.map +1 -1
- package/dist/encryption/index.d.cts +2 -2
- package/dist/encryption/index.d.ts +2 -2
- package/dist/encryption/index.js +199 -100
- package/dist/encryption/index.js.map +1 -1
- package/dist/index.cjs +739 -258
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -42
- package/dist/index.d.ts +66 -42
- package/dist/index.js +714 -240
- package/dist/index.js.map +1 -1
- package/dist/internal/decrypt-transport-response-error.d.cts +1 -1
- package/dist/internal/decrypt-transport-response-error.d.ts +1 -1
- package/dist/kv/index.cjs +568 -74
- package/dist/kv/index.cjs.map +1 -1
- package/dist/kv/index.d.cts +197 -15
- package/dist/kv/index.d.ts +197 -15
- package/dist/kv/index.js +568 -74
- package/dist/kv/index.js.map +1 -1
- package/dist/sql/index.cjs +191 -35
- package/dist/sql/index.cjs.map +1 -1
- package/dist/sql/index.d.cts +6 -3
- package/dist/sql/index.d.ts +6 -3
- package/dist/sql/index.js +182 -26
- package/dist/sql/index.js.map +1 -1
- package/dist/{types-qwMmB7qB.d.cts → types-CVwwxqXF.d.cts} +8 -0
- package/dist/{types-qwMmB7qB.d.ts → types-CVwwxqXF.d.ts} +8 -0
- package/package.json +3 -3
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",
|
|
@@ -71,6 +79,7 @@ function serviceError(code, message, service, options) {
|
|
|
71
79
|
}
|
|
72
80
|
|
|
73
81
|
// src/errors.ts
|
|
82
|
+
import { CAPABILITY_REGISTRY } from "@tinycloud/bootstrap";
|
|
74
83
|
function authRequiredError(service) {
|
|
75
84
|
return {
|
|
76
85
|
code: ErrorCodes.AUTH_REQUIRED,
|
|
@@ -78,11 +87,31 @@ function authRequiredError(service) {
|
|
|
78
87
|
service
|
|
79
88
|
};
|
|
80
89
|
}
|
|
81
|
-
|
|
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
|
+
}
|
|
82
109
|
return {
|
|
83
110
|
code: ErrorCodes.TIMEOUT,
|
|
84
|
-
message:
|
|
85
|
-
service
|
|
111
|
+
message: `Request timed out after ${timeoutMs}ms.`,
|
|
112
|
+
service,
|
|
113
|
+
...cause === void 0 ? {} : { cause },
|
|
114
|
+
meta: { timeoutMs }
|
|
86
115
|
};
|
|
87
116
|
}
|
|
88
117
|
function abortedError(service) {
|
|
@@ -99,8 +128,47 @@ function parseAuthError(responseText) {
|
|
|
99
128
|
}
|
|
100
129
|
return {};
|
|
101
130
|
}
|
|
131
|
+
var grantableActions = new Set(
|
|
132
|
+
CAPABILITY_REGISTRY.map(({ urn }) => urn).filter((urn) => !urn.endsWith("/*"))
|
|
133
|
+
);
|
|
134
|
+
var capabilityServices = new Set(
|
|
135
|
+
CAPABILITY_REGISTRY.map(({ service }) => service.slice("tinycloud.".length))
|
|
136
|
+
);
|
|
137
|
+
function storageQuotaExceededError(service, message, meta) {
|
|
138
|
+
return {
|
|
139
|
+
code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,
|
|
140
|
+
message,
|
|
141
|
+
service,
|
|
142
|
+
meta
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function storageLimitReachedError(service, message, meta) {
|
|
146
|
+
return {
|
|
147
|
+
code: ErrorCodes.STORAGE_LIMIT_REACHED,
|
|
148
|
+
message,
|
|
149
|
+
service,
|
|
150
|
+
meta
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
var STORAGE_FULL_MESSAGE = "TinyCloud storage is full, so this change was not saved. Reading still works. Free up space or upgrade your plan to save again.";
|
|
154
|
+
var STORAGE_WRITE_TOO_LARGE_MESSAGE = "This change is larger than the TinyCloud storage you have left, so it was not saved. Reading still works. Free up space or upgrade your plan to save it.";
|
|
155
|
+
function parseStorageQuotaBytes(responseText) {
|
|
156
|
+
const match = responseText.match(/Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i);
|
|
157
|
+
if (!match) return void 0;
|
|
158
|
+
return {
|
|
159
|
+
usedBytes: parseInt(match[1], 10),
|
|
160
|
+
limitBytes: parseInt(match[2], 10)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function storageRejectionError(service, status, meta, responseText) {
|
|
164
|
+
const quotaMeta = { ...meta, ...parseStorageQuotaBytes(responseText) };
|
|
165
|
+
return status === 402 ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta) : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);
|
|
166
|
+
}
|
|
102
167
|
function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
|
|
103
168
|
if (error instanceof Error) {
|
|
169
|
+
if (isRequestTimeoutError(error)) {
|
|
170
|
+
return timeoutError(service, error.timeoutMs, error);
|
|
171
|
+
}
|
|
104
172
|
if (error.name === "AbortError") {
|
|
105
173
|
return abortedError(service);
|
|
106
174
|
}
|
|
@@ -122,6 +190,7 @@ function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
|
|
|
122
190
|
}
|
|
123
191
|
|
|
124
192
|
// src/base/BaseService.ts
|
|
193
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
125
194
|
var BaseService = class {
|
|
126
195
|
constructor() {
|
|
127
196
|
/**
|
|
@@ -302,6 +371,64 @@ var BaseService = class {
|
|
|
302
371
|
}
|
|
303
372
|
return controller.signal;
|
|
304
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
|
+
}
|
|
305
432
|
/**
|
|
306
433
|
* Wrap an operation with error handling and telemetry.
|
|
307
434
|
*
|
|
@@ -490,6 +617,7 @@ var SQLService = class extends BaseService {
|
|
|
490
617
|
if (!this.requireAuth()) {
|
|
491
618
|
return err(authRequiredError("sql"));
|
|
492
619
|
}
|
|
620
|
+
const request = this.createRequestSignal(options?.signal);
|
|
493
621
|
try {
|
|
494
622
|
const body = {
|
|
495
623
|
action: "query",
|
|
@@ -498,19 +626,22 @@ var SQLService = class extends BaseService {
|
|
|
498
626
|
};
|
|
499
627
|
if (options?.maxRows !== void 0) body.maxRows = options.maxRows;
|
|
500
628
|
if (options?.maxBytes !== void 0) body.maxBytes = options.maxBytes;
|
|
629
|
+
const action = this.actionForSql(sql, SQLAction.READ);
|
|
501
630
|
const response = await this.invokeSQL(
|
|
502
631
|
dbName,
|
|
503
|
-
|
|
632
|
+
action,
|
|
504
633
|
body,
|
|
505
|
-
|
|
634
|
+
request.signal
|
|
506
635
|
);
|
|
507
636
|
if (!response.ok) {
|
|
508
|
-
return this.handleErrorResponse(response, "query");
|
|
637
|
+
return this.handleErrorResponse(response, "query", dbName, [action]);
|
|
509
638
|
}
|
|
510
639
|
const data = await response.json();
|
|
511
640
|
return ok(data);
|
|
512
641
|
} catch (error) {
|
|
513
642
|
return err(wrapError("sql", error));
|
|
643
|
+
} finally {
|
|
644
|
+
request.dispose();
|
|
514
645
|
}
|
|
515
646
|
});
|
|
516
647
|
}
|
|
@@ -519,6 +650,7 @@ var SQLService = class extends BaseService {
|
|
|
519
650
|
if (!this.requireAuth()) {
|
|
520
651
|
return err(authRequiredError("sql"));
|
|
521
652
|
}
|
|
653
|
+
const request = this.createRequestSignal(options?.signal);
|
|
522
654
|
try {
|
|
523
655
|
const body = {
|
|
524
656
|
action: "execute",
|
|
@@ -534,19 +666,22 @@ var SQLService = class extends BaseService {
|
|
|
534
666
|
(statement) => this.actionForSql(statement, SQLAction.SCHEMA)
|
|
535
667
|
)
|
|
536
668
|
];
|
|
669
|
+
const requestedActions = this.dedupeActions(actions);
|
|
537
670
|
const response = await this.invokeSQL(
|
|
538
671
|
dbName,
|
|
539
|
-
|
|
672
|
+
requestedActions,
|
|
540
673
|
body,
|
|
541
|
-
|
|
674
|
+
request.signal
|
|
542
675
|
);
|
|
543
676
|
if (!response.ok) {
|
|
544
|
-
return this.handleErrorResponse(response, "execute");
|
|
677
|
+
return this.handleErrorResponse(response, "execute", dbName, requestedActions);
|
|
545
678
|
}
|
|
546
679
|
const data = await response.json();
|
|
547
680
|
return ok(data);
|
|
548
681
|
} catch (error) {
|
|
549
682
|
return err(wrapError("sql", error));
|
|
683
|
+
} finally {
|
|
684
|
+
request.dispose();
|
|
550
685
|
}
|
|
551
686
|
});
|
|
552
687
|
}
|
|
@@ -555,20 +690,24 @@ var SQLService = class extends BaseService {
|
|
|
555
690
|
if (!this.requireAuth()) {
|
|
556
691
|
return err(authRequiredError("sql"));
|
|
557
692
|
}
|
|
693
|
+
const request = this.createRequestSignal(options?.signal);
|
|
558
694
|
try {
|
|
695
|
+
const requestedActions = this.actionsForSqlBatch(statements);
|
|
559
696
|
const response = await this.invokeSQL(
|
|
560
697
|
dbName,
|
|
561
|
-
|
|
698
|
+
requestedActions,
|
|
562
699
|
{ action: "batch", statements },
|
|
563
|
-
|
|
700
|
+
request.signal
|
|
564
701
|
);
|
|
565
702
|
if (!response.ok) {
|
|
566
|
-
return this.handleErrorResponse(response, "batch");
|
|
703
|
+
return this.handleErrorResponse(response, "batch", dbName, requestedActions);
|
|
567
704
|
}
|
|
568
705
|
const data = await response.json();
|
|
569
706
|
return ok(data);
|
|
570
707
|
} catch (error) {
|
|
571
708
|
return err(wrapError("sql", error));
|
|
709
|
+
} finally {
|
|
710
|
+
request.dispose();
|
|
572
711
|
}
|
|
573
712
|
});
|
|
574
713
|
}
|
|
@@ -577,20 +716,23 @@ var SQLService = class extends BaseService {
|
|
|
577
716
|
if (!this.requireAuth()) {
|
|
578
717
|
return err(authRequiredError("sql"));
|
|
579
718
|
}
|
|
719
|
+
const request = this.createRequestSignal(options?.signal);
|
|
580
720
|
try {
|
|
581
721
|
const response = await this.invokeSQL(
|
|
582
722
|
dbName,
|
|
583
723
|
SQLAction.WRITE,
|
|
584
724
|
{ action: "execute_statement", name, params: params ?? [] },
|
|
585
|
-
|
|
725
|
+
request.signal
|
|
586
726
|
);
|
|
587
727
|
if (!response.ok) {
|
|
588
|
-
return this.handleErrorResponse(response, "executeStatement");
|
|
728
|
+
return this.handleErrorResponse(response, "executeStatement", dbName, [SQLAction.WRITE]);
|
|
589
729
|
}
|
|
590
730
|
const data = await response.json();
|
|
591
731
|
return ok(data);
|
|
592
732
|
} catch (error) {
|
|
593
733
|
return err(wrapError("sql", error));
|
|
734
|
+
} finally {
|
|
735
|
+
request.dispose();
|
|
594
736
|
}
|
|
595
737
|
});
|
|
596
738
|
}
|
|
@@ -599,15 +741,16 @@ var SQLService = class extends BaseService {
|
|
|
599
741
|
if (!this.requireAuth()) {
|
|
600
742
|
return err(authRequiredError("sql"));
|
|
601
743
|
}
|
|
744
|
+
const request = this.createRequestSignal(options?.signal);
|
|
602
745
|
try {
|
|
603
746
|
const response = await this.invokeSQL(
|
|
604
747
|
dbName,
|
|
605
748
|
SQLAction.READ,
|
|
606
749
|
{ action: "export" },
|
|
607
|
-
|
|
750
|
+
request.signal
|
|
608
751
|
);
|
|
609
752
|
if (!response.ok) {
|
|
610
|
-
return this.handleErrorResponse(response, "export");
|
|
753
|
+
return this.handleErrorResponse(response, "export", dbName, [SQLAction.READ]);
|
|
611
754
|
}
|
|
612
755
|
const resp = response;
|
|
613
756
|
if (typeof resp.blob === "function") {
|
|
@@ -618,26 +761,31 @@ var SQLService = class extends BaseService {
|
|
|
618
761
|
return ok(text);
|
|
619
762
|
} catch (error) {
|
|
620
763
|
return err(wrapError("sql", error));
|
|
764
|
+
} finally {
|
|
765
|
+
request.dispose();
|
|
621
766
|
}
|
|
622
767
|
});
|
|
623
768
|
}
|
|
624
769
|
async applyMigrationsOnDbUnlocked(dbName, options) {
|
|
625
770
|
return this.withTelemetry("migrations.apply", dbName, async () => {
|
|
626
|
-
const created = await this.ensureMigrationsTable(dbName, options.signal);
|
|
627
|
-
if (!created.ok) return created;
|
|
628
771
|
const listed = await this.queryOnDb(
|
|
629
772
|
dbName,
|
|
630
773
|
`SELECT id FROM ${MIGRATIONS_TABLE} WHERE namespace = ? ORDER BY applied_at, id`,
|
|
631
774
|
[options.namespace],
|
|
632
775
|
{ signal: options.signal }
|
|
633
776
|
);
|
|
634
|
-
|
|
777
|
+
const tableMissing = !listed.ok && (listed.error.code === ErrorCodes.SQL_DATABASE_NOT_FOUND || isMissingTableError(listed.error.message));
|
|
778
|
+
if (!listed.ok && !tableMissing) return listed;
|
|
635
779
|
const appliedIds = new Set(
|
|
636
|
-
listed.data.rows.map((row) => rowValue(row, 0)).filter((id) => typeof id === "string")
|
|
780
|
+
(listed.ok ? listed.data.rows : []).map((row) => rowValue(row, 0)).filter((id) => typeof id === "string")
|
|
637
781
|
);
|
|
638
782
|
const skipped = options.migrations.filter((migration) => appliedIds.has(migration.id)).map((migration) => migration.id);
|
|
639
783
|
const pending = options.migrations.filter((migration) => !appliedIds.has(migration.id));
|
|
640
784
|
const applied = [];
|
|
785
|
+
if (pending.length > 0 && tableMissing) {
|
|
786
|
+
const created = await this.ensureMigrationsTable(dbName, options.signal);
|
|
787
|
+
if (!created.ok) return created;
|
|
788
|
+
}
|
|
641
789
|
for (const migration of pending) {
|
|
642
790
|
const result = await this.applyOneMigration(dbName, options.namespace, migration, options.signal);
|
|
643
791
|
if (!result.ok) return result;
|
|
@@ -705,7 +853,7 @@ var SQLService = class extends BaseService {
|
|
|
705
853
|
"Content-Type": "application/json"
|
|
706
854
|
},
|
|
707
855
|
body: JSON.stringify(body),
|
|
708
|
-
signal
|
|
856
|
+
signal
|
|
709
857
|
});
|
|
710
858
|
}
|
|
711
859
|
actionForSql(sql, fallback) {
|
|
@@ -738,8 +886,12 @@ var SQLService = class extends BaseService {
|
|
|
738
886
|
}))
|
|
739
887
|
);
|
|
740
888
|
}
|
|
741
|
-
async handleErrorResponse(response, operation) {
|
|
889
|
+
async handleErrorResponse(response, operation, dbName, requestedActions) {
|
|
742
890
|
const errorText = await response.text();
|
|
891
|
+
const meta = responseErrorMeta(response.status, response.statusText, errorText);
|
|
892
|
+
if (response.status === 402) {
|
|
893
|
+
return err(storageRejectionError("sql", 402, meta, errorText));
|
|
894
|
+
}
|
|
743
895
|
const errorBody = parseServiceErrorBody(errorText);
|
|
744
896
|
const errorCode = this.mapHttpStatusToErrorCode(
|
|
745
897
|
response.status,
|
|
@@ -752,11 +904,12 @@ var SQLService = class extends BaseService {
|
|
|
752
904
|
errorText,
|
|
753
905
|
errorBody
|
|
754
906
|
);
|
|
755
|
-
const meta = responseErrorMeta(response.status, response.statusText, errorText);
|
|
756
907
|
if (response.status === 401) {
|
|
757
|
-
const {
|
|
758
|
-
if (action
|
|
759
|
-
|
|
908
|
+
const { action } = parseAuthError(errorText);
|
|
909
|
+
if (action && requestedActions.includes(action)) {
|
|
910
|
+
meta.requiredAction = action;
|
|
911
|
+
meta.resource = `${this.context.session.spaceId}/sql/${dbName}`;
|
|
912
|
+
}
|
|
760
913
|
}
|
|
761
914
|
return err(
|
|
762
915
|
serviceError(errorCode, message, "sql", { meta })
|
|
@@ -853,6 +1006,9 @@ function serializeSqlValues(values) {
|
|
|
853
1006
|
(value) => value instanceof Uint8Array ? Array.from(value) : value
|
|
854
1007
|
);
|
|
855
1008
|
}
|
|
1009
|
+
function isMissingTableError(message) {
|
|
1010
|
+
return /no such table/i.test(message);
|
|
1011
|
+
}
|
|
856
1012
|
function migrationKey(namespace, id) {
|
|
857
1013
|
return `${encodeURIComponent(namespace)}:${encodeURIComponent(id)}`;
|
|
858
1014
|
}
|