@aouda/client 0.1.14 → 0.1.16

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
@@ -60,6 +60,7 @@ __export(index_exports, {
60
60
  MaterializedQueriesApi: () => MaterializedQueriesApi,
61
61
  MaterializedQueryState: () => MaterializedQueryState,
62
62
  MaterializedQueryType: () => MaterializedQueryType,
63
+ MemoryConsistencyTokenStore: () => MemoryConsistencyTokenStore,
63
64
  MetricsAdminApi: () => MetricsAdminApi,
64
65
  NamedMutationsApi: () => NamedMutationsApi,
65
66
  NamedQueriesApi: () => NamedQueriesApi,
@@ -75,10 +76,12 @@ __export(index_exports, {
75
76
  applyLocalNetworkAccess: () => applyLocalNetworkAccess,
76
77
  coerceColumnarValue: () => coerceColumnarValue,
77
78
  columnarToRows: () => columnarToRows,
79
+ compareOrdinal: () => compareOrdinal,
78
80
  createAoudaClient: () => createAoudaClient,
79
81
  createAoudaClusterMcpToolSet: () => createAoudaClusterMcpToolSet,
80
82
  installLocalNetworkFetch: () => installLocalNetworkFetch,
81
83
  localNetworkFetch: () => localNetworkFetch,
84
+ maxToken: () => maxToken,
82
85
  resolveTargetAddressSpace: () => resolveTargetAddressSpace,
83
86
  version: () => version
84
87
  });
@@ -87,7 +90,7 @@ module.exports = __toCommonJS(index_exports);
87
90
  // package.json
88
91
  var package_default = {
89
92
  name: "@aouda/client",
90
- version: "0.1.14",
93
+ version: "0.1.16",
91
94
  description: "Official TypeScript/JavaScript client library for Aouda",
92
95
  type: "module",
93
96
  main: "./dist/index.cjs",
@@ -197,9 +200,10 @@ var AoudaError = class extends Error {
197
200
  }
198
201
  };
199
202
  var AoudaConnectionError = class extends AoudaError {
200
- constructor(message, cause) {
203
+ constructor(message, cause, rowErrors) {
201
204
  super(message);
202
205
  this.cause = cause;
206
+ this.rowErrors = rowErrors;
203
207
  this.name = "AoudaConnectionError";
204
208
  }
205
209
  };
@@ -219,43 +223,45 @@ var AoudaResponseError = class extends AoudaError {
219
223
  }
220
224
  };
221
225
  var AoudaApiError = class extends AoudaError {
222
- constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
226
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
223
227
  super(message);
224
228
  this.code = code;
225
229
  this.statusCode = statusCode;
226
230
  this.details = details;
227
231
  this.requestId = requestId;
228
232
  this.retryAfterSeconds = retryAfterSeconds;
233
+ this.token = token;
234
+ this.rowErrors = rowErrors;
229
235
  this.name = "AoudaApiError";
230
236
  }
231
237
  };
232
238
  var AoudaNotFoundError = class extends AoudaApiError {
233
- constructor(message, code, statusCode, details, requestId) {
234
- super(message, code, statusCode, details, requestId);
239
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
240
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
235
241
  this.name = "AoudaNotFoundError";
236
242
  }
237
243
  };
238
244
  var AoudaConflictError = class extends AoudaApiError {
239
- constructor(message, code, statusCode, details, requestId) {
240
- super(message, code, statusCode, details, requestId);
245
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
246
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
241
247
  this.name = "AoudaConflictError";
242
248
  }
243
249
  };
244
250
  var AoudaValidationError = class extends AoudaApiError {
245
- constructor(message, code, statusCode, details, requestId) {
246
- super(message, code, statusCode, details, requestId);
251
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
252
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
247
253
  this.name = "AoudaValidationError";
248
254
  }
249
255
  };
250
256
  var AoudaServerError = class extends AoudaApiError {
251
- constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
252
- super(message, code, statusCode, details, requestId, retryAfterSeconds);
257
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
258
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
253
259
  this.name = "AoudaServerError";
254
260
  }
255
261
  };
256
262
  var AoudaAuthenticationError = class extends AoudaApiError {
257
- constructor(message, code, statusCode, details, requestId) {
258
- super(message, code, statusCode, details, requestId);
263
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
264
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
259
265
  this.name = "AoudaAuthenticationError";
260
266
  }
261
267
  };
@@ -1067,7 +1073,7 @@ function buildHandle(jobId, commitResp, wasResumed, transport, database, signal)
1067
1073
  rowsDurablyCommitted: commitResp.rowsLoaded,
1068
1074
  segmentsCreated: commitResp.segmentsCreated,
1069
1075
  committedAtUtc: commitResp.committedAtUtc,
1070
- walPosition: commitResp.walPosition,
1076
+ token: commitResp.token,
1071
1077
  writeConcernSatisfied: commitResp.writeConcernAchieved ?? "acknowledged",
1072
1078
  writeConcernTimedOut: commitResp.writeConcernTimedOut,
1073
1079
  wasResumed,
@@ -1193,6 +1199,7 @@ var PROTOCOL_VERSION_HEADER = "X-Aouda-Protocol-Version";
1193
1199
  var PROTOCOL_VERSION = "1";
1194
1200
  var CONTENT_TYPE_JSON2 = "application/json";
1195
1201
  var REQUEST_ID_HEADER2 = "X-Request-Id";
1202
+ var TOKEN_HEADER = "X-Aouda-Token";
1196
1203
  var ERROR_CODE_MAP = {
1197
1204
  TABLE_NOT_FOUND: AoudaNotFoundError,
1198
1205
  COLUMN_NOT_FOUND: AoudaNotFoundError,
@@ -1207,6 +1214,10 @@ var ERROR_CODE_MAP = {
1207
1214
  INVALID_OPERATOR: AoudaValidationError,
1208
1215
  INVALID_COLUMN: AoudaValidationError,
1209
1216
  INVALID_VALUE: AoudaValidationError,
1217
+ CONSTRAINT_CHECK_VIOLATION: AoudaValidationError,
1218
+ TRANSFORM_DERIVED_READONLY: AoudaValidationError,
1219
+ TRANSFORM_ROUTE_UNMATCHED: AoudaValidationError,
1220
+ TRANSFORM_ROUTE_AMBIGUOUS: AoudaValidationError,
1210
1221
  UNSUPPORTED_VERSION: AoudaValidationError,
1211
1222
  MALFORMED_REQUEST: AoudaValidationError,
1212
1223
  INTERNAL_ERROR: AoudaServerError,
@@ -1232,7 +1243,12 @@ var ERROR_CODE_MAP = {
1232
1243
  AUTH_IDENTITY_INVALID: AoudaValidationError,
1233
1244
  AUTH_IDENTITY_NOT_FOUND: AoudaValidationError,
1234
1245
  BULK_LOAD_TRANSFORM_INTENT_REQUIRED: AoudaValidationError,
1235
- BULK_LOAD_TRANSFORM_INTENT_CONFLICT: AoudaValidationError
1246
+ BULK_LOAD_TRANSFORM_INTENT_CONFLICT: AoudaValidationError,
1247
+ TOKEN_MALFORMED: AoudaValidationError,
1248
+ TOKEN_FOREIGN_DATABASE: AoudaValidationError,
1249
+ TOKEN_EPOCH_SUPERSEDED: AoudaConflictError,
1250
+ TOKEN_UNSATISFIED: AoudaConflictError,
1251
+ TOKEN_FETCH_PRIMARY: AoudaApiError
1236
1252
  };
1237
1253
  function createComposedAbortController(...signals) {
1238
1254
  const controller = new AbortController();
@@ -1262,6 +1278,9 @@ function parseRetryAfterSeconds(header) {
1262
1278
  if (!Number.isFinite(n) || n < 0) return void 0;
1263
1279
  return n;
1264
1280
  }
1281
+ function nonEmptyRowErrors(rowErrors) {
1282
+ return Array.isArray(rowErrors) && rowErrors.length > 0 ? rowErrors : void 0;
1283
+ }
1265
1284
  function createApiError(statusCode, statusText, body, retryAfterHeader) {
1266
1285
  const message = body.error ?? `${statusCode} ${statusText}`;
1267
1286
  const code = body.code ?? "UNKNOWN";
@@ -1269,7 +1288,16 @@ function createApiError(statusCode, statusText, body, retryAfterHeader) {
1269
1288
  const requestId = body.requestId;
1270
1289
  const retryAfterSeconds = parseRetryAfterSeconds(retryAfterHeader ?? null);
1271
1290
  const Ctor = ERROR_CODE_MAP[code] ?? AoudaApiError;
1272
- return new Ctor(message, code, statusCode, details, requestId, retryAfterSeconds);
1291
+ return new Ctor(
1292
+ message,
1293
+ code,
1294
+ statusCode,
1295
+ details,
1296
+ requestId,
1297
+ retryAfterSeconds,
1298
+ body.token,
1299
+ nonEmptyRowErrors(body.rowErrors)
1300
+ );
1273
1301
  }
1274
1302
  var HttpTransport = class {
1275
1303
  constructor(options) {
@@ -1281,6 +1309,29 @@ var HttpTransport = class {
1281
1309
  ...options.defaultHeaders
1282
1310
  };
1283
1311
  this.abortController = new AbortController();
1312
+ this.database = options.database;
1313
+ this.store = options.consistencyTokenStore;
1314
+ }
1315
+ presentTokenHeaders(path, headers) {
1316
+ if (this.store == null || this.database == null || isAuthPath(path)) {
1317
+ return;
1318
+ }
1319
+ const token = this.store.get(this.database);
1320
+ if (token != null && token.length > 0) {
1321
+ headers[TOKEN_HEADER] = token;
1322
+ }
1323
+ }
1324
+ observeResponse(_path, response, bodyText, errorCode) {
1325
+ if (this.store == null || this.database == null) {
1326
+ return;
1327
+ }
1328
+ if (errorCode === "TOKEN_MALFORMED" || errorCode === "TOKEN_FOREIGN_DATABASE") {
1329
+ return;
1330
+ }
1331
+ const header = response.headers.get(TOKEN_HEADER);
1332
+ const bodyToken = readBodyToken(bodyText);
1333
+ const token = header != null && header.trim().length > 0 ? header : bodyToken;
1334
+ this.store.observe(this.database, token);
1284
1335
  }
1285
1336
  /**
1286
1337
  * Abort all in-flight and future requests (e.g. on client disconnect).
@@ -1318,6 +1369,7 @@ var HttpTransport = class {
1318
1369
  ...this.defaultHeaders,
1319
1370
  ...config.headers
1320
1371
  };
1372
+ this.presentTokenHeaders(config.path, headers);
1321
1373
  if (config.body !== void 0 && config.body !== null) {
1322
1374
  headers["Content-Type"] = CONTENT_TYPE_JSON2;
1323
1375
  }
@@ -1337,6 +1389,7 @@ var HttpTransport = class {
1337
1389
  const text = await response.text();
1338
1390
  if (config.allowStatuses?.includes(response.status) && text) {
1339
1391
  try {
1392
+ this.observeResponse(config.path, response, text);
1340
1393
  return JSON.parse(text);
1341
1394
  } catch {
1342
1395
  }
@@ -1350,15 +1403,20 @@ var HttpTransport = class {
1350
1403
  }
1351
1404
  if (response.status === 401) {
1352
1405
  const code = errorBody?.code ?? "AUTH_TOKEN_MISSING";
1406
+ this.observeResponse(config.path, response, text, code);
1353
1407
  throw new AoudaAuthenticationError(
1354
1408
  errorBody?.error ?? "Unauthorized",
1355
1409
  code,
1356
1410
  401,
1357
1411
  errorBody?.details,
1358
- errorBody?.requestId
1412
+ errorBody?.requestId,
1413
+ void 0,
1414
+ errorBody?.token,
1415
+ nonEmptyRowErrors(errorBody?.rowErrors)
1359
1416
  );
1360
1417
  }
1361
1418
  if (errorBody?.code != null) {
1419
+ this.observeResponse(config.path, response, text, errorBody.code);
1362
1420
  throw createApiError(
1363
1421
  response.status,
1364
1422
  response.statusText,
@@ -1366,6 +1424,7 @@ var HttpTransport = class {
1366
1424
  response.headers.get("Retry-After")
1367
1425
  );
1368
1426
  }
1427
+ this.observeResponse(config.path, response, text);
1369
1428
  throw new AoudaResponseError(
1370
1429
  errorBody?.error ?? `${response.status} ${response.statusText}`,
1371
1430
  response.status,
@@ -1373,6 +1432,7 @@ var HttpTransport = class {
1373
1432
  );
1374
1433
  }
1375
1434
  const responseText = await response.text();
1435
+ this.observeResponse(config.path, response, responseText);
1376
1436
  if (config.rawText) {
1377
1437
  return responseText;
1378
1438
  }
@@ -1433,6 +1493,7 @@ var HttpTransport = class {
1433
1493
  ...this.defaultHeaders,
1434
1494
  ...config.headers
1435
1495
  };
1496
+ this.presentTokenHeaders(config.path, headers);
1436
1497
  if (config.rawBodyStr === void 0 && config.body !== void 0 && config.body !== null) {
1437
1498
  headers["Content-Type"] = CONTENT_TYPE_JSON2;
1438
1499
  }
@@ -1449,6 +1510,7 @@ var HttpTransport = class {
1449
1510
  try {
1450
1511
  const response = await localNetworkFetch(url, init);
1451
1512
  if (response.ok || config.allowStatuses?.includes(response.status)) {
1513
+ this.observeResponse(config.path, response, void 0);
1452
1514
  return response;
1453
1515
  }
1454
1516
  const text = await response.text();
@@ -1461,15 +1523,20 @@ var HttpTransport = class {
1461
1523
  }
1462
1524
  if (response.status === 401) {
1463
1525
  const code = errorBody?.code ?? "AUTH_TOKEN_MISSING";
1526
+ this.observeResponse(config.path, response, text, code);
1464
1527
  throw new AoudaAuthenticationError(
1465
1528
  errorBody?.error ?? "Unauthorized",
1466
1529
  code,
1467
1530
  401,
1468
1531
  errorBody?.details,
1469
- errorBody?.requestId
1532
+ errorBody?.requestId,
1533
+ void 0,
1534
+ errorBody?.token,
1535
+ nonEmptyRowErrors(errorBody?.rowErrors)
1470
1536
  );
1471
1537
  }
1472
1538
  if (errorBody?.code != null) {
1539
+ this.observeResponse(config.path, response, text, errorBody.code);
1473
1540
  throw createApiError(
1474
1541
  response.status,
1475
1542
  response.statusText,
@@ -1477,6 +1544,7 @@ var HttpTransport = class {
1477
1544
  response.headers.get("Retry-After")
1478
1545
  );
1479
1546
  }
1547
+ this.observeResponse(config.path, response, text);
1480
1548
  throw new AoudaResponseError(
1481
1549
  errorBody?.error ?? `${response.status} ${response.statusText}`,
1482
1550
  response.status,
@@ -1631,6 +1699,47 @@ var HttpTransport = class {
1631
1699
  });
1632
1700
  }
1633
1701
  };
1702
+ function isAuthPath(path) {
1703
+ let p = path;
1704
+ const q = p.indexOf("?");
1705
+ if (q >= 0) {
1706
+ p = p.slice(0, q);
1707
+ }
1708
+ if (/^https?:\/\//i.test(p)) {
1709
+ try {
1710
+ p = new URL(p).pathname;
1711
+ } catch {
1712
+ }
1713
+ }
1714
+ const lower = p.toLowerCase();
1715
+ if (lower === "/api/auth" || lower.startsWith("/api/auth/")) {
1716
+ return true;
1717
+ }
1718
+ const prefix = "/api/databases/";
1719
+ if (!lower.startsWith(prefix)) {
1720
+ return false;
1721
+ }
1722
+ const rest = lower.slice(prefix.length);
1723
+ const slash = rest.indexOf("/");
1724
+ if (slash < 0) {
1725
+ return false;
1726
+ }
1727
+ const afterDb = rest.slice(slash + 1);
1728
+ return afterDb === "auth" || afterDb.startsWith("auth/");
1729
+ }
1730
+ function readBodyToken(bodyText) {
1731
+ if (bodyText == null || bodyText.trim().length === 0) {
1732
+ return void 0;
1733
+ }
1734
+ try {
1735
+ const parsed = JSON.parse(bodyText);
1736
+ if (parsed !== null && typeof parsed === "object" && "token" in parsed && typeof parsed.token === "string") {
1737
+ return parsed.token;
1738
+ }
1739
+ } catch {
1740
+ }
1741
+ return void 0;
1742
+ }
1634
1743
 
1635
1744
  // src/resilience/retry.ts
1636
1745
  var DEFAULT_MAX_RETRIES = 3;
@@ -2063,6 +2172,48 @@ function databasePath2(db) {
2063
2172
  return `/api/databases/${encodeURIComponent(db)}`;
2064
2173
  }
2065
2174
 
2175
+ // src/consistency-token-store.ts
2176
+ var MemoryConsistencyTokenStore = class {
2177
+ constructor() {
2178
+ this.tokens = /* @__PURE__ */ new Map();
2179
+ }
2180
+ get(database) {
2181
+ if (typeof database !== "string" || database.trim().length === 0) {
2182
+ throw new Error("database must be a non-empty string");
2183
+ }
2184
+ return this.tokens.get(normalizeKey(database));
2185
+ }
2186
+ observe(database, token) {
2187
+ if (typeof database !== "string" || database.trim().length === 0) {
2188
+ throw new Error("database must be a non-empty string");
2189
+ }
2190
+ if (token == null || token.trim().length === 0) {
2191
+ return;
2192
+ }
2193
+ const key = normalizeKey(database);
2194
+ const stored = this.tokens.get(key);
2195
+ if (stored === void 0 || compareOrdinal(token, stored) > 0) {
2196
+ this.tokens.set(key, token);
2197
+ }
2198
+ }
2199
+ };
2200
+ function compareOrdinal(left, right) {
2201
+ if (left === right) return 0;
2202
+ return left < right ? -1 : 1;
2203
+ }
2204
+ function maxToken(left, right) {
2205
+ if (left == null || left.length === 0) {
2206
+ return right == null || right.length === 0 ? void 0 : right;
2207
+ }
2208
+ if (right == null || right.length === 0) {
2209
+ return left;
2210
+ }
2211
+ return compareOrdinal(left, right) >= 0 ? left : right;
2212
+ }
2213
+ function normalizeKey(database) {
2214
+ return database.toLowerCase();
2215
+ }
2216
+
2066
2217
  // src/streaming/subscription.ts
2067
2218
  var AsyncEventQueue = class {
2068
2219
  constructor() {
@@ -2119,7 +2270,7 @@ var AsyncEventQueue = class {
2119
2270
  }
2120
2271
  };
2121
2272
  var TableSubscription = class {
2122
- constructor(transport, identity, options = {}, onWarnings) {
2273
+ constructor(transport, identity, options = {}, onWarnings, store, database) {
2123
2274
  this._queue = new AsyncEventQueue();
2124
2275
  this._active = true;
2125
2276
  this._started = false;
@@ -2135,6 +2286,11 @@ var TableSubscription = class {
2135
2286
  this._onError = options.onError;
2136
2287
  this._onWarnings = onWarnings;
2137
2288
  this._conflate = options.conflate;
2289
+ this._atLeast = options.atLeast;
2290
+ this._waitMs = options.waitMs;
2291
+ this._onExceeded = options.onExceeded;
2292
+ this._store = store;
2293
+ this._database = database;
2138
2294
  this._reconnectHandlerKey = `${this.id}::reconnect`;
2139
2295
  }
2140
2296
  get lastVersion() {
@@ -2199,7 +2355,7 @@ var TableSubscription = class {
2199
2355
  id: this.id
2200
2356
  };
2201
2357
  if (this._identity.kind === "named") {
2202
- message.hash = this._identity.hash;
2358
+ message.name = this._identity.name;
2203
2359
  if (this._identity.args !== void 0) {
2204
2360
  message.args = this._identity.args;
2205
2361
  }
@@ -2218,8 +2374,30 @@ var TableSubscription = class {
2218
2374
  if (this._conflate !== void 0) {
2219
2375
  message.conflate = this._conflate;
2220
2376
  }
2377
+ const pin = this._resolvePin();
2378
+ if (pin !== void 0) {
2379
+ message.at_least = pin;
2380
+ }
2381
+ if (this._waitMs !== void 0) {
2382
+ message.wait_ms = this._waitMs;
2383
+ }
2384
+ if (this._onExceeded !== void 0) {
2385
+ message.on_exceeded = this._onExceeded;
2386
+ }
2221
2387
  await this._transport.send(message);
2222
2388
  }
2389
+ _resolvePin() {
2390
+ if (this._atLeast !== void 0 && this._store != null && this._database != null) {
2391
+ this._store.observe(this._database, this._atLeast);
2392
+ }
2393
+ const stored = this._store != null && this._database != null ? this._store.get(this._database) : void 0;
2394
+ return maxToken(this._atLeast, stored);
2395
+ }
2396
+ _observeToken(token) {
2397
+ if (token !== void 0 && this._store != null && this._database != null) {
2398
+ this._store.observe(this._database, token);
2399
+ }
2400
+ }
2223
2401
  _handleMessage(message) {
2224
2402
  if (!this._active) {
2225
2403
  return;
@@ -2246,6 +2424,7 @@ var TableSubscription = class {
2246
2424
  }
2247
2425
  _handleSnapshotPage(message) {
2248
2426
  this._pendingSnapshotRows.push(...message.rows);
2427
+ this._observeToken(message.token);
2249
2428
  }
2250
2429
  _handleSnapshotComplete(message) {
2251
2430
  this._lastVersion = message.version;
@@ -2263,6 +2442,10 @@ var TableSubscription = class {
2263
2442
  if (message.total_matches !== void 0) {
2264
2443
  snapshot.totalMatches = message.total_matches;
2265
2444
  }
2445
+ if (message.token !== void 0) {
2446
+ snapshot.token = message.token;
2447
+ this._observeToken(message.token);
2448
+ }
2266
2449
  this._queue.push(snapshot);
2267
2450
  }
2268
2451
  async _handleGap(message) {
@@ -2288,6 +2471,10 @@ var TableSubscription = class {
2288
2471
  if (message.values_skipped !== void 0) {
2289
2472
  event.values_skipped = message.values_skipped;
2290
2473
  }
2474
+ if (message.token !== void 0) {
2475
+ event.token = message.token;
2476
+ this._observeToken(message.token);
2477
+ }
2291
2478
  this._onChange?.(event);
2292
2479
  this._queue.push(event);
2293
2480
  }
@@ -2435,8 +2622,11 @@ var TableWriteStream = class {
2435
2622
  resolve?.();
2436
2623
  }
2437
2624
  _handleServerError(message) {
2625
+ const rowErrors = Array.isArray(message.errors) && message.errors.length > 0 ? message.errors : void 0;
2438
2626
  const error = new AoudaConnectionError(
2439
- `Write stream error (${message.code}): ${message.message}`
2627
+ `Write stream error (${message.code}): ${message.message}`,
2628
+ void 0,
2629
+ rowErrors
2440
2630
  );
2441
2631
  const openReject = this._openReject;
2442
2632
  this._openResolve = null;
@@ -2632,11 +2822,12 @@ var TableQuery = class _TableQuery {
2632
2822
  * @param state - Optional initial state (used for immutable chaining).
2633
2823
  * @internal Use `client.table()` to create queries.
2634
2824
  */
2635
- constructor(transport, tableName, database, state, getWebSocketTransport) {
2825
+ constructor(transport, tableName, database, state, getWebSocketTransport, store) {
2636
2826
  this.transport = transport;
2637
2827
  this.tableName = tableName;
2638
2828
  this.database = database;
2639
2829
  this.getWebSocketTransport = getWebSocketTransport;
2830
+ this.store = store;
2640
2831
  this.state = state ?? {
2641
2832
  predicates: [],
2642
2833
  groupClauses: [],
@@ -2652,6 +2843,16 @@ var TableQuery = class _TableQuery {
2652
2843
  isDistinct: false
2653
2844
  };
2654
2845
  }
2846
+ withState(state) {
2847
+ return new _TableQuery(
2848
+ this.transport,
2849
+ this.tableName,
2850
+ this.database,
2851
+ state,
2852
+ this.getWebSocketTransport,
2853
+ this.store
2854
+ );
2855
+ }
2655
2856
  where(column, operator, value) {
2656
2857
  const newPredicates = buildWherePredicates(column, operator, value);
2657
2858
  return new _TableQuery(
@@ -2662,7 +2863,8 @@ var TableQuery = class _TableQuery {
2662
2863
  ...this.state,
2663
2864
  predicates: [...this.state.predicates, ...newPredicates]
2664
2865
  },
2665
- this.getWebSocketTransport
2866
+ this.getWebSocketTransport,
2867
+ this.store
2666
2868
  );
2667
2869
  }
2668
2870
  /**
@@ -2681,7 +2883,7 @@ var TableQuery = class _TableQuery {
2681
2883
  return new _TableQuery(this.transport, this.tableName, this.database, {
2682
2884
  ...this.state,
2683
2885
  groupClauses: [...this.state.groupClauses, sub]
2684
- }, this.getWebSocketTransport);
2886
+ }, this.getWebSocketTransport, this.store);
2685
2887
  }
2686
2888
  /**
2687
2889
  * Sets the primary sort column for the query.
@@ -2706,7 +2908,7 @@ var TableQuery = class _TableQuery {
2706
2908
  return new _TableQuery(this.transport, this.tableName, this.database, {
2707
2909
  ...this.state,
2708
2910
  orderByClauses: [orderByClause]
2709
- }, this.getWebSocketTransport);
2911
+ }, this.getWebSocketTransport, this.store);
2710
2912
  }
2711
2913
  /**
2712
2914
  * Sets the primary sort column to descending order.
@@ -2752,7 +2954,7 @@ var TableQuery = class _TableQuery {
2752
2954
  return new _TableQuery(this.transport, this.tableName, this.database, {
2753
2955
  ...this.state,
2754
2956
  orderByClauses: [...this.state.orderByClauses, orderByClause]
2755
- }, this.getWebSocketTransport);
2957
+ }, this.getWebSocketTransport, this.store);
2756
2958
  }
2757
2959
  /**
2758
2960
  * Sets the maximum number of rows to return.
@@ -2769,7 +2971,7 @@ var TableQuery = class _TableQuery {
2769
2971
  return new _TableQuery(this.transport, this.tableName, this.database, {
2770
2972
  ...this.state,
2771
2973
  limitValue: count
2772
- }, this.getWebSocketTransport);
2974
+ }, this.getWebSocketTransport, this.store);
2773
2975
  }
2774
2976
  /**
2775
2977
  * Sets the number of rows to skip.
@@ -2786,7 +2988,7 @@ var TableQuery = class _TableQuery {
2786
2988
  return new _TableQuery(this.transport, this.tableName, this.database, {
2787
2989
  ...this.state,
2788
2990
  offsetValue: count
2789
- }, this.getWebSocketTransport);
2991
+ }, this.getWebSocketTransport, this.store);
2790
2992
  }
2791
2993
  /**
2792
2994
  * Requests cross-partition access for this query.
@@ -2801,10 +3003,21 @@ var TableQuery = class _TableQuery {
2801
3003
  return new _TableQuery(this.transport, this.tableName, this.database, {
2802
3004
  ...this.state,
2803
3005
  crossPartitionAccess: true
2804
- }, this.getWebSocketTransport);
3006
+ }, this.getWebSocketTransport, this.store);
2805
3007
  }
2806
3008
  /**
2807
- * Restricts the columns returned in the result.
3009
+ * Pin this query at at least this C-1 token. Observes the token into the
3010
+ * client store (I3, sticky) and presents it on execute via `X-Aouda-Token`.
3011
+ */
3012
+ atLeast(token) {
3013
+ if (typeof token !== "string" || token.trim().length === 0) {
3014
+ throw new Error("atLeast() requires a non-empty token");
3015
+ }
3016
+ this.store?.observe(this.database, token);
3017
+ return this.withState({ ...this.state, atLeast: token });
3018
+ }
3019
+ /**
3020
+ * Selects specific columns to return.
2808
3021
  * If not called, all columns are returned.
2809
3022
  *
2810
3023
  * When T is a specific row type, only keys of T are accepted as column names.
@@ -2826,7 +3039,7 @@ var TableQuery = class _TableQuery {
2826
3039
  return new _TableQuery(this.transport, this.tableName, this.database, {
2827
3040
  ...this.state,
2828
3041
  selectColumns: columns.length > 0 ? columns : null
2829
- }, this.getWebSocketTransport);
3042
+ }, this.getWebSocketTransport, this.store);
2830
3043
  }
2831
3044
  /**
2832
3045
  * Return only distinct (de-duplicated) rows for the given columns — SQL `SELECT DISTINCT`.
@@ -2856,7 +3069,7 @@ var TableQuery = class _TableQuery {
2856
3069
  ...this.state,
2857
3070
  selectColumns: columns,
2858
3071
  isDistinct: true
2859
- }, this.getWebSocketTransport);
3072
+ }, this.getWebSocketTransport, this.store);
2860
3073
  }
2861
3074
  /**
2862
3075
  * Adds server-side computed columns to the query result.
@@ -2865,6 +3078,12 @@ var TableQuery = class _TableQuery {
2865
3078
  * evaluated per row on the server. Computed columns are appended after any physical-column
2866
3079
  * `select()` projection.
2867
3080
  *
3081
+ * Result types are inferred by the server where the expression permits
3082
+ * (e.g. Int32 → `number`). Uninferable expressions use wire `"Unknown"` /
3083
+ * codegen `unknown`. Computed columns are always nullable. Named-query
3084
+ * `*Row` properties pick this up when regenerated against a post-S08 server.
3085
+ * See aouda-docs/guides/browser-tier-read-limits.md#selectexpr-result-types
3086
+ *
2868
3087
  * @param projections - One or more `{ alias, expr }` pairs.
2869
3088
  * @returns A new TableQuery with computed columns set.
2870
3089
  *
@@ -2885,7 +3104,7 @@ var TableQuery = class _TableQuery {
2885
3104
  return new _TableQuery(this.transport, this.tableName, this.database, {
2886
3105
  ...this.state,
2887
3106
  selectExprs: projections
2888
- }, this.getWebSocketTransport);
3107
+ }, this.getWebSocketTransport, this.store);
2889
3108
  }
2890
3109
  join(rightTable, leftColumnOrColumns, rightColumnOrColumns) {
2891
3110
  return this.addJoinClause(
@@ -2943,7 +3162,8 @@ var TableQuery = class _TableQuery {
2943
3162
  }
2944
3163
  ]
2945
3164
  },
2946
- this.getWebSocketTransport
3165
+ this.getWebSocketTransport,
3166
+ this.store
2947
3167
  );
2948
3168
  }
2949
3169
  /**
@@ -2979,7 +3199,8 @@ var TableQuery = class _TableQuery {
2979
3199
  ...this.state,
2980
3200
  groupByColumns: [...columns]
2981
3201
  },
2982
- this.getWebSocketTransport
3202
+ this.getWebSocketTransport,
3203
+ this.store
2983
3204
  );
2984
3205
  }
2985
3206
  /**
@@ -3013,8 +3234,11 @@ var TableQuery = class _TableQuery {
3013
3234
  onSnapshot: options.onSnapshot,
3014
3235
  onChange: options.onChange,
3015
3236
  onError: options.onError,
3016
- conflate: options.conflate
3017
- });
3237
+ conflate: options.conflate,
3238
+ atLeast: options.atLeast ?? this.state.atLeast,
3239
+ waitMs: options.waitMs,
3240
+ onExceeded: options.onExceeded
3241
+ }, void 0, this.store, this.database);
3018
3242
  subscription.start();
3019
3243
  return subscription;
3020
3244
  }
@@ -3129,7 +3353,8 @@ var TableQuery = class _TableQuery {
3129
3353
  ...this.state,
3130
3354
  joinClauses: [...this.state.joinClauses, joinClause]
3131
3355
  },
3132
- this.getWebSocketTransport
3356
+ this.getWebSocketTransport,
3357
+ this.store
3133
3358
  );
3134
3359
  }
3135
3360
  addAggregate(op, column) {
@@ -3150,7 +3375,8 @@ var TableQuery = class _TableQuery {
3150
3375
  }
3151
3376
  ]
3152
3377
  },
3153
- this.getWebSocketTransport
3378
+ this.getWebSocketTransport,
3379
+ this.store
3154
3380
  );
3155
3381
  }
3156
3382
  requireWebSocketTransport() {
@@ -3180,17 +3406,23 @@ var TableQuery = class _TableQuery {
3180
3406
  * ```
3181
3407
  */
3182
3408
  async execute() {
3409
+ if (this.state.atLeast) {
3410
+ this.store?.observe(this.database, this.state.atLeast);
3411
+ }
3183
3412
  const request = this.buildRequest();
3184
3413
  const path = `${databasePath2(this.database)}/query`;
3185
3414
  const response = await this.transport.post(path, request);
3186
3415
  const rows = columnarToRows(response);
3187
3416
  const stats = response.stats;
3188
- return { rows, stats };
3417
+ return { rows, stats, token: response.token };
3189
3418
  }
3190
3419
  /**
3191
3420
  * Executes the query and returns the raw columnar JSON payload (no row-object conversion).
3192
3421
  */
3193
3422
  async toColumnar() {
3423
+ if (this.state.atLeast) {
3424
+ this.store?.observe(this.database, this.state.atLeast);
3425
+ }
3194
3426
  const request = this.buildRequest();
3195
3427
  const path = `${databasePath2(this.database)}/query`;
3196
3428
  return this.transport.post(path, request);
@@ -3220,8 +3452,12 @@ var TableQuery = class _TableQuery {
3220
3452
  limitValue: 0,
3221
3453
  selectColumns: []
3222
3454
  },
3223
- this.getWebSocketTransport
3455
+ this.getWebSocketTransport,
3456
+ this.store
3224
3457
  );
3458
+ if (this.state.atLeast) {
3459
+ this.store?.observe(this.database, this.state.atLeast);
3460
+ }
3225
3461
  const request = countQuery.buildRequest();
3226
3462
  const path = `${databasePath2(this.database)}/query`;
3227
3463
  const response = await this.transport.post(path, request);
@@ -3268,7 +3504,8 @@ var TableQuery = class _TableQuery {
3268
3504
  const response = await this.transport.post(path, body);
3269
3505
  const result = {
3270
3506
  rowsInserted: response.rowsInserted,
3271
- executionMs: response.executionMs
3507
+ executionMs: response.executionMs,
3508
+ token: response.token
3272
3509
  };
3273
3510
  if (response.generatedValues !== void 0) {
3274
3511
  result.generatedValues = response.generatedValues;
@@ -3318,7 +3555,8 @@ var TableQuery = class _TableQuery {
3318
3555
  const response = await this.transport.post(path, body);
3319
3556
  const result = {
3320
3557
  rowsInserted: response.rowsInserted,
3321
- executionMs: response.executionMs
3558
+ executionMs: response.executionMs,
3559
+ token: response.token
3322
3560
  };
3323
3561
  if (response.generatedValues !== void 0) {
3324
3562
  result.generatedValues = response.generatedValues;
@@ -3370,6 +3608,7 @@ var TableQuery = class _TableQuery {
3370
3608
  return {
3371
3609
  rowsAffected: response.rowsUpdated,
3372
3610
  executionMs: response.executionMs,
3611
+ token: response.token,
3373
3612
  ...response.rows ? { rows: columnarToRows(response.rows) } : {},
3374
3613
  ...response.rowsTruncated ? { rowsTruncated: true } : {}
3375
3614
  };
@@ -3420,6 +3659,7 @@ var TableQuery = class _TableQuery {
3420
3659
  rowsAffected: response.rowsDeleted,
3421
3660
  executionMs: response.executionMs,
3422
3661
  hasMore: response.hasMore,
3662
+ token: response.token,
3423
3663
  ...response.rows ? { rows: columnarToRows(response.rows) } : {},
3424
3664
  ...response.rowsTruncated ? { rowsTruncated: true } : {}
3425
3665
  };
@@ -3439,7 +3679,8 @@ var TableQuery = class _TableQuery {
3439
3679
  return {
3440
3680
  rowsAffected: response.rowsDeleted,
3441
3681
  executionMs: response.executionMs,
3442
- hasMore: false
3682
+ hasMore: false,
3683
+ token: response.token
3443
3684
  };
3444
3685
  }
3445
3686
  /**
@@ -3452,7 +3693,14 @@ var TableQuery = class _TableQuery {
3452
3693
  throw new Error("batch() requires a non-empty operations array");
3453
3694
  }
3454
3695
  const wireOperations = operations.map((op, index) => {
3455
- const base = new _TableQuery(this.transport, this.tableName, this.database);
3696
+ const base = new _TableQuery(
3697
+ this.transport,
3698
+ this.tableName,
3699
+ this.database,
3700
+ void 0,
3701
+ this.getWebSocketTransport,
3702
+ this.store
3703
+ );
3456
3704
  const scoped = op.where(base);
3457
3705
  const where = scoped.buildWhereClause();
3458
3706
  if (!where) {
@@ -4820,12 +5068,12 @@ function raiseDeprecationWarnings(sink, warnings) {
4820
5068
  continue;
4821
5069
  }
4822
5070
  const sunset = warning.sunsetAt != null ? ` sunsetAt=${warning.sunsetAt}` : "";
4823
- const hash = warning.hash != null && warning.hash.length > 0 ? ` hash=${warning.hash}` : "";
5071
+ const name = warning.name != null && warning.name.length > 0 ? ` name=${warning.name}` : "";
4824
5072
  sink({
4825
5073
  code: warning.code,
4826
- hash: warning.hash,
5074
+ name: warning.name,
4827
5075
  sunsetAt: warning.sunsetAt,
4828
- message: `${warning.code}:${hash}${sunset}`.trim()
5076
+ message: `${warning.code}:${name}${sunset}`.trim()
4829
5077
  });
4830
5078
  }
4831
5079
  }
@@ -4838,24 +5086,28 @@ function emptyStats() {
4838
5086
  };
4839
5087
  }
4840
5088
  var NamedQueriesApi = class {
4841
- constructor(transport, database, onWarning, getStreamingTransport) {
5089
+ constructor(transport, database, onWarning, getStreamingTransport, store) {
4842
5090
  this.transport = transport;
4843
5091
  this.database = database;
4844
5092
  this.onWarning = onWarning;
4845
5093
  this.getStreamingTransport = getStreamingTransport;
5094
+ this.store = store;
4846
5095
  }
4847
- async execute(hash, args, options) {
4848
- if (typeof hash !== "string" || hash.trim().length === 0) {
4849
- throw new Error("Named query hash must be a non-empty string");
5096
+ async execute(name, args, options) {
5097
+ if (typeof name !== "string" || name.trim().length === 0) {
5098
+ throw new Error("Named query name must be a non-empty string");
4850
5099
  }
4851
5100
  const prefix = databasePath2(this.database);
4852
- const path = `${prefix}/named-queries/${encodeURIComponent(hash)}/query?format=columnar`;
5101
+ const path = `${prefix}/named-queries/${encodeURIComponent(name)}/query?format=columnar`;
4853
5102
  const body = {
4854
5103
  args: args ?? {}
4855
5104
  };
4856
5105
  if (options?.orderByIndex !== void 0) {
4857
5106
  body.orderByIndex = options.orderByIndex;
4858
5107
  }
5108
+ if (options?.atLeast !== void 0) {
5109
+ this.store?.observe(this.database, options.atLeast);
5110
+ }
4859
5111
  const response = await this.transport.post(
4860
5112
  path,
4861
5113
  body,
@@ -4867,7 +5119,8 @@ var NamedQueriesApi = class {
4867
5119
  rows,
4868
5120
  stats: response.stats,
4869
5121
  warnings: response.warnings,
4870
- totalMatches: response.totalMatches
5122
+ totalMatches: response.totalMatches,
5123
+ token: response.token
4871
5124
  };
4872
5125
  }
4873
5126
  async batch(items, options) {
@@ -4885,6 +5138,9 @@ var NamedQueriesApi = class {
4885
5138
  400
4886
5139
  );
4887
5140
  }
5141
+ if (options?.atLeast !== void 0) {
5142
+ this.store?.observe(this.database, options.atLeast);
5143
+ }
4888
5144
  const prefix = databasePath2(this.database);
4889
5145
  const path = `${prefix}/named-queries/batch?format=columnar`;
4890
5146
  const envelope = await this.transport.post(
@@ -4907,34 +5163,41 @@ var NamedQueriesApi = class {
4907
5163
  rowCount: slot.rowCount ?? 0,
4908
5164
  stats: slot.stats ?? emptyStats(),
4909
5165
  warnings: slot.warnings,
4910
- totalMatches: slot.totalMatches
5166
+ totalMatches: slot.totalMatches,
5167
+ token: slot.token
4911
5168
  };
4912
5169
  const result = {
4913
5170
  rows: columnarToRows(columnar),
4914
5171
  stats: columnar.stats,
4915
5172
  warnings: slot.warnings,
4916
- totalMatches: slot.totalMatches
5173
+ totalMatches: slot.totalMatches,
5174
+ token: slot.token
4917
5175
  };
4918
5176
  raiseDeprecationWarnings(this.onWarning, slot.warnings);
4919
5177
  return { isError: false, result };
4920
5178
  });
4921
5179
  }
4922
- subscribe(hash, args, options = {}) {
4923
- if (typeof hash !== "string" || hash.trim().length === 0) {
4924
- throw new Error("Named query hash must be a non-empty string");
5180
+ subscribe(name, args, options = {}) {
5181
+ if (typeof name !== "string" || name.trim().length === 0) {
5182
+ throw new Error("Named query name must be a non-empty string");
4925
5183
  }
4926
5184
  const subscription = new TableSubscription(
4927
5185
  this.getStreamingTransport(),
4928
- { kind: "named", hash, args, orderByIndex: options.orderByIndex },
5186
+ { kind: "named", name, args, orderByIndex: options.orderByIndex },
4929
5187
  {
4930
5188
  onSnapshot: options.onSnapshot,
4931
5189
  onChange: options.onChange,
4932
5190
  onError: options.onError,
4933
- conflate: options.conflate
5191
+ conflate: options.conflate,
5192
+ atLeast: options.atLeast,
5193
+ waitMs: options.waitMs,
5194
+ onExceeded: options.onExceeded
4934
5195
  },
4935
5196
  (warnings) => {
4936
5197
  raiseDeprecationWarnings(this.onWarning, warnings);
4937
- }
5198
+ },
5199
+ this.store,
5200
+ this.database
4938
5201
  );
4939
5202
  subscription.start();
4940
5203
  return subscription;
@@ -4946,12 +5209,12 @@ var NamedMutationsApi = class {
4946
5209
  this.database = database;
4947
5210
  this.onWarning = onWarning;
4948
5211
  }
4949
- async execute(hash, args) {
4950
- if (typeof hash !== "string" || hash.trim().length === 0) {
4951
- throw new Error("Named mutation hash must be a non-empty string");
5212
+ async execute(name, args) {
5213
+ if (typeof name !== "string" || name.trim().length === 0) {
5214
+ throw new Error("Named mutation name must be a non-empty string");
4952
5215
  }
4953
5216
  const prefix = databasePath2(this.database);
4954
- const path = `${prefix}/named-mutations/${encodeURIComponent(hash)}/execute`;
5217
+ const path = `${prefix}/named-mutations/${encodeURIComponent(name)}/execute`;
4955
5218
  const wire = await this.transport.post(path, {
4956
5219
  args: args ?? {}
4957
5220
  });
@@ -5012,6 +5275,7 @@ var WebSocketTransport = class {
5012
5275
  // Serialized-send queue: each send appends to this tail.
5013
5276
  this._sendTail = Promise.resolve();
5014
5277
  this._lastVersion = 0;
5278
+ this._lastToken = null;
5015
5279
  this._reconnectAttempt = 0;
5016
5280
  this._disposed = false;
5017
5281
  this._missedPings = 0;
@@ -5029,6 +5293,7 @@ var WebSocketTransport = class {
5029
5293
  this._pingIntervalMs = options.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
5030
5294
  this._maxMissedHeartbeats = options.maxMissedHeartbeats ?? DEFAULT_MAX_MISSED_HEARTBEATS;
5031
5295
  this._enableCompression = options.enableCompression ?? true;
5296
+ this._store = options.consistencyTokenStore;
5032
5297
  this._wireMode = options.wireMode ?? "json";
5033
5298
  this._authHandler?.setOnAccessTokenRefreshed((token) => {
5034
5299
  if (this._handshakeComplete && this._ws?.readyState === WS_READY_STATE_OPEN) {
@@ -5037,10 +5302,14 @@ var WebSocketTransport = class {
5037
5302
  });
5038
5303
  }
5039
5304
  // ─── Public API ────────────────────────────────────────────────────────────
5040
- /** Latest version number from the most recent server `heartbeat` message. */
5305
+ /** Latest change-event sequence from the most recent server `heartbeat` message. */
5041
5306
  get lastVersion() {
5042
5307
  return this._lastVersion;
5043
5308
  }
5309
+ /** Consistency token from the most recent server `heartbeat` (ADR 0042 D-13). */
5310
+ get lastToken() {
5311
+ return this._lastToken;
5312
+ }
5044
5313
  /**
5045
5314
  * Registers a handler for incoming server messages with the given channel id.
5046
5315
  * Use `"__global__"` to receive broadcast messages that carry no `id`.
@@ -5228,6 +5497,10 @@ var WebSocketTransport = class {
5228
5497
  }
5229
5498
  case "heartbeat":
5230
5499
  this._lastVersion = msg.version;
5500
+ this._lastToken = msg.token ?? null;
5501
+ if (this._lastToken != null && this._lastToken.length > 0) {
5502
+ this._store?.observe(this._database, this._lastToken);
5503
+ }
5231
5504
  break;
5232
5505
  case "pong":
5233
5506
  this._missedPings = 0;
@@ -5393,16 +5666,21 @@ var LongPollTransport = class {
5393
5666
  this._connected = false;
5394
5667
  this._pollTask = null;
5395
5668
  this._lastVersion = 0;
5669
+ this._lastToken = null;
5396
5670
  this._serverUrl = serverUrl;
5397
5671
  this._database = database;
5398
5672
  this._authHandler = options.authHandler;
5399
5673
  this._onReconnected = options.onReconnected;
5400
5674
  this._waitMs = Math.max(1, options.waitMs ?? DEFAULT_WAIT_MS);
5401
5675
  this._fetch = options.fetchImpl ?? localNetworkFetch;
5676
+ this._store = options.consistencyTokenStore;
5402
5677
  }
5403
5678
  get lastVersion() {
5404
5679
  return this._lastVersion;
5405
5680
  }
5681
+ get lastToken() {
5682
+ return this._lastToken;
5683
+ }
5406
5684
  registerHandler(id, handler) {
5407
5685
  this._handlers.set(id, handler);
5408
5686
  }
@@ -5525,6 +5803,10 @@ var LongPollTransport = class {
5525
5803
  for (const msg of payload.messages) {
5526
5804
  if (msg.type === "heartbeat") {
5527
5805
  this._lastVersion = msg.version;
5806
+ this._lastToken = msg.token ?? null;
5807
+ if (this._lastToken != null && this._lastToken.length > 0) {
5808
+ this._store?.observe(this._database, this._lastToken);
5809
+ }
5528
5810
  }
5529
5811
  const id = "id" in msg ? msg.id : void 0;
5530
5812
  if (id !== void 0 && id !== null) {
@@ -5604,6 +5886,9 @@ var FallbackStreamingTransport = class {
5604
5886
  get lastVersion() {
5605
5887
  return this._active.lastVersion;
5606
5888
  }
5889
+ get lastToken() {
5890
+ return this._active.lastToken;
5891
+ }
5607
5892
  registerHandler(id, handler) {
5608
5893
  this._handlers.set(id, handler);
5609
5894
  this._active.registerHandler(id, handler);
@@ -5683,6 +5968,7 @@ var AoudaClient = class {
5683
5968
  this.baseUrl = normalizeBaseUrl(options.serverUrl);
5684
5969
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
5685
5970
  this.database = options.database.trim();
5971
+ this._store = options.consistencyTokenStore ?? new MemoryConsistencyTokenStore();
5686
5972
  if (!Number.isFinite(this.timeout) || this.timeout <= 0) {
5687
5973
  throw new Error("timeout must be a finite positive number");
5688
5974
  }
@@ -5717,7 +6003,9 @@ var AoudaClient = class {
5717
6003
  }
5718
6004
  const httpTransport = new HttpTransport({
5719
6005
  baseUrl: this.baseUrl,
5720
- timeout: this.timeout
6006
+ timeout: this.timeout,
6007
+ database: this.database,
6008
+ consistencyTokenStore: this._store
5721
6009
  });
5722
6010
  const activeAuth = options.serverAuth ?? options.appAuth;
5723
6011
  if (activeAuth) {
@@ -5777,7 +6065,8 @@ var AoudaClient = class {
5777
6065
  this.transport,
5778
6066
  this.database,
5779
6067
  onNamedArtifactWarning,
5780
- () => this._getOrCreateWebSocketTransport()
6068
+ () => this._getOrCreateWebSocketTransport(),
6069
+ this._store
5781
6070
  );
5782
6071
  this._namedMutations = new NamedMutationsApi(
5783
6072
  this.transport,
@@ -5876,7 +6165,8 @@ var AoudaClient = class {
5876
6165
  name,
5877
6166
  this.database,
5878
6167
  void 0,
5879
- () => this._getOrCreateWebSocketTransport()
6168
+ () => this._getOrCreateWebSocketTransport(),
6169
+ this._store
5880
6170
  );
5881
6171
  }
5882
6172
  /**
@@ -5928,13 +6218,26 @@ var AoudaClient = class {
5928
6218
  return this._materializedQueries;
5929
6219
  }
5930
6220
  /**
5931
- * Hash-only named-query execute and batch. Names are codegen aliases (D-5).
6221
+ * Named-query execute, read-only batch, and subscribe by unique schema name.
5932
6222
  */
5933
6223
  get namedQueries() {
5934
6224
  return this._namedQueries;
5935
6225
  }
6226
+ observeConsistencyToken(token) {
6227
+ this._store.observe(this.database, token);
6228
+ }
6229
+ getObservedConsistencyToken() {
6230
+ return this._store.get(this.database);
6231
+ }
6232
+ async getConsistencyToken() {
6233
+ const body = await this.transport.get(
6234
+ `/api/databases/${encodeURIComponent(this.database)}/token`
6235
+ );
6236
+ this._store.observe(this.database, body.token);
6237
+ return this._store.get(this.database) ?? body.token;
6238
+ }
5936
6239
  /**
5937
- * Hash-only named-mutation execute. No batch.
6240
+ * Named-mutation execute by unique schema name. No batch.
5938
6241
  */
5939
6242
  get namedMutations() {
5940
6243
  return this._namedMutations;
@@ -6061,13 +6364,15 @@ var AoudaClient = class {
6061
6364
  const primary = new WebSocketTransport(this.baseUrl, this.database, {
6062
6365
  authHandler: this._authHandler,
6063
6366
  enableCompression: this._streamingEnableCompression,
6064
- wireMode: this._streamingWireMode
6367
+ wireMode: this._streamingWireMode,
6368
+ consistencyTokenStore: this._store
6065
6369
  });
6066
6370
  if (this._streamingEnableLongPollFallback) {
6067
6371
  this._wsTransport = new FallbackStreamingTransport(primary, () => {
6068
6372
  return new LongPollTransport(this.baseUrl, this.database, {
6069
6373
  authHandler: this._authHandler,
6070
- waitMs: this._streamingLongPollWaitMs
6374
+ waitMs: this._streamingLongPollWaitMs,
6375
+ consistencyTokenStore: this._store
6071
6376
  });
6072
6377
  });
6073
6378
  } else {
@@ -6365,6 +6670,7 @@ var version = package_default.version;
6365
6670
  MaterializedQueriesApi,
6366
6671
  MaterializedQueryState,
6367
6672
  MaterializedQueryType,
6673
+ MemoryConsistencyTokenStore,
6368
6674
  MetricsAdminApi,
6369
6675
  NamedMutationsApi,
6370
6676
  NamedQueriesApi,
@@ -6380,10 +6686,12 @@ var version = package_default.version;
6380
6686
  applyLocalNetworkAccess,
6381
6687
  coerceColumnarValue,
6382
6688
  columnarToRows,
6689
+ compareOrdinal,
6383
6690
  createAoudaClient,
6384
6691
  createAoudaClusterMcpToolSet,
6385
6692
  installLocalNetworkFetch,
6386
6693
  localNetworkFetch,
6694
+ maxToken,
6387
6695
  resolveTargetAddressSpace,
6388
6696
  version
6389
6697
  });