@tinycloud/operations 0.3.4-beta.2 → 0.3.4-beta.4

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
  }
@@ -1665,6 +1680,9 @@ function defaultVaultMessage(input) {
1665
1680
  return input.message ?? `Public key not found for ${input.did}`;
1666
1681
  case "STORAGE_ERROR":
1667
1682
  return input.message ?? input.cause.message;
1683
+ case "STORAGE_QUOTA_EXCEEDED":
1684
+ case "STORAGE_LIMIT_REACHED":
1685
+ return input.message;
1668
1686
  }
1669
1687
  }
1670
1688
  function vaultError(input) {
@@ -1675,6 +1693,15 @@ function vaultError(input) {
1675
1693
  };
1676
1694
  return { ok: false, error };
1677
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
+ }
1678
1705
  function canonicalize(value) {
1679
1706
  if (value === void 0) {
1680
1707
  return "";
@@ -2433,7 +2460,7 @@ function encOk(data) {
2433
2460
  function encErr(error) {
2434
2461
  return { ok: false, error };
2435
2462
  }
2436
- 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;
2437
2464
  var init_dist2 = __esm({
2438
2465
  "../sdk-services/dist/index.js"() {
2439
2466
  "use strict";
@@ -2716,6 +2743,8 @@ var init_dist2 = __esm({
2716
2743
  /** The session key JWK (required for invoke) */
2717
2744
  jwk: import_zod.z.object({}).passthrough()
2718
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.";
2719
2748
  BaseService = class {
2720
2749
  constructor() {
2721
2750
  this.abortController = new AbortController();
@@ -3064,47 +3093,21 @@ var init_dist2 = __esm({
3064
3093
  get config() {
3065
3094
  return this._config;
3066
3095
  }
3067
- // Parses "Used: X bytes, Limit: Y bytes" from tinycloud-node error responses
3068
- parseQuotaInfo(errorText) {
3069
- const match = errorText.match(
3070
- /Used:\s*(\d+)\s*bytes,\s*Limit:\s*(\d+)\s*bytes/i
3071
- );
3072
- if (match) {
3073
- return {
3074
- usedBytes: parseInt(match[1], 10),
3075
- limitBytes: parseInt(match[2], 10)
3076
- };
3077
- }
3078
- return void 0;
3079
- }
3080
3096
  handleQuotaErrorResponse(response, errorText, key) {
3081
- if (response.status === 402) {
3082
- const quotaInfo = this.parseQuotaInfo(errorText);
3083
- return err(
3084
- storageQuotaExceededError(
3085
- "kv",
3086
- `Storage quota exceeded for key ${JSON.stringify(key)}: ${errorText}`,
3087
- {
3088
- status: response.status,
3089
- ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
3090
- }
3091
- )
3092
- );
3097
+ if (response.status !== 402 && response.status !== 413) {
3098
+ return void 0;
3093
3099
  }
3094
- if (response.status === 413) {
3095
- const quotaInfo = this.parseQuotaInfo(errorText);
3096
- return err(
3097
- storageLimitReachedError(
3098
- "kv",
3099
- `Storage limit reached for key ${JSON.stringify(key)}: ${errorText}`,
3100
- {
3101
- status: response.status,
3102
- ...quotaInfo ? { usedBytes: quotaInfo.usedBytes, limitBytes: quotaInfo.limitBytes } : {}
3103
- }
3104
- )
3105
- );
3100
+ if (response.status === 413 && !parseStorageQuotaBytes(errorText)) {
3101
+ return void 0;
3106
3102
  }
3107
- 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
+ );
3108
3111
  }
3109
3112
  /**
3110
3113
  * Classify a KV 404 by reading the response body once.
@@ -4393,21 +4396,24 @@ var init_dist2 = __esm({
4393
4396
  }
4394
4397
  async applyMigrationsOnDbUnlocked(dbName, options) {
4395
4398
  return this.withTelemetry("migrations.apply", dbName, async () => {
4396
- const created = await this.ensureMigrationsTable(dbName, options.signal);
4397
- if (!created.ok) return created;
4398
4399
  const listed = await this.queryOnDb(
4399
4400
  dbName,
4400
4401
  `SELECT id FROM ${MIGRATIONS_TABLE} WHERE namespace = ? ORDER BY applied_at, id`,
4401
4402
  [options.namespace],
4402
4403
  { signal: options.signal }
4403
4404
  );
4404
- 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;
4405
4407
  const appliedIds = new Set(
4406
- 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")
4407
4409
  );
4408
4410
  const skipped = options.migrations.filter((migration) => appliedIds.has(migration.id)).map((migration) => migration.id);
4409
4411
  const pending = options.migrations.filter((migration) => !appliedIds.has(migration.id));
4410
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
+ }
4411
4417
  for (const migration of pending) {
4412
4418
  const result = await this.applyOneMigration(dbName, options.namespace, migration, options.signal);
4413
4419
  if (!result.ok) return result;
@@ -4510,6 +4516,10 @@ var init_dist2 = __esm({
4510
4516
  }
4511
4517
  async handleErrorResponse(response, operation) {
4512
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
+ }
4513
4523
  const errorBody = parseServiceErrorBody(errorText);
4514
4524
  const errorCode = this.mapHttpStatusToErrorCode(
4515
4525
  response.status,
@@ -4522,7 +4532,6 @@ var init_dist2 = __esm({
4522
4532
  errorText,
4523
4533
  errorBody
4524
4534
  );
4525
- const meta = responseErrorMeta(response.status, response.statusText, errorText);
4526
4535
  if (response.status === 401) {
4527
4536
  const { resource, action } = parseAuthError(errorText);
4528
4537
  if (action) meta.requiredAction = action;
@@ -4858,6 +4867,10 @@ var init_dist2 = __esm({
4858
4867
  }
4859
4868
  async handleErrorResponse(response, operation) {
4860
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
+ }
4861
4874
  const errorBody = parseServiceErrorBody(errorText);
4862
4875
  const errorCode = this.mapHttpStatusToErrorCode(
4863
4876
  response.status,
@@ -4870,7 +4883,6 @@ var init_dist2 = __esm({
4870
4883
  errorText,
4871
4884
  errorBody
4872
4885
  );
4873
- const meta = responseErrorMeta(response.status, response.statusText, errorText);
4874
4886
  if (response.status === 401) {
4875
4887
  const { resource, action } = parseAuthError(errorText);
4876
4888
  if (action) meta.requiredAction = action;
@@ -5605,12 +5617,7 @@ var init_dist2 = __esm({
5605
5617
  JSON.stringify(envelopeResult.data)
5606
5618
  );
5607
5619
  if (!valuePutResult.ok) {
5608
- return vaultError({
5609
- code: "STORAGE_ERROR",
5610
- cause: new Error(
5611
- `Failed to store encrypted value: ${valuePutResult.error.message}`
5612
- )
5613
- });
5620
+ return kvWriteError("Failed to store encrypted value", valuePutResult.error);
5614
5621
  }
5615
5622
  return { ok: true, data: void 0 };
5616
5623
  } catch (error) {
@@ -5828,12 +5835,7 @@ var init_dist2 = __esm({
5828
5835
  keyPayload
5829
5836
  );
5830
5837
  if (!keyPutResult.ok) {
5831
- return vaultError({
5832
- code: "STORAGE_ERROR",
5833
- cause: new Error(
5834
- `Failed to store key blob: ${keyPutResult.error.message}`
5835
- )
5836
- });
5838
+ return kvWriteError("Failed to store key blob", keyPutResult.error);
5837
5839
  }
5838
5840
  const valuePayload = JSON.stringify({
5839
5841
  data: base64Encode(encrypted),
@@ -5844,12 +5846,7 @@ var init_dist2 = __esm({
5844
5846
  valuePayload
5845
5847
  );
5846
5848
  if (!valuePutResult.ok) {
5847
- return vaultError({
5848
- code: "STORAGE_ERROR",
5849
- cause: new Error(
5850
- `Failed to store encrypted value: ${valuePutResult.error.message}`
5851
- )
5852
- });
5849
+ return kvWriteError("Failed to store encrypted value", valuePutResult.error);
5853
5850
  }
5854
5851
  return ok(void 0);
5855
5852
  } catch (error) {
@@ -6180,12 +6177,7 @@ var init_dist2 = __esm({
6180
6177
  grantPayload
6181
6178
  );
6182
6179
  if (!grantPutResult.ok) {
6183
- return vaultError({
6184
- code: "STORAGE_ERROR",
6185
- cause: new Error(
6186
- `Failed to store grant: ${grantPutResult.error.message}`
6187
- )
6188
- });
6180
+ return kvWriteError("Failed to store grant", grantPutResult.error);
6189
6181
  }
6190
6182
  return ok(void 0);
6191
6183
  } catch (error) {
@@ -6530,12 +6522,7 @@ var init_dist2 = __esm({
6530
6522
  keyPayload
6531
6523
  );
6532
6524
  if (!keyPutResult.ok) {
6533
- return vaultError({
6534
- code: "STORAGE_ERROR",
6535
- cause: new Error(
6536
- `Failed to store rotated key blob: ${keyPutResult.error.message}`
6537
- )
6538
- });
6525
+ return kvWriteError("Failed to store rotated key blob", keyPutResult.error);
6539
6526
  }
6540
6527
  const valuePayload = JSON.stringify({
6541
6528
  data: base64Encode(encrypted),
@@ -6546,12 +6533,7 @@ var init_dist2 = __esm({
6546
6533
  valuePayload
6547
6534
  );
6548
6535
  if (!valuePutResult.ok) {
6549
- return vaultError({
6550
- code: "STORAGE_ERROR",
6551
- cause: new Error(
6552
- `Failed to store re-encrypted value: ${valuePutResult.error.message}`
6553
- )
6554
- });
6536
+ return kvWriteError("Failed to store re-encrypted value", valuePutResult.error);
6555
6537
  }
6556
6538
  for (const did of remainingGrantees) {
6557
6539
  const grantResult = await this.reencrypt(key, did);
@@ -7818,12 +7800,12 @@ function canonicalizeSpaceUri(space) {
7818
7800
  }
7819
7801
  function buildSecretSetupUrl(reference) {
7820
7802
  const parameters = [
7821
- ["name", reference.name],
7803
+ ["secret", reference.name],
7822
7804
  ...reference.scope === void 0 ? [] : [["scope", reference.scope]],
7823
7805
  ["space", reference.space]
7824
7806
  ];
7825
7807
  const query = parameters.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
7826
- return `${SECRET_MANAGER_BASE_URL}?${query}`;
7808
+ return `${SECRET_MANAGER_BASE_URL}/app?${query}`;
7827
7809
  }
7828
7810
  function canonicalizeSecretScope(scope) {
7829
7811
  if (scope === void 0 || scope === "") return void 0;