@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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "@aouda/client",
4
- version: "0.1.14",
4
+ version: "0.1.16",
5
5
  description: "Official TypeScript/JavaScript client library for Aouda",
6
6
  type: "module",
7
7
  main: "./dist/index.cjs",
@@ -111,9 +111,10 @@ var AoudaError = class extends Error {
111
111
  }
112
112
  };
113
113
  var AoudaConnectionError = class extends AoudaError {
114
- constructor(message, cause) {
114
+ constructor(message, cause, rowErrors) {
115
115
  super(message);
116
116
  this.cause = cause;
117
+ this.rowErrors = rowErrors;
117
118
  this.name = "AoudaConnectionError";
118
119
  }
119
120
  };
@@ -133,43 +134,45 @@ var AoudaResponseError = class extends AoudaError {
133
134
  }
134
135
  };
135
136
  var AoudaApiError = class extends AoudaError {
136
- constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
137
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
137
138
  super(message);
138
139
  this.code = code;
139
140
  this.statusCode = statusCode;
140
141
  this.details = details;
141
142
  this.requestId = requestId;
142
143
  this.retryAfterSeconds = retryAfterSeconds;
144
+ this.token = token;
145
+ this.rowErrors = rowErrors;
143
146
  this.name = "AoudaApiError";
144
147
  }
145
148
  };
146
149
  var AoudaNotFoundError = class extends AoudaApiError {
147
- constructor(message, code, statusCode, details, requestId) {
148
- super(message, code, statusCode, details, requestId);
150
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
151
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
149
152
  this.name = "AoudaNotFoundError";
150
153
  }
151
154
  };
152
155
  var AoudaConflictError = class extends AoudaApiError {
153
- constructor(message, code, statusCode, details, requestId) {
154
- super(message, code, statusCode, details, requestId);
156
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
157
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
155
158
  this.name = "AoudaConflictError";
156
159
  }
157
160
  };
158
161
  var AoudaValidationError = class extends AoudaApiError {
159
- constructor(message, code, statusCode, details, requestId) {
160
- super(message, code, statusCode, details, requestId);
162
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
163
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
161
164
  this.name = "AoudaValidationError";
162
165
  }
163
166
  };
164
167
  var AoudaServerError = class extends AoudaApiError {
165
- constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
166
- super(message, code, statusCode, details, requestId, retryAfterSeconds);
168
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
169
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
167
170
  this.name = "AoudaServerError";
168
171
  }
169
172
  };
170
173
  var AoudaAuthenticationError = class extends AoudaApiError {
171
- constructor(message, code, statusCode, details, requestId) {
172
- super(message, code, statusCode, details, requestId);
174
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
175
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
173
176
  this.name = "AoudaAuthenticationError";
174
177
  }
175
178
  };
@@ -981,7 +984,7 @@ function buildHandle(jobId, commitResp, wasResumed, transport, database, signal)
981
984
  rowsDurablyCommitted: commitResp.rowsLoaded,
982
985
  segmentsCreated: commitResp.segmentsCreated,
983
986
  committedAtUtc: commitResp.committedAtUtc,
984
- walPosition: commitResp.walPosition,
987
+ token: commitResp.token,
985
988
  writeConcernSatisfied: commitResp.writeConcernAchieved ?? "acknowledged",
986
989
  writeConcernTimedOut: commitResp.writeConcernTimedOut,
987
990
  wasResumed,
@@ -1107,6 +1110,7 @@ var PROTOCOL_VERSION_HEADER = "X-Aouda-Protocol-Version";
1107
1110
  var PROTOCOL_VERSION = "1";
1108
1111
  var CONTENT_TYPE_JSON2 = "application/json";
1109
1112
  var REQUEST_ID_HEADER2 = "X-Request-Id";
1113
+ var TOKEN_HEADER = "X-Aouda-Token";
1110
1114
  var ERROR_CODE_MAP = {
1111
1115
  TABLE_NOT_FOUND: AoudaNotFoundError,
1112
1116
  COLUMN_NOT_FOUND: AoudaNotFoundError,
@@ -1121,6 +1125,10 @@ var ERROR_CODE_MAP = {
1121
1125
  INVALID_OPERATOR: AoudaValidationError,
1122
1126
  INVALID_COLUMN: AoudaValidationError,
1123
1127
  INVALID_VALUE: AoudaValidationError,
1128
+ CONSTRAINT_CHECK_VIOLATION: AoudaValidationError,
1129
+ TRANSFORM_DERIVED_READONLY: AoudaValidationError,
1130
+ TRANSFORM_ROUTE_UNMATCHED: AoudaValidationError,
1131
+ TRANSFORM_ROUTE_AMBIGUOUS: AoudaValidationError,
1124
1132
  UNSUPPORTED_VERSION: AoudaValidationError,
1125
1133
  MALFORMED_REQUEST: AoudaValidationError,
1126
1134
  INTERNAL_ERROR: AoudaServerError,
@@ -1146,7 +1154,12 @@ var ERROR_CODE_MAP = {
1146
1154
  AUTH_IDENTITY_INVALID: AoudaValidationError,
1147
1155
  AUTH_IDENTITY_NOT_FOUND: AoudaValidationError,
1148
1156
  BULK_LOAD_TRANSFORM_INTENT_REQUIRED: AoudaValidationError,
1149
- BULK_LOAD_TRANSFORM_INTENT_CONFLICT: AoudaValidationError
1157
+ BULK_LOAD_TRANSFORM_INTENT_CONFLICT: AoudaValidationError,
1158
+ TOKEN_MALFORMED: AoudaValidationError,
1159
+ TOKEN_FOREIGN_DATABASE: AoudaValidationError,
1160
+ TOKEN_EPOCH_SUPERSEDED: AoudaConflictError,
1161
+ TOKEN_UNSATISFIED: AoudaConflictError,
1162
+ TOKEN_FETCH_PRIMARY: AoudaApiError
1150
1163
  };
1151
1164
  function createComposedAbortController(...signals) {
1152
1165
  const controller = new AbortController();
@@ -1176,6 +1189,9 @@ function parseRetryAfterSeconds(header) {
1176
1189
  if (!Number.isFinite(n) || n < 0) return void 0;
1177
1190
  return n;
1178
1191
  }
1192
+ function nonEmptyRowErrors(rowErrors) {
1193
+ return Array.isArray(rowErrors) && rowErrors.length > 0 ? rowErrors : void 0;
1194
+ }
1179
1195
  function createApiError(statusCode, statusText, body, retryAfterHeader) {
1180
1196
  const message = body.error ?? `${statusCode} ${statusText}`;
1181
1197
  const code = body.code ?? "UNKNOWN";
@@ -1183,7 +1199,16 @@ function createApiError(statusCode, statusText, body, retryAfterHeader) {
1183
1199
  const requestId = body.requestId;
1184
1200
  const retryAfterSeconds = parseRetryAfterSeconds(retryAfterHeader ?? null);
1185
1201
  const Ctor = ERROR_CODE_MAP[code] ?? AoudaApiError;
1186
- return new Ctor(message, code, statusCode, details, requestId, retryAfterSeconds);
1202
+ return new Ctor(
1203
+ message,
1204
+ code,
1205
+ statusCode,
1206
+ details,
1207
+ requestId,
1208
+ retryAfterSeconds,
1209
+ body.token,
1210
+ nonEmptyRowErrors(body.rowErrors)
1211
+ );
1187
1212
  }
1188
1213
  var HttpTransport = class {
1189
1214
  constructor(options) {
@@ -1195,6 +1220,29 @@ var HttpTransport = class {
1195
1220
  ...options.defaultHeaders
1196
1221
  };
1197
1222
  this.abortController = new AbortController();
1223
+ this.database = options.database;
1224
+ this.store = options.consistencyTokenStore;
1225
+ }
1226
+ presentTokenHeaders(path, headers) {
1227
+ if (this.store == null || this.database == null || isAuthPath(path)) {
1228
+ return;
1229
+ }
1230
+ const token = this.store.get(this.database);
1231
+ if (token != null && token.length > 0) {
1232
+ headers[TOKEN_HEADER] = token;
1233
+ }
1234
+ }
1235
+ observeResponse(_path, response, bodyText, errorCode) {
1236
+ if (this.store == null || this.database == null) {
1237
+ return;
1238
+ }
1239
+ if (errorCode === "TOKEN_MALFORMED" || errorCode === "TOKEN_FOREIGN_DATABASE") {
1240
+ return;
1241
+ }
1242
+ const header = response.headers.get(TOKEN_HEADER);
1243
+ const bodyToken = readBodyToken(bodyText);
1244
+ const token = header != null && header.trim().length > 0 ? header : bodyToken;
1245
+ this.store.observe(this.database, token);
1198
1246
  }
1199
1247
  /**
1200
1248
  * Abort all in-flight and future requests (e.g. on client disconnect).
@@ -1232,6 +1280,7 @@ var HttpTransport = class {
1232
1280
  ...this.defaultHeaders,
1233
1281
  ...config.headers
1234
1282
  };
1283
+ this.presentTokenHeaders(config.path, headers);
1235
1284
  if (config.body !== void 0 && config.body !== null) {
1236
1285
  headers["Content-Type"] = CONTENT_TYPE_JSON2;
1237
1286
  }
@@ -1251,6 +1300,7 @@ var HttpTransport = class {
1251
1300
  const text = await response.text();
1252
1301
  if (config.allowStatuses?.includes(response.status) && text) {
1253
1302
  try {
1303
+ this.observeResponse(config.path, response, text);
1254
1304
  return JSON.parse(text);
1255
1305
  } catch {
1256
1306
  }
@@ -1264,15 +1314,20 @@ var HttpTransport = class {
1264
1314
  }
1265
1315
  if (response.status === 401) {
1266
1316
  const code = errorBody?.code ?? "AUTH_TOKEN_MISSING";
1317
+ this.observeResponse(config.path, response, text, code);
1267
1318
  throw new AoudaAuthenticationError(
1268
1319
  errorBody?.error ?? "Unauthorized",
1269
1320
  code,
1270
1321
  401,
1271
1322
  errorBody?.details,
1272
- errorBody?.requestId
1323
+ errorBody?.requestId,
1324
+ void 0,
1325
+ errorBody?.token,
1326
+ nonEmptyRowErrors(errorBody?.rowErrors)
1273
1327
  );
1274
1328
  }
1275
1329
  if (errorBody?.code != null) {
1330
+ this.observeResponse(config.path, response, text, errorBody.code);
1276
1331
  throw createApiError(
1277
1332
  response.status,
1278
1333
  response.statusText,
@@ -1280,6 +1335,7 @@ var HttpTransport = class {
1280
1335
  response.headers.get("Retry-After")
1281
1336
  );
1282
1337
  }
1338
+ this.observeResponse(config.path, response, text);
1283
1339
  throw new AoudaResponseError(
1284
1340
  errorBody?.error ?? `${response.status} ${response.statusText}`,
1285
1341
  response.status,
@@ -1287,6 +1343,7 @@ var HttpTransport = class {
1287
1343
  );
1288
1344
  }
1289
1345
  const responseText = await response.text();
1346
+ this.observeResponse(config.path, response, responseText);
1290
1347
  if (config.rawText) {
1291
1348
  return responseText;
1292
1349
  }
@@ -1347,6 +1404,7 @@ var HttpTransport = class {
1347
1404
  ...this.defaultHeaders,
1348
1405
  ...config.headers
1349
1406
  };
1407
+ this.presentTokenHeaders(config.path, headers);
1350
1408
  if (config.rawBodyStr === void 0 && config.body !== void 0 && config.body !== null) {
1351
1409
  headers["Content-Type"] = CONTENT_TYPE_JSON2;
1352
1410
  }
@@ -1363,6 +1421,7 @@ var HttpTransport = class {
1363
1421
  try {
1364
1422
  const response = await localNetworkFetch(url, init);
1365
1423
  if (response.ok || config.allowStatuses?.includes(response.status)) {
1424
+ this.observeResponse(config.path, response, void 0);
1366
1425
  return response;
1367
1426
  }
1368
1427
  const text = await response.text();
@@ -1375,15 +1434,20 @@ var HttpTransport = class {
1375
1434
  }
1376
1435
  if (response.status === 401) {
1377
1436
  const code = errorBody?.code ?? "AUTH_TOKEN_MISSING";
1437
+ this.observeResponse(config.path, response, text, code);
1378
1438
  throw new AoudaAuthenticationError(
1379
1439
  errorBody?.error ?? "Unauthorized",
1380
1440
  code,
1381
1441
  401,
1382
1442
  errorBody?.details,
1383
- errorBody?.requestId
1443
+ errorBody?.requestId,
1444
+ void 0,
1445
+ errorBody?.token,
1446
+ nonEmptyRowErrors(errorBody?.rowErrors)
1384
1447
  );
1385
1448
  }
1386
1449
  if (errorBody?.code != null) {
1450
+ this.observeResponse(config.path, response, text, errorBody.code);
1387
1451
  throw createApiError(
1388
1452
  response.status,
1389
1453
  response.statusText,
@@ -1391,6 +1455,7 @@ var HttpTransport = class {
1391
1455
  response.headers.get("Retry-After")
1392
1456
  );
1393
1457
  }
1458
+ this.observeResponse(config.path, response, text);
1394
1459
  throw new AoudaResponseError(
1395
1460
  errorBody?.error ?? `${response.status} ${response.statusText}`,
1396
1461
  response.status,
@@ -1545,6 +1610,47 @@ var HttpTransport = class {
1545
1610
  });
1546
1611
  }
1547
1612
  };
1613
+ function isAuthPath(path) {
1614
+ let p = path;
1615
+ const q = p.indexOf("?");
1616
+ if (q >= 0) {
1617
+ p = p.slice(0, q);
1618
+ }
1619
+ if (/^https?:\/\//i.test(p)) {
1620
+ try {
1621
+ p = new URL(p).pathname;
1622
+ } catch {
1623
+ }
1624
+ }
1625
+ const lower = p.toLowerCase();
1626
+ if (lower === "/api/auth" || lower.startsWith("/api/auth/")) {
1627
+ return true;
1628
+ }
1629
+ const prefix = "/api/databases/";
1630
+ if (!lower.startsWith(prefix)) {
1631
+ return false;
1632
+ }
1633
+ const rest = lower.slice(prefix.length);
1634
+ const slash = rest.indexOf("/");
1635
+ if (slash < 0) {
1636
+ return false;
1637
+ }
1638
+ const afterDb = rest.slice(slash + 1);
1639
+ return afterDb === "auth" || afterDb.startsWith("auth/");
1640
+ }
1641
+ function readBodyToken(bodyText) {
1642
+ if (bodyText == null || bodyText.trim().length === 0) {
1643
+ return void 0;
1644
+ }
1645
+ try {
1646
+ const parsed = JSON.parse(bodyText);
1647
+ if (parsed !== null && typeof parsed === "object" && "token" in parsed && typeof parsed.token === "string") {
1648
+ return parsed.token;
1649
+ }
1650
+ } catch {
1651
+ }
1652
+ return void 0;
1653
+ }
1548
1654
 
1549
1655
  // src/resilience/retry.ts
1550
1656
  var DEFAULT_MAX_RETRIES = 3;
@@ -1977,6 +2083,48 @@ function databasePath2(db) {
1977
2083
  return `/api/databases/${encodeURIComponent(db)}`;
1978
2084
  }
1979
2085
 
2086
+ // src/consistency-token-store.ts
2087
+ var MemoryConsistencyTokenStore = class {
2088
+ constructor() {
2089
+ this.tokens = /* @__PURE__ */ new Map();
2090
+ }
2091
+ get(database) {
2092
+ if (typeof database !== "string" || database.trim().length === 0) {
2093
+ throw new Error("database must be a non-empty string");
2094
+ }
2095
+ return this.tokens.get(normalizeKey(database));
2096
+ }
2097
+ observe(database, token) {
2098
+ if (typeof database !== "string" || database.trim().length === 0) {
2099
+ throw new Error("database must be a non-empty string");
2100
+ }
2101
+ if (token == null || token.trim().length === 0) {
2102
+ return;
2103
+ }
2104
+ const key = normalizeKey(database);
2105
+ const stored = this.tokens.get(key);
2106
+ if (stored === void 0 || compareOrdinal(token, stored) > 0) {
2107
+ this.tokens.set(key, token);
2108
+ }
2109
+ }
2110
+ };
2111
+ function compareOrdinal(left, right) {
2112
+ if (left === right) return 0;
2113
+ return left < right ? -1 : 1;
2114
+ }
2115
+ function maxToken(left, right) {
2116
+ if (left == null || left.length === 0) {
2117
+ return right == null || right.length === 0 ? void 0 : right;
2118
+ }
2119
+ if (right == null || right.length === 0) {
2120
+ return left;
2121
+ }
2122
+ return compareOrdinal(left, right) >= 0 ? left : right;
2123
+ }
2124
+ function normalizeKey(database) {
2125
+ return database.toLowerCase();
2126
+ }
2127
+
1980
2128
  // src/streaming/subscription.ts
1981
2129
  var AsyncEventQueue = class {
1982
2130
  constructor() {
@@ -2033,7 +2181,7 @@ var AsyncEventQueue = class {
2033
2181
  }
2034
2182
  };
2035
2183
  var TableSubscription = class {
2036
- constructor(transport, identity, options = {}, onWarnings) {
2184
+ constructor(transport, identity, options = {}, onWarnings, store, database) {
2037
2185
  this._queue = new AsyncEventQueue();
2038
2186
  this._active = true;
2039
2187
  this._started = false;
@@ -2049,6 +2197,11 @@ var TableSubscription = class {
2049
2197
  this._onError = options.onError;
2050
2198
  this._onWarnings = onWarnings;
2051
2199
  this._conflate = options.conflate;
2200
+ this._atLeast = options.atLeast;
2201
+ this._waitMs = options.waitMs;
2202
+ this._onExceeded = options.onExceeded;
2203
+ this._store = store;
2204
+ this._database = database;
2052
2205
  this._reconnectHandlerKey = `${this.id}::reconnect`;
2053
2206
  }
2054
2207
  get lastVersion() {
@@ -2113,7 +2266,7 @@ var TableSubscription = class {
2113
2266
  id: this.id
2114
2267
  };
2115
2268
  if (this._identity.kind === "named") {
2116
- message.hash = this._identity.hash;
2269
+ message.name = this._identity.name;
2117
2270
  if (this._identity.args !== void 0) {
2118
2271
  message.args = this._identity.args;
2119
2272
  }
@@ -2132,8 +2285,30 @@ var TableSubscription = class {
2132
2285
  if (this._conflate !== void 0) {
2133
2286
  message.conflate = this._conflate;
2134
2287
  }
2288
+ const pin = this._resolvePin();
2289
+ if (pin !== void 0) {
2290
+ message.at_least = pin;
2291
+ }
2292
+ if (this._waitMs !== void 0) {
2293
+ message.wait_ms = this._waitMs;
2294
+ }
2295
+ if (this._onExceeded !== void 0) {
2296
+ message.on_exceeded = this._onExceeded;
2297
+ }
2135
2298
  await this._transport.send(message);
2136
2299
  }
2300
+ _resolvePin() {
2301
+ if (this._atLeast !== void 0 && this._store != null && this._database != null) {
2302
+ this._store.observe(this._database, this._atLeast);
2303
+ }
2304
+ const stored = this._store != null && this._database != null ? this._store.get(this._database) : void 0;
2305
+ return maxToken(this._atLeast, stored);
2306
+ }
2307
+ _observeToken(token) {
2308
+ if (token !== void 0 && this._store != null && this._database != null) {
2309
+ this._store.observe(this._database, token);
2310
+ }
2311
+ }
2137
2312
  _handleMessage(message) {
2138
2313
  if (!this._active) {
2139
2314
  return;
@@ -2160,6 +2335,7 @@ var TableSubscription = class {
2160
2335
  }
2161
2336
  _handleSnapshotPage(message) {
2162
2337
  this._pendingSnapshotRows.push(...message.rows);
2338
+ this._observeToken(message.token);
2163
2339
  }
2164
2340
  _handleSnapshotComplete(message) {
2165
2341
  this._lastVersion = message.version;
@@ -2177,6 +2353,10 @@ var TableSubscription = class {
2177
2353
  if (message.total_matches !== void 0) {
2178
2354
  snapshot.totalMatches = message.total_matches;
2179
2355
  }
2356
+ if (message.token !== void 0) {
2357
+ snapshot.token = message.token;
2358
+ this._observeToken(message.token);
2359
+ }
2180
2360
  this._queue.push(snapshot);
2181
2361
  }
2182
2362
  async _handleGap(message) {
@@ -2202,6 +2382,10 @@ var TableSubscription = class {
2202
2382
  if (message.values_skipped !== void 0) {
2203
2383
  event.values_skipped = message.values_skipped;
2204
2384
  }
2385
+ if (message.token !== void 0) {
2386
+ event.token = message.token;
2387
+ this._observeToken(message.token);
2388
+ }
2205
2389
  this._onChange?.(event);
2206
2390
  this._queue.push(event);
2207
2391
  }
@@ -2349,8 +2533,11 @@ var TableWriteStream = class {
2349
2533
  resolve?.();
2350
2534
  }
2351
2535
  _handleServerError(message) {
2536
+ const rowErrors = Array.isArray(message.errors) && message.errors.length > 0 ? message.errors : void 0;
2352
2537
  const error = new AoudaConnectionError(
2353
- `Write stream error (${message.code}): ${message.message}`
2538
+ `Write stream error (${message.code}): ${message.message}`,
2539
+ void 0,
2540
+ rowErrors
2354
2541
  );
2355
2542
  const openReject = this._openReject;
2356
2543
  this._openResolve = null;
@@ -2546,11 +2733,12 @@ var TableQuery = class _TableQuery {
2546
2733
  * @param state - Optional initial state (used for immutable chaining).
2547
2734
  * @internal Use `client.table()` to create queries.
2548
2735
  */
2549
- constructor(transport, tableName, database, state, getWebSocketTransport) {
2736
+ constructor(transport, tableName, database, state, getWebSocketTransport, store) {
2550
2737
  this.transport = transport;
2551
2738
  this.tableName = tableName;
2552
2739
  this.database = database;
2553
2740
  this.getWebSocketTransport = getWebSocketTransport;
2741
+ this.store = store;
2554
2742
  this.state = state ?? {
2555
2743
  predicates: [],
2556
2744
  groupClauses: [],
@@ -2566,6 +2754,16 @@ var TableQuery = class _TableQuery {
2566
2754
  isDistinct: false
2567
2755
  };
2568
2756
  }
2757
+ withState(state) {
2758
+ return new _TableQuery(
2759
+ this.transport,
2760
+ this.tableName,
2761
+ this.database,
2762
+ state,
2763
+ this.getWebSocketTransport,
2764
+ this.store
2765
+ );
2766
+ }
2569
2767
  where(column, operator, value) {
2570
2768
  const newPredicates = buildWherePredicates(column, operator, value);
2571
2769
  return new _TableQuery(
@@ -2576,7 +2774,8 @@ var TableQuery = class _TableQuery {
2576
2774
  ...this.state,
2577
2775
  predicates: [...this.state.predicates, ...newPredicates]
2578
2776
  },
2579
- this.getWebSocketTransport
2777
+ this.getWebSocketTransport,
2778
+ this.store
2580
2779
  );
2581
2780
  }
2582
2781
  /**
@@ -2595,7 +2794,7 @@ var TableQuery = class _TableQuery {
2595
2794
  return new _TableQuery(this.transport, this.tableName, this.database, {
2596
2795
  ...this.state,
2597
2796
  groupClauses: [...this.state.groupClauses, sub]
2598
- }, this.getWebSocketTransport);
2797
+ }, this.getWebSocketTransport, this.store);
2599
2798
  }
2600
2799
  /**
2601
2800
  * Sets the primary sort column for the query.
@@ -2620,7 +2819,7 @@ var TableQuery = class _TableQuery {
2620
2819
  return new _TableQuery(this.transport, this.tableName, this.database, {
2621
2820
  ...this.state,
2622
2821
  orderByClauses: [orderByClause]
2623
- }, this.getWebSocketTransport);
2822
+ }, this.getWebSocketTransport, this.store);
2624
2823
  }
2625
2824
  /**
2626
2825
  * Sets the primary sort column to descending order.
@@ -2666,7 +2865,7 @@ var TableQuery = class _TableQuery {
2666
2865
  return new _TableQuery(this.transport, this.tableName, this.database, {
2667
2866
  ...this.state,
2668
2867
  orderByClauses: [...this.state.orderByClauses, orderByClause]
2669
- }, this.getWebSocketTransport);
2868
+ }, this.getWebSocketTransport, this.store);
2670
2869
  }
2671
2870
  /**
2672
2871
  * Sets the maximum number of rows to return.
@@ -2683,7 +2882,7 @@ var TableQuery = class _TableQuery {
2683
2882
  return new _TableQuery(this.transport, this.tableName, this.database, {
2684
2883
  ...this.state,
2685
2884
  limitValue: count
2686
- }, this.getWebSocketTransport);
2885
+ }, this.getWebSocketTransport, this.store);
2687
2886
  }
2688
2887
  /**
2689
2888
  * Sets the number of rows to skip.
@@ -2700,7 +2899,7 @@ var TableQuery = class _TableQuery {
2700
2899
  return new _TableQuery(this.transport, this.tableName, this.database, {
2701
2900
  ...this.state,
2702
2901
  offsetValue: count
2703
- }, this.getWebSocketTransport);
2902
+ }, this.getWebSocketTransport, this.store);
2704
2903
  }
2705
2904
  /**
2706
2905
  * Requests cross-partition access for this query.
@@ -2715,10 +2914,21 @@ var TableQuery = class _TableQuery {
2715
2914
  return new _TableQuery(this.transport, this.tableName, this.database, {
2716
2915
  ...this.state,
2717
2916
  crossPartitionAccess: true
2718
- }, this.getWebSocketTransport);
2917
+ }, this.getWebSocketTransport, this.store);
2719
2918
  }
2720
2919
  /**
2721
- * Restricts the columns returned in the result.
2920
+ * Pin this query at at least this C-1 token. Observes the token into the
2921
+ * client store (I3, sticky) and presents it on execute via `X-Aouda-Token`.
2922
+ */
2923
+ atLeast(token) {
2924
+ if (typeof token !== "string" || token.trim().length === 0) {
2925
+ throw new Error("atLeast() requires a non-empty token");
2926
+ }
2927
+ this.store?.observe(this.database, token);
2928
+ return this.withState({ ...this.state, atLeast: token });
2929
+ }
2930
+ /**
2931
+ * Selects specific columns to return.
2722
2932
  * If not called, all columns are returned.
2723
2933
  *
2724
2934
  * When T is a specific row type, only keys of T are accepted as column names.
@@ -2740,7 +2950,7 @@ var TableQuery = class _TableQuery {
2740
2950
  return new _TableQuery(this.transport, this.tableName, this.database, {
2741
2951
  ...this.state,
2742
2952
  selectColumns: columns.length > 0 ? columns : null
2743
- }, this.getWebSocketTransport);
2953
+ }, this.getWebSocketTransport, this.store);
2744
2954
  }
2745
2955
  /**
2746
2956
  * Return only distinct (de-duplicated) rows for the given columns — SQL `SELECT DISTINCT`.
@@ -2770,7 +2980,7 @@ var TableQuery = class _TableQuery {
2770
2980
  ...this.state,
2771
2981
  selectColumns: columns,
2772
2982
  isDistinct: true
2773
- }, this.getWebSocketTransport);
2983
+ }, this.getWebSocketTransport, this.store);
2774
2984
  }
2775
2985
  /**
2776
2986
  * Adds server-side computed columns to the query result.
@@ -2779,6 +2989,12 @@ var TableQuery = class _TableQuery {
2779
2989
  * evaluated per row on the server. Computed columns are appended after any physical-column
2780
2990
  * `select()` projection.
2781
2991
  *
2992
+ * Result types are inferred by the server where the expression permits
2993
+ * (e.g. Int32 → `number`). Uninferable expressions use wire `"Unknown"` /
2994
+ * codegen `unknown`. Computed columns are always nullable. Named-query
2995
+ * `*Row` properties pick this up when regenerated against a post-S08 server.
2996
+ * See aouda-docs/guides/browser-tier-read-limits.md#selectexpr-result-types
2997
+ *
2782
2998
  * @param projections - One or more `{ alias, expr }` pairs.
2783
2999
  * @returns A new TableQuery with computed columns set.
2784
3000
  *
@@ -2799,7 +3015,7 @@ var TableQuery = class _TableQuery {
2799
3015
  return new _TableQuery(this.transport, this.tableName, this.database, {
2800
3016
  ...this.state,
2801
3017
  selectExprs: projections
2802
- }, this.getWebSocketTransport);
3018
+ }, this.getWebSocketTransport, this.store);
2803
3019
  }
2804
3020
  join(rightTable, leftColumnOrColumns, rightColumnOrColumns) {
2805
3021
  return this.addJoinClause(
@@ -2857,7 +3073,8 @@ var TableQuery = class _TableQuery {
2857
3073
  }
2858
3074
  ]
2859
3075
  },
2860
- this.getWebSocketTransport
3076
+ this.getWebSocketTransport,
3077
+ this.store
2861
3078
  );
2862
3079
  }
2863
3080
  /**
@@ -2893,7 +3110,8 @@ var TableQuery = class _TableQuery {
2893
3110
  ...this.state,
2894
3111
  groupByColumns: [...columns]
2895
3112
  },
2896
- this.getWebSocketTransport
3113
+ this.getWebSocketTransport,
3114
+ this.store
2897
3115
  );
2898
3116
  }
2899
3117
  /**
@@ -2927,8 +3145,11 @@ var TableQuery = class _TableQuery {
2927
3145
  onSnapshot: options.onSnapshot,
2928
3146
  onChange: options.onChange,
2929
3147
  onError: options.onError,
2930
- conflate: options.conflate
2931
- });
3148
+ conflate: options.conflate,
3149
+ atLeast: options.atLeast ?? this.state.atLeast,
3150
+ waitMs: options.waitMs,
3151
+ onExceeded: options.onExceeded
3152
+ }, void 0, this.store, this.database);
2932
3153
  subscription.start();
2933
3154
  return subscription;
2934
3155
  }
@@ -3043,7 +3264,8 @@ var TableQuery = class _TableQuery {
3043
3264
  ...this.state,
3044
3265
  joinClauses: [...this.state.joinClauses, joinClause]
3045
3266
  },
3046
- this.getWebSocketTransport
3267
+ this.getWebSocketTransport,
3268
+ this.store
3047
3269
  );
3048
3270
  }
3049
3271
  addAggregate(op, column) {
@@ -3064,7 +3286,8 @@ var TableQuery = class _TableQuery {
3064
3286
  }
3065
3287
  ]
3066
3288
  },
3067
- this.getWebSocketTransport
3289
+ this.getWebSocketTransport,
3290
+ this.store
3068
3291
  );
3069
3292
  }
3070
3293
  requireWebSocketTransport() {
@@ -3094,17 +3317,23 @@ var TableQuery = class _TableQuery {
3094
3317
  * ```
3095
3318
  */
3096
3319
  async execute() {
3320
+ if (this.state.atLeast) {
3321
+ this.store?.observe(this.database, this.state.atLeast);
3322
+ }
3097
3323
  const request = this.buildRequest();
3098
3324
  const path = `${databasePath2(this.database)}/query`;
3099
3325
  const response = await this.transport.post(path, request);
3100
3326
  const rows = columnarToRows(response);
3101
3327
  const stats = response.stats;
3102
- return { rows, stats };
3328
+ return { rows, stats, token: response.token };
3103
3329
  }
3104
3330
  /**
3105
3331
  * Executes the query and returns the raw columnar JSON payload (no row-object conversion).
3106
3332
  */
3107
3333
  async toColumnar() {
3334
+ if (this.state.atLeast) {
3335
+ this.store?.observe(this.database, this.state.atLeast);
3336
+ }
3108
3337
  const request = this.buildRequest();
3109
3338
  const path = `${databasePath2(this.database)}/query`;
3110
3339
  return this.transport.post(path, request);
@@ -3134,8 +3363,12 @@ var TableQuery = class _TableQuery {
3134
3363
  limitValue: 0,
3135
3364
  selectColumns: []
3136
3365
  },
3137
- this.getWebSocketTransport
3366
+ this.getWebSocketTransport,
3367
+ this.store
3138
3368
  );
3369
+ if (this.state.atLeast) {
3370
+ this.store?.observe(this.database, this.state.atLeast);
3371
+ }
3139
3372
  const request = countQuery.buildRequest();
3140
3373
  const path = `${databasePath2(this.database)}/query`;
3141
3374
  const response = await this.transport.post(path, request);
@@ -3182,7 +3415,8 @@ var TableQuery = class _TableQuery {
3182
3415
  const response = await this.transport.post(path, body);
3183
3416
  const result = {
3184
3417
  rowsInserted: response.rowsInserted,
3185
- executionMs: response.executionMs
3418
+ executionMs: response.executionMs,
3419
+ token: response.token
3186
3420
  };
3187
3421
  if (response.generatedValues !== void 0) {
3188
3422
  result.generatedValues = response.generatedValues;
@@ -3232,7 +3466,8 @@ var TableQuery = class _TableQuery {
3232
3466
  const response = await this.transport.post(path, body);
3233
3467
  const result = {
3234
3468
  rowsInserted: response.rowsInserted,
3235
- executionMs: response.executionMs
3469
+ executionMs: response.executionMs,
3470
+ token: response.token
3236
3471
  };
3237
3472
  if (response.generatedValues !== void 0) {
3238
3473
  result.generatedValues = response.generatedValues;
@@ -3284,6 +3519,7 @@ var TableQuery = class _TableQuery {
3284
3519
  return {
3285
3520
  rowsAffected: response.rowsUpdated,
3286
3521
  executionMs: response.executionMs,
3522
+ token: response.token,
3287
3523
  ...response.rows ? { rows: columnarToRows(response.rows) } : {},
3288
3524
  ...response.rowsTruncated ? { rowsTruncated: true } : {}
3289
3525
  };
@@ -3334,6 +3570,7 @@ var TableQuery = class _TableQuery {
3334
3570
  rowsAffected: response.rowsDeleted,
3335
3571
  executionMs: response.executionMs,
3336
3572
  hasMore: response.hasMore,
3573
+ token: response.token,
3337
3574
  ...response.rows ? { rows: columnarToRows(response.rows) } : {},
3338
3575
  ...response.rowsTruncated ? { rowsTruncated: true } : {}
3339
3576
  };
@@ -3353,7 +3590,8 @@ var TableQuery = class _TableQuery {
3353
3590
  return {
3354
3591
  rowsAffected: response.rowsDeleted,
3355
3592
  executionMs: response.executionMs,
3356
- hasMore: false
3593
+ hasMore: false,
3594
+ token: response.token
3357
3595
  };
3358
3596
  }
3359
3597
  /**
@@ -3366,7 +3604,14 @@ var TableQuery = class _TableQuery {
3366
3604
  throw new Error("batch() requires a non-empty operations array");
3367
3605
  }
3368
3606
  const wireOperations = operations.map((op, index) => {
3369
- const base = new _TableQuery(this.transport, this.tableName, this.database);
3607
+ const base = new _TableQuery(
3608
+ this.transport,
3609
+ this.tableName,
3610
+ this.database,
3611
+ void 0,
3612
+ this.getWebSocketTransport,
3613
+ this.store
3614
+ );
3370
3615
  const scoped = op.where(base);
3371
3616
  const where = scoped.buildWhereClause();
3372
3617
  if (!where) {
@@ -4734,12 +4979,12 @@ function raiseDeprecationWarnings(sink, warnings) {
4734
4979
  continue;
4735
4980
  }
4736
4981
  const sunset = warning.sunsetAt != null ? ` sunsetAt=${warning.sunsetAt}` : "";
4737
- const hash = warning.hash != null && warning.hash.length > 0 ? ` hash=${warning.hash}` : "";
4982
+ const name = warning.name != null && warning.name.length > 0 ? ` name=${warning.name}` : "";
4738
4983
  sink({
4739
4984
  code: warning.code,
4740
- hash: warning.hash,
4985
+ name: warning.name,
4741
4986
  sunsetAt: warning.sunsetAt,
4742
- message: `${warning.code}:${hash}${sunset}`.trim()
4987
+ message: `${warning.code}:${name}${sunset}`.trim()
4743
4988
  });
4744
4989
  }
4745
4990
  }
@@ -4752,24 +4997,28 @@ function emptyStats() {
4752
4997
  };
4753
4998
  }
4754
4999
  var NamedQueriesApi = class {
4755
- constructor(transport, database, onWarning, getStreamingTransport) {
5000
+ constructor(transport, database, onWarning, getStreamingTransport, store) {
4756
5001
  this.transport = transport;
4757
5002
  this.database = database;
4758
5003
  this.onWarning = onWarning;
4759
5004
  this.getStreamingTransport = getStreamingTransport;
5005
+ this.store = store;
4760
5006
  }
4761
- async execute(hash, args, options) {
4762
- if (typeof hash !== "string" || hash.trim().length === 0) {
4763
- throw new Error("Named query hash must be a non-empty string");
5007
+ async execute(name, args, options) {
5008
+ if (typeof name !== "string" || name.trim().length === 0) {
5009
+ throw new Error("Named query name must be a non-empty string");
4764
5010
  }
4765
5011
  const prefix = databasePath2(this.database);
4766
- const path = `${prefix}/named-queries/${encodeURIComponent(hash)}/query?format=columnar`;
5012
+ const path = `${prefix}/named-queries/${encodeURIComponent(name)}/query?format=columnar`;
4767
5013
  const body = {
4768
5014
  args: args ?? {}
4769
5015
  };
4770
5016
  if (options?.orderByIndex !== void 0) {
4771
5017
  body.orderByIndex = options.orderByIndex;
4772
5018
  }
5019
+ if (options?.atLeast !== void 0) {
5020
+ this.store?.observe(this.database, options.atLeast);
5021
+ }
4773
5022
  const response = await this.transport.post(
4774
5023
  path,
4775
5024
  body,
@@ -4781,7 +5030,8 @@ var NamedQueriesApi = class {
4781
5030
  rows,
4782
5031
  stats: response.stats,
4783
5032
  warnings: response.warnings,
4784
- totalMatches: response.totalMatches
5033
+ totalMatches: response.totalMatches,
5034
+ token: response.token
4785
5035
  };
4786
5036
  }
4787
5037
  async batch(items, options) {
@@ -4799,6 +5049,9 @@ var NamedQueriesApi = class {
4799
5049
  400
4800
5050
  );
4801
5051
  }
5052
+ if (options?.atLeast !== void 0) {
5053
+ this.store?.observe(this.database, options.atLeast);
5054
+ }
4802
5055
  const prefix = databasePath2(this.database);
4803
5056
  const path = `${prefix}/named-queries/batch?format=columnar`;
4804
5057
  const envelope = await this.transport.post(
@@ -4821,34 +5074,41 @@ var NamedQueriesApi = class {
4821
5074
  rowCount: slot.rowCount ?? 0,
4822
5075
  stats: slot.stats ?? emptyStats(),
4823
5076
  warnings: slot.warnings,
4824
- totalMatches: slot.totalMatches
5077
+ totalMatches: slot.totalMatches,
5078
+ token: slot.token
4825
5079
  };
4826
5080
  const result = {
4827
5081
  rows: columnarToRows(columnar),
4828
5082
  stats: columnar.stats,
4829
5083
  warnings: slot.warnings,
4830
- totalMatches: slot.totalMatches
5084
+ totalMatches: slot.totalMatches,
5085
+ token: slot.token
4831
5086
  };
4832
5087
  raiseDeprecationWarnings(this.onWarning, slot.warnings);
4833
5088
  return { isError: false, result };
4834
5089
  });
4835
5090
  }
4836
- subscribe(hash, args, options = {}) {
4837
- if (typeof hash !== "string" || hash.trim().length === 0) {
4838
- throw new Error("Named query hash must be a non-empty string");
5091
+ subscribe(name, args, options = {}) {
5092
+ if (typeof name !== "string" || name.trim().length === 0) {
5093
+ throw new Error("Named query name must be a non-empty string");
4839
5094
  }
4840
5095
  const subscription = new TableSubscription(
4841
5096
  this.getStreamingTransport(),
4842
- { kind: "named", hash, args, orderByIndex: options.orderByIndex },
5097
+ { kind: "named", name, args, orderByIndex: options.orderByIndex },
4843
5098
  {
4844
5099
  onSnapshot: options.onSnapshot,
4845
5100
  onChange: options.onChange,
4846
5101
  onError: options.onError,
4847
- conflate: options.conflate
5102
+ conflate: options.conflate,
5103
+ atLeast: options.atLeast,
5104
+ waitMs: options.waitMs,
5105
+ onExceeded: options.onExceeded
4848
5106
  },
4849
5107
  (warnings) => {
4850
5108
  raiseDeprecationWarnings(this.onWarning, warnings);
4851
- }
5109
+ },
5110
+ this.store,
5111
+ this.database
4852
5112
  );
4853
5113
  subscription.start();
4854
5114
  return subscription;
@@ -4860,12 +5120,12 @@ var NamedMutationsApi = class {
4860
5120
  this.database = database;
4861
5121
  this.onWarning = onWarning;
4862
5122
  }
4863
- async execute(hash, args) {
4864
- if (typeof hash !== "string" || hash.trim().length === 0) {
4865
- throw new Error("Named mutation hash must be a non-empty string");
5123
+ async execute(name, args) {
5124
+ if (typeof name !== "string" || name.trim().length === 0) {
5125
+ throw new Error("Named mutation name must be a non-empty string");
4866
5126
  }
4867
5127
  const prefix = databasePath2(this.database);
4868
- const path = `${prefix}/named-mutations/${encodeURIComponent(hash)}/execute`;
5128
+ const path = `${prefix}/named-mutations/${encodeURIComponent(name)}/execute`;
4869
5129
  const wire = await this.transport.post(path, {
4870
5130
  args: args ?? {}
4871
5131
  });
@@ -4926,6 +5186,7 @@ var WebSocketTransport = class {
4926
5186
  // Serialized-send queue: each send appends to this tail.
4927
5187
  this._sendTail = Promise.resolve();
4928
5188
  this._lastVersion = 0;
5189
+ this._lastToken = null;
4929
5190
  this._reconnectAttempt = 0;
4930
5191
  this._disposed = false;
4931
5192
  this._missedPings = 0;
@@ -4943,6 +5204,7 @@ var WebSocketTransport = class {
4943
5204
  this._pingIntervalMs = options.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
4944
5205
  this._maxMissedHeartbeats = options.maxMissedHeartbeats ?? DEFAULT_MAX_MISSED_HEARTBEATS;
4945
5206
  this._enableCompression = options.enableCompression ?? true;
5207
+ this._store = options.consistencyTokenStore;
4946
5208
  this._wireMode = options.wireMode ?? "json";
4947
5209
  this._authHandler?.setOnAccessTokenRefreshed((token) => {
4948
5210
  if (this._handshakeComplete && this._ws?.readyState === WS_READY_STATE_OPEN) {
@@ -4951,10 +5213,14 @@ var WebSocketTransport = class {
4951
5213
  });
4952
5214
  }
4953
5215
  // ─── Public API ────────────────────────────────────────────────────────────
4954
- /** Latest version number from the most recent server `heartbeat` message. */
5216
+ /** Latest change-event sequence from the most recent server `heartbeat` message. */
4955
5217
  get lastVersion() {
4956
5218
  return this._lastVersion;
4957
5219
  }
5220
+ /** Consistency token from the most recent server `heartbeat` (ADR 0042 D-13). */
5221
+ get lastToken() {
5222
+ return this._lastToken;
5223
+ }
4958
5224
  /**
4959
5225
  * Registers a handler for incoming server messages with the given channel id.
4960
5226
  * Use `"__global__"` to receive broadcast messages that carry no `id`.
@@ -5142,6 +5408,10 @@ var WebSocketTransport = class {
5142
5408
  }
5143
5409
  case "heartbeat":
5144
5410
  this._lastVersion = msg.version;
5411
+ this._lastToken = msg.token ?? null;
5412
+ if (this._lastToken != null && this._lastToken.length > 0) {
5413
+ this._store?.observe(this._database, this._lastToken);
5414
+ }
5145
5415
  break;
5146
5416
  case "pong":
5147
5417
  this._missedPings = 0;
@@ -5307,16 +5577,21 @@ var LongPollTransport = class {
5307
5577
  this._connected = false;
5308
5578
  this._pollTask = null;
5309
5579
  this._lastVersion = 0;
5580
+ this._lastToken = null;
5310
5581
  this._serverUrl = serverUrl;
5311
5582
  this._database = database;
5312
5583
  this._authHandler = options.authHandler;
5313
5584
  this._onReconnected = options.onReconnected;
5314
5585
  this._waitMs = Math.max(1, options.waitMs ?? DEFAULT_WAIT_MS);
5315
5586
  this._fetch = options.fetchImpl ?? localNetworkFetch;
5587
+ this._store = options.consistencyTokenStore;
5316
5588
  }
5317
5589
  get lastVersion() {
5318
5590
  return this._lastVersion;
5319
5591
  }
5592
+ get lastToken() {
5593
+ return this._lastToken;
5594
+ }
5320
5595
  registerHandler(id, handler) {
5321
5596
  this._handlers.set(id, handler);
5322
5597
  }
@@ -5439,6 +5714,10 @@ var LongPollTransport = class {
5439
5714
  for (const msg of payload.messages) {
5440
5715
  if (msg.type === "heartbeat") {
5441
5716
  this._lastVersion = msg.version;
5717
+ this._lastToken = msg.token ?? null;
5718
+ if (this._lastToken != null && this._lastToken.length > 0) {
5719
+ this._store?.observe(this._database, this._lastToken);
5720
+ }
5442
5721
  }
5443
5722
  const id = "id" in msg ? msg.id : void 0;
5444
5723
  if (id !== void 0 && id !== null) {
@@ -5518,6 +5797,9 @@ var FallbackStreamingTransport = class {
5518
5797
  get lastVersion() {
5519
5798
  return this._active.lastVersion;
5520
5799
  }
5800
+ get lastToken() {
5801
+ return this._active.lastToken;
5802
+ }
5521
5803
  registerHandler(id, handler) {
5522
5804
  this._handlers.set(id, handler);
5523
5805
  this._active.registerHandler(id, handler);
@@ -5597,6 +5879,7 @@ var AoudaClient = class {
5597
5879
  this.baseUrl = normalizeBaseUrl(options.serverUrl);
5598
5880
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
5599
5881
  this.database = options.database.trim();
5882
+ this._store = options.consistencyTokenStore ?? new MemoryConsistencyTokenStore();
5600
5883
  if (!Number.isFinite(this.timeout) || this.timeout <= 0) {
5601
5884
  throw new Error("timeout must be a finite positive number");
5602
5885
  }
@@ -5631,7 +5914,9 @@ var AoudaClient = class {
5631
5914
  }
5632
5915
  const httpTransport = new HttpTransport({
5633
5916
  baseUrl: this.baseUrl,
5634
- timeout: this.timeout
5917
+ timeout: this.timeout,
5918
+ database: this.database,
5919
+ consistencyTokenStore: this._store
5635
5920
  });
5636
5921
  const activeAuth = options.serverAuth ?? options.appAuth;
5637
5922
  if (activeAuth) {
@@ -5691,7 +5976,8 @@ var AoudaClient = class {
5691
5976
  this.transport,
5692
5977
  this.database,
5693
5978
  onNamedArtifactWarning,
5694
- () => this._getOrCreateWebSocketTransport()
5979
+ () => this._getOrCreateWebSocketTransport(),
5980
+ this._store
5695
5981
  );
5696
5982
  this._namedMutations = new NamedMutationsApi(
5697
5983
  this.transport,
@@ -5790,7 +6076,8 @@ var AoudaClient = class {
5790
6076
  name,
5791
6077
  this.database,
5792
6078
  void 0,
5793
- () => this._getOrCreateWebSocketTransport()
6079
+ () => this._getOrCreateWebSocketTransport(),
6080
+ this._store
5794
6081
  );
5795
6082
  }
5796
6083
  /**
@@ -5842,13 +6129,26 @@ var AoudaClient = class {
5842
6129
  return this._materializedQueries;
5843
6130
  }
5844
6131
  /**
5845
- * Hash-only named-query execute and batch. Names are codegen aliases (D-5).
6132
+ * Named-query execute, read-only batch, and subscribe by unique schema name.
5846
6133
  */
5847
6134
  get namedQueries() {
5848
6135
  return this._namedQueries;
5849
6136
  }
6137
+ observeConsistencyToken(token) {
6138
+ this._store.observe(this.database, token);
6139
+ }
6140
+ getObservedConsistencyToken() {
6141
+ return this._store.get(this.database);
6142
+ }
6143
+ async getConsistencyToken() {
6144
+ const body = await this.transport.get(
6145
+ `/api/databases/${encodeURIComponent(this.database)}/token`
6146
+ );
6147
+ this._store.observe(this.database, body.token);
6148
+ return this._store.get(this.database) ?? body.token;
6149
+ }
5850
6150
  /**
5851
- * Hash-only named-mutation execute. No batch.
6151
+ * Named-mutation execute by unique schema name. No batch.
5852
6152
  */
5853
6153
  get namedMutations() {
5854
6154
  return this._namedMutations;
@@ -5975,13 +6275,15 @@ var AoudaClient = class {
5975
6275
  const primary = new WebSocketTransport(this.baseUrl, this.database, {
5976
6276
  authHandler: this._authHandler,
5977
6277
  enableCompression: this._streamingEnableCompression,
5978
- wireMode: this._streamingWireMode
6278
+ wireMode: this._streamingWireMode,
6279
+ consistencyTokenStore: this._store
5979
6280
  });
5980
6281
  if (this._streamingEnableLongPollFallback) {
5981
6282
  this._wsTransport = new FallbackStreamingTransport(primary, () => {
5982
6283
  return new LongPollTransport(this.baseUrl, this.database, {
5983
6284
  authHandler: this._authHandler,
5984
- waitMs: this._streamingLongPollWaitMs
6285
+ waitMs: this._streamingLongPollWaitMs,
6286
+ consistencyTokenStore: this._store
5985
6287
  });
5986
6288
  });
5987
6289
  } else {
@@ -6278,6 +6580,7 @@ export {
6278
6580
  MaterializedQueriesApi,
6279
6581
  MaterializedQueryState,
6280
6582
  MaterializedQueryType,
6583
+ MemoryConsistencyTokenStore,
6281
6584
  MetricsAdminApi,
6282
6585
  NamedMutationsApi,
6283
6586
  NamedQueriesApi,
@@ -6293,10 +6596,12 @@ export {
6293
6596
  applyLocalNetworkAccess,
6294
6597
  coerceColumnarValue,
6295
6598
  columnarToRows,
6599
+ compareOrdinal,
6296
6600
  createAoudaClient,
6297
6601
  createAoudaClusterMcpToolSet,
6298
6602
  installLocalNetworkFetch,
6299
6603
  localNetworkFetch,
6604
+ maxToken,
6300
6605
  resolveTargetAddressSpace,
6301
6606
  version
6302
6607
  };