@tinycloud/operations 0.3.4-beta.1 → 0.3.4-beta.3

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
@@ -1051,6 +1051,18 @@ function storageLimitReachedError(service, message, meta) {
1051
1051
  meta
1052
1052
  };
1053
1053
  }
1054
+ function parseStorageQuotaBytes(responseText) {
1055
+ const match = responseText.match(/Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i);
1056
+ if (!match) return void 0;
1057
+ return {
1058
+ usedBytes: parseInt(match[1], 10),
1059
+ limitBytes: parseInt(match[2], 10)
1060
+ };
1061
+ }
1062
+ function storageRejectionError(service, status, meta, responseText) {
1063
+ const quotaMeta = { ...meta, ...parseStorageQuotaBytes(responseText) };
1064
+ return status === 402 ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta) : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);
1065
+ }
1054
1066
  function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
1055
1067
  if (error instanceof Error) {
1056
1068
  if (error.name === "AbortError") {
@@ -1180,6 +1192,9 @@ function serializeSqlValues(values) {
1180
1192
  (value) => value instanceof Uint8Array ? Array.from(value) : value
1181
1193
  );
1182
1194
  }
1195
+ function isMissingTableError(message) {
1196
+ return /no such table/i.test(message);
1197
+ }
1183
1198
  function migrationKey(namespace, id) {
1184
1199
  return `${encodeURIComponent(namespace)}:${encodeURIComponent(id)}`;
1185
1200
  }
@@ -1256,10 +1271,14 @@ async function responseError(service, message, response) {
1256
1271
  }
1257
1272
  } catch {
1258
1273
  }
1259
- return wrapError(
1274
+ const error = wrapError(
1260
1275
  service,
1261
1276
  new Error(`${message}: ${response.status} ${detail}`)
1262
1277
  );
1278
+ return {
1279
+ ...error,
1280
+ meta: { ...error.meta, status: response.status, statusText: response.statusText }
1281
+ };
1263
1282
  }
1264
1283
  function isAbortError(error) {
1265
1284
  return error instanceof DOMException && error.name === "AbortError";
@@ -1661,6 +1680,9 @@ function defaultVaultMessage(input) {
1661
1680
  return input.message ?? `Public key not found for ${input.did}`;
1662
1681
  case "STORAGE_ERROR":
1663
1682
  return input.message ?? input.cause.message;
1683
+ case "STORAGE_QUOTA_EXCEEDED":
1684
+ case "STORAGE_LIMIT_REACHED":
1685
+ return input.message;
1664
1686
  }
1665
1687
  }
1666
1688
  function vaultError(input) {
@@ -1671,6 +1693,15 @@ function vaultError(input) {
1671
1693
  };
1672
1694
  return { ok: false, error };
1673
1695
  }
1696
+ function kvWriteError(context, error) {
1697
+ if (error.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED || error.code === ErrorCodes.STORAGE_LIMIT_REACHED) {
1698
+ return vaultError({ code: error.code, message: error.message, meta: error.meta });
1699
+ }
1700
+ return vaultError({
1701
+ code: "STORAGE_ERROR",
1702
+ cause: new Error(`${context}: ${error.message}`)
1703
+ });
1704
+ }
1674
1705
  function canonicalize(value) {
1675
1706
  if (value === void 0) {
1676
1707
  return "";
@@ -2429,7 +2460,7 @@ function encOk(data) {
2429
2460
  function encErr(error) {
2430
2461
  return { ok: false, error };
2431
2462
  }
2432
- var import_zod, import_decrypt_transport_response_error, 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;
2463
+ var import_zod, import_decrypt_transport_response_error, 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, STORAGE_FULL_MESSAGE, STORAGE_WRITE_TOO_LARGE_MESSAGE, 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;
2433
2464
  var init_dist2 = __esm({
2434
2465
  "../sdk-services/dist/index.js"() {
2435
2466
  "use strict";
@@ -2712,6 +2743,8 @@ var init_dist2 = __esm({
2712
2743
  /** The session key JWK (required for invoke) */
2713
2744
  jwk: import_zod.z.object({}).passthrough()
2714
2745
  });
2746
+ 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.";
2747
+ 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.";
2715
2748
  BaseService = class {
2716
2749
  constructor() {
2717
2750
  this.abortController = new AbortController();
@@ -3060,47 +3093,21 @@ var init_dist2 = __esm({
3060
3093
  get config() {
3061
3094
  return this._config;
3062
3095
  }
3063
- // Parses "Used: X bytes, Limit: Y bytes" from tinycloud-node error responses
3064
- parseQuotaInfo(errorText) {
3065
- const match = errorText.match(
3066
- /Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i
3067
- );
3068
- if (match) {
3069
- return {
3070
- usedBytes: parseInt(match[1], 10),
3071
- limitBytes: parseInt(match[2], 10)
3072
- };
3073
- }
3074
- return void 0;
3075
- }
3076
3096
  handleQuotaErrorResponse(response, errorText, key) {
3077
- if (response.status === 402) {
3078
- const quotaInfo = this.parseQuotaInfo(errorText);
3079
- return err(
3080
- storageQuotaExceededError(
3081
- "kv",
3082
- `Storage quota exceeded for key "${key}": ${errorText}`,
3083
- {
3084
- status: response.status,
3085
- ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
3086
- }
3087
- )
3088
- );
3097
+ if (response.status !== 402 && response.status !== 413) {
3098
+ return void 0;
3089
3099
  }
3090
- if (response.status === 413) {
3091
- const quotaInfo = this.parseQuotaInfo(errorText);
3092
- return err(
3093
- storageLimitReachedError(
3094
- "kv",
3095
- `Storage limit reached for key "${key}": ${errorText}`,
3096
- {
3097
- status: response.status,
3098
- ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
3099
- }
3100
- )
3101
- );
3100
+ if (response.status === 413 && !parseStorageQuotaBytes(errorText)) {
3101
+ return void 0;
3102
3102
  }
3103
- return void 0;
3103
+ return err(
3104
+ storageRejectionError(
3105
+ "kv",
3106
+ response.status,
3107
+ { status: response.status, statusText: response.statusText, key },
3108
+ errorText
3109
+ )
3110
+ );
3104
3111
  }
3105
3112
  /**
3106
3113
  * Classify a KV 404 by reading the response body once.
@@ -3378,12 +3385,11 @@ var init_dist2 = __esm({
3378
3385
  if (!response.ok) {
3379
3386
  const errorText = await response.text();
3380
3387
  if (response.status === 401 || response.status === 403) {
3381
- const { resource, action: requiredAction } = parseAuthError(errorText);
3382
- return err(authUnauthorizedError("kv", errorText, {
3383
- status: response.status,
3384
- ...requiredAction && { requiredAction },
3385
- ...resource && { resource }
3386
- }));
3388
+ return this.authorizationFailure(
3389
+ `Failed to batch read ${keys.length} key(s)`,
3390
+ response,
3391
+ errorText
3392
+ );
3387
3393
  }
3388
3394
  if (response.status === 413) {
3389
3395
  return err(serviceError(
@@ -3482,23 +3488,39 @@ var init_dist2 = __esm({
3482
3488
  } catch {
3483
3489
  }
3484
3490
  if (response.status === 401 || response.status === 403) {
3485
- const { resource, action } = parseAuthError(errorText);
3486
- return err(authUnauthorizedError("kv", errorText, {
3487
- status: response.status,
3488
- ...action && { requiredAction: action },
3489
- ...resource && { resource }
3490
- }));
3491
+ return this.authorizationFailure(
3492
+ `Failed to create signed read URL for key ${JSON.stringify(key)}`,
3493
+ response,
3494
+ errorText
3495
+ );
3491
3496
  }
3492
3497
  const code = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
3493
3498
  return err(
3494
3499
  serviceError(
3495
3500
  code,
3496
- `Failed to create signed read URL for key "${key}": ${response.status} - ${errorText}`,
3501
+ `Failed to create signed read URL for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
3497
3502
  "kv",
3498
3503
  { meta: { status: response.status, statusText: response.statusText } }
3499
3504
  )
3500
3505
  );
3501
3506
  }
3507
+ /**
3508
+ * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP
3509
+ * status and the server text (`<context>: <status> - <text>`, the same
3510
+ * shape as every other KV failure) so callers that rethrow only the message
3511
+ * still see the status; `meta.status` carries it for typed callers.
3512
+ * Resource/ability hints are parsed from the raw body, never the message.
3513
+ */
3514
+ authorizationFailure(context, response, errorText, extraMeta = {}) {
3515
+ const { resource, action } = parseAuthError(errorText);
3516
+ const detail = errorText.trim().length > 0 ? errorText : response.statusText || "authorization failed";
3517
+ return err(authUnauthorizedError("kv", `${context}: ${response.status} - ${detail}`, {
3518
+ status: response.status,
3519
+ ...action && { requiredAction: action },
3520
+ ...resource && { resource },
3521
+ ...extraMeta
3522
+ }));
3523
+ }
3502
3524
  normalizeSignedReadUrlResponse(data) {
3503
3525
  if (!data || typeof data !== "object") {
3504
3526
  return void 0;
@@ -3541,14 +3563,13 @@ var init_dist2 = __esm({
3541
3563
  if (!response.ok) {
3542
3564
  if (response.status === 401 || response.status === 403) {
3543
3565
  const errorText2 = await response.text();
3544
- const { resource, action } = parseAuthError(errorText2);
3545
3566
  const permissionHint = parsePermissionHintFromErrorText(errorText2);
3546
- return err(authUnauthorizedError("kv", errorText2, {
3547
- status: response.status,
3548
- ...action && { requiredAction: action },
3549
- ...resource && { resource },
3550
- ...permissionHint === void 0 ? {} : { permissionHint }
3551
- }));
3567
+ return this.authorizationFailure(
3568
+ `Failed to get key ${JSON.stringify(key)}`,
3569
+ response,
3570
+ errorText2,
3571
+ permissionHint === void 0 ? {} : { permissionHint }
3572
+ );
3552
3573
  }
3553
3574
  if (response.status === 404) {
3554
3575
  return this.classifyNotFound(response, key);
@@ -3557,7 +3578,7 @@ var init_dist2 = __esm({
3557
3578
  if (response.status === 413) {
3558
3579
  return err(serviceError(
3559
3580
  ErrorCodes.KV_RESPONSE_TOO_LARGE,
3560
- `KV value at key "${key}" exceeds the requested response limit`,
3581
+ `KV value at key ${JSON.stringify(key)} exceeds the requested response limit`,
3561
3582
  "kv",
3562
3583
  { meta: { status: response.status, statusText: response.statusText } }
3563
3584
  ));
@@ -3565,7 +3586,7 @@ var init_dist2 = __esm({
3565
3586
  return err(
3566
3587
  serviceError(
3567
3588
  ErrorCodes.NETWORK_ERROR,
3568
- `Failed to get key "${key}": ${response.status} - ${errorText}`,
3589
+ `Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
3569
3590
  "kv",
3570
3591
  { meta: { status: response.status, statusText: response.statusText } }
3571
3592
  )
@@ -3621,21 +3642,18 @@ var init_dist2 = __esm({
3621
3642
  }
3622
3643
  );
3623
3644
  if (response.status === 401 || response.status === 403) {
3624
- const errorText = await response.text();
3625
- const message = errorText.trim().length > 0 ? errorText : `Failed to put key "${key}": ${response.status} - ${response.statusText || "authorization failed"}`;
3626
- const { resource, action: requiredAction } = parseAuthError(errorText);
3627
- return err(authUnauthorizedError("kv", message, {
3628
- status: response.status,
3629
- ...requiredAction && { requiredAction },
3630
- ...resource && { resource }
3631
- }));
3645
+ return this.authorizationFailure(
3646
+ `Failed to put key ${JSON.stringify(key)}`,
3647
+ response,
3648
+ await response.text()
3649
+ );
3632
3650
  }
3633
3651
  if (!response.ok) {
3634
3652
  const errorText = await response.text();
3635
3653
  if (response.status === 412) {
3636
3654
  return err(serviceError(
3637
3655
  ErrorCodes.KV_PRECONDITION_FAILED,
3638
- `KV precondition failed for key "${key}"`,
3656
+ `KV precondition failed for key ${JSON.stringify(key)}`,
3639
3657
  "kv",
3640
3658
  { meta: { status: response.status, statusText: response.statusText } }
3641
3659
  ));
@@ -3643,7 +3661,7 @@ var init_dist2 = __esm({
3643
3661
  if (response.status === 503 && (options?.ifMatch !== void 0 || options?.ifNoneMatch !== void 0)) {
3644
3662
  return err(serviceError(
3645
3663
  ErrorCodes.KV_CONFLICT,
3646
- `Concurrent KV update conflicted for key "${key}"`,
3664
+ `Concurrent KV update conflicted for key ${JSON.stringify(key)}`,
3647
3665
  "kv",
3648
3666
  { meta: { status: response.status, statusText: response.statusText } }
3649
3667
  ));
@@ -3659,7 +3677,7 @@ var init_dist2 = __esm({
3659
3677
  return err(
3660
3678
  serviceError(
3661
3679
  ErrorCodes.KV_WRITE_FAILED,
3662
- `Failed to put key "${key}": ${response.status} - ${errorText}`,
3680
+ `Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
3663
3681
  "kv",
3664
3682
  { meta: { status: response.status, statusText: response.statusText } }
3665
3683
  )
@@ -3753,12 +3771,11 @@ var init_dist2 = __esm({
3753
3771
  );
3754
3772
  }
3755
3773
  if (response.status === 401 || response.status === 403) {
3756
- const { resource, action } = parseAuthError(errorText);
3757
- return err(authUnauthorizedError("kv", errorText, {
3758
- status: response.status,
3759
- ...action && { requiredAction: action },
3760
- ...resource && { resource }
3761
- }));
3774
+ return this.authorizationFailure(
3775
+ `Failed to batch put ${items.length} key(s)`,
3776
+ response,
3777
+ errorText
3778
+ );
3762
3779
  }
3763
3780
  const quotaError = this.handleQuotaErrorResponse(
3764
3781
  response,
@@ -3879,13 +3896,11 @@ var init_dist2 = __esm({
3879
3896
  );
3880
3897
  if (!response.ok) {
3881
3898
  if (response.status === 401) {
3882
- const errorText2 = await response.text();
3883
- const { resource, action } = parseAuthError(errorText2);
3884
- return err(authUnauthorizedError("kv", errorText2, {
3885
- status: response.status,
3886
- ...action && { requiredAction: action },
3887
- ...resource && { resource }
3888
- }));
3899
+ return this.authorizationFailure(
3900
+ "Failed to list keys",
3901
+ response,
3902
+ await response.text()
3903
+ );
3889
3904
  }
3890
3905
  const errorText = await response.text();
3891
3906
  return err(
@@ -3934,13 +3949,11 @@ var init_dist2 = __esm({
3934
3949
  );
3935
3950
  if (!response.ok) {
3936
3951
  if (response.status === 401) {
3937
- const errorText2 = await response.text();
3938
- const { resource, action } = parseAuthError(errorText2);
3939
- return err(authUnauthorizedError("kv", errorText2, {
3940
- status: response.status,
3941
- ...action && { requiredAction: action },
3942
- ...resource && { resource }
3943
- }));
3952
+ return this.authorizationFailure(
3953
+ `Failed to delete key ${JSON.stringify(key)}`,
3954
+ response,
3955
+ await response.text()
3956
+ );
3944
3957
  }
3945
3958
  if (response.status === 404) {
3946
3959
  return this.classifyNotFound(response, key);
@@ -3949,7 +3962,7 @@ var init_dist2 = __esm({
3949
3962
  if (response.status === 412) {
3950
3963
  return err(serviceError(
3951
3964
  ErrorCodes.KV_PRECONDITION_FAILED,
3952
- `KV precondition failed for key "${key}"`,
3965
+ `KV precondition failed for key ${JSON.stringify(key)}`,
3953
3966
  "kv",
3954
3967
  { meta: { status: response.status, statusText: response.statusText } }
3955
3968
  ));
@@ -3957,7 +3970,7 @@ var init_dist2 = __esm({
3957
3970
  if (response.status === 503 && options?.ifMatch !== void 0) {
3958
3971
  return err(serviceError(
3959
3972
  ErrorCodes.KV_CONFLICT,
3960
- `Concurrent KV delete conflicted for key "${key}"`,
3973
+ `Concurrent KV delete conflicted for key ${JSON.stringify(key)}`,
3961
3974
  "kv",
3962
3975
  { meta: { status: response.status, statusText: response.statusText } }
3963
3976
  ));
@@ -3965,7 +3978,7 @@ var init_dist2 = __esm({
3965
3978
  return err(
3966
3979
  serviceError(
3967
3980
  ErrorCodes.NETWORK_ERROR,
3968
- `Failed to delete key "${key}": ${response.status} - ${errorText}`,
3981
+ `Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
3969
3982
  "kv",
3970
3983
  { meta: { status: response.status, statusText: response.statusText } }
3971
3984
  )
@@ -3998,13 +4011,11 @@ var init_dist2 = __esm({
3998
4011
  );
3999
4012
  if (!response.ok) {
4000
4013
  if (response.status === 401) {
4001
- const errorText2 = await response.text();
4002
- const { resource, action } = parseAuthError(errorText2);
4003
- return err(authUnauthorizedError("kv", errorText2, {
4004
- status: response.status,
4005
- ...action && { requiredAction: action },
4006
- ...resource && { resource }
4007
- }));
4014
+ return this.authorizationFailure(
4015
+ `Failed to get metadata for key ${JSON.stringify(key)}`,
4016
+ response,
4017
+ await response.text()
4018
+ );
4008
4019
  }
4009
4020
  if (response.status === 404) {
4010
4021
  return this.classifyNotFound(response, key);
@@ -4013,7 +4024,7 @@ var init_dist2 = __esm({
4013
4024
  return err(
4014
4025
  serviceError(
4015
4026
  ErrorCodes.NETWORK_ERROR,
4016
- `Failed to get metadata for key "${key}": ${response.status} - ${errorText}`,
4027
+ `Failed to get metadata for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
4017
4028
  "kv",
4018
4029
  { meta: { status: response.status, statusText: response.statusText } }
4019
4030
  )
@@ -4385,21 +4396,24 @@ var init_dist2 = __esm({
4385
4396
  }
4386
4397
  async applyMigrationsOnDbUnlocked(dbName, options) {
4387
4398
  return this.withTelemetry("migrations.apply", dbName, async () => {
4388
- const created = await this.ensureMigrationsTable(dbName, options.signal);
4389
- if (!created.ok) return created;
4390
4399
  const listed = await this.queryOnDb(
4391
4400
  dbName,
4392
4401
  `SELECT id FROM ${MIGRATIONS_TABLE} WHERE namespace = ? ORDER BY applied_at, id`,
4393
4402
  [options.namespace],
4394
4403
  { signal: options.signal }
4395
4404
  );
4396
- if (!listed.ok) return listed;
4405
+ const tableMissing = !listed.ok && (listed.error.code === ErrorCodes.SQL_DATABASE_NOT_FOUND || isMissingTableError(listed.error.message));
4406
+ if (!listed.ok && !tableMissing) return listed;
4397
4407
  const appliedIds = new Set(
4398
- listed.data.rows.map((row) => rowValue(row, 0)).filter((id) => typeof id === "string")
4408
+ (listed.ok ? listed.data.rows : []).map((row) => rowValue(row, 0)).filter((id) => typeof id === "string")
4399
4409
  );
4400
4410
  const skipped = options.migrations.filter((migration) => appliedIds.has(migration.id)).map((migration) => migration.id);
4401
4411
  const pending = options.migrations.filter((migration) => !appliedIds.has(migration.id));
4402
4412
  const applied = [];
4413
+ if (pending.length > 0 && tableMissing) {
4414
+ const created = await this.ensureMigrationsTable(dbName, options.signal);
4415
+ if (!created.ok) return created;
4416
+ }
4403
4417
  for (const migration of pending) {
4404
4418
  const result = await this.applyOneMigration(dbName, options.namespace, migration, options.signal);
4405
4419
  if (!result.ok) return result;
@@ -4502,6 +4516,10 @@ var init_dist2 = __esm({
4502
4516
  }
4503
4517
  async handleErrorResponse(response, operation) {
4504
4518
  const errorText = await response.text();
4519
+ const meta = responseErrorMeta(response.status, response.statusText, errorText);
4520
+ if (response.status === 402) {
4521
+ return err(storageRejectionError("sql", 402, meta, errorText));
4522
+ }
4505
4523
  const errorBody = parseServiceErrorBody(errorText);
4506
4524
  const errorCode = this.mapHttpStatusToErrorCode(
4507
4525
  response.status,
@@ -4514,7 +4532,6 @@ var init_dist2 = __esm({
4514
4532
  errorText,
4515
4533
  errorBody
4516
4534
  );
4517
- const meta = responseErrorMeta(response.status, response.statusText, errorText);
4518
4535
  if (response.status === 401) {
4519
4536
  const { resource, action } = parseAuthError(errorText);
4520
4537
  if (action) meta.requiredAction = action;
@@ -4850,6 +4867,10 @@ var init_dist2 = __esm({
4850
4867
  }
4851
4868
  async handleErrorResponse(response, operation) {
4852
4869
  const errorText = await response.text();
4870
+ const meta = responseErrorMeta(response.status, response.statusText, errorText);
4871
+ if (response.status === 402) {
4872
+ return err(storageRejectionError("duckdb", 402, meta, errorText));
4873
+ }
4853
4874
  const errorBody = parseServiceErrorBody(errorText);
4854
4875
  const errorCode = this.mapHttpStatusToErrorCode(
4855
4876
  response.status,
@@ -4862,7 +4883,6 @@ var init_dist2 = __esm({
4862
4883
  errorText,
4863
4884
  errorBody
4864
4885
  );
4865
- const meta = responseErrorMeta(response.status, response.statusText, errorText);
4866
4886
  if (response.status === 401) {
4867
4887
  const { resource, action } = parseAuthError(errorText);
4868
4888
  if (action) meta.requiredAction = action;
@@ -5597,12 +5617,7 @@ var init_dist2 = __esm({
5597
5617
  JSON.stringify(envelopeResult.data)
5598
5618
  );
5599
5619
  if (!valuePutResult.ok) {
5600
- return vaultError({
5601
- code: "STORAGE_ERROR",
5602
- cause: new Error(
5603
- `Failed to store encrypted value: ${valuePutResult.error.message}`
5604
- )
5605
- });
5620
+ return kvWriteError("Failed to store encrypted value", valuePutResult.error);
5606
5621
  }
5607
5622
  return { ok: true, data: void 0 };
5608
5623
  } catch (error) {
@@ -5820,12 +5835,7 @@ var init_dist2 = __esm({
5820
5835
  keyPayload
5821
5836
  );
5822
5837
  if (!keyPutResult.ok) {
5823
- return vaultError({
5824
- code: "STORAGE_ERROR",
5825
- cause: new Error(
5826
- `Failed to store key blob: ${keyPutResult.error.message}`
5827
- )
5828
- });
5838
+ return kvWriteError("Failed to store key blob", keyPutResult.error);
5829
5839
  }
5830
5840
  const valuePayload = JSON.stringify({
5831
5841
  data: base64Encode(encrypted),
@@ -5836,12 +5846,7 @@ var init_dist2 = __esm({
5836
5846
  valuePayload
5837
5847
  );
5838
5848
  if (!valuePutResult.ok) {
5839
- return vaultError({
5840
- code: "STORAGE_ERROR",
5841
- cause: new Error(
5842
- `Failed to store encrypted value: ${valuePutResult.error.message}`
5843
- )
5844
- });
5849
+ return kvWriteError("Failed to store encrypted value", valuePutResult.error);
5845
5850
  }
5846
5851
  return ok(void 0);
5847
5852
  } catch (error) {
@@ -6172,12 +6177,7 @@ var init_dist2 = __esm({
6172
6177
  grantPayload
6173
6178
  );
6174
6179
  if (!grantPutResult.ok) {
6175
- return vaultError({
6176
- code: "STORAGE_ERROR",
6177
- cause: new Error(
6178
- `Failed to store grant: ${grantPutResult.error.message}`
6179
- )
6180
- });
6180
+ return kvWriteError("Failed to store grant", grantPutResult.error);
6181
6181
  }
6182
6182
  return ok(void 0);
6183
6183
  } catch (error) {
@@ -6522,12 +6522,7 @@ var init_dist2 = __esm({
6522
6522
  keyPayload
6523
6523
  );
6524
6524
  if (!keyPutResult.ok) {
6525
- return vaultError({
6526
- code: "STORAGE_ERROR",
6527
- cause: new Error(
6528
- `Failed to store rotated key blob: ${keyPutResult.error.message}`
6529
- )
6530
- });
6525
+ return kvWriteError("Failed to store rotated key blob", keyPutResult.error);
6531
6526
  }
6532
6527
  const valuePayload = JSON.stringify({
6533
6528
  data: base64Encode(encrypted),
@@ -6538,12 +6533,7 @@ var init_dist2 = __esm({
6538
6533
  valuePayload
6539
6534
  );
6540
6535
  if (!valuePutResult.ok) {
6541
- return vaultError({
6542
- code: "STORAGE_ERROR",
6543
- cause: new Error(
6544
- `Failed to store re-encrypted value: ${valuePutResult.error.message}`
6545
- )
6546
- });
6536
+ return kvWriteError("Failed to store re-encrypted value", valuePutResult.error);
6547
6537
  }
6548
6538
  for (const did of remainingGrantees) {
6549
6539
  const grantResult = await this.reencrypt(key, did);