@tinycloud/sdk-services 3.2.6-beta.4 → 3.3.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-1Ug6y7DC.d.cts → BaseService-Cc6Ueicm.d.cts} +1 -1
- package/dist/{BaseService-Bn07Vudy.d.ts → BaseService-DOdXd7sr.d.ts} +1 -1
- package/dist/encryption/index.cjs +8 -0
- 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 +8 -0
- package/dist/encryption/index.js.map +1 -1
- package/dist/index.cjs +94 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -6
- package/dist/index.d.ts +29 -6
- package/dist/index.js +93 -57
- 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 +66 -21
- package/dist/kv/index.cjs.map +1 -1
- package/dist/kv/index.d.cts +4 -4
- package/dist/kv/index.d.ts +4 -4
- package/dist/kv/index.js +66 -21
- package/dist/kv/index.js.map +1 -1
- package/dist/kv/replication/grantPolicy.cjs +82 -0
- package/dist/kv/replication/grantPolicy.cjs.map +1 -0
- package/dist/kv/replication/grantPolicy.d.cts +19 -0
- package/dist/kv/replication/grantPolicy.d.ts +19 -0
- package/dist/kv/replication/grantPolicy.js +50 -0
- package/dist/kv/replication/grantPolicy.js.map +1 -0
- package/dist/kv/replication/index.cjs +571 -128
- package/dist/kv/replication/index.cjs.map +1 -1
- package/dist/kv/replication/index.d.cts +3 -3
- package/dist/kv/replication/index.d.ts +3 -3
- package/dist/kv/replication/index.js +571 -128
- package/dist/kv/replication/index.js.map +1 -1
- package/dist/{scope-BnyVPZ5E.d.ts → scope-B-m6cKH-.d.ts} +310 -7
- package/dist/{scope-CQ5kmHlt.d.cts → scope-B7oeKdLM.d.cts} +310 -7
- package/dist/sql/index.cjs +70 -36
- package/dist/sql/index.cjs.map +1 -1
- package/dist/sql/index.d.cts +2 -2
- package/dist/sql/index.d.ts +2 -2
- package/dist/sql/index.js +70 -36
- package/dist/sql/index.js.map +1 -1
- package/dist/{types-gmdUiorz.d.cts → types-BjH0ZtGy.d.cts} +6 -0
- package/dist/{types-gmdUiorz.d.ts → types-BjH0ZtGy.d.ts} +6 -0
- package/package.json +16 -2
package/dist/index.cjs
CHANGED
|
@@ -122,6 +122,7 @@ __export(src_exports, {
|
|
|
122
122
|
kvPrefixCovers: () => kvPrefixCovers,
|
|
123
123
|
networkDiscoveryKey: () => networkDiscoveryKey,
|
|
124
124
|
networkError: () => networkError,
|
|
125
|
+
nodeRetryError: () => nodeRetryError,
|
|
125
126
|
notFoundError: () => notFoundError,
|
|
126
127
|
ok: () => ok,
|
|
127
128
|
openWrappedKey: () => openWrappedKey,
|
|
@@ -179,6 +180,14 @@ var ErrorCodes = {
|
|
|
179
180
|
ABORTED: "ABORTED",
|
|
180
181
|
INVALID_INPUT: "INVALID_INPUT",
|
|
181
182
|
PERMISSION_DENIED: "PERMISSION_DENIED",
|
|
183
|
+
// Node retry signals (tinycloud-node #347). `meta.retryable` says whether a
|
|
184
|
+
// retry is safe; `meta.retryAfterMs` carries the node's Retry-After.
|
|
185
|
+
/** HTTP 503 `sql_busy:<stage>` / `unavailable_retryable`: nothing was applied; retry after `meta.retryAfterMs`. */
|
|
186
|
+
NODE_BUSY: "NODE_BUSY",
|
|
187
|
+
/** HTTP 503 `request_timeout`: the node's deadline passed before any write began; nothing was applied. */
|
|
188
|
+
REQUEST_TIMEOUT: "REQUEST_TIMEOUT",
|
|
189
|
+
/** HTTP 500 `outcome_unknown`: a write may have been applied; read back before retrying a non-idempotent operation. */
|
|
190
|
+
OUTCOME_UNKNOWN: "OUTCOME_UNKNOWN",
|
|
182
191
|
// KV-specific errors
|
|
183
192
|
KV_NOT_FOUND: "KV_NOT_FOUND",
|
|
184
193
|
KV_WRITE_FAILED: "KV_WRITE_FAILED",
|
|
@@ -1123,6 +1132,42 @@ var ServiceContext = class {
|
|
|
1123
1132
|
}
|
|
1124
1133
|
};
|
|
1125
1134
|
|
|
1135
|
+
// src/responseErrors.ts
|
|
1136
|
+
function parseServiceErrorBody(errorText) {
|
|
1137
|
+
try {
|
|
1138
|
+
return JSON.parse(errorText);
|
|
1139
|
+
} catch {
|
|
1140
|
+
return {};
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
function formatServiceResponseError(serviceLabel, operation, status, errorText, parsed) {
|
|
1144
|
+
if (parsed.message) {
|
|
1145
|
+
return compactMessage(parsed.message);
|
|
1146
|
+
}
|
|
1147
|
+
if (looksLikeHtml(errorText)) {
|
|
1148
|
+
if (status === 524 || /524\s*[:-]/i.test(errorText) || /a timeout occurred/i.test(errorText)) {
|
|
1149
|
+
return `${serviceLabel} ${operation} failed: upstream request timed out. Please retry.`;
|
|
1150
|
+
}
|
|
1151
|
+
return `${serviceLabel} ${operation} failed: upstream service returned an HTML error page (${status}).`;
|
|
1152
|
+
}
|
|
1153
|
+
return `${serviceLabel} ${operation} failed: ${status} - ${compactMessage(errorText)}`;
|
|
1154
|
+
}
|
|
1155
|
+
function responseErrorMeta(status, statusText, errorText) {
|
|
1156
|
+
const meta = { status, statusText };
|
|
1157
|
+
const snippet = compactMessage(errorText);
|
|
1158
|
+
if (snippet) {
|
|
1159
|
+
meta.responseSnippet = snippet.slice(0, 300);
|
|
1160
|
+
}
|
|
1161
|
+
return meta;
|
|
1162
|
+
}
|
|
1163
|
+
function looksLikeHtml(text) {
|
|
1164
|
+
return /<!doctype html/i.test(text) || /<html[\s>]/i.test(text);
|
|
1165
|
+
}
|
|
1166
|
+
function compactMessage(message) {
|
|
1167
|
+
const compact = message.replace(/\s+/g, " ").trim();
|
|
1168
|
+
return compact.length > 500 ? `${compact.slice(0, 500)}...` : compact;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1126
1171
|
// src/errors.ts
|
|
1127
1172
|
var import_bootstrap = require("@tinycloud/bootstrap");
|
|
1128
1173
|
|
|
@@ -1282,6 +1327,30 @@ function networkError(service, message, cause) {
|
|
|
1282
1327
|
cause
|
|
1283
1328
|
};
|
|
1284
1329
|
}
|
|
1330
|
+
function nodeRetryError(service, message, response, errorText) {
|
|
1331
|
+
const body = errorText.trim();
|
|
1332
|
+
const [, token, stage] = /^([a-z_]+)(?::\s*([a-z_]+):)?/.exec(body) ?? [];
|
|
1333
|
+
let code;
|
|
1334
|
+
if (response.status === 503 && (token === "sql_busy" || token === "unavailable_retryable")) {
|
|
1335
|
+
code = ErrorCodes.NODE_BUSY;
|
|
1336
|
+
} else if (response.status === 503 && token === "request_timeout") {
|
|
1337
|
+
code = ErrorCodes.REQUEST_TIMEOUT;
|
|
1338
|
+
} else if (response.status === 500 && token === "outcome_unknown") {
|
|
1339
|
+
code = ErrorCodes.OUTCOME_UNKNOWN;
|
|
1340
|
+
} else {
|
|
1341
|
+
return void 0;
|
|
1342
|
+
}
|
|
1343
|
+
const retryable = code !== ErrorCodes.OUTCOME_UNKNOWN;
|
|
1344
|
+
const retryAfter = response.headers.get("retry-after")?.trim();
|
|
1345
|
+
return serviceError(code, message, service, {
|
|
1346
|
+
meta: {
|
|
1347
|
+
...responseErrorMeta(response.status, response.statusText, errorText),
|
|
1348
|
+
retryable,
|
|
1349
|
+
...token === "sql_busy" && stage ? { stage } : {},
|
|
1350
|
+
...retryable && retryAfter && /^\d{1,9}$/.test(retryAfter) ? { retryAfterMs: Number(retryAfter) * 1e3 } : {}
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1285
1354
|
var RequestTimeoutError = class extends Error {
|
|
1286
1355
|
constructor(timeoutMs) {
|
|
1287
1356
|
super(`Request timed out after ${timeoutMs}ms.`);
|
|
@@ -2620,9 +2689,12 @@ var KVService = class extends BaseService {
|
|
|
2620
2689
|
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2621
2690
|
));
|
|
2622
2691
|
}
|
|
2692
|
+
const message = `Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`;
|
|
2693
|
+
const retryError = nodeRetryError("kv", message, response, errorText);
|
|
2694
|
+
if (retryError) return err(retryError);
|
|
2623
2695
|
return err(serviceError(
|
|
2624
2696
|
ErrorCodes.NETWORK_ERROR,
|
|
2625
|
-
|
|
2697
|
+
message,
|
|
2626
2698
|
"kv",
|
|
2627
2699
|
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2628
2700
|
));
|
|
@@ -2704,19 +2776,14 @@ var KVService = class extends BaseService {
|
|
|
2704
2776
|
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2705
2777
|
));
|
|
2706
2778
|
}
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
`Concurrent KV update conflicted for key ${JSON.stringify(key)}`,
|
|
2711
|
-
"kv",
|
|
2712
|
-
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2713
|
-
));
|
|
2714
|
-
}
|
|
2779
|
+
const message = `Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`;
|
|
2780
|
+
const retryError = nodeRetryError("kv", message, response, errorText);
|
|
2781
|
+
if (retryError) return err(retryError);
|
|
2715
2782
|
const quotaError = this.handleQuotaErrorResponse(response, errorText, key);
|
|
2716
2783
|
if (quotaError) return quotaError;
|
|
2717
2784
|
return err(serviceError(
|
|
2718
2785
|
ErrorCodes.KV_WRITE_FAILED,
|
|
2719
|
-
|
|
2786
|
+
message,
|
|
2720
2787
|
"kv",
|
|
2721
2788
|
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2722
2789
|
));
|
|
@@ -3016,9 +3083,12 @@ var KVService = class extends BaseService {
|
|
|
3016
3083
|
);
|
|
3017
3084
|
}
|
|
3018
3085
|
const errorText = await response.text();
|
|
3086
|
+
const message = `Failed to list keys: ${response.status} - ${errorText}`;
|
|
3087
|
+
const retryError = nodeRetryError("kv", message, response, errorText);
|
|
3088
|
+
if (retryError) return err(retryError);
|
|
3019
3089
|
return err(serviceError(
|
|
3020
3090
|
ErrorCodes.NETWORK_ERROR,
|
|
3021
|
-
|
|
3091
|
+
message,
|
|
3022
3092
|
"kv",
|
|
3023
3093
|
{ meta: { status: response.status, statusText: response.statusText } }
|
|
3024
3094
|
));
|
|
@@ -3269,17 +3339,12 @@ var KVService = class extends BaseService {
|
|
|
3269
3339
|
{ meta: { status: response.status, statusText: response.statusText } }
|
|
3270
3340
|
));
|
|
3271
3341
|
}
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
`Concurrent KV delete conflicted for key ${JSON.stringify(key)}`,
|
|
3276
|
-
"kv",
|
|
3277
|
-
{ meta: { status: response.status, statusText: response.statusText } }
|
|
3278
|
-
));
|
|
3279
|
-
}
|
|
3342
|
+
const message = `Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`;
|
|
3343
|
+
const retryError = nodeRetryError("kv", message, response, errorText);
|
|
3344
|
+
if (retryError) return err(retryError);
|
|
3280
3345
|
return err(serviceError(
|
|
3281
3346
|
ErrorCodes.NETWORK_ERROR,
|
|
3282
|
-
|
|
3347
|
+
message,
|
|
3283
3348
|
"kv",
|
|
3284
3349
|
{ meta: { status: response.status, statusText: response.statusText } }
|
|
3285
3350
|
));
|
|
@@ -3320,10 +3385,13 @@ var KVService = class extends BaseService {
|
|
|
3320
3385
|
return await this.classifyNotFound(response, key);
|
|
3321
3386
|
}
|
|
3322
3387
|
const errorText = await response.text();
|
|
3388
|
+
const message = `Failed to get metadata for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`;
|
|
3389
|
+
const retryError = nodeRetryError("kv", message, response, errorText);
|
|
3390
|
+
if (retryError) return err(retryError);
|
|
3323
3391
|
return err(
|
|
3324
3392
|
serviceError(
|
|
3325
3393
|
ErrorCodes.NETWORK_ERROR,
|
|
3326
|
-
|
|
3394
|
+
message,
|
|
3327
3395
|
"kv",
|
|
3328
3396
|
{ meta: { status: response.status, statusText: response.statusText } }
|
|
3329
3397
|
)
|
|
@@ -3515,42 +3583,6 @@ function requiresSecretsOptIn(space, prefix) {
|
|
|
3515
3583
|
return prefix.split("/", 1)[0] === "vault";
|
|
3516
3584
|
}
|
|
3517
3585
|
|
|
3518
|
-
// src/responseErrors.ts
|
|
3519
|
-
function parseServiceErrorBody(errorText) {
|
|
3520
|
-
try {
|
|
3521
|
-
return JSON.parse(errorText);
|
|
3522
|
-
} catch {
|
|
3523
|
-
return {};
|
|
3524
|
-
}
|
|
3525
|
-
}
|
|
3526
|
-
function formatServiceResponseError(serviceLabel, operation, status, errorText, parsed) {
|
|
3527
|
-
if (parsed.message) {
|
|
3528
|
-
return compactMessage(parsed.message);
|
|
3529
|
-
}
|
|
3530
|
-
if (looksLikeHtml(errorText)) {
|
|
3531
|
-
if (status === 524 || /524\s*[:-]/i.test(errorText) || /a timeout occurred/i.test(errorText)) {
|
|
3532
|
-
return `${serviceLabel} ${operation} failed: upstream request timed out. Please retry.`;
|
|
3533
|
-
}
|
|
3534
|
-
return `${serviceLabel} ${operation} failed: upstream service returned an HTML error page (${status}).`;
|
|
3535
|
-
}
|
|
3536
|
-
return `${serviceLabel} ${operation} failed: ${status} - ${compactMessage(errorText)}`;
|
|
3537
|
-
}
|
|
3538
|
-
function responseErrorMeta(status, statusText, errorText) {
|
|
3539
|
-
const meta = { status, statusText };
|
|
3540
|
-
const snippet = compactMessage(errorText);
|
|
3541
|
-
if (snippet) {
|
|
3542
|
-
meta.responseSnippet = snippet.slice(0, 300);
|
|
3543
|
-
}
|
|
3544
|
-
return meta;
|
|
3545
|
-
}
|
|
3546
|
-
function looksLikeHtml(text) {
|
|
3547
|
-
return /<!doctype html/i.test(text) || /<html[\s>]/i.test(text);
|
|
3548
|
-
}
|
|
3549
|
-
function compactMessage(message) {
|
|
3550
|
-
const compact = message.replace(/\s+/g, " ").trim();
|
|
3551
|
-
return compact.length > 500 ? `${compact.slice(0, 500)}...` : compact;
|
|
3552
|
-
}
|
|
3553
|
-
|
|
3554
3586
|
// src/sql/SQLMigrations.ts
|
|
3555
3587
|
var SQLMigrations = class {
|
|
3556
3588
|
constructor(service, dbName) {
|
|
@@ -3966,6 +3998,8 @@ var SQLService = class extends BaseService {
|
|
|
3966
3998
|
if (response.status === 413 && parseStorageRejection(errorText)) {
|
|
3967
3999
|
return err(storageRejectionError("sql", 413, meta, errorText));
|
|
3968
4000
|
}
|
|
4001
|
+
const retryError = nodeRetryError("sql", `SQL ${operation} failed: ${response.status} - ${errorText}`, response, errorText);
|
|
4002
|
+
if (retryError) return err(retryError);
|
|
3969
4003
|
const errorBody = parseServiceErrorBody(errorText);
|
|
3970
4004
|
const errorCode = this.mapHttpStatusToErrorCode(
|
|
3971
4005
|
response.status,
|
|
@@ -4435,6 +4469,8 @@ var DuckDbService = class extends BaseService {
|
|
|
4435
4469
|
if (response.status === 413 && parseStorageRejection(errorText)) {
|
|
4436
4470
|
return err(storageRejectionError("duckdb", 413, meta, errorText));
|
|
4437
4471
|
}
|
|
4472
|
+
const retryError = nodeRetryError("duckdb", `DuckDB ${operation} failed: ${response.status} - ${errorText}`, response, errorText);
|
|
4473
|
+
if (retryError) return err(retryError);
|
|
4438
4474
|
const errorBody = parseServiceErrorBody(errorText);
|
|
4439
4475
|
const errorCode = this.mapHttpStatusToErrorCode(
|
|
4440
4476
|
response.status,
|
|
@@ -8476,6 +8512,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
8476
8512
|
kvPrefixCovers,
|
|
8477
8513
|
networkDiscoveryKey,
|
|
8478
8514
|
networkError,
|
|
8515
|
+
nodeRetryError,
|
|
8479
8516
|
notFoundError,
|
|
8480
8517
|
ok,
|
|
8481
8518
|
openWrappedKey,
|