@tinycloud/sdk-services 2.2.0-beta.10 → 2.2.0-beta.12

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 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
  *
@@ -3972,6 +4087,7 @@ var SecretsService = class {
3972
4087
  // Annotate the CommonJS export names for ESM import in node:
3973
4088
  0 && (module.exports = {
3974
4089
  BaseService,
4090
+ DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
3975
4091
  DataVaultService,
3976
4092
  DatabaseHandle,
3977
4093
  DuckDbAction,