@tinycloud/sdk-services 2.2.0-beta.10 → 2.2.0-beta.13
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/index.cjs +157 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +156 -21
- package/dist/index.js.map +1 -1
- package/dist/kv/index.cjs +116 -0
- package/dist/kv/index.cjs.map +1 -1
- package/dist/kv/index.d.cts +99 -1
- package/dist/kv/index.d.ts +99 -1
- package/dist/kv/index.js +115 -0
- package/dist/kv/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
BaseService: () => BaseService,
|
|
24
|
+
DEFAULT_SIGNED_READ_URL_EXPIRY_MS: () => DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
24
25
|
DataVaultService: () => DataVaultService,
|
|
25
26
|
DatabaseHandle: () => DatabaseHandle,
|
|
26
27
|
DuckDbAction: () => DuckDbAction,
|
|
@@ -904,6 +905,13 @@ var PrefixedKVService = class _PrefixedKVService {
|
|
|
904
905
|
const fullKey = this.getFullKey(key);
|
|
905
906
|
return this._kv.head(fullKey, { ...options, prefix: "" });
|
|
906
907
|
}
|
|
908
|
+
/**
|
|
909
|
+
* Create a short-lived signed URL for reading a KV object.
|
|
910
|
+
*/
|
|
911
|
+
async createSignedReadUrl(key, options) {
|
|
912
|
+
const fullKey = this.getFullKey(key);
|
|
913
|
+
return this._kv.createSignedReadUrl(fullKey, { ...options, prefix: "" });
|
|
914
|
+
}
|
|
907
915
|
/**
|
|
908
916
|
* Create a nested prefix-scoped view.
|
|
909
917
|
*/
|
|
@@ -915,6 +923,7 @@ var PrefixedKVService = class _PrefixedKVService {
|
|
|
915
923
|
};
|
|
916
924
|
|
|
917
925
|
// src/kv/types.ts
|
|
926
|
+
var DEFAULT_SIGNED_READ_URL_EXPIRY_MS = 5 * 60 * 1e3;
|
|
918
927
|
var KVAction = {
|
|
919
928
|
GET: "tinycloud.kv/get",
|
|
920
929
|
PUT: "tinycloud.kv/put",
|
|
@@ -999,6 +1008,15 @@ var KVService = class extends BaseService {
|
|
|
999
1008
|
get host() {
|
|
1000
1009
|
return this.context.hosts[0];
|
|
1001
1010
|
}
|
|
1011
|
+
withJsonContentType(headers) {
|
|
1012
|
+
if (Array.isArray(headers)) {
|
|
1013
|
+
return [...headers, ["content-type", "application/json"]];
|
|
1014
|
+
}
|
|
1015
|
+
return {
|
|
1016
|
+
...headers,
|
|
1017
|
+
"content-type": "application/json"
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1002
1020
|
/**
|
|
1003
1021
|
* Execute an invoke operation.
|
|
1004
1022
|
*
|
|
@@ -1068,6 +1086,48 @@ var KVService = class extends BaseService {
|
|
|
1068
1086
|
return text;
|
|
1069
1087
|
}
|
|
1070
1088
|
}
|
|
1089
|
+
async createSignedReadUrlError(response, key) {
|
|
1090
|
+
let errorText = response.statusText;
|
|
1091
|
+
try {
|
|
1092
|
+
const text = await response.text();
|
|
1093
|
+
if (text) {
|
|
1094
|
+
errorText = text;
|
|
1095
|
+
}
|
|
1096
|
+
} catch {
|
|
1097
|
+
}
|
|
1098
|
+
if (response.status === 401 || response.status === 403) {
|
|
1099
|
+
const { resource, action } = parseAuthError(errorText);
|
|
1100
|
+
return err(authUnauthorizedError("kv", errorText, {
|
|
1101
|
+
status: response.status,
|
|
1102
|
+
...action && { requiredAction: action },
|
|
1103
|
+
...resource && { resource }
|
|
1104
|
+
}));
|
|
1105
|
+
}
|
|
1106
|
+
const code = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
|
|
1107
|
+
return err(
|
|
1108
|
+
serviceError(
|
|
1109
|
+
code,
|
|
1110
|
+
`Failed to create signed read URL for key "${key}": ${response.status} - ${errorText}`,
|
|
1111
|
+
"kv",
|
|
1112
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
1113
|
+
)
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
normalizeSignedReadUrlResponse(data) {
|
|
1117
|
+
if (!data || typeof data !== "object") {
|
|
1118
|
+
return void 0;
|
|
1119
|
+
}
|
|
1120
|
+
const response = data;
|
|
1121
|
+
if (typeof response.url !== "string" || typeof response.ticketId !== "string" || typeof response.expiresAt !== "string") {
|
|
1122
|
+
return void 0;
|
|
1123
|
+
}
|
|
1124
|
+
return {
|
|
1125
|
+
url: new URL(response.url, this.host).toString(),
|
|
1126
|
+
relativeUrl: response.url,
|
|
1127
|
+
ticketId: response.ticketId,
|
|
1128
|
+
expiresAt: response.expiresAt
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1071
1131
|
/**
|
|
1072
1132
|
* Get a value by key.
|
|
1073
1133
|
*/
|
|
@@ -1340,6 +1400,61 @@ var KVService = class extends BaseService {
|
|
|
1340
1400
|
}
|
|
1341
1401
|
});
|
|
1342
1402
|
}
|
|
1403
|
+
/**
|
|
1404
|
+
* Create a short-lived signed URL for reading a KV object.
|
|
1405
|
+
*/
|
|
1406
|
+
async createSignedReadUrl(key, options) {
|
|
1407
|
+
return this.withTelemetry("createSignedReadUrl", key, async () => {
|
|
1408
|
+
if (!this.requireAuth()) {
|
|
1409
|
+
return err(authRequiredError("kv"));
|
|
1410
|
+
}
|
|
1411
|
+
const path = this.getFullPath(key, options?.prefix);
|
|
1412
|
+
const session = this.context.session;
|
|
1413
|
+
const headers = this.context.invoke(
|
|
1414
|
+
session,
|
|
1415
|
+
"kv",
|
|
1416
|
+
path,
|
|
1417
|
+
KVAction.GET
|
|
1418
|
+
);
|
|
1419
|
+
const body = {
|
|
1420
|
+
space: session.spaceId,
|
|
1421
|
+
path,
|
|
1422
|
+
ttl_seconds: options?.expiresInSeconds ?? Math.ceil(DEFAULT_SIGNED_READ_URL_EXPIRY_MS / 1e3)
|
|
1423
|
+
};
|
|
1424
|
+
if (options?.contentHash !== void 0) {
|
|
1425
|
+
body.content_hash = options.contentHash;
|
|
1426
|
+
}
|
|
1427
|
+
if (options?.etag !== void 0) {
|
|
1428
|
+
body.etag = options.etag;
|
|
1429
|
+
}
|
|
1430
|
+
try {
|
|
1431
|
+
const response = await this.context.fetch(`${this.host}/signed/kv`, {
|
|
1432
|
+
method: "POST",
|
|
1433
|
+
headers: this.withJsonContentType(headers),
|
|
1434
|
+
body: JSON.stringify(body),
|
|
1435
|
+
signal: this.combineSignals(options?.signal)
|
|
1436
|
+
});
|
|
1437
|
+
if (!response.ok) {
|
|
1438
|
+
return this.createSignedReadUrlError(response, key);
|
|
1439
|
+
}
|
|
1440
|
+
const signedUrl = this.normalizeSignedReadUrlResponse(
|
|
1441
|
+
await response.json()
|
|
1442
|
+
);
|
|
1443
|
+
if (!signedUrl) {
|
|
1444
|
+
return err(
|
|
1445
|
+
serviceError(
|
|
1446
|
+
ErrorCodes.NETWORK_ERROR,
|
|
1447
|
+
"Signed read URL response did not include url, ticketId, and expiresAt",
|
|
1448
|
+
"kv"
|
|
1449
|
+
)
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
return ok(signedUrl);
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
return err(wrapError("kv", error));
|
|
1455
|
+
}
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1343
1458
|
/**
|
|
1344
1459
|
* Create a prefix-scoped view of this KV service.
|
|
1345
1460
|
*
|
|
@@ -2909,6 +3024,9 @@ function base64Decode(str) {
|
|
|
2909
3024
|
}
|
|
2910
3025
|
return bytes;
|
|
2911
3026
|
}
|
|
3027
|
+
function isUnlockSigner(signer) {
|
|
3028
|
+
return typeof signer === "object" && signer !== null && typeof signer.signMessage === "function";
|
|
3029
|
+
}
|
|
2912
3030
|
function defaultVaultMessage(input) {
|
|
2913
3031
|
switch (input.code) {
|
|
2914
3032
|
case "DECRYPTION_FAILED":
|
|
@@ -2946,6 +3064,7 @@ var DataVaultService = class extends BaseService {
|
|
|
2946
3064
|
this.masterKey = null;
|
|
2947
3065
|
this.encryptionIdentity = null;
|
|
2948
3066
|
this._isUnlocked = false;
|
|
3067
|
+
this.unlockInFlight = null;
|
|
2949
3068
|
this.vaultConfig = config;
|
|
2950
3069
|
this._config = config;
|
|
2951
3070
|
}
|
|
@@ -3005,30 +3124,40 @@ var DataVaultService = class extends BaseService {
|
|
|
3005
3124
|
* signatures exist (browser only).
|
|
3006
3125
|
*/
|
|
3007
3126
|
async unlock(signer) {
|
|
3008
|
-
|
|
3127
|
+
const unlockSigner = isUnlockSigner(signer) ? signer : void 0;
|
|
3128
|
+
if (this._isUnlocked && this.masterKey && (this.encryptionIdentity || !unlockSigner)) {
|
|
3129
|
+
return { ok: true, data: void 0 };
|
|
3130
|
+
}
|
|
3131
|
+
if (this.unlockInFlight) {
|
|
3132
|
+
return this.unlockInFlight;
|
|
3133
|
+
}
|
|
3134
|
+
this.unlockInFlight = this.withTelemetry("unlock", void 0, async () => {
|
|
3009
3135
|
const spaceId = this.vaultConfig.spaceId;
|
|
3010
3136
|
const versionConfig = VaultVersionConfig[CURRENT_VAULT_VERSION];
|
|
3011
3137
|
const masterCacheKey = `vault-master:${spaceId}`;
|
|
3012
3138
|
const identityCacheKey = `vault-identity:${this.tc.address}`;
|
|
3013
3139
|
try {
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
if (!
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3140
|
+
if (!this.masterKey) {
|
|
3141
|
+
let masterSigBytes = await loadCachedSignature(masterCacheKey);
|
|
3142
|
+
if (!masterSigBytes) {
|
|
3143
|
+
if (!unlockSigner) {
|
|
3144
|
+
return vaultError({
|
|
3145
|
+
code: "VAULT_LOCKED",
|
|
3146
|
+
message: "Signer is required when no cached master signature exists"
|
|
3147
|
+
});
|
|
3148
|
+
}
|
|
3149
|
+
const sig = await unlockSigner.signMessage(
|
|
3150
|
+
versionConfig.masterMessage(spaceId)
|
|
3151
|
+
);
|
|
3152
|
+
masterSigBytes = toBytes(sig);
|
|
3153
|
+
await cacheSignature(masterCacheKey, masterSigBytes);
|
|
3021
3154
|
}
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
masterSigBytes,
|
|
3029
|
-
this.crypto.sha256(toBytes(spaceId)),
|
|
3030
|
-
toBytes("vault-master")
|
|
3031
|
-
);
|
|
3155
|
+
this.masterKey = this.crypto.deriveKey(
|
|
3156
|
+
masterSigBytes,
|
|
3157
|
+
this.crypto.sha256(toBytes(spaceId)),
|
|
3158
|
+
toBytes("vault-master")
|
|
3159
|
+
);
|
|
3160
|
+
}
|
|
3032
3161
|
const publicSpaceId = this.tc.makePublicSpaceId(this.tc.address, this.tc.chainId);
|
|
3033
3162
|
let existingPubKey = null;
|
|
3034
3163
|
try {
|
|
@@ -3051,13 +3180,14 @@ var DataVaultService = class extends BaseService {
|
|
|
3051
3180
|
} else {
|
|
3052
3181
|
let identitySigBytes = await loadCachedSignature(identityCacheKey);
|
|
3053
3182
|
if (!identitySigBytes) {
|
|
3054
|
-
if (!
|
|
3183
|
+
if (!unlockSigner) {
|
|
3055
3184
|
this.encryptionIdentity = null;
|
|
3056
3185
|
this._isUnlocked = true;
|
|
3057
3186
|
return ok(void 0);
|
|
3058
3187
|
}
|
|
3059
|
-
const
|
|
3060
|
-
|
|
3188
|
+
const sig = await unlockSigner.signMessage(
|
|
3189
|
+
versionConfig.identityMessage
|
|
3190
|
+
);
|
|
3061
3191
|
identitySigBytes = toBytes(sig);
|
|
3062
3192
|
await cacheSignature(identityCacheKey, identitySigBytes);
|
|
3063
3193
|
}
|
|
@@ -3087,6 +3217,11 @@ var DataVaultService = class extends BaseService {
|
|
|
3087
3217
|
});
|
|
3088
3218
|
}
|
|
3089
3219
|
});
|
|
3220
|
+
try {
|
|
3221
|
+
return await this.unlockInFlight;
|
|
3222
|
+
} finally {
|
|
3223
|
+
this.unlockInFlight = null;
|
|
3224
|
+
}
|
|
3090
3225
|
}
|
|
3091
3226
|
/**
|
|
3092
3227
|
* Clear the cached vault signatures.
|
|
@@ -3972,6 +4107,7 @@ var SecretsService = class {
|
|
|
3972
4107
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3973
4108
|
0 && (module.exports = {
|
|
3974
4109
|
BaseService,
|
|
4110
|
+
DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
|
|
3975
4111
|
DataVaultService,
|
|
3976
4112
|
DatabaseHandle,
|
|
3977
4113
|
DuckDbAction,
|