@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.
@@ -1,4 +1,4 @@
1
- import { A as AoudaClient } from '../client-BbyXG5AL.cjs';
1
+ import { A as AoudaClient } from '../client-CxP9Vnc8.cjs';
2
2
 
3
3
  /**
4
4
  * CLI for @aouda/client.
@@ -1,4 +1,4 @@
1
- import { A as AoudaClient } from '../client-BbyXG5AL.js';
1
+ import { A as AoudaClient } from '../client-CxP9Vnc8.js';
2
2
 
3
3
  /**
4
4
  * CLI for @aouda/client.
package/dist/cli/index.js CHANGED
@@ -392,7 +392,7 @@ import { fileURLToPath } from "url";
392
392
  // package.json
393
393
  var package_default = {
394
394
  name: "@aouda/client",
395
- version: "0.1.7",
395
+ version: "0.1.9",
396
396
  description: "Official TypeScript/JavaScript client library for Aouda",
397
397
  type: "module",
398
398
  main: "./dist/index.cjs",
@@ -507,12 +507,13 @@ var AoudaResponseError = class extends AoudaError {
507
507
  }
508
508
  };
509
509
  var AoudaApiError = class extends AoudaError {
510
- constructor(message, code, statusCode, details, requestId) {
510
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
511
511
  super(message);
512
512
  this.code = code;
513
513
  this.statusCode = statusCode;
514
514
  this.details = details;
515
515
  this.requestId = requestId;
516
+ this.retryAfterSeconds = retryAfterSeconds;
516
517
  this.name = "AoudaApiError";
517
518
  }
518
519
  };
@@ -535,8 +536,8 @@ var AoudaValidationError = class extends AoudaApiError {
535
536
  }
536
537
  };
537
538
  var AoudaServerError = class extends AoudaApiError {
538
- constructor(message, code, statusCode, details, requestId) {
539
- super(message, code, statusCode, details, requestId);
539
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
540
+ super(message, code, statusCode, details, requestId, retryAfterSeconds);
540
541
  this.name = "AoudaServerError";
541
542
  }
542
543
  };
@@ -1475,6 +1476,8 @@ var ERROR_CODE_MAP = {
1475
1476
  SERVICE_UNAVAILABLE: AoudaServerError,
1476
1477
  TIMEOUT: AoudaServerError,
1477
1478
  OVERLOADED: AoudaServerError,
1479
+ MEMORY_BUDGET_EXCEEDED: AoudaServerError,
1480
+ WAL_CAPACITY_EXCEEDED: AoudaServerError,
1478
1481
  AUTH_TOKEN_MISSING: AoudaAuthenticationError,
1479
1482
  AUTH_TOKEN_EXPIRED: AoudaAuthenticationError,
1480
1483
  AUTH_TOKEN_INVALID: AoudaAuthenticationError,
@@ -1502,13 +1505,22 @@ function createComposedAbortController(...signals) {
1502
1505
  }
1503
1506
  return controller;
1504
1507
  }
1505
- function createApiError(statusCode, statusText, body) {
1508
+ function parseRetryAfterSeconds(header) {
1509
+ if (header == null || header.trim() === "") return void 0;
1510
+ const trimmed = header.trim();
1511
+ if (!/^\d+$/.test(trimmed)) return void 0;
1512
+ const n = Number.parseInt(trimmed, 10);
1513
+ if (!Number.isFinite(n) || n < 0) return void 0;
1514
+ return n;
1515
+ }
1516
+ function createApiError(statusCode, statusText, body, retryAfterHeader) {
1506
1517
  const message = body.error ?? `${statusCode} ${statusText}`;
1507
1518
  const code = body.code ?? "UNKNOWN";
1508
1519
  const details = body.details;
1509
1520
  const requestId = body.requestId;
1521
+ const retryAfterSeconds = parseRetryAfterSeconds(retryAfterHeader ?? null);
1510
1522
  const Ctor = ERROR_CODE_MAP[code] ?? AoudaApiError;
1511
- return new Ctor(message, code, statusCode, details, requestId);
1523
+ return new Ctor(message, code, statusCode, details, requestId, retryAfterSeconds);
1512
1524
  }
1513
1525
  var HttpTransport = class {
1514
1526
  constructor(options) {
@@ -1601,7 +1613,8 @@ var HttpTransport = class {
1601
1613
  throw createApiError(
1602
1614
  response.status,
1603
1615
  response.statusText,
1604
- errorBody
1616
+ errorBody,
1617
+ response.headers.get("Retry-After")
1605
1618
  );
1606
1619
  }
1607
1620
  throw new AoudaResponseError(
@@ -1711,7 +1724,8 @@ var HttpTransport = class {
1711
1724
  throw createApiError(
1712
1725
  response.status,
1713
1726
  response.statusText,
1714
- errorBody
1727
+ errorBody,
1728
+ response.headers.get("Retry-After")
1715
1729
  );
1716
1730
  }
1717
1731
  throw new AoudaResponseError(
@@ -2057,6 +2071,17 @@ var CIRCUIT_BREAKER_POLICY_DISABLED = {
2057
2071
 
2058
2072
  // src/resilience/resilient-transport.ts
2059
2073
  var REQUEST_ID_HEADER3 = "X-Request-Id";
2074
+ function isCapacityBackPressure(error) {
2075
+ return error instanceof AoudaApiError && (error.code === "MEMORY_BUDGET_EXCEEDED" || error.code === "WAL_CAPACITY_EXCEEDED");
2076
+ }
2077
+ function retryDelayMs(retryPolicy, attempt, error) {
2078
+ const backoff = calculateDelay(retryPolicy, attempt);
2079
+ if (!isCapacityBackPressure(error) || !(error instanceof AoudaApiError)) {
2080
+ return backoff;
2081
+ }
2082
+ const headerMs = error.retryAfterSeconds != null ? error.retryAfterSeconds * 1e3 : 0;
2083
+ return Math.max(backoff, headerMs);
2084
+ }
2060
2085
  function delayMs(ms, signal) {
2061
2086
  if (ms <= 0) return Promise.resolve();
2062
2087
  return new Promise((resolve4, reject) => {
@@ -2111,7 +2136,7 @@ var ResilientTransport = class {
2111
2136
  } catch (error) {
2112
2137
  lastError = error;
2113
2138
  const retryable = isRetryable(error);
2114
- if (retryable) {
2139
+ if (retryable && !isCapacityBackPressure(error)) {
2115
2140
  this.circuitBreaker.recordFailure(error);
2116
2141
  }
2117
2142
  if (!retryable) {
@@ -2121,7 +2146,7 @@ var ResilientTransport = class {
2121
2146
  if (attempt > this.retryPolicy.maxRetries) {
2122
2147
  throw error;
2123
2148
  }
2124
- const delay = calculateDelay(this.retryPolicy, attempt);
2149
+ const delay = retryDelayMs(this.retryPolicy, attempt, error);
2125
2150
  try {
2126
2151
  await delayMs(delay, config.signal);
2127
2152
  } catch {
@@ -2157,7 +2182,7 @@ var ResilientTransport = class {
2157
2182
  } catch (error) {
2158
2183
  lastError = error;
2159
2184
  const retryable = isRetryable(error);
2160
- if (retryable) {
2185
+ if (retryable && !isCapacityBackPressure(error)) {
2161
2186
  this.circuitBreaker.recordFailure(error);
2162
2187
  }
2163
2188
  if (!retryable) {
@@ -2167,7 +2192,7 @@ var ResilientTransport = class {
2167
2192
  if (attempt > this.retryPolicy.maxRetries) {
2168
2193
  throw error;
2169
2194
  }
2170
- const delay = calculateDelay(this.retryPolicy, attempt);
2195
+ const delay = retryDelayMs(this.retryPolicy, attempt, error);
2171
2196
  try {
2172
2197
  await delayMs(delay, config.signal);
2173
2198
  } catch {
@@ -3924,23 +3949,68 @@ var TablesApi = class {
3924
3949
  );
3925
3950
  }
3926
3951
  /**
3927
- * Renames a column.
3952
+ * Alters a column (type, nullable, encoder, autoIncrement, references, and/or rename).
3953
+ * PATCH /api/databases/{db}/tables/{t}/columns/{c}.
3954
+ * Omit a property to leave it unchanged. For `references`, omit unchanged; `""` or `null` clears.
3928
3955
  * @param tableName - Table name.
3929
3956
  * @param columnName - Current column name.
3930
- * @param body - Request body (database, newName).
3931
- * @returns 200 response body (column detail).
3932
- * @throws AoudaNotFoundError if table or column does not exist; AoudaApiError for WRITE_NOT_ALLOWED, etc.
3957
+ * @param body - Fields to change. `database` is injected from the client scope when omitted.
3958
+ * @returns Updated column detail.
3933
3959
  */
3934
- async renameColumn(tableName, columnName, body) {
3960
+ async alterColumn(tableName, columnName, body) {
3935
3961
  validateNonEmptyString(tableName, "Table name");
3936
3962
  validateNonEmptyString(columnName, "Column name");
3937
- validateNonEmptyString(body.newName, "New column name");
3963
+ 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;
3964
+ if (!hasField) {
3965
+ throw new Error(
3966
+ "AlterColumnRequest must set at least one of: newName, type, nullable, encoder, autoIncrement, references"
3967
+ );
3968
+ }
3938
3969
  const prefix = databasePath2(this.database);
3939
3970
  const encodedTable = encodeURIComponent(tableName);
3940
3971
  const encodedColumn = encodeURIComponent(columnName);
3972
+ const requestBody = {
3973
+ database: this.database,
3974
+ ...body
3975
+ };
3941
3976
  return this.transport.patch(
3942
3977
  `${prefix}/tables/${encodedTable}/columns/${encodedColumn}`,
3943
- body
3978
+ requestBody
3979
+ );
3980
+ }
3981
+ /**
3982
+ * Renames a column (convenience wrapper over {@link alterColumn}).
3983
+ * @param tableName - Table name.
3984
+ * @param columnName - Current column name.
3985
+ * @param body - Request body with newName (database optional; injected when omitted).
3986
+ * @returns 200 response body (column detail).
3987
+ * @throws AoudaNotFoundError if table or column does not exist; AoudaApiError for WRITE_NOT_ALLOWED, etc.
3988
+ */
3989
+ async renameColumn(tableName, columnName, body) {
3990
+ validateNonEmptyString(body.newName ?? "", "New column name");
3991
+ return this.alterColumn(tableName, columnName, { newName: body.newName });
3992
+ }
3993
+ /**
3994
+ * Reorders columns in a table.
3995
+ * PUT /api/databases/{db}/tables/{t}/columns:order (204 No Content).
3996
+ * @param tableName - Table name.
3997
+ * @param columnsOrBody - Ordered column names, or a request body with `columns`.
3998
+ */
3999
+ async reorderColumns(tableName, columnsOrBody) {
4000
+ validateNonEmptyString(tableName, "Table name");
4001
+ const columns = Array.isArray(columnsOrBody) ? columnsOrBody : columnsOrBody.columns;
4002
+ if (!columns?.length) {
4003
+ throw new Error("columns array is required and must be non-empty");
4004
+ }
4005
+ const prefix = databasePath2(this.database);
4006
+ const encodedTable = encodeURIComponent(tableName);
4007
+ const requestBody = {
4008
+ database: this.database,
4009
+ columns
4010
+ };
4011
+ await this.transport.put(
4012
+ `${prefix}/tables/${encodedTable}/columns:order`,
4013
+ requestBody
3944
4014
  );
3945
4015
  }
3946
4016
  /**
@@ -4180,6 +4250,37 @@ var BranchesApi = class {
4180
4250
  }
4181
4251
  };
4182
4252
 
4253
+ // src/jobs.ts
4254
+ function validateNonEmptyString2(value, name) {
4255
+ if (typeof value !== "string" || value.trim().length === 0) {
4256
+ throw new Error(`${name} must be a non-empty string`);
4257
+ }
4258
+ }
4259
+ var JobsApi = class {
4260
+ constructor(transport, database) {
4261
+ this.transport = transport;
4262
+ this.database = database;
4263
+ }
4264
+ get prefix() {
4265
+ return `${databasePath2(this.database)}/jobs`;
4266
+ }
4267
+ /**
4268
+ * Lists jobs whose params target this database.
4269
+ */
4270
+ async list() {
4271
+ return this.transport.get(this.prefix);
4272
+ }
4273
+ /**
4274
+ * Gets a single job by id when it belongs to this database.
4275
+ * @param jobId - Job GUID string.
4276
+ */
4277
+ async get(jobId) {
4278
+ validateNonEmptyString2(jobId, "Job id");
4279
+ const encoded = encodeURIComponent(jobId);
4280
+ return this.transport.get(`${this.prefix}/${encoded}`);
4281
+ }
4282
+ };
4283
+
4183
4284
  // src/admin/server.ts
4184
4285
  var ServerAdminApi = class {
4185
4286
  constructor(transport) {
@@ -5457,7 +5558,7 @@ var DEFAULT_TIMEOUT_MS = 3e4;
5457
5558
  function normalizeBaseUrl(url) {
5458
5559
  return url.replace(/\/+$/, "");
5459
5560
  }
5460
- function validateNonEmptyString2(value, name) {
5561
+ function validateNonEmptyString3(value, name) {
5461
5562
  if (typeof value !== "string" || value.trim().length === 0) {
5462
5563
  throw new Error(`${name} must be a non-empty string`);
5463
5564
  }
@@ -5472,8 +5573,8 @@ var AoudaClient = class {
5472
5573
  constructor(options) {
5473
5574
  this.connected = false;
5474
5575
  this._wsTransport = null;
5475
- validateNonEmptyString2(options.serverUrl, "serverUrl");
5476
- validateNonEmptyString2(options.database, "database");
5576
+ validateNonEmptyString3(options.serverUrl, "serverUrl");
5577
+ validateNonEmptyString3(options.database, "database");
5477
5578
  this.baseUrl = normalizeBaseUrl(options.serverUrl);
5478
5579
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
5479
5580
  this.database = options.database.trim();
@@ -5555,6 +5656,7 @@ var AoudaClient = class {
5555
5656
  this._authHandler = null;
5556
5657
  }
5557
5658
  this._tables = new TablesApi(this.transport, this.database);
5659
+ this._jobs = new JobsApi(this.transport, this.database);
5558
5660
  this._databases = new DatabasesApi(this.transport);
5559
5661
  this._schema = new SchemaApi(this.transport, this.database);
5560
5662
  this._branches = new BranchesApi(this.transport, this.database);
@@ -5648,7 +5750,7 @@ var AoudaClient = class {
5648
5750
  * ```
5649
5751
  */
5650
5752
  table(name) {
5651
- validateNonEmptyString2(name, "Table name");
5753
+ validateNonEmptyString3(name, "Table name");
5652
5754
  return new TableQuery(
5653
5755
  this.transport,
5654
5756
  name,
@@ -5664,6 +5766,13 @@ var AoudaClient = class {
5664
5766
  get tables() {
5665
5767
  return this._tables;
5666
5768
  }
5769
+ /**
5770
+ * Access pending/background jobs for the current database (e.g. ColumnRewrite).
5771
+ * @returns The jobs API.
5772
+ */
5773
+ get jobs() {
5774
+ return this._jobs;
5775
+ }
5667
5776
  /**
5668
5777
  * Access server-level database operations (list, create, get, drop).
5669
5778
  * @returns The databases API.
@@ -5760,7 +5869,7 @@ var AoudaClient = class {
5760
5869
  * ```
5761
5870
  */
5762
5871
  bulkLoad(tableName, rows, options) {
5763
- validateNonEmptyString2(tableName, "tableName");
5872
+ validateNonEmptyString3(tableName, "tableName");
5764
5873
  return new BulkLoadCoordinator(this.transport, this.database).run(
5765
5874
  [tableName],
5766
5875
  rows,