@tinycloud/sdk-services 3.2.0-beta.5 → 3.2.0-beta.8
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-BDQXjWMx.d.cts → BaseService-4wwJq1Gn.d.cts} +1 -1
- package/dist/{BaseService-Di_hVfUV.d.ts → BaseService-BGr2slUz.d.ts} +1 -1
- package/dist/encryption/index.cjs +5 -1
- 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 +5 -1
- package/dist/encryption/index.js.map +1 -1
- package/dist/index.cjs +180 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +156 -6
- package/dist/index.d.ts +156 -6
- package/dist/index.js +174 -9
- 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 +42 -3
- 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 +42 -3
- package/dist/kv/index.js.map +1 -1
- package/dist/kv/replication/index.cjs +2 -0
- 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 +2 -0
- package/dist/kv/replication/index.js.map +1 -1
- package/dist/{scope-BKge7zDT.d.ts → scope-CoKKmMiI.d.ts} +1 -1
- package/dist/{scope-C05VVAXH.d.cts → scope-D7wmjbMH.d.cts} +1 -1
- package/dist/sql/index.cjs +44 -2
- 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 +44 -2
- package/dist/sql/index.js.map +1 -1
- package/dist/{types-7A5rHWRY.d.cts → types-D-9A3Sut.d.cts} +6 -0
- package/dist/{types-7A5rHWRY.d.ts → types-D-9A3Sut.d.ts} +6 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -36,6 +36,8 @@ var ErrorCodes = {
|
|
|
36
36
|
// Storage quota errors
|
|
37
37
|
STORAGE_QUOTA_EXCEEDED: "STORAGE_QUOTA_EXCEEDED",
|
|
38
38
|
STORAGE_LIMIT_REACHED: "STORAGE_LIMIT_REACHED",
|
|
39
|
+
/** The node answered the usage read without usage numbers (it predates the read). */
|
|
40
|
+
STORAGE_STATUS_UNAVAILABLE: "STORAGE_STATUS_UNAVAILABLE",
|
|
39
41
|
// DuckDB-specific errors
|
|
40
42
|
DUCKDB_ERROR: "DUCKDB_ERROR",
|
|
41
43
|
DUCKDB_PERMISSION_DENIED: "DUCKDB_PERMISSION_DENIED",
|
|
@@ -65,7 +67,9 @@ var TelemetryEvents = {
|
|
|
65
67
|
REPLICATION_WRITE: "replication.write",
|
|
66
68
|
REPLICATION_SYNC: "replication.sync",
|
|
67
69
|
REPLICATION_STATE: "replication.state",
|
|
68
|
-
REPLICATION_DIVERGENCE: "replication.divergence"
|
|
70
|
+
REPLICATION_DIVERGENCE: "replication.divergence",
|
|
71
|
+
/** Emitted once, on the first write rejected because storage is full. */
|
|
72
|
+
STORAGE_FULL: "storage.full"
|
|
69
73
|
};
|
|
70
74
|
function ok(data) {
|
|
71
75
|
return { ok: true, data };
|
|
@@ -1284,8 +1288,43 @@ function parseStorageQuotaBytes(responseText) {
|
|
|
1284
1288
|
limitBytes: parseInt(match[2], 10)
|
|
1285
1289
|
};
|
|
1286
1290
|
}
|
|
1291
|
+
var STORAGE_REJECTION_BODY_ERRORS = {
|
|
1292
|
+
storage_quota_exceeded: true,
|
|
1293
|
+
storage_limit_reached: true
|
|
1294
|
+
};
|
|
1295
|
+
function isByteCount(value) {
|
|
1296
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1297
|
+
}
|
|
1298
|
+
function byteCountsOf(value) {
|
|
1299
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
1300
|
+
const { usedBytes, limitBytes } = value;
|
|
1301
|
+
return isByteCount(usedBytes) && isByteCount(limitBytes) ? { usedBytes, limitBytes } : void 0;
|
|
1302
|
+
}
|
|
1303
|
+
function parseStorageRejection(responseText) {
|
|
1304
|
+
let body;
|
|
1305
|
+
try {
|
|
1306
|
+
body = JSON.parse(responseText);
|
|
1307
|
+
} catch {
|
|
1308
|
+
return parseStorageQuotaBytes(responseText);
|
|
1309
|
+
}
|
|
1310
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
1311
|
+
const record = body;
|
|
1312
|
+
const message = typeof record.message === "string" ? record.message : "";
|
|
1313
|
+
const space = byteCountsOf(record.space) ?? parseStorageQuotaBytes(message);
|
|
1314
|
+
const isStorageBody = typeof record.error === "string" && STORAGE_REJECTION_BODY_ERRORS[record.error] === true;
|
|
1315
|
+
if (!space && !isStorageBody) {
|
|
1316
|
+
return void 0;
|
|
1317
|
+
}
|
|
1318
|
+
const details = { ...space };
|
|
1319
|
+
const account = byteCountsOf(record.account);
|
|
1320
|
+
if (account) {
|
|
1321
|
+
const plan = record.account.plan;
|
|
1322
|
+
details.account = typeof plan === "string" && plan !== "" ? { ...account, plan } : account;
|
|
1323
|
+
}
|
|
1324
|
+
return details;
|
|
1325
|
+
}
|
|
1287
1326
|
function storageRejectionError(service, status, meta, responseText) {
|
|
1288
|
-
const quotaMeta = { ...meta, ...
|
|
1327
|
+
const quotaMeta = { ...meta, ...parseStorageRejection(responseText) };
|
|
1289
1328
|
return status === 402 ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta) : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);
|
|
1290
1329
|
}
|
|
1291
1330
|
function isStorageFullError(error) {
|
|
@@ -1814,7 +1853,7 @@ var KVService = class extends BaseService {
|
|
|
1814
1853
|
if (response.status !== 402 && response.status !== 413) {
|
|
1815
1854
|
return void 0;
|
|
1816
1855
|
}
|
|
1817
|
-
if (response.status === 413 && !
|
|
1856
|
+
if (response.status === 413 && !parseStorageRejection(errorText)) {
|
|
1818
1857
|
return void 0;
|
|
1819
1858
|
}
|
|
1820
1859
|
return err(
|
|
@@ -3737,6 +3776,9 @@ var SQLService = class extends BaseService {
|
|
|
3737
3776
|
if (response.status === 402) {
|
|
3738
3777
|
return err(storageRejectionError("sql", 402, meta, errorText));
|
|
3739
3778
|
}
|
|
3779
|
+
if (response.status === 413 && parseStorageRejection(errorText)) {
|
|
3780
|
+
return err(storageRejectionError("sql", 413, meta, errorText));
|
|
3781
|
+
}
|
|
3740
3782
|
const errorBody = parseServiceErrorBody(errorText);
|
|
3741
3783
|
const errorCode = this.mapHttpStatusToErrorCode(
|
|
3742
3784
|
response.status,
|
|
@@ -4203,6 +4245,9 @@ var DuckDbService = class extends BaseService {
|
|
|
4203
4245
|
if (response.status === 402) {
|
|
4204
4246
|
return err(storageRejectionError("duckdb", 402, meta, errorText));
|
|
4205
4247
|
}
|
|
4248
|
+
if (response.status === 413 && parseStorageRejection(errorText)) {
|
|
4249
|
+
return err(storageRejectionError("duckdb", 413, meta, errorText));
|
|
4250
|
+
}
|
|
4206
4251
|
const errorBody = parseServiceErrorBody(errorText);
|
|
4207
4252
|
const errorCode = this.mapHttpStatusToErrorCode(
|
|
4208
4253
|
response.status,
|
|
@@ -5036,14 +5081,12 @@ async function responseError(service, message, response) {
|
|
|
5036
5081
|
}
|
|
5037
5082
|
} catch {
|
|
5038
5083
|
}
|
|
5039
|
-
|
|
5084
|
+
return serviceError(
|
|
5085
|
+
response.status === 408 || response.status === 504 ? ErrorCodes.TIMEOUT : ErrorCodes.NETWORK_ERROR,
|
|
5086
|
+
`${message}: ${response.status} ${detail}`,
|
|
5040
5087
|
service,
|
|
5041
|
-
|
|
5088
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
5042
5089
|
);
|
|
5043
|
-
return {
|
|
5044
|
-
...error,
|
|
5045
|
-
meta: { ...error.meta, status: response.status, statusText: response.statusText }
|
|
5046
|
-
};
|
|
5047
5090
|
}
|
|
5048
5091
|
async function* parseSseStream(body, signal) {
|
|
5049
5092
|
if (!body) {
|
|
@@ -5264,6 +5307,122 @@ function parsedStringArrayField(value, key) {
|
|
|
5264
5307
|
}
|
|
5265
5308
|
}
|
|
5266
5309
|
|
|
5310
|
+
// src/storage/StorageFullMonitor.ts
|
|
5311
|
+
var StorageFullMonitor = class {
|
|
5312
|
+
/**
|
|
5313
|
+
* @param ownerOf Owner key of a space id. Spaces with one key share one
|
|
5314
|
+
* storage budget. Defaults to the space id itself.
|
|
5315
|
+
*/
|
|
5316
|
+
constructor(ownerOf = (spaceId) => spaceId) {
|
|
5317
|
+
this.ownerOf = ownerOf;
|
|
5318
|
+
/** First rejection per owner key; {@link UNKNOWN_OWNER} when the space was unknown. */
|
|
5319
|
+
this.full = /* @__PURE__ */ new Map();
|
|
5320
|
+
this.handlers = /* @__PURE__ */ new Set();
|
|
5321
|
+
}
|
|
5322
|
+
/** True while any owner's storage is known to be full. */
|
|
5323
|
+
get isFull() {
|
|
5324
|
+
return this.full.size > 0;
|
|
5325
|
+
}
|
|
5326
|
+
/** The rejection `storage.full` announced, until the episode ends. */
|
|
5327
|
+
get rejection() {
|
|
5328
|
+
return this.event;
|
|
5329
|
+
}
|
|
5330
|
+
/**
|
|
5331
|
+
* True when a write to `spaceId`'s owner was rejected, or a write whose
|
|
5332
|
+
* space was unknown was: that one could have been any owner's.
|
|
5333
|
+
*/
|
|
5334
|
+
isFullFor(spaceId) {
|
|
5335
|
+
return this.full.has(UNKNOWN_OWNER) || spaceId !== void 0 && this.full.has(this.ownerOf(spaceId));
|
|
5336
|
+
}
|
|
5337
|
+
/** Watch a context's service errors. Returns the unsubscribe function. */
|
|
5338
|
+
observe(context) {
|
|
5339
|
+
return context.on(TelemetryEvents.SERVICE_ERROR, (data) => {
|
|
5340
|
+
const { service, error } = data;
|
|
5341
|
+
if (!error || !isStorageFullError(error)) return;
|
|
5342
|
+
const spaceId = context.session?.spaceId;
|
|
5343
|
+
const owner = spaceId ? this.ownerOf(spaceId) : UNKNOWN_OWNER;
|
|
5344
|
+
if (this.full.has(owner)) return;
|
|
5345
|
+
const meta = error.meta ?? {};
|
|
5346
|
+
const event = { service: service ?? error.service, error };
|
|
5347
|
+
if (typeof meta.usedBytes === "number") event.usedBytes = meta.usedBytes;
|
|
5348
|
+
if (typeof meta.limitBytes === "number") event.limitBytes = meta.limitBytes;
|
|
5349
|
+
if (typeof meta.account === "object" && meta.account !== null) {
|
|
5350
|
+
event.account = meta.account;
|
|
5351
|
+
}
|
|
5352
|
+
if (spaceId) event.spaceId = spaceId;
|
|
5353
|
+
this.full.set(owner, event);
|
|
5354
|
+
if (this.event) return;
|
|
5355
|
+
this.event = event;
|
|
5356
|
+
context.emit(TelemetryEvents.STORAGE_FULL, event);
|
|
5357
|
+
for (const handler of this.handlers) {
|
|
5358
|
+
try {
|
|
5359
|
+
handler(event);
|
|
5360
|
+
} catch (handlerError) {
|
|
5361
|
+
console.error(`Error in "${TelemetryEvents.STORAGE_FULL}" handler:`, handlerError);
|
|
5362
|
+
}
|
|
5363
|
+
}
|
|
5364
|
+
});
|
|
5365
|
+
}
|
|
5366
|
+
/** Subscribe to the `storage.full` event. Returns the unsubscribe function. */
|
|
5367
|
+
on(handler) {
|
|
5368
|
+
this.handlers.add(handler);
|
|
5369
|
+
return () => {
|
|
5370
|
+
this.handlers.delete(handler);
|
|
5371
|
+
};
|
|
5372
|
+
}
|
|
5373
|
+
/** Forget that any owner's storage is full; the next rejection emits `storage.full` again. */
|
|
5374
|
+
clear() {
|
|
5375
|
+
this.full.clear();
|
|
5376
|
+
this.event = void 0;
|
|
5377
|
+
}
|
|
5378
|
+
/**
|
|
5379
|
+
* Forget that `spaceId`'s owner is full (`undefined`: the rejections whose
|
|
5380
|
+
* space was unknown). Once no owner is full, the next rejection emits
|
|
5381
|
+
* `storage.full` again.
|
|
5382
|
+
*/
|
|
5383
|
+
clearFor(spaceId) {
|
|
5384
|
+
this.full.delete(spaceId === void 0 ? UNKNOWN_OWNER : this.ownerOf(spaceId));
|
|
5385
|
+
if (this.full.size === 0) this.event = void 0;
|
|
5386
|
+
}
|
|
5387
|
+
};
|
|
5388
|
+
var UNKNOWN_OWNER = "\0unknown";
|
|
5389
|
+
|
|
5390
|
+
// src/storage/status.ts
|
|
5391
|
+
var STORAGE_MANAGE_URL = "https://account.tinycloud.xyz/billing";
|
|
5392
|
+
var STORAGE_NEARLY_FULL_RATIO = 0.9;
|
|
5393
|
+
function storageUsageState(usedBytes, limitBytes) {
|
|
5394
|
+
if (limitBytes === null) return "ok";
|
|
5395
|
+
if (usedBytes >= limitBytes) return "full";
|
|
5396
|
+
return usedBytes >= limitBytes * STORAGE_NEARLY_FULL_RATIO ? "nearly_full" : "ok";
|
|
5397
|
+
}
|
|
5398
|
+
function storageUsageOf(value) {
|
|
5399
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
5400
|
+
const { usedBytes, limitBytes } = value;
|
|
5401
|
+
const isByteCount2 = (n) => typeof n === "number" && Number.isSafeInteger(n) && n >= 0;
|
|
5402
|
+
if (!isByteCount2(usedBytes)) return void 0;
|
|
5403
|
+
if (limitBytes !== null && !isByteCount2(limitBytes)) return void 0;
|
|
5404
|
+
return { usedBytes, limitBytes };
|
|
5405
|
+
}
|
|
5406
|
+
function parseStorageStatus(body) {
|
|
5407
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
5408
|
+
const record = body;
|
|
5409
|
+
const space = storageUsageOf(record.space);
|
|
5410
|
+
if (!space) return void 0;
|
|
5411
|
+
const status = {
|
|
5412
|
+
...space,
|
|
5413
|
+
state: storageUsageState(space.usedBytes, space.limitBytes),
|
|
5414
|
+
manageUrl: typeof record.manageUrl === "string" && record.manageUrl.startsWith("https://") ? record.manageUrl : STORAGE_MANAGE_URL
|
|
5415
|
+
};
|
|
5416
|
+
const account = storageUsageOf(record.account);
|
|
5417
|
+
if (account) {
|
|
5418
|
+
const plan = record.account.plan;
|
|
5419
|
+
status.account = typeof plan === "string" && plan !== "" ? { ...account, plan } : account;
|
|
5420
|
+
if (status.account.plan) status.plan = status.account.plan;
|
|
5421
|
+
status.state = storageUsageState(account.usedBytes, account.limitBytes);
|
|
5422
|
+
}
|
|
5423
|
+
return status;
|
|
5424
|
+
}
|
|
5425
|
+
|
|
5267
5426
|
// src/quota/TinyCloudQuota.ts
|
|
5268
5427
|
var TinyCloudQuota = class {
|
|
5269
5428
|
constructor(config = {}) {
|
|
@@ -8066,6 +8225,8 @@ export {
|
|
|
8066
8225
|
SQLMigrations,
|
|
8067
8226
|
SQLService,
|
|
8068
8227
|
STORAGE_FULL_MESSAGE,
|
|
8228
|
+
STORAGE_MANAGE_URL,
|
|
8229
|
+
STORAGE_NEARLY_FULL_RATIO,
|
|
8069
8230
|
STORAGE_WRITE_TOO_LARGE_MESSAGE,
|
|
8070
8231
|
SecretsService,
|
|
8071
8232
|
ServiceContext,
|
|
@@ -8075,6 +8236,7 @@ export {
|
|
|
8075
8236
|
ServiceResponseEventSchema,
|
|
8076
8237
|
ServiceRetryEventSchema,
|
|
8077
8238
|
ServiceSessionSchema,
|
|
8239
|
+
StorageFullMonitor,
|
|
8078
8240
|
TC832_RESERVED_SCOPES,
|
|
8079
8241
|
TelemetryEvents,
|
|
8080
8242
|
TelemetrySpanEventSchema,
|
|
@@ -8136,6 +8298,8 @@ export {
|
|
|
8136
8298
|
parsePermissionHintFromErrorText,
|
|
8137
8299
|
parseSecretCatalogKey,
|
|
8138
8300
|
parseStorageQuotaBytes,
|
|
8301
|
+
parseStorageRejection,
|
|
8302
|
+
parseStorageStatus,
|
|
8139
8303
|
permissionDeniedError,
|
|
8140
8304
|
replicationIdentityKey,
|
|
8141
8305
|
requiresSecretsOptIn,
|
|
@@ -8145,6 +8309,7 @@ export {
|
|
|
8145
8309
|
storageLimitReachedError,
|
|
8146
8310
|
storageQuotaExceededError,
|
|
8147
8311
|
storageRejectionError,
|
|
8312
|
+
storageUsageState,
|
|
8148
8313
|
timeoutError,
|
|
8149
8314
|
tinyCloudDebugLogger,
|
|
8150
8315
|
utf8Decode,
|