@tinycloud/sdk-services 2.7.0 → 2.8.0

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.
@@ -1,5 +1,5 @@
1
- import { B as BaseService } from '../BaseService-BwBU5CQ4.cjs';
2
- import { R as Result, I as IService } from '../types-DAY1O1kW.cjs';
1
+ import { B as BaseService } from '../BaseService-DxSU4U-v.cjs';
2
+ import { R as Result, I as IService } from '../types-KCcgZiab.cjs';
3
3
 
4
4
  /**
5
5
  * KV Service Types
@@ -48,6 +48,11 @@ interface KVGetOptions {
48
48
  * precedence over {@link raw}.
49
49
  */
50
50
  binary?: boolean;
51
+ /**
52
+ * Ask the node to reject the response when the stored value exceeds this
53
+ * number of bytes. This bounds downloads before the response body is read.
54
+ */
55
+ maxResponseBytes?: number;
51
56
  /**
52
57
  * Custom timeout for this operation in milliseconds.
53
58
  */
@@ -74,6 +79,10 @@ interface KVPutOptions {
74
79
  * Custom metadata headers to store with the value.
75
80
  */
76
81
  metadata?: Record<string, string>;
82
+ /** Only write when the current object has this strong ETag. */
83
+ ifMatch?: string;
84
+ /** Only write when the key does not exist. */
85
+ ifNoneMatch?: "*";
77
86
  /**
78
87
  * Custom timeout for this operation in milliseconds.
79
88
  */
@@ -155,6 +164,8 @@ interface KVListOptions {
155
164
  * Return raw response instead of parsed JSON.
156
165
  */
157
166
  raw?: boolean;
167
+ /** Maximum number of keys the node may return. */
168
+ limit?: number;
158
169
  /**
159
170
  * Custom timeout for this operation in milliseconds.
160
171
  */
@@ -172,6 +183,8 @@ interface KVDeleteOptions {
172
183
  * Override the default prefix for this operation.
173
184
  */
174
185
  prefix?: string;
186
+ /** Only delete when the current object has this strong ETag. */
187
+ ifMatch?: string;
175
188
  /**
176
189
  * Custom timeout for this operation in milliseconds.
177
190
  */
@@ -290,6 +303,8 @@ interface KVListResponse {
290
303
  * Array of keys matching the list criteria.
291
304
  */
292
305
  keys: string[];
306
+ /** True when more matching keys exist than the requested limit. */
307
+ truncated?: boolean;
293
308
  }
294
309
  /**
295
310
  * Response from signed KV read URL creation.
@@ -439,7 +454,7 @@ interface IPrefixedKVService {
439
454
  * // -> Deletes: /app.myapp.com/old-settings.json
440
455
  * ```
441
456
  */
442
- delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<void>>;
457
+ delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<KVResponse<void>>>;
443
458
  /**
444
459
  * Get metadata for a key without retrieving the value.
445
460
  *
@@ -495,7 +510,7 @@ interface IKVServiceLike {
495
510
  put(key: string, value: unknown, options?: KVPutOptions): Promise<Result<KVResponse<void>>>;
496
511
  batchPut(items: KVBatchPutItem[], options?: KVBatchPutOptions): Promise<Result<KVBatchPutResponse>>;
497
512
  list(options?: KVListOptions): Promise<Result<KVListResponse>>;
498
- delete(key: string, options?: KVDeleteOptions): Promise<Result<void>>;
513
+ delete(key: string, options?: KVDeleteOptions): Promise<Result<KVResponse<void>>>;
499
514
  head(key: string, options?: KVHeadOptions): Promise<Result<KVResponse<void>>>;
500
515
  createSignedReadUrl(key: string, options?: KVCreateSignedReadUrlOptions): Promise<Result<KVSignedReadUrlResponse>>;
501
516
  }
@@ -577,7 +592,7 @@ declare class PrefixedKVService implements IPrefixedKVService {
577
592
  /**
578
593
  * Delete a key.
579
594
  */
580
- delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<void>>;
595
+ delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<KVResponse<void>>>;
581
596
  /**
582
597
  * Get metadata for a key without retrieving the value.
583
598
  */
@@ -699,7 +714,7 @@ interface IKVService extends IService {
699
714
  * }
700
715
  * ```
701
716
  */
702
- delete(key: string, options?: KVDeleteOptions): Promise<Result<void>>;
717
+ delete(key: string, options?: KVDeleteOptions): Promise<Result<KVResponse<void>>>;
703
718
  /**
704
719
  * Get metadata for a key without retrieving the value.
705
720
  *
@@ -895,7 +910,7 @@ declare class KVService extends BaseService implements IKVService {
895
910
  /**
896
911
  * Delete a key.
897
912
  */
898
- delete(key: string, options?: KVDeleteOptions): Promise<Result<void>>;
913
+ delete(key: string, options?: KVDeleteOptions): Promise<Result<KVResponse<void>>>;
899
914
  /**
900
915
  * Get metadata for a key without retrieving the value.
901
916
  */
@@ -1,5 +1,5 @@
1
- import { B as BaseService } from '../BaseService-CnPTKR5G.js';
2
- import { R as Result, I as IService } from '../types-DAY1O1kW.js';
1
+ import { B as BaseService } from '../BaseService-BiYeqCRT.js';
2
+ import { R as Result, I as IService } from '../types-KCcgZiab.js';
3
3
 
4
4
  /**
5
5
  * KV Service Types
@@ -48,6 +48,11 @@ interface KVGetOptions {
48
48
  * precedence over {@link raw}.
49
49
  */
50
50
  binary?: boolean;
51
+ /**
52
+ * Ask the node to reject the response when the stored value exceeds this
53
+ * number of bytes. This bounds downloads before the response body is read.
54
+ */
55
+ maxResponseBytes?: number;
51
56
  /**
52
57
  * Custom timeout for this operation in milliseconds.
53
58
  */
@@ -74,6 +79,10 @@ interface KVPutOptions {
74
79
  * Custom metadata headers to store with the value.
75
80
  */
76
81
  metadata?: Record<string, string>;
82
+ /** Only write when the current object has this strong ETag. */
83
+ ifMatch?: string;
84
+ /** Only write when the key does not exist. */
85
+ ifNoneMatch?: "*";
77
86
  /**
78
87
  * Custom timeout for this operation in milliseconds.
79
88
  */
@@ -155,6 +164,8 @@ interface KVListOptions {
155
164
  * Return raw response instead of parsed JSON.
156
165
  */
157
166
  raw?: boolean;
167
+ /** Maximum number of keys the node may return. */
168
+ limit?: number;
158
169
  /**
159
170
  * Custom timeout for this operation in milliseconds.
160
171
  */
@@ -172,6 +183,8 @@ interface KVDeleteOptions {
172
183
  * Override the default prefix for this operation.
173
184
  */
174
185
  prefix?: string;
186
+ /** Only delete when the current object has this strong ETag. */
187
+ ifMatch?: string;
175
188
  /**
176
189
  * Custom timeout for this operation in milliseconds.
177
190
  */
@@ -290,6 +303,8 @@ interface KVListResponse {
290
303
  * Array of keys matching the list criteria.
291
304
  */
292
305
  keys: string[];
306
+ /** True when more matching keys exist than the requested limit. */
307
+ truncated?: boolean;
293
308
  }
294
309
  /**
295
310
  * Response from signed KV read URL creation.
@@ -439,7 +454,7 @@ interface IPrefixedKVService {
439
454
  * // -> Deletes: /app.myapp.com/old-settings.json
440
455
  * ```
441
456
  */
442
- delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<void>>;
457
+ delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<KVResponse<void>>>;
443
458
  /**
444
459
  * Get metadata for a key without retrieving the value.
445
460
  *
@@ -495,7 +510,7 @@ interface IKVServiceLike {
495
510
  put(key: string, value: unknown, options?: KVPutOptions): Promise<Result<KVResponse<void>>>;
496
511
  batchPut(items: KVBatchPutItem[], options?: KVBatchPutOptions): Promise<Result<KVBatchPutResponse>>;
497
512
  list(options?: KVListOptions): Promise<Result<KVListResponse>>;
498
- delete(key: string, options?: KVDeleteOptions): Promise<Result<void>>;
513
+ delete(key: string, options?: KVDeleteOptions): Promise<Result<KVResponse<void>>>;
499
514
  head(key: string, options?: KVHeadOptions): Promise<Result<KVResponse<void>>>;
500
515
  createSignedReadUrl(key: string, options?: KVCreateSignedReadUrlOptions): Promise<Result<KVSignedReadUrlResponse>>;
501
516
  }
@@ -577,7 +592,7 @@ declare class PrefixedKVService implements IPrefixedKVService {
577
592
  /**
578
593
  * Delete a key.
579
594
  */
580
- delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<void>>;
595
+ delete(key: string, options?: Omit<KVDeleteOptions, 'prefix'>): Promise<Result<KVResponse<void>>>;
581
596
  /**
582
597
  * Get metadata for a key without retrieving the value.
583
598
  */
@@ -699,7 +714,7 @@ interface IKVService extends IService {
699
714
  * }
700
715
  * ```
701
716
  */
702
- delete(key: string, options?: KVDeleteOptions): Promise<Result<void>>;
717
+ delete(key: string, options?: KVDeleteOptions): Promise<Result<KVResponse<void>>>;
703
718
  /**
704
719
  * Get metadata for a key without retrieving the value.
705
720
  *
@@ -895,7 +910,7 @@ declare class KVService extends BaseService implements IKVService {
895
910
  /**
896
911
  * Delete a key.
897
912
  */
898
- delete(key: string, options?: KVDeleteOptions): Promise<Result<void>>;
913
+ delete(key: string, options?: KVDeleteOptions): Promise<Result<KVResponse<void>>>;
899
914
  /**
900
915
  * Get metadata for a key without retrieving the value.
901
916
  */
package/dist/kv/index.js CHANGED
@@ -13,6 +13,9 @@ var ErrorCodes = {
13
13
  // KV-specific errors
14
14
  KV_NOT_FOUND: "KV_NOT_FOUND",
15
15
  KV_WRITE_FAILED: "KV_WRITE_FAILED",
16
+ KV_PRECONDITION_FAILED: "KV_PRECONDITION_FAILED",
17
+ KV_CONFLICT: "KV_CONFLICT",
18
+ KV_RESPONSE_TOO_LARGE: "KV_RESPONSE_TOO_LARGE",
16
19
  // SQL-specific errors
17
20
  SQL_ERROR: "SQL_ERROR",
18
21
  SQL_PERMISSION_DENIED: "SQL_PERMISSION_DENIED",
@@ -614,7 +617,7 @@ var KVService = class extends BaseService {
614
617
  * @param signal - Optional abort signal
615
618
  * @returns Fetch response
616
619
  */
617
- async invokeOperation(path, action, body, signal) {
620
+ async invokeOperation(path, action, body, signal, extraHeaders) {
618
621
  const session = this.context.session;
619
622
  const headers = this.context.invoke(
620
623
  session,
@@ -622,9 +625,10 @@ var KVService = class extends BaseService {
622
625
  path,
623
626
  action
624
627
  );
628
+ const requestHeaders = Array.isArray(headers) ? [...headers, ...Object.entries(extraHeaders ?? {})] : { ...headers, ...extraHeaders };
625
629
  return this.context.fetch(`${this.host}/invoke`, {
626
630
  method: "POST",
627
- headers,
631
+ headers: requestHeaders,
628
632
  body,
629
633
  signal: this.combineSignals(signal)
630
634
  });
@@ -806,13 +810,21 @@ var KVService = class extends BaseService {
806
810
  if (!this.requireAuth()) {
807
811
  return err(authRequiredError("kv"));
808
812
  }
813
+ if (options?.maxResponseBytes !== void 0 && (!Number.isSafeInteger(options.maxResponseBytes) || options.maxResponseBytes <= 0)) {
814
+ return err(serviceError(
815
+ ErrorCodes.INVALID_INPUT,
816
+ "KV maxResponseBytes must be a positive safe integer",
817
+ "kv"
818
+ ));
819
+ }
809
820
  const path = this.getFullPath(key, options?.prefix);
810
821
  try {
811
822
  const response = await this.invokeOperation(
812
823
  path,
813
824
  KVAction.GET,
814
825
  void 0,
815
- options?.signal
826
+ options?.signal,
827
+ options?.maxResponseBytes === void 0 ? void 0 : { "x-tinycloud-max-response-bytes": String(options.maxResponseBytes) }
816
828
  );
817
829
  if (!response.ok) {
818
830
  if (response.status === 401 || response.status === 403) {
@@ -830,6 +842,14 @@ var KVService = class extends BaseService {
830
842
  return this.classifyNotFound(response, key);
831
843
  }
832
844
  const errorText = await response.text();
845
+ if (response.status === 413) {
846
+ return err(serviceError(
847
+ ErrorCodes.KV_RESPONSE_TOO_LARGE,
848
+ `KV value at key "${key}" exceeds the requested response limit`,
849
+ "kv",
850
+ { meta: { status: response.status, statusText: response.statusText } }
851
+ ));
852
+ }
833
853
  return err(
834
854
  serviceError(
835
855
  ErrorCodes.NETWORK_ERROR,
@@ -861,6 +881,13 @@ var KVService = class extends BaseService {
861
881
  if (!this.requireAuth()) {
862
882
  return err(authRequiredError("kv"));
863
883
  }
884
+ if (options?.ifMatch !== void 0 && options.ifNoneMatch !== void 0) {
885
+ return err(serviceError(
886
+ ErrorCodes.INVALID_INPUT,
887
+ "KV put cannot combine ifMatch and ifNoneMatch",
888
+ "kv"
889
+ ));
890
+ }
864
891
  const path = this.getFullPath(key, options?.prefix);
865
892
  const body = this.serializePutValue(value, options?.contentType);
866
893
  try {
@@ -868,7 +895,11 @@ var KVService = class extends BaseService {
868
895
  path,
869
896
  KVAction.PUT,
870
897
  body,
871
- options?.signal
898
+ options?.signal,
899
+ {
900
+ ...options?.ifMatch === void 0 ? {} : { "if-match": options.ifMatch },
901
+ ...options?.ifNoneMatch === void 0 ? {} : { "if-none-match": options.ifNoneMatch }
902
+ }
872
903
  );
873
904
  if (!response.ok) {
874
905
  if (response.status === 401) {
@@ -881,6 +912,22 @@ var KVService = class extends BaseService {
881
912
  }));
882
913
  }
883
914
  const errorText = await response.text();
915
+ if (response.status === 412) {
916
+ return err(serviceError(
917
+ ErrorCodes.KV_PRECONDITION_FAILED,
918
+ `KV precondition failed for key "${key}"`,
919
+ "kv",
920
+ { meta: { status: response.status, statusText: response.statusText } }
921
+ ));
922
+ }
923
+ if (response.status === 503 && (options?.ifMatch !== void 0 || options?.ifNoneMatch !== void 0)) {
924
+ return err(serviceError(
925
+ ErrorCodes.KV_CONFLICT,
926
+ `Concurrent KV update conflicted for key "${key}"`,
927
+ "kv",
928
+ { meta: { status: response.status, statusText: response.statusText } }
929
+ ));
930
+ }
884
931
  const quotaError = this.handleQuotaErrorResponse(
885
932
  response,
886
933
  errorText,
@@ -1016,6 +1063,13 @@ var KVService = class extends BaseService {
1016
1063
  if (!this.requireAuth()) {
1017
1064
  return err(authRequiredError("kv"));
1018
1065
  }
1066
+ if (options?.limit !== void 0 && (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 1e3)) {
1067
+ return err(serviceError(
1068
+ ErrorCodes.INVALID_INPUT,
1069
+ "KV list limit must be an integer from 1 through 1000",
1070
+ "kv"
1071
+ ));
1072
+ }
1019
1073
  let listPath = options?.prefix ?? this._config.prefix ?? "";
1020
1074
  if (options?.path) {
1021
1075
  listPath = listPath ? `${listPath}/${options.path}` : options.path;
@@ -1025,7 +1079,8 @@ var KVService = class extends BaseService {
1025
1079
  listPath,
1026
1080
  KVAction.LIST,
1027
1081
  void 0,
1028
- options?.signal
1082
+ options?.signal,
1083
+ options?.limit === void 0 ? void 0 : { "x-tinycloud-limit": String(options.limit) }
1029
1084
  );
1030
1085
  if (!response.ok) {
1031
1086
  if (response.status === 401) {
@@ -1055,7 +1110,10 @@ var KVService = class extends BaseService {
1055
1110
  (key) => key.startsWith(prefixWithSlash) ? key.slice(prefixWithSlash.length) : key
1056
1111
  );
1057
1112
  }
1058
- return ok({ keys });
1113
+ return ok({
1114
+ keys,
1115
+ ...response.headers.get("x-tinycloud-truncated") === null ? {} : { truncated: response.headers.get("x-tinycloud-truncated") === "true" }
1116
+ });
1059
1117
  } catch (error) {
1060
1118
  return err(wrapError("kv", error));
1061
1119
  }
@@ -1075,7 +1133,8 @@ var KVService = class extends BaseService {
1075
1133
  path,
1076
1134
  KVAction.DELETE,
1077
1135
  void 0,
1078
- options?.signal
1136
+ options?.signal,
1137
+ options?.ifMatch === void 0 ? void 0 : { "if-match": options.ifMatch }
1079
1138
  );
1080
1139
  if (!response.ok) {
1081
1140
  if (response.status === 401) {
@@ -1091,6 +1150,22 @@ var KVService = class extends BaseService {
1091
1150
  return this.classifyNotFound(response, key);
1092
1151
  }
1093
1152
  const errorText = await response.text();
1153
+ if (response.status === 412) {
1154
+ return err(serviceError(
1155
+ ErrorCodes.KV_PRECONDITION_FAILED,
1156
+ `KV precondition failed for key "${key}"`,
1157
+ "kv",
1158
+ { meta: { status: response.status, statusText: response.statusText } }
1159
+ ));
1160
+ }
1161
+ if (response.status === 503 && options?.ifMatch !== void 0) {
1162
+ return err(serviceError(
1163
+ ErrorCodes.KV_CONFLICT,
1164
+ `Concurrent KV delete conflicted for key "${key}"`,
1165
+ "kv",
1166
+ { meta: { status: response.status, statusText: response.statusText } }
1167
+ ));
1168
+ }
1094
1169
  return err(
1095
1170
  serviceError(
1096
1171
  ErrorCodes.NETWORK_ERROR,
@@ -1100,7 +1175,10 @@ var KVService = class extends BaseService {
1100
1175
  )
1101
1176
  );
1102
1177
  }
1103
- return ok(void 0);
1178
+ return ok({
1179
+ data: void 0,
1180
+ headers: this.createResponseHeaders(response.headers)
1181
+ });
1104
1182
  } catch (error) {
1105
1183
  return err(wrapError("kv", error));
1106
1184
  }