@tinycloud/sdk-services 3.1.0-beta.1 → 3.1.0-beta.2
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/encryption/index.cjs.map +1 -1
- package/dist/encryption/index.js.map +1 -1
- package/dist/index.cjs +75 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -1
- package/dist/index.d.ts +35 -1
- package/dist/index.js +70 -80
- package/dist/index.js.map +1 -1
- package/dist/kv/index.cjs +26 -38
- package/dist/kv/index.cjs.map +1 -1
- package/dist/kv/index.d.cts +0 -1
- package/dist/kv/index.d.ts +0 -1
- package/dist/kv/index.js +26 -38
- package/dist/kv/index.js.map +1 -1
- package/dist/sql/index.cjs +44 -5
- package/dist/sql/index.cjs.map +1 -1
- package/dist/sql/index.js +44 -5
- package/dist/sql/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -55,6 +55,8 @@ __export(src_exports, {
|
|
|
55
55
|
SQLAction: () => SQLAction,
|
|
56
56
|
SQLMigrations: () => SQLMigrations,
|
|
57
57
|
SQLService: () => SQLService,
|
|
58
|
+
STORAGE_FULL_MESSAGE: () => STORAGE_FULL_MESSAGE,
|
|
59
|
+
STORAGE_WRITE_TOO_LARGE_MESSAGE: () => STORAGE_WRITE_TOO_LARGE_MESSAGE,
|
|
58
60
|
SecretsService: () => SecretsService,
|
|
59
61
|
ServiceContext: () => ServiceContext,
|
|
60
62
|
ServiceErrorEventSchema: () => ServiceErrorEventSchema,
|
|
@@ -107,6 +109,7 @@ __export(src_exports, {
|
|
|
107
109
|
hexEncode: () => hexEncode2,
|
|
108
110
|
installTinyCloudDebugGlobals: () => installTinyCloudDebugGlobals,
|
|
109
111
|
isNetworkId: () => isNetworkId,
|
|
112
|
+
isStorageFullError: () => isStorageFullError,
|
|
110
113
|
networkDiscoveryKey: () => networkDiscoveryKey,
|
|
111
114
|
networkError: () => networkError,
|
|
112
115
|
notFoundError: () => notFoundError,
|
|
@@ -117,12 +120,14 @@ __export(src_exports, {
|
|
|
117
120
|
parsePermissionHint: () => parsePermissionHint,
|
|
118
121
|
parsePermissionHintFromErrorText: () => parsePermissionHintFromErrorText,
|
|
119
122
|
parseSecretCatalogKey: () => parseSecretCatalogKey,
|
|
123
|
+
parseStorageQuotaBytes: () => parseStorageQuotaBytes,
|
|
120
124
|
permissionDeniedError: () => permissionDeniedError,
|
|
121
125
|
resolveSecretListPrefix: () => resolveSecretListPrefix,
|
|
122
126
|
resolveSecretPath: () => resolveSecretPath,
|
|
123
127
|
serviceError: () => serviceError,
|
|
124
128
|
storageLimitReachedError: () => storageLimitReachedError,
|
|
125
129
|
storageQuotaExceededError: () => storageQuotaExceededError,
|
|
130
|
+
storageRejectionError: () => storageRejectionError,
|
|
126
131
|
timeoutError: () => timeoutError,
|
|
127
132
|
tinyCloudDebugLogger: () => tinyCloudDebugLogger,
|
|
128
133
|
utf8Decode: () => utf8Decode,
|
|
@@ -1183,6 +1188,23 @@ function storageLimitReachedError(service, message, meta) {
|
|
|
1183
1188
|
meta
|
|
1184
1189
|
};
|
|
1185
1190
|
}
|
|
1191
|
+
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.";
|
|
1192
|
+
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.";
|
|
1193
|
+
function parseStorageQuotaBytes(responseText) {
|
|
1194
|
+
const match = responseText.match(/Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i);
|
|
1195
|
+
if (!match) return void 0;
|
|
1196
|
+
return {
|
|
1197
|
+
usedBytes: parseInt(match[1], 10),
|
|
1198
|
+
limitBytes: parseInt(match[2], 10)
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
function storageRejectionError(service, status, meta, responseText) {
|
|
1202
|
+
const quotaMeta = { ...meta, ...parseStorageQuotaBytes(responseText) };
|
|
1203
|
+
return status === 402 ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta) : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);
|
|
1204
|
+
}
|
|
1205
|
+
function isStorageFullError(error) {
|
|
1206
|
+
return error?.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED || error?.code === ErrorCodes.STORAGE_LIMIT_REACHED;
|
|
1207
|
+
}
|
|
1186
1208
|
function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
|
|
1187
1209
|
if (error instanceof Error) {
|
|
1188
1210
|
if (error.name === "AbortError") {
|
|
@@ -1576,47 +1598,21 @@ var KVService = class extends BaseService {
|
|
|
1576
1598
|
get config() {
|
|
1577
1599
|
return this._config;
|
|
1578
1600
|
}
|
|
1579
|
-
// Parses "Used: X bytes, Limit: Y bytes" from tinycloud-node error responses
|
|
1580
|
-
parseQuotaInfo(errorText) {
|
|
1581
|
-
const match = errorText.match(
|
|
1582
|
-
/Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i
|
|
1583
|
-
);
|
|
1584
|
-
if (match) {
|
|
1585
|
-
return {
|
|
1586
|
-
usedBytes: parseInt(match[1], 10),
|
|
1587
|
-
limitBytes: parseInt(match[2], 10)
|
|
1588
|
-
};
|
|
1589
|
-
}
|
|
1590
|
-
return void 0;
|
|
1591
|
-
}
|
|
1592
1601
|
handleQuotaErrorResponse(response, errorText, key) {
|
|
1593
|
-
if (response.status
|
|
1594
|
-
|
|
1595
|
-
return err(
|
|
1596
|
-
storageQuotaExceededError(
|
|
1597
|
-
"kv",
|
|
1598
|
-
`Storage quota exceeded for key ${JSON.stringify(key)}: ${errorText}`,
|
|
1599
|
-
{
|
|
1600
|
-
status: response.status,
|
|
1601
|
-
...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
|
|
1602
|
-
}
|
|
1603
|
-
)
|
|
1604
|
-
);
|
|
1602
|
+
if (response.status !== 402 && response.status !== 413) {
|
|
1603
|
+
return void 0;
|
|
1605
1604
|
}
|
|
1606
|
-
if (response.status === 413) {
|
|
1607
|
-
|
|
1608
|
-
return err(
|
|
1609
|
-
storageLimitReachedError(
|
|
1610
|
-
"kv",
|
|
1611
|
-
`Storage limit reached for key ${JSON.stringify(key)}: ${errorText}`,
|
|
1612
|
-
{
|
|
1613
|
-
status: response.status,
|
|
1614
|
-
...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
|
|
1615
|
-
}
|
|
1616
|
-
)
|
|
1617
|
-
);
|
|
1605
|
+
if (response.status === 413 && !parseStorageQuotaBytes(errorText)) {
|
|
1606
|
+
return void 0;
|
|
1618
1607
|
}
|
|
1619
|
-
return
|
|
1608
|
+
return err(
|
|
1609
|
+
storageRejectionError(
|
|
1610
|
+
"kv",
|
|
1611
|
+
response.status,
|
|
1612
|
+
{ status: response.status, statusText: response.statusText, key },
|
|
1613
|
+
errorText
|
|
1614
|
+
)
|
|
1615
|
+
);
|
|
1620
1616
|
}
|
|
1621
1617
|
/**
|
|
1622
1618
|
* Classify a KV 404 by reading the response body once.
|
|
@@ -2953,21 +2949,24 @@ var SQLService = class extends BaseService {
|
|
|
2953
2949
|
}
|
|
2954
2950
|
async applyMigrationsOnDbUnlocked(dbName, options) {
|
|
2955
2951
|
return this.withTelemetry("migrations.apply", dbName, async () => {
|
|
2956
|
-
const created = await this.ensureMigrationsTable(dbName, options.signal);
|
|
2957
|
-
if (!created.ok) return created;
|
|
2958
2952
|
const listed = await this.queryOnDb(
|
|
2959
2953
|
dbName,
|
|
2960
2954
|
`SELECT id FROM ${MIGRATIONS_TABLE} WHERE namespace = ? ORDER BY applied_at, id`,
|
|
2961
2955
|
[options.namespace],
|
|
2962
2956
|
{ signal: options.signal }
|
|
2963
2957
|
);
|
|
2964
|
-
|
|
2958
|
+
const tableMissing = !listed.ok && (listed.error.code === ErrorCodes.SQL_DATABASE_NOT_FOUND || isMissingTableError(listed.error.message));
|
|
2959
|
+
if (!listed.ok && !tableMissing) return listed;
|
|
2965
2960
|
const appliedIds = new Set(
|
|
2966
|
-
listed.data.rows.map((row) => rowValue(row, 0)).filter((id) => typeof id === "string")
|
|
2961
|
+
(listed.ok ? listed.data.rows : []).map((row) => rowValue(row, 0)).filter((id) => typeof id === "string")
|
|
2967
2962
|
);
|
|
2968
2963
|
const skipped = options.migrations.filter((migration) => appliedIds.has(migration.id)).map((migration) => migration.id);
|
|
2969
2964
|
const pending = options.migrations.filter((migration) => !appliedIds.has(migration.id));
|
|
2970
2965
|
const applied = [];
|
|
2966
|
+
if (pending.length > 0 && tableMissing) {
|
|
2967
|
+
const created = await this.ensureMigrationsTable(dbName, options.signal);
|
|
2968
|
+
if (!created.ok) return created;
|
|
2969
|
+
}
|
|
2971
2970
|
for (const migration of pending) {
|
|
2972
2971
|
const result = await this.applyOneMigration(dbName, options.namespace, migration, options.signal);
|
|
2973
2972
|
if (!result.ok) return result;
|
|
@@ -3070,6 +3069,10 @@ var SQLService = class extends BaseService {
|
|
|
3070
3069
|
}
|
|
3071
3070
|
async handleErrorResponse(response, operation) {
|
|
3072
3071
|
const errorText = await response.text();
|
|
3072
|
+
const meta = responseErrorMeta(response.status, response.statusText, errorText);
|
|
3073
|
+
if (response.status === 402) {
|
|
3074
|
+
return err(storageRejectionError("sql", 402, meta, errorText));
|
|
3075
|
+
}
|
|
3073
3076
|
const errorBody = parseServiceErrorBody(errorText);
|
|
3074
3077
|
const errorCode = this.mapHttpStatusToErrorCode(
|
|
3075
3078
|
response.status,
|
|
@@ -3082,7 +3085,6 @@ var SQLService = class extends BaseService {
|
|
|
3082
3085
|
errorText,
|
|
3083
3086
|
errorBody
|
|
3084
3087
|
);
|
|
3085
|
-
const meta = responseErrorMeta(response.status, response.statusText, errorText);
|
|
3086
3088
|
if (response.status === 401) {
|
|
3087
3089
|
const { resource, action } = parseAuthError(errorText);
|
|
3088
3090
|
if (action) meta.requiredAction = action;
|
|
@@ -3183,6 +3185,9 @@ function serializeSqlValues(values) {
|
|
|
3183
3185
|
(value) => value instanceof Uint8Array ? Array.from(value) : value
|
|
3184
3186
|
);
|
|
3185
3187
|
}
|
|
3188
|
+
function isMissingTableError(message) {
|
|
3189
|
+
return /no such table/i.test(message);
|
|
3190
|
+
}
|
|
3186
3191
|
function migrationKey(namespace, id) {
|
|
3187
3192
|
return `${encodeURIComponent(namespace)}:${encodeURIComponent(id)}`;
|
|
3188
3193
|
}
|
|
@@ -3504,6 +3509,10 @@ var DuckDbService = class extends BaseService {
|
|
|
3504
3509
|
}
|
|
3505
3510
|
async handleErrorResponse(response, operation) {
|
|
3506
3511
|
const errorText = await response.text();
|
|
3512
|
+
const meta = responseErrorMeta(response.status, response.statusText, errorText);
|
|
3513
|
+
if (response.status === 402) {
|
|
3514
|
+
return err(storageRejectionError("duckdb", 402, meta, errorText));
|
|
3515
|
+
}
|
|
3507
3516
|
const errorBody = parseServiceErrorBody(errorText);
|
|
3508
3517
|
const errorCode = this.mapHttpStatusToErrorCode(
|
|
3509
3518
|
response.status,
|
|
@@ -3516,7 +3525,6 @@ var DuckDbService = class extends BaseService {
|
|
|
3516
3525
|
errorText,
|
|
3517
3526
|
errorBody
|
|
3518
3527
|
);
|
|
3519
|
-
const meta = responseErrorMeta(response.status, response.statusText, errorText);
|
|
3520
3528
|
if (response.status === 401) {
|
|
3521
3529
|
const { resource, action } = parseAuthError(errorText);
|
|
3522
3530
|
if (action) meta.requiredAction = action;
|
|
@@ -4488,6 +4496,9 @@ function defaultVaultMessage(input) {
|
|
|
4488
4496
|
return input.message ?? `Public key not found for ${input.did}`;
|
|
4489
4497
|
case "STORAGE_ERROR":
|
|
4490
4498
|
return input.message ?? input.cause.message;
|
|
4499
|
+
case "STORAGE_QUOTA_EXCEEDED":
|
|
4500
|
+
case "STORAGE_LIMIT_REACHED":
|
|
4501
|
+
return input.message;
|
|
4491
4502
|
}
|
|
4492
4503
|
}
|
|
4493
4504
|
function vaultError(input) {
|
|
@@ -4498,6 +4509,15 @@ function vaultError(input) {
|
|
|
4498
4509
|
};
|
|
4499
4510
|
return { ok: false, error };
|
|
4500
4511
|
}
|
|
4512
|
+
function kvWriteError(context, error) {
|
|
4513
|
+
if (error.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED || error.code === ErrorCodes.STORAGE_LIMIT_REACHED) {
|
|
4514
|
+
return vaultError({ code: error.code, message: error.message, meta: error.meta });
|
|
4515
|
+
}
|
|
4516
|
+
return vaultError({
|
|
4517
|
+
code: "STORAGE_ERROR",
|
|
4518
|
+
cause: new Error(`${context}: ${error.message}`)
|
|
4519
|
+
});
|
|
4520
|
+
}
|
|
4501
4521
|
var DataVaultService = class extends BaseService {
|
|
4502
4522
|
/**
|
|
4503
4523
|
* Create a new DataVaultService instance.
|
|
@@ -4780,12 +4800,7 @@ var DataVaultService = class extends BaseService {
|
|
|
4780
4800
|
JSON.stringify(envelopeResult.data)
|
|
4781
4801
|
);
|
|
4782
4802
|
if (!valuePutResult.ok) {
|
|
4783
|
-
return
|
|
4784
|
-
code: "STORAGE_ERROR",
|
|
4785
|
-
cause: new Error(
|
|
4786
|
-
`Failed to store encrypted value: ${valuePutResult.error.message}`
|
|
4787
|
-
)
|
|
4788
|
-
});
|
|
4803
|
+
return kvWriteError("Failed to store encrypted value", valuePutResult.error);
|
|
4789
4804
|
}
|
|
4790
4805
|
return { ok: true, data: void 0 };
|
|
4791
4806
|
} catch (error) {
|
|
@@ -5003,12 +5018,7 @@ var DataVaultService = class extends BaseService {
|
|
|
5003
5018
|
keyPayload
|
|
5004
5019
|
);
|
|
5005
5020
|
if (!keyPutResult.ok) {
|
|
5006
|
-
return
|
|
5007
|
-
code: "STORAGE_ERROR",
|
|
5008
|
-
cause: new Error(
|
|
5009
|
-
`Failed to store key blob: ${keyPutResult.error.message}`
|
|
5010
|
-
)
|
|
5011
|
-
});
|
|
5021
|
+
return kvWriteError("Failed to store key blob", keyPutResult.error);
|
|
5012
5022
|
}
|
|
5013
5023
|
const valuePayload = JSON.stringify({
|
|
5014
5024
|
data: base64Encode(encrypted),
|
|
@@ -5019,12 +5029,7 @@ var DataVaultService = class extends BaseService {
|
|
|
5019
5029
|
valuePayload
|
|
5020
5030
|
);
|
|
5021
5031
|
if (!valuePutResult.ok) {
|
|
5022
|
-
return
|
|
5023
|
-
code: "STORAGE_ERROR",
|
|
5024
|
-
cause: new Error(
|
|
5025
|
-
`Failed to store encrypted value: ${valuePutResult.error.message}`
|
|
5026
|
-
)
|
|
5027
|
-
});
|
|
5032
|
+
return kvWriteError("Failed to store encrypted value", valuePutResult.error);
|
|
5028
5033
|
}
|
|
5029
5034
|
return ok(void 0);
|
|
5030
5035
|
} catch (error) {
|
|
@@ -5355,12 +5360,7 @@ var DataVaultService = class extends BaseService {
|
|
|
5355
5360
|
grantPayload
|
|
5356
5361
|
);
|
|
5357
5362
|
if (!grantPutResult.ok) {
|
|
5358
|
-
return
|
|
5359
|
-
code: "STORAGE_ERROR",
|
|
5360
|
-
cause: new Error(
|
|
5361
|
-
`Failed to store grant: ${grantPutResult.error.message}`
|
|
5362
|
-
)
|
|
5363
|
-
});
|
|
5363
|
+
return kvWriteError("Failed to store grant", grantPutResult.error);
|
|
5364
5364
|
}
|
|
5365
5365
|
return ok(void 0);
|
|
5366
5366
|
} catch (error) {
|
|
@@ -5705,12 +5705,7 @@ var DataVaultService = class extends BaseService {
|
|
|
5705
5705
|
keyPayload
|
|
5706
5706
|
);
|
|
5707
5707
|
if (!keyPutResult.ok) {
|
|
5708
|
-
return
|
|
5709
|
-
code: "STORAGE_ERROR",
|
|
5710
|
-
cause: new Error(
|
|
5711
|
-
`Failed to store rotated key blob: ${keyPutResult.error.message}`
|
|
5712
|
-
)
|
|
5713
|
-
});
|
|
5708
|
+
return kvWriteError("Failed to store rotated key blob", keyPutResult.error);
|
|
5714
5709
|
}
|
|
5715
5710
|
const valuePayload = JSON.stringify({
|
|
5716
5711
|
data: base64Encode(encrypted),
|
|
@@ -5721,12 +5716,7 @@ var DataVaultService = class extends BaseService {
|
|
|
5721
5716
|
valuePayload
|
|
5722
5717
|
);
|
|
5723
5718
|
if (!valuePutResult.ok) {
|
|
5724
|
-
return
|
|
5725
|
-
code: "STORAGE_ERROR",
|
|
5726
|
-
cause: new Error(
|
|
5727
|
-
`Failed to store re-encrypted value: ${valuePutResult.error.message}`
|
|
5728
|
-
)
|
|
5729
|
-
});
|
|
5719
|
+
return kvWriteError("Failed to store re-encrypted value", valuePutResult.error);
|
|
5730
5720
|
}
|
|
5731
5721
|
for (const did of remainingGrantees) {
|
|
5732
5722
|
const grantResult = await this.reencrypt(key, did);
|
|
@@ -7056,6 +7046,8 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
7056
7046
|
SQLAction,
|
|
7057
7047
|
SQLMigrations,
|
|
7058
7048
|
SQLService,
|
|
7049
|
+
STORAGE_FULL_MESSAGE,
|
|
7050
|
+
STORAGE_WRITE_TOO_LARGE_MESSAGE,
|
|
7059
7051
|
SecretsService,
|
|
7060
7052
|
ServiceContext,
|
|
7061
7053
|
ServiceErrorEventSchema,
|
|
@@ -7108,6 +7100,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
7108
7100
|
hexEncode,
|
|
7109
7101
|
installTinyCloudDebugGlobals,
|
|
7110
7102
|
isNetworkId,
|
|
7103
|
+
isStorageFullError,
|
|
7111
7104
|
networkDiscoveryKey,
|
|
7112
7105
|
networkError,
|
|
7113
7106
|
notFoundError,
|
|
@@ -7118,12 +7111,14 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
7118
7111
|
parsePermissionHint,
|
|
7119
7112
|
parsePermissionHintFromErrorText,
|
|
7120
7113
|
parseSecretCatalogKey,
|
|
7114
|
+
parseStorageQuotaBytes,
|
|
7121
7115
|
permissionDeniedError,
|
|
7122
7116
|
resolveSecretListPrefix,
|
|
7123
7117
|
resolveSecretPath,
|
|
7124
7118
|
serviceError,
|
|
7125
7119
|
storageLimitReachedError,
|
|
7126
7120
|
storageQuotaExceededError,
|
|
7121
|
+
storageRejectionError,
|
|
7127
7122
|
timeoutError,
|
|
7128
7123
|
tinyCloudDebugLogger,
|
|
7129
7124
|
utf8Decode,
|