@aouda/client 0.1.7 → 0.1.9

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
@@ -56,6 +56,7 @@ __export(index_exports, {
56
56
  DatabasesApi: () => DatabasesApi,
57
57
  FILTER_OPERATORS: () => FILTER_OPERATORS,
58
58
  HealthAdminApi: () => HealthAdminApi,
59
+ JobsApi: () => JobsApi,
59
60
  MaterializedQueriesApi: () => MaterializedQueriesApi,
60
61
  MaterializedQueryState: () => MaterializedQueryState,
61
62
  MaterializedQueryType: () => MaterializedQueryType,
@@ -82,7 +83,7 @@ module.exports = __toCommonJS(index_exports);
82
83
  // package.json
83
84
  var package_default = {
84
85
  name: "@aouda/client",
85
- version: "0.1.7",
86
+ version: "0.1.9",
86
87
  description: "Official TypeScript/JavaScript client library for Aouda",
87
88
  type: "module",
88
89
  main: "./dist/index.cjs",
@@ -214,12 +215,13 @@ var AoudaResponseError = class extends AoudaError {
214
215
  }
215
216
  };
216
217
  var AoudaApiError = class extends AoudaError {
217
- constructor(message, code, statusCode, details, requestId) {
218
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
218
219
  super(message);
219
220
  this.code = code;
220
221
  this.statusCode = statusCode;
221
222
  this.details = details;
222
223
  this.requestId = requestId;
224
+ this.retryAfterSeconds = retryAfterSeconds;
223
225
  this.name = "AoudaApiError";
224
226
  }
225
227
  };
@@ -242,8 +244,8 @@ var AoudaValidationError = class extends AoudaApiError {
242
244
  }
243
245
  };
244
246
  var AoudaServerError = class extends AoudaApiError {
245
- constructor(message, code, statusCode, details, requestId) {
246
- super(message, code, statusCode, details, requestId);
247
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
248
+ super(message, code, statusCode, details, requestId, retryAfterSeconds);
247
249
  this.name = "AoudaServerError";
248
250
  }
249
251
  };
@@ -1198,6 +1200,8 @@ var ERROR_CODE_MAP = {
1198
1200
  SERVICE_UNAVAILABLE: AoudaServerError,
1199
1201
  TIMEOUT: AoudaServerError,
1200
1202
  OVERLOADED: AoudaServerError,
1203
+ MEMORY_BUDGET_EXCEEDED: AoudaServerError,
1204
+ WAL_CAPACITY_EXCEEDED: AoudaServerError,
1201
1205
  AUTH_TOKEN_MISSING: AoudaAuthenticationError,
1202
1206
  AUTH_TOKEN_EXPIRED: AoudaAuthenticationError,
1203
1207
  AUTH_TOKEN_INVALID: AoudaAuthenticationError,
@@ -1225,13 +1229,22 @@ function createComposedAbortController(...signals) {
1225
1229
  }
1226
1230
  return controller;
1227
1231
  }
1228
- function createApiError(statusCode, statusText, body) {
1232
+ function parseRetryAfterSeconds(header) {
1233
+ if (header == null || header.trim() === "") return void 0;
1234
+ const trimmed = header.trim();
1235
+ if (!/^\d+$/.test(trimmed)) return void 0;
1236
+ const n = Number.parseInt(trimmed, 10);
1237
+ if (!Number.isFinite(n) || n < 0) return void 0;
1238
+ return n;
1239
+ }
1240
+ function createApiError(statusCode, statusText, body, retryAfterHeader) {
1229
1241
  const message = body.error ?? `${statusCode} ${statusText}`;
1230
1242
  const code = body.code ?? "UNKNOWN";
1231
1243
  const details = body.details;
1232
1244
  const requestId = body.requestId;
1245
+ const retryAfterSeconds = parseRetryAfterSeconds(retryAfterHeader ?? null);
1233
1246
  const Ctor = ERROR_CODE_MAP[code] ?? AoudaApiError;
1234
- return new Ctor(message, code, statusCode, details, requestId);
1247
+ return new Ctor(message, code, statusCode, details, requestId, retryAfterSeconds);
1235
1248
  }
1236
1249
  var HttpTransport = class {
1237
1250
  constructor(options) {
@@ -1324,7 +1337,8 @@ var HttpTransport = class {
1324
1337
  throw createApiError(
1325
1338
  response.status,
1326
1339
  response.statusText,
1327
- errorBody
1340
+ errorBody,
1341
+ response.headers.get("Retry-After")
1328
1342
  );
1329
1343
  }
1330
1344
  throw new AoudaResponseError(
@@ -1434,7 +1448,8 @@ var HttpTransport = class {
1434
1448
  throw createApiError(
1435
1449
  response.status,
1436
1450
  response.statusText,
1437
- errorBody
1451
+ errorBody,
1452
+ response.headers.get("Retry-After")
1438
1453
  );
1439
1454
  }
1440
1455
  throw new AoudaResponseError(
@@ -1780,6 +1795,17 @@ var CIRCUIT_BREAKER_POLICY_DISABLED = {
1780
1795
 
1781
1796
  // src/resilience/resilient-transport.ts
1782
1797
  var REQUEST_ID_HEADER3 = "X-Request-Id";
1798
+ function isCapacityBackPressure(error) {
1799
+ return error instanceof AoudaApiError && (error.code === "MEMORY_BUDGET_EXCEEDED" || error.code === "WAL_CAPACITY_EXCEEDED");
1800
+ }
1801
+ function retryDelayMs(retryPolicy, attempt, error) {
1802
+ const backoff = calculateDelay(retryPolicy, attempt);
1803
+ if (!isCapacityBackPressure(error) || !(error instanceof AoudaApiError)) {
1804
+ return backoff;
1805
+ }
1806
+ const headerMs = error.retryAfterSeconds != null ? error.retryAfterSeconds * 1e3 : 0;
1807
+ return Math.max(backoff, headerMs);
1808
+ }
1783
1809
  function delayMs(ms, signal) {
1784
1810
  if (ms <= 0) return Promise.resolve();
1785
1811
  return new Promise((resolve, reject) => {
@@ -1834,7 +1860,7 @@ var ResilientTransport = class {
1834
1860
  } catch (error) {
1835
1861
  lastError = error;
1836
1862
  const retryable = isRetryable(error);
1837
- if (retryable) {
1863
+ if (retryable && !isCapacityBackPressure(error)) {
1838
1864
  this.circuitBreaker.recordFailure(error);
1839
1865
  }
1840
1866
  if (!retryable) {
@@ -1844,7 +1870,7 @@ var ResilientTransport = class {
1844
1870
  if (attempt > this.retryPolicy.maxRetries) {
1845
1871
  throw error;
1846
1872
  }
1847
- const delay = calculateDelay(this.retryPolicy, attempt);
1873
+ const delay = retryDelayMs(this.retryPolicy, attempt, error);
1848
1874
  try {
1849
1875
  await delayMs(delay, config.signal);
1850
1876
  } catch {
@@ -1880,7 +1906,7 @@ var ResilientTransport = class {
1880
1906
  } catch (error) {
1881
1907
  lastError = error;
1882
1908
  const retryable = isRetryable(error);
1883
- if (retryable) {
1909
+ if (retryable && !isCapacityBackPressure(error)) {
1884
1910
  this.circuitBreaker.recordFailure(error);
1885
1911
  }
1886
1912
  if (!retryable) {
@@ -1890,7 +1916,7 @@ var ResilientTransport = class {
1890
1916
  if (attempt > this.retryPolicy.maxRetries) {
1891
1917
  throw error;
1892
1918
  }
1893
- const delay = calculateDelay(this.retryPolicy, attempt);
1919
+ const delay = retryDelayMs(this.retryPolicy, attempt, error);
1894
1920
  try {
1895
1921
  await delayMs(delay, config.signal);
1896
1922
  } catch {
@@ -3647,23 +3673,68 @@ var TablesApi = class {
3647
3673
  );
3648
3674
  }
3649
3675
  /**
3650
- * Renames a column.
3676
+ * Alters a column (type, nullable, encoder, autoIncrement, references, and/or rename).
3677
+ * PATCH /api/databases/{db}/tables/{t}/columns/{c}.
3678
+ * Omit a property to leave it unchanged. For `references`, omit unchanged; `""` or `null` clears.
3651
3679
  * @param tableName - Table name.
3652
3680
  * @param columnName - Current column name.
3653
- * @param body - Request body (database, newName).
3654
- * @returns 200 response body (column detail).
3655
- * @throws AoudaNotFoundError if table or column does not exist; AoudaApiError for WRITE_NOT_ALLOWED, etc.
3681
+ * @param body - Fields to change. `database` is injected from the client scope when omitted.
3682
+ * @returns Updated column detail.
3656
3683
  */
3657
- async renameColumn(tableName, columnName, body) {
3684
+ async alterColumn(tableName, columnName, body) {
3658
3685
  validateNonEmptyString(tableName, "Table name");
3659
3686
  validateNonEmptyString(columnName, "Column name");
3660
- validateNonEmptyString(body.newName, "New column name");
3687
+ const hasField = body.newName !== void 0 || body.type !== void 0 || body.nullable !== void 0 || body.encoder !== void 0 || body.autoIncrement !== void 0 || body.references !== void 0;
3688
+ if (!hasField) {
3689
+ throw new Error(
3690
+ "AlterColumnRequest must set at least one of: newName, type, nullable, encoder, autoIncrement, references"
3691
+ );
3692
+ }
3661
3693
  const prefix = databasePath2(this.database);
3662
3694
  const encodedTable = encodeURIComponent(tableName);
3663
3695
  const encodedColumn = encodeURIComponent(columnName);
3696
+ const requestBody = {
3697
+ database: this.database,
3698
+ ...body
3699
+ };
3664
3700
  return this.transport.patch(
3665
3701
  `${prefix}/tables/${encodedTable}/columns/${encodedColumn}`,
3666
- body
3702
+ requestBody
3703
+ );
3704
+ }
3705
+ /**
3706
+ * Renames a column (convenience wrapper over {@link alterColumn}).
3707
+ * @param tableName - Table name.
3708
+ * @param columnName - Current column name.
3709
+ * @param body - Request body with newName (database optional; injected when omitted).
3710
+ * @returns 200 response body (column detail).
3711
+ * @throws AoudaNotFoundError if table or column does not exist; AoudaApiError for WRITE_NOT_ALLOWED, etc.
3712
+ */
3713
+ async renameColumn(tableName, columnName, body) {
3714
+ validateNonEmptyString(body.newName ?? "", "New column name");
3715
+ return this.alterColumn(tableName, columnName, { newName: body.newName });
3716
+ }
3717
+ /**
3718
+ * Reorders columns in a table.
3719
+ * PUT /api/databases/{db}/tables/{t}/columns:order (204 No Content).
3720
+ * @param tableName - Table name.
3721
+ * @param columnsOrBody - Ordered column names, or a request body with `columns`.
3722
+ */
3723
+ async reorderColumns(tableName, columnsOrBody) {
3724
+ validateNonEmptyString(tableName, "Table name");
3725
+ const columns = Array.isArray(columnsOrBody) ? columnsOrBody : columnsOrBody.columns;
3726
+ if (!columns?.length) {
3727
+ throw new Error("columns array is required and must be non-empty");
3728
+ }
3729
+ const prefix = databasePath2(this.database);
3730
+ const encodedTable = encodeURIComponent(tableName);
3731
+ const requestBody = {
3732
+ database: this.database,
3733
+ columns
3734
+ };
3735
+ await this.transport.put(
3736
+ `${prefix}/tables/${encodedTable}/columns:order`,
3737
+ requestBody
3667
3738
  );
3668
3739
  }
3669
3740
  /**
@@ -3903,6 +3974,37 @@ var BranchesApi = class {
3903
3974
  }
3904
3975
  };
3905
3976
 
3977
+ // src/jobs.ts
3978
+ function validateNonEmptyString2(value, name) {
3979
+ if (typeof value !== "string" || value.trim().length === 0) {
3980
+ throw new Error(`${name} must be a non-empty string`);
3981
+ }
3982
+ }
3983
+ var JobsApi = class {
3984
+ constructor(transport, database) {
3985
+ this.transport = transport;
3986
+ this.database = database;
3987
+ }
3988
+ get prefix() {
3989
+ return `${databasePath2(this.database)}/jobs`;
3990
+ }
3991
+ /**
3992
+ * Lists jobs whose params target this database.
3993
+ */
3994
+ async list() {
3995
+ return this.transport.get(this.prefix);
3996
+ }
3997
+ /**
3998
+ * Gets a single job by id when it belongs to this database.
3999
+ * @param jobId - Job GUID string.
4000
+ */
4001
+ async get(jobId) {
4002
+ validateNonEmptyString2(jobId, "Job id");
4003
+ const encoded = encodeURIComponent(jobId);
4004
+ return this.transport.get(`${this.prefix}/${encoded}`);
4005
+ }
4006
+ };
4007
+
3906
4008
  // src/admin/server.ts
3907
4009
  var ServerAdminApi = class {
3908
4010
  constructor(transport) {
@@ -5211,7 +5313,7 @@ var DEFAULT_TIMEOUT_MS = 3e4;
5211
5313
  function normalizeBaseUrl(url) {
5212
5314
  return url.replace(/\/+$/, "");
5213
5315
  }
5214
- function validateNonEmptyString2(value, name) {
5316
+ function validateNonEmptyString3(value, name) {
5215
5317
  if (typeof value !== "string" || value.trim().length === 0) {
5216
5318
  throw new Error(`${name} must be a non-empty string`);
5217
5319
  }
@@ -5226,8 +5328,8 @@ var AoudaClient = class {
5226
5328
  constructor(options) {
5227
5329
  this.connected = false;
5228
5330
  this._wsTransport = null;
5229
- validateNonEmptyString2(options.serverUrl, "serverUrl");
5230
- validateNonEmptyString2(options.database, "database");
5331
+ validateNonEmptyString3(options.serverUrl, "serverUrl");
5332
+ validateNonEmptyString3(options.database, "database");
5231
5333
  this.baseUrl = normalizeBaseUrl(options.serverUrl);
5232
5334
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
5233
5335
  this.database = options.database.trim();
@@ -5309,6 +5411,7 @@ var AoudaClient = class {
5309
5411
  this._authHandler = null;
5310
5412
  }
5311
5413
  this._tables = new TablesApi(this.transport, this.database);
5414
+ this._jobs = new JobsApi(this.transport, this.database);
5312
5415
  this._databases = new DatabasesApi(this.transport);
5313
5416
  this._schema = new SchemaApi(this.transport, this.database);
5314
5417
  this._branches = new BranchesApi(this.transport, this.database);
@@ -5402,7 +5505,7 @@ var AoudaClient = class {
5402
5505
  * ```
5403
5506
  */
5404
5507
  table(name) {
5405
- validateNonEmptyString2(name, "Table name");
5508
+ validateNonEmptyString3(name, "Table name");
5406
5509
  return new TableQuery(
5407
5510
  this.transport,
5408
5511
  name,
@@ -5418,6 +5521,13 @@ var AoudaClient = class {
5418
5521
  get tables() {
5419
5522
  return this._tables;
5420
5523
  }
5524
+ /**
5525
+ * Access pending/background jobs for the current database (e.g. ColumnRewrite).
5526
+ * @returns The jobs API.
5527
+ */
5528
+ get jobs() {
5529
+ return this._jobs;
5530
+ }
5421
5531
  /**
5422
5532
  * Access server-level database operations (list, create, get, drop).
5423
5533
  * @returns The databases API.
@@ -5514,7 +5624,7 @@ var AoudaClient = class {
5514
5624
  * ```
5515
5625
  */
5516
5626
  bulkLoad(tableName, rows, options) {
5517
- validateNonEmptyString2(tableName, "tableName");
5627
+ validateNonEmptyString3(tableName, "tableName");
5518
5628
  return new BulkLoadCoordinator(this.transport, this.database).run(
5519
5629
  [tableName],
5520
5630
  rows,
@@ -5868,6 +5978,7 @@ var version = package_default.version;
5868
5978
  DatabasesApi,
5869
5979
  FILTER_OPERATORS,
5870
5980
  HealthAdminApi,
5981
+ JobsApi,
5871
5982
  MaterializedQueriesApi,
5872
5983
  MaterializedQueryState,
5873
5984
  MaterializedQueryType,