@tinycloud/operations 0.3.0-beta.0 → 0.3.1-beta.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.
package/dist/index.js CHANGED
@@ -2396,7 +2396,7 @@ function encOk(data) {
2396
2396
  function encErr(error) {
2397
2397
  return { ok: false, error };
2398
2398
  }
2399
- var import_decrypt_transport_response_error, import_decrypt_transport_response_error2, import_decrypt_transport_response_error3, ErrorCodes, defaultRetryPolicy, TelemetryEvents, REDACTED, SAFE_NUMBER_FIELDS, SAFE_BOOLEAN_FIELDS, DEBUG_FLAG, MAX_EVENTS, TinyCloudDebugLogger, tinyCloudDebugLogger, ServiceErrorSchema, GenericResultSchema, KVResponseHeadersSchema, GenericKVResponseSchema, KVListResponseSchema, KVListResultSchema, ServiceRequestEventSchema, ServiceResponseEventSchema, ServiceErrorEventSchema, ServiceRetryEventSchema, TelemetrySpanEventSchema, RetryPolicySchema, ServiceSessionSchema, BaseService, PrefixedKVService, DEFAULT_SIGNED_READ_URL_EXPIRY_MS, KVAction, KVService, SQLMigrations, DatabaseHandle, SQLAction, DDL_TOKENS, MIGRATIONS_TABLE, MIGRATIONS_SCHEMA, MIGRATIONS_META_NAMESPACE, MIGRATIONS_META_ID, SQLService, DuckDbDatabaseHandle, DuckDbAction, DuckDbService, AsyncQueue, HooksService, VaultVersionConfig, CURRENT_VAULT_VERSION, VaultHeaders, DB_NAME, DB_VERSION, STORE_NAME, WRAP_KEY_ID, DataVaultService, HEX, URN_PREFIX, NETWORK_NAME_RE, PKH_EIP155_DID_RE, NetworkIdError, DEFAULT_ENCRYPTION_ALG, ENVELOPE_VERSION, DEFAULT_KEY_VERSION, DECRYPT_FACT_TYPE, DECRYPT_RESULT_TYPE, EncryptionService;
2399
+ var import_decrypt_transport_response_error, import_decrypt_transport_response_error2, import_decrypt_transport_response_error3, ErrorCodes, defaultRetryPolicy, TelemetryEvents, REDACTED, SAFE_NUMBER_FIELDS, SAFE_BOOLEAN_FIELDS, DEBUG_FLAG, MAX_EVENTS, TinyCloudDebugLogger, tinyCloudDebugLogger, ServiceErrorSchema, GenericResultSchema, KVResponseHeadersSchema, GenericKVResponseSchema, KVListResponseSchema, KVListResultSchema, ServiceRequestEventSchema, ServiceResponseEventSchema, ServiceErrorEventSchema, ServiceRetryEventSchema, TelemetrySpanEventSchema, RetryPolicySchema, ServiceSessionSchema, BaseService, PrefixedKVService, DEFAULT_SIGNED_READ_URL_EXPIRY_MS, KVAction, MAX_KV_BATCH_READ_ITEMS, KVService, SQLMigrations, DatabaseHandle, SQLAction, DDL_TOKENS, MIGRATIONS_TABLE, MIGRATIONS_SCHEMA, MIGRATIONS_META_NAMESPACE, MIGRATIONS_META_ID, SQLService, DuckDbDatabaseHandle, DuckDbAction, DuckDbService, AsyncQueue, HooksService, VaultVersionConfig, CURRENT_VAULT_VERSION, VaultHeaders, DB_NAME, DB_VERSION, STORE_NAME, WRAP_KEY_ID, DataVaultService, HEX, URN_PREFIX, NETWORK_NAME_RE, PKH_EIP155_DID_RE, NetworkIdError, DEFAULT_ENCRYPTION_ALG, ENVELOPE_VERSION, DEFAULT_KEY_VERSION, DECRYPT_FACT_TYPE, DECRYPT_RESULT_TYPE, EncryptionService;
2400
2400
  var init_dist2 = __esm({
2401
2401
  "../sdk-services/dist/index.js"() {
2402
2402
  "use strict";
@@ -2913,6 +2913,20 @@ var init_dist2 = __esm({
2913
2913
  const fullKey = this.getFullKey(key);
2914
2914
  return this._kv.get(fullKey, { ...options, prefix: "" });
2915
2915
  }
2916
+ async batchGet(keys, options) {
2917
+ const response = await this._kv.batchGet(
2918
+ keys.map((key) => this.getFullKey(key)),
2919
+ { ...options, prefix: "" }
2920
+ );
2921
+ if (!response.ok) return response;
2922
+ return ok({
2923
+ ...response.data,
2924
+ results: response.data.results.map((item, index) => ({
2925
+ ...item,
2926
+ key: keys[index]
2927
+ }))
2928
+ });
2929
+ }
2916
2930
  /**
2917
2931
  * Store a value at a key.
2918
2932
  */
@@ -2957,6 +2971,20 @@ var init_dist2 = __esm({
2957
2971
  const fullKey = this.getFullKey(key);
2958
2972
  return this._kv.head(fullKey, { ...options, prefix: "" });
2959
2973
  }
2974
+ async batchHead(keys, options) {
2975
+ const response = await this._kv.batchHead(
2976
+ keys.map((key) => this.getFullKey(key)),
2977
+ { ...options, prefix: "" }
2978
+ );
2979
+ if (!response.ok) return response;
2980
+ return ok({
2981
+ ...response.data,
2982
+ results: response.data.results.map((item, index) => ({
2983
+ ...item,
2984
+ key: keys[index]
2985
+ }))
2986
+ });
2987
+ }
2960
2988
  /**
2961
2989
  * Create a short-lived signed URL for reading a KV object.
2962
2990
  */
@@ -2981,6 +3009,7 @@ var init_dist2 = __esm({
2981
3009
  DELETE: "tinycloud.kv/del",
2982
3010
  HEAD: "tinycloud.kv/metadata"
2983
3011
  };
3012
+ MAX_KV_BATCH_READ_ITEMS = 100;
2984
3013
  KVService = class extends BaseService {
2985
3014
  /**
2986
3015
  * Create a new KVService instance.
@@ -3209,6 +3238,173 @@ var init_dist2 = __esm({
3209
3238
  get: (name) => headers.get(name)
3210
3239
  };
3211
3240
  }
3241
+ createBatchResponseHeaders(values) {
3242
+ const normalized = new Map(
3243
+ Object.entries(values).map(([name, value]) => [name.toLowerCase(), value])
3244
+ );
3245
+ return this.createResponseHeaders({
3246
+ get: (name) => normalized.get(name.toLowerCase()) ?? null
3247
+ });
3248
+ }
3249
+ parseBatchValue(dataBase64, headers, raw = false, binary = false) {
3250
+ const encoded = globalThis.atob(dataBase64);
3251
+ const bytes = Uint8Array.from(encoded, (character) => character.charCodeAt(0));
3252
+ if (binary) return bytes;
3253
+ const text = new TextDecoder().decode(bytes);
3254
+ if (raw) return text;
3255
+ const contentType = Object.entries(headers).find(
3256
+ ([name]) => name.toLowerCase() === "content-type"
3257
+ )?.[1];
3258
+ return contentType?.includes("application/json") ? JSON.parse(text) : text;
3259
+ }
3260
+ normalizeBatchReadResponse(data, paths, requireData) {
3261
+ if (!data || typeof data !== "object") return void 0;
3262
+ const response = data;
3263
+ if (!Array.isArray(response.results) || response.results.length !== paths.length) {
3264
+ return void 0;
3265
+ }
3266
+ for (let index = 0; index < response.results.length; index++) {
3267
+ const item = response.results[index];
3268
+ if (!item || typeof item !== "object" || item.key !== paths[index] || typeof item.ok !== "boolean") {
3269
+ return void 0;
3270
+ }
3271
+ if (item.ok && (!item.headers || typeof item.headers !== "object" || Object.values(item.headers).some((value) => typeof value !== "string") || requireData && typeof item.dataBase64 !== "string")) {
3272
+ return void 0;
3273
+ }
3274
+ if (!item.ok && (!item.error || typeof item.error.code !== "string" || typeof item.error.message !== "string")) {
3275
+ return void 0;
3276
+ }
3277
+ }
3278
+ return response;
3279
+ }
3280
+ async batchRead(keys, action, options) {
3281
+ if (!this.requireAuth()) return err(authRequiredError("kv"));
3282
+ if (keys.length === 0) return ok({ results: [], count: 0 });
3283
+ if (keys.length > MAX_KV_BATCH_READ_ITEMS) {
3284
+ return err(serviceError(
3285
+ ErrorCodes.INVALID_INPUT,
3286
+ `KV batch reads accept at most ${MAX_KV_BATCH_READ_ITEMS} keys`,
3287
+ "kv"
3288
+ ));
3289
+ }
3290
+ if (keys.length === 1) {
3291
+ const key = keys[0];
3292
+ const result = action === KVAction.GET ? await this.get(key, options) : await this.head(key, options);
3293
+ return ok({
3294
+ results: [{ key, result }],
3295
+ count: 1
3296
+ });
3297
+ }
3298
+ if (!this.context.invokeAny) {
3299
+ return err(serviceError(
3300
+ ErrorCodes.INVALID_INPUT,
3301
+ "KV batch reads require SDK runtime support for multi-resource invocations",
3302
+ "kv"
3303
+ ));
3304
+ }
3305
+ const getOptions = action === KVAction.GET ? options : void 0;
3306
+ if (getOptions?.maxResponseBytes !== void 0 && (!Number.isSafeInteger(getOptions.maxResponseBytes) || getOptions.maxResponseBytes <= 0)) {
3307
+ return err(serviceError(
3308
+ ErrorCodes.INVALID_INPUT,
3309
+ "KV maxResponseBytes must be a positive safe integer",
3310
+ "kv"
3311
+ ));
3312
+ }
3313
+ const paths = keys.map((key) => this.getFullPath(key, options?.prefix));
3314
+ if (new Set(paths).size !== paths.length) {
3315
+ return err(serviceError(
3316
+ ErrorCodes.INVALID_INPUT,
3317
+ "KV batch read received duplicate keys after prefix resolution",
3318
+ "kv"
3319
+ ));
3320
+ }
3321
+ try {
3322
+ const session = this.context.session;
3323
+ const invocationHeaders = this.context.invokeAny(
3324
+ session,
3325
+ paths.map((path) => ({
3326
+ spaceId: session.spaceId,
3327
+ service: "kv",
3328
+ path,
3329
+ action
3330
+ }))
3331
+ );
3332
+ const limitHeaders = {};
3333
+ if (getOptions?.maxResponseBytes !== void 0) {
3334
+ limitHeaders["x-tinycloud-max-response-bytes"] = String(
3335
+ getOptions.maxResponseBytes
3336
+ );
3337
+ }
3338
+ const headers = Array.isArray(invocationHeaders) ? [...invocationHeaders, ...Object.entries(limitHeaders)] : { ...invocationHeaders, ...limitHeaders };
3339
+ const response = await this.context.fetch(`${this.host}/invoke`, {
3340
+ method: "POST",
3341
+ headers,
3342
+ signal: this.combineSignals(options?.signal)
3343
+ });
3344
+ if (!response.ok) {
3345
+ const errorText = await response.text();
3346
+ if (response.status === 401 || response.status === 403) {
3347
+ const { resource, action: requiredAction } = parseAuthError(errorText);
3348
+ return err(authUnauthorizedError("kv", errorText, {
3349
+ status: response.status,
3350
+ ...requiredAction && { requiredAction },
3351
+ ...resource && { resource }
3352
+ }));
3353
+ }
3354
+ if (response.status === 413) {
3355
+ return err(serviceError(
3356
+ ErrorCodes.KV_RESPONSE_TOO_LARGE,
3357
+ "A KV batch value exceeds the requested response limit",
3358
+ "kv",
3359
+ { meta: { status: response.status, statusText: response.statusText } }
3360
+ ));
3361
+ }
3362
+ return err(serviceError(
3363
+ ErrorCodes.NETWORK_ERROR,
3364
+ `Failed to batch read ${keys.length} key(s): ${response.status} - ${errorText}`,
3365
+ "kv",
3366
+ { meta: { status: response.status, statusText: response.statusText } }
3367
+ ));
3368
+ }
3369
+ const payload = this.normalizeBatchReadResponse(
3370
+ await response.json(),
3371
+ paths,
3372
+ action === KVAction.GET
3373
+ );
3374
+ if (!payload) {
3375
+ return err(serviceError(
3376
+ ErrorCodes.NETWORK_ERROR,
3377
+ "KV batch read response did not match the requested keys",
3378
+ "kv"
3379
+ ));
3380
+ }
3381
+ const results = payload.results.map((item, index) => {
3382
+ if (!item.ok) {
3383
+ return {
3384
+ key: keys[index],
3385
+ result: err(serviceError(item.error.code, item.error.message, "kv"))
3386
+ };
3387
+ }
3388
+ const headers2 = item.headers;
3389
+ const data = action === KVAction.HEAD ? void 0 : this.parseBatchValue(
3390
+ item.dataBase64,
3391
+ headers2,
3392
+ getOptions?.raw,
3393
+ getOptions?.binary
3394
+ );
3395
+ return {
3396
+ key: keys[index],
3397
+ result: ok({
3398
+ data,
3399
+ headers: this.createBatchResponseHeaders(headers2)
3400
+ })
3401
+ };
3402
+ });
3403
+ return ok({ results, count: results.length });
3404
+ } catch (error) {
3405
+ return err(wrapError("kv", error));
3406
+ }
3407
+ }
3212
3408
  /**
3213
3409
  * Parse response body based on content type.
3214
3410
  *
@@ -3355,6 +3551,13 @@ var init_dist2 = __esm({
3355
3551
  }
3356
3552
  });
3357
3553
  }
3554
+ async batchGet(keys, options) {
3555
+ return this.withTelemetry(
3556
+ "batchGet",
3557
+ String(keys.length),
3558
+ () => this.batchRead(keys, KVAction.GET, options)
3559
+ );
3560
+ }
3358
3561
  /**
3359
3562
  * Store a value at a key.
3360
3563
  */
@@ -3714,6 +3917,13 @@ var init_dist2 = __esm({
3714
3917
  }
3715
3918
  });
3716
3919
  }
3920
+ async batchHead(keys, options) {
3921
+ return this.withTelemetry(
3922
+ "batchHead",
3923
+ String(keys.length),
3924
+ () => this.batchRead(keys, KVAction.HEAD, options)
3925
+ );
3926
+ }
3717
3927
  /**
3718
3928
  * Create a short-lived signed URL for reading a KV object.
3719
3929
  */