@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.cjs
CHANGED
|
@@ -59,6 +59,8 @@ __export(src_exports, {
|
|
|
59
59
|
SQLMigrations: () => SQLMigrations,
|
|
60
60
|
SQLService: () => SQLService,
|
|
61
61
|
STORAGE_FULL_MESSAGE: () => STORAGE_FULL_MESSAGE,
|
|
62
|
+
STORAGE_MANAGE_URL: () => STORAGE_MANAGE_URL,
|
|
63
|
+
STORAGE_NEARLY_FULL_RATIO: () => STORAGE_NEARLY_FULL_RATIO,
|
|
62
64
|
STORAGE_WRITE_TOO_LARGE_MESSAGE: () => STORAGE_WRITE_TOO_LARGE_MESSAGE,
|
|
63
65
|
SecretsService: () => SecretsService,
|
|
64
66
|
ServiceContext: () => ServiceContext,
|
|
@@ -68,6 +70,7 @@ __export(src_exports, {
|
|
|
68
70
|
ServiceResponseEventSchema: () => ServiceResponseEventSchema,
|
|
69
71
|
ServiceRetryEventSchema: () => ServiceRetryEventSchema,
|
|
70
72
|
ServiceSessionSchema: () => ServiceSessionSchema,
|
|
73
|
+
StorageFullMonitor: () => StorageFullMonitor,
|
|
71
74
|
TC832_RESERVED_SCOPES: () => TC832_RESERVED_SCOPES,
|
|
72
75
|
TelemetryEvents: () => TelemetryEvents,
|
|
73
76
|
TelemetrySpanEventSchema: () => TelemetrySpanEventSchema,
|
|
@@ -129,6 +132,8 @@ __export(src_exports, {
|
|
|
129
132
|
parsePermissionHintFromErrorText: () => parsePermissionHintFromErrorText,
|
|
130
133
|
parseSecretCatalogKey: () => parseSecretCatalogKey,
|
|
131
134
|
parseStorageQuotaBytes: () => parseStorageQuotaBytes,
|
|
135
|
+
parseStorageRejection: () => parseStorageRejection,
|
|
136
|
+
parseStorageStatus: () => parseStorageStatus,
|
|
132
137
|
permissionDeniedError: () => permissionDeniedError,
|
|
133
138
|
replicationIdentityKey: () => replicationIdentityKey,
|
|
134
139
|
requiresSecretsOptIn: () => requiresSecretsOptIn,
|
|
@@ -138,6 +143,7 @@ __export(src_exports, {
|
|
|
138
143
|
storageLimitReachedError: () => storageLimitReachedError,
|
|
139
144
|
storageQuotaExceededError: () => storageQuotaExceededError,
|
|
140
145
|
storageRejectionError: () => storageRejectionError,
|
|
146
|
+
storageUsageState: () => storageUsageState,
|
|
141
147
|
timeoutError: () => timeoutError,
|
|
142
148
|
tinyCloudDebugLogger: () => tinyCloudDebugLogger,
|
|
143
149
|
utf8Decode: () => utf8Decode,
|
|
@@ -195,6 +201,8 @@ var ErrorCodes = {
|
|
|
195
201
|
// Storage quota errors
|
|
196
202
|
STORAGE_QUOTA_EXCEEDED: "STORAGE_QUOTA_EXCEEDED",
|
|
197
203
|
STORAGE_LIMIT_REACHED: "STORAGE_LIMIT_REACHED",
|
|
204
|
+
/** The node answered the usage read without usage numbers (it predates the read). */
|
|
205
|
+
STORAGE_STATUS_UNAVAILABLE: "STORAGE_STATUS_UNAVAILABLE",
|
|
198
206
|
// DuckDB-specific errors
|
|
199
207
|
DUCKDB_ERROR: "DUCKDB_ERROR",
|
|
200
208
|
DUCKDB_PERMISSION_DENIED: "DUCKDB_PERMISSION_DENIED",
|
|
@@ -224,7 +232,9 @@ var TelemetryEvents = {
|
|
|
224
232
|
REPLICATION_WRITE: "replication.write",
|
|
225
233
|
REPLICATION_SYNC: "replication.sync",
|
|
226
234
|
REPLICATION_STATE: "replication.state",
|
|
227
|
-
REPLICATION_DIVERGENCE: "replication.divergence"
|
|
235
|
+
REPLICATION_DIVERGENCE: "replication.divergence",
|
|
236
|
+
/** Emitted once, on the first write rejected because storage is full. */
|
|
237
|
+
STORAGE_FULL: "storage.full"
|
|
228
238
|
};
|
|
229
239
|
function ok(data) {
|
|
230
240
|
return { ok: true, data };
|
|
@@ -1443,8 +1453,43 @@ function parseStorageQuotaBytes(responseText) {
|
|
|
1443
1453
|
limitBytes: parseInt(match[2], 10)
|
|
1444
1454
|
};
|
|
1445
1455
|
}
|
|
1456
|
+
var STORAGE_REJECTION_BODY_ERRORS = {
|
|
1457
|
+
storage_quota_exceeded: true,
|
|
1458
|
+
storage_limit_reached: true
|
|
1459
|
+
};
|
|
1460
|
+
function isByteCount(value) {
|
|
1461
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1462
|
+
}
|
|
1463
|
+
function byteCountsOf(value) {
|
|
1464
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
1465
|
+
const { usedBytes, limitBytes } = value;
|
|
1466
|
+
return isByteCount(usedBytes) && isByteCount(limitBytes) ? { usedBytes, limitBytes } : void 0;
|
|
1467
|
+
}
|
|
1468
|
+
function parseStorageRejection(responseText) {
|
|
1469
|
+
let body;
|
|
1470
|
+
try {
|
|
1471
|
+
body = JSON.parse(responseText);
|
|
1472
|
+
} catch {
|
|
1473
|
+
return parseStorageQuotaBytes(responseText);
|
|
1474
|
+
}
|
|
1475
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
1476
|
+
const record = body;
|
|
1477
|
+
const message = typeof record.message === "string" ? record.message : "";
|
|
1478
|
+
const space = byteCountsOf(record.space) ?? parseStorageQuotaBytes(message);
|
|
1479
|
+
const isStorageBody = typeof record.error === "string" && STORAGE_REJECTION_BODY_ERRORS[record.error] === true;
|
|
1480
|
+
if (!space && !isStorageBody) {
|
|
1481
|
+
return void 0;
|
|
1482
|
+
}
|
|
1483
|
+
const details = { ...space };
|
|
1484
|
+
const account = byteCountsOf(record.account);
|
|
1485
|
+
if (account) {
|
|
1486
|
+
const plan = record.account.plan;
|
|
1487
|
+
details.account = typeof plan === "string" && plan !== "" ? { ...account, plan } : account;
|
|
1488
|
+
}
|
|
1489
|
+
return details;
|
|
1490
|
+
}
|
|
1446
1491
|
function storageRejectionError(service, status, meta, responseText) {
|
|
1447
|
-
const quotaMeta = { ...meta, ...
|
|
1492
|
+
const quotaMeta = { ...meta, ...parseStorageRejection(responseText) };
|
|
1448
1493
|
return status === 402 ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta) : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);
|
|
1449
1494
|
}
|
|
1450
1495
|
function isStorageFullError(error) {
|
|
@@ -1973,7 +2018,7 @@ var KVService = class extends BaseService {
|
|
|
1973
2018
|
if (response.status !== 402 && response.status !== 413) {
|
|
1974
2019
|
return void 0;
|
|
1975
2020
|
}
|
|
1976
|
-
if (response.status === 413 && !
|
|
2021
|
+
if (response.status === 413 && !parseStorageRejection(errorText)) {
|
|
1977
2022
|
return void 0;
|
|
1978
2023
|
}
|
|
1979
2024
|
return err(
|
|
@@ -3896,6 +3941,9 @@ var SQLService = class extends BaseService {
|
|
|
3896
3941
|
if (response.status === 402) {
|
|
3897
3942
|
return err(storageRejectionError("sql", 402, meta, errorText));
|
|
3898
3943
|
}
|
|
3944
|
+
if (response.status === 413 && parseStorageRejection(errorText)) {
|
|
3945
|
+
return err(storageRejectionError("sql", 413, meta, errorText));
|
|
3946
|
+
}
|
|
3899
3947
|
const errorBody = parseServiceErrorBody(errorText);
|
|
3900
3948
|
const errorCode = this.mapHttpStatusToErrorCode(
|
|
3901
3949
|
response.status,
|
|
@@ -4362,6 +4410,9 @@ var DuckDbService = class extends BaseService {
|
|
|
4362
4410
|
if (response.status === 402) {
|
|
4363
4411
|
return err(storageRejectionError("duckdb", 402, meta, errorText));
|
|
4364
4412
|
}
|
|
4413
|
+
if (response.status === 413 && parseStorageRejection(errorText)) {
|
|
4414
|
+
return err(storageRejectionError("duckdb", 413, meta, errorText));
|
|
4415
|
+
}
|
|
4365
4416
|
const errorBody = parseServiceErrorBody(errorText);
|
|
4366
4417
|
const errorCode = this.mapHttpStatusToErrorCode(
|
|
4367
4418
|
response.status,
|
|
@@ -5195,14 +5246,12 @@ async function responseError(service, message, response) {
|
|
|
5195
5246
|
}
|
|
5196
5247
|
} catch {
|
|
5197
5248
|
}
|
|
5198
|
-
|
|
5249
|
+
return serviceError(
|
|
5250
|
+
response.status === 408 || response.status === 504 ? ErrorCodes.TIMEOUT : ErrorCodes.NETWORK_ERROR,
|
|
5251
|
+
`${message}: ${response.status} ${detail}`,
|
|
5199
5252
|
service,
|
|
5200
|
-
|
|
5253
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
5201
5254
|
);
|
|
5202
|
-
return {
|
|
5203
|
-
...error,
|
|
5204
|
-
meta: { ...error.meta, status: response.status, statusText: response.statusText }
|
|
5205
|
-
};
|
|
5206
5255
|
}
|
|
5207
5256
|
async function* parseSseStream(body, signal) {
|
|
5208
5257
|
if (!body) {
|
|
@@ -5423,6 +5472,122 @@ function parsedStringArrayField(value, key) {
|
|
|
5423
5472
|
}
|
|
5424
5473
|
}
|
|
5425
5474
|
|
|
5475
|
+
// src/storage/StorageFullMonitor.ts
|
|
5476
|
+
var StorageFullMonitor = class {
|
|
5477
|
+
/**
|
|
5478
|
+
* @param ownerOf Owner key of a space id. Spaces with one key share one
|
|
5479
|
+
* storage budget. Defaults to the space id itself.
|
|
5480
|
+
*/
|
|
5481
|
+
constructor(ownerOf = (spaceId) => spaceId) {
|
|
5482
|
+
this.ownerOf = ownerOf;
|
|
5483
|
+
/** First rejection per owner key; {@link UNKNOWN_OWNER} when the space was unknown. */
|
|
5484
|
+
this.full = /* @__PURE__ */ new Map();
|
|
5485
|
+
this.handlers = /* @__PURE__ */ new Set();
|
|
5486
|
+
}
|
|
5487
|
+
/** True while any owner's storage is known to be full. */
|
|
5488
|
+
get isFull() {
|
|
5489
|
+
return this.full.size > 0;
|
|
5490
|
+
}
|
|
5491
|
+
/** The rejection `storage.full` announced, until the episode ends. */
|
|
5492
|
+
get rejection() {
|
|
5493
|
+
return this.event;
|
|
5494
|
+
}
|
|
5495
|
+
/**
|
|
5496
|
+
* True when a write to `spaceId`'s owner was rejected, or a write whose
|
|
5497
|
+
* space was unknown was: that one could have been any owner's.
|
|
5498
|
+
*/
|
|
5499
|
+
isFullFor(spaceId) {
|
|
5500
|
+
return this.full.has(UNKNOWN_OWNER) || spaceId !== void 0 && this.full.has(this.ownerOf(spaceId));
|
|
5501
|
+
}
|
|
5502
|
+
/** Watch a context's service errors. Returns the unsubscribe function. */
|
|
5503
|
+
observe(context) {
|
|
5504
|
+
return context.on(TelemetryEvents.SERVICE_ERROR, (data) => {
|
|
5505
|
+
const { service, error } = data;
|
|
5506
|
+
if (!error || !isStorageFullError(error)) return;
|
|
5507
|
+
const spaceId = context.session?.spaceId;
|
|
5508
|
+
const owner = spaceId ? this.ownerOf(spaceId) : UNKNOWN_OWNER;
|
|
5509
|
+
if (this.full.has(owner)) return;
|
|
5510
|
+
const meta = error.meta ?? {};
|
|
5511
|
+
const event = { service: service ?? error.service, error };
|
|
5512
|
+
if (typeof meta.usedBytes === "number") event.usedBytes = meta.usedBytes;
|
|
5513
|
+
if (typeof meta.limitBytes === "number") event.limitBytes = meta.limitBytes;
|
|
5514
|
+
if (typeof meta.account === "object" && meta.account !== null) {
|
|
5515
|
+
event.account = meta.account;
|
|
5516
|
+
}
|
|
5517
|
+
if (spaceId) event.spaceId = spaceId;
|
|
5518
|
+
this.full.set(owner, event);
|
|
5519
|
+
if (this.event) return;
|
|
5520
|
+
this.event = event;
|
|
5521
|
+
context.emit(TelemetryEvents.STORAGE_FULL, event);
|
|
5522
|
+
for (const handler of this.handlers) {
|
|
5523
|
+
try {
|
|
5524
|
+
handler(event);
|
|
5525
|
+
} catch (handlerError) {
|
|
5526
|
+
console.error(`Error in "${TelemetryEvents.STORAGE_FULL}" handler:`, handlerError);
|
|
5527
|
+
}
|
|
5528
|
+
}
|
|
5529
|
+
});
|
|
5530
|
+
}
|
|
5531
|
+
/** Subscribe to the `storage.full` event. Returns the unsubscribe function. */
|
|
5532
|
+
on(handler) {
|
|
5533
|
+
this.handlers.add(handler);
|
|
5534
|
+
return () => {
|
|
5535
|
+
this.handlers.delete(handler);
|
|
5536
|
+
};
|
|
5537
|
+
}
|
|
5538
|
+
/** Forget that any owner's storage is full; the next rejection emits `storage.full` again. */
|
|
5539
|
+
clear() {
|
|
5540
|
+
this.full.clear();
|
|
5541
|
+
this.event = void 0;
|
|
5542
|
+
}
|
|
5543
|
+
/**
|
|
5544
|
+
* Forget that `spaceId`'s owner is full (`undefined`: the rejections whose
|
|
5545
|
+
* space was unknown). Once no owner is full, the next rejection emits
|
|
5546
|
+
* `storage.full` again.
|
|
5547
|
+
*/
|
|
5548
|
+
clearFor(spaceId) {
|
|
5549
|
+
this.full.delete(spaceId === void 0 ? UNKNOWN_OWNER : this.ownerOf(spaceId));
|
|
5550
|
+
if (this.full.size === 0) this.event = void 0;
|
|
5551
|
+
}
|
|
5552
|
+
};
|
|
5553
|
+
var UNKNOWN_OWNER = "\0unknown";
|
|
5554
|
+
|
|
5555
|
+
// src/storage/status.ts
|
|
5556
|
+
var STORAGE_MANAGE_URL = "https://account.tinycloud.xyz/billing";
|
|
5557
|
+
var STORAGE_NEARLY_FULL_RATIO = 0.9;
|
|
5558
|
+
function storageUsageState(usedBytes, limitBytes) {
|
|
5559
|
+
if (limitBytes === null) return "ok";
|
|
5560
|
+
if (usedBytes >= limitBytes) return "full";
|
|
5561
|
+
return usedBytes >= limitBytes * STORAGE_NEARLY_FULL_RATIO ? "nearly_full" : "ok";
|
|
5562
|
+
}
|
|
5563
|
+
function storageUsageOf(value) {
|
|
5564
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
5565
|
+
const { usedBytes, limitBytes } = value;
|
|
5566
|
+
const isByteCount2 = (n) => typeof n === "number" && Number.isSafeInteger(n) && n >= 0;
|
|
5567
|
+
if (!isByteCount2(usedBytes)) return void 0;
|
|
5568
|
+
if (limitBytes !== null && !isByteCount2(limitBytes)) return void 0;
|
|
5569
|
+
return { usedBytes, limitBytes };
|
|
5570
|
+
}
|
|
5571
|
+
function parseStorageStatus(body) {
|
|
5572
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
5573
|
+
const record = body;
|
|
5574
|
+
const space = storageUsageOf(record.space);
|
|
5575
|
+
if (!space) return void 0;
|
|
5576
|
+
const status = {
|
|
5577
|
+
...space,
|
|
5578
|
+
state: storageUsageState(space.usedBytes, space.limitBytes),
|
|
5579
|
+
manageUrl: typeof record.manageUrl === "string" && record.manageUrl.startsWith("https://") ? record.manageUrl : STORAGE_MANAGE_URL
|
|
5580
|
+
};
|
|
5581
|
+
const account = storageUsageOf(record.account);
|
|
5582
|
+
if (account) {
|
|
5583
|
+
const plan = record.account.plan;
|
|
5584
|
+
status.account = typeof plan === "string" && plan !== "" ? { ...account, plan } : account;
|
|
5585
|
+
if (status.account.plan) status.plan = status.account.plan;
|
|
5586
|
+
status.state = storageUsageState(account.usedBytes, account.limitBytes);
|
|
5587
|
+
}
|
|
5588
|
+
return status;
|
|
5589
|
+
}
|
|
5590
|
+
|
|
5426
5591
|
// src/quota/TinyCloudQuota.ts
|
|
5427
5592
|
var TinyCloudQuota = class {
|
|
5428
5593
|
constructor(config = {}) {
|
|
@@ -8226,6 +8391,8 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
8226
8391
|
SQLMigrations,
|
|
8227
8392
|
SQLService,
|
|
8228
8393
|
STORAGE_FULL_MESSAGE,
|
|
8394
|
+
STORAGE_MANAGE_URL,
|
|
8395
|
+
STORAGE_NEARLY_FULL_RATIO,
|
|
8229
8396
|
STORAGE_WRITE_TOO_LARGE_MESSAGE,
|
|
8230
8397
|
SecretsService,
|
|
8231
8398
|
ServiceContext,
|
|
@@ -8235,6 +8402,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
8235
8402
|
ServiceResponseEventSchema,
|
|
8236
8403
|
ServiceRetryEventSchema,
|
|
8237
8404
|
ServiceSessionSchema,
|
|
8405
|
+
StorageFullMonitor,
|
|
8238
8406
|
TC832_RESERVED_SCOPES,
|
|
8239
8407
|
TelemetryEvents,
|
|
8240
8408
|
TelemetrySpanEventSchema,
|
|
@@ -8296,6 +8464,8 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
8296
8464
|
parsePermissionHintFromErrorText,
|
|
8297
8465
|
parseSecretCatalogKey,
|
|
8298
8466
|
parseStorageQuotaBytes,
|
|
8467
|
+
parseStorageRejection,
|
|
8468
|
+
parseStorageStatus,
|
|
8299
8469
|
permissionDeniedError,
|
|
8300
8470
|
replicationIdentityKey,
|
|
8301
8471
|
requiresSecretsOptIn,
|
|
@@ -8305,6 +8475,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
8305
8475
|
storageLimitReachedError,
|
|
8306
8476
|
storageQuotaExceededError,
|
|
8307
8477
|
storageRejectionError,
|
|
8478
|
+
storageUsageState,
|
|
8308
8479
|
timeoutError,
|
|
8309
8480
|
tinyCloudDebugLogger,
|
|
8310
8481
|
utf8Decode,
|