@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.
@@ -421,7 +421,7 @@ var import_node_url = require("url");
421
421
  // package.json
422
422
  var package_default = {
423
423
  name: "@aouda/client",
424
- version: "0.1.14",
424
+ version: "0.1.16",
425
425
  description: "Official TypeScript/JavaScript client library for Aouda",
426
426
  type: "module",
427
427
  main: "./dist/index.cjs",
@@ -514,9 +514,10 @@ var AoudaError = class extends Error {
514
514
  }
515
515
  };
516
516
  var AoudaConnectionError = class extends AoudaError {
517
- constructor(message, cause) {
517
+ constructor(message, cause, rowErrors) {
518
518
  super(message);
519
519
  this.cause = cause;
520
+ this.rowErrors = rowErrors;
520
521
  this.name = "AoudaConnectionError";
521
522
  }
522
523
  };
@@ -536,43 +537,45 @@ var AoudaResponseError = class extends AoudaError {
536
537
  }
537
538
  };
538
539
  var AoudaApiError = class extends AoudaError {
539
- constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
540
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
540
541
  super(message);
541
542
  this.code = code;
542
543
  this.statusCode = statusCode;
543
544
  this.details = details;
544
545
  this.requestId = requestId;
545
546
  this.retryAfterSeconds = retryAfterSeconds;
547
+ this.token = token;
548
+ this.rowErrors = rowErrors;
546
549
  this.name = "AoudaApiError";
547
550
  }
548
551
  };
549
552
  var AoudaNotFoundError = class extends AoudaApiError {
550
- constructor(message, code, statusCode, details, requestId) {
551
- super(message, code, statusCode, details, requestId);
553
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
554
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
552
555
  this.name = "AoudaNotFoundError";
553
556
  }
554
557
  };
555
558
  var AoudaConflictError = class extends AoudaApiError {
556
- constructor(message, code, statusCode, details, requestId) {
557
- super(message, code, statusCode, details, requestId);
559
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
560
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
558
561
  this.name = "AoudaConflictError";
559
562
  }
560
563
  };
561
564
  var AoudaValidationError = class extends AoudaApiError {
562
- constructor(message, code, statusCode, details, requestId) {
563
- super(message, code, statusCode, details, requestId);
565
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
566
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
564
567
  this.name = "AoudaValidationError";
565
568
  }
566
569
  };
567
570
  var AoudaServerError = class extends AoudaApiError {
568
- constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
569
- super(message, code, statusCode, details, requestId, retryAfterSeconds);
571
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
572
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
570
573
  this.name = "AoudaServerError";
571
574
  }
572
575
  };
573
576
  var AoudaAuthenticationError = class extends AoudaApiError {
574
- constructor(message, code, statusCode, details, requestId) {
575
- super(message, code, statusCode, details, requestId);
577
+ constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
578
+ super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
576
579
  this.name = "AoudaAuthenticationError";
577
580
  }
578
581
  };
@@ -1368,7 +1371,7 @@ function buildHandle(jobId, commitResp, wasResumed, transport, database, signal)
1368
1371
  rowsDurablyCommitted: commitResp.rowsLoaded,
1369
1372
  segmentsCreated: commitResp.segmentsCreated,
1370
1373
  committedAtUtc: commitResp.committedAtUtc,
1371
- walPosition: commitResp.walPosition,
1374
+ token: commitResp.token,
1372
1375
  writeConcernSatisfied: commitResp.writeConcernAchieved ?? "acknowledged",
1373
1376
  writeConcernTimedOut: commitResp.writeConcernTimedOut,
1374
1377
  wasResumed,
@@ -1494,6 +1497,7 @@ var PROTOCOL_VERSION_HEADER = "X-Aouda-Protocol-Version";
1494
1497
  var PROTOCOL_VERSION = "1";
1495
1498
  var CONTENT_TYPE_JSON2 = "application/json";
1496
1499
  var REQUEST_ID_HEADER2 = "X-Request-Id";
1500
+ var TOKEN_HEADER = "X-Aouda-Token";
1497
1501
  var ERROR_CODE_MAP = {
1498
1502
  TABLE_NOT_FOUND: AoudaNotFoundError,
1499
1503
  COLUMN_NOT_FOUND: AoudaNotFoundError,
@@ -1508,6 +1512,10 @@ var ERROR_CODE_MAP = {
1508
1512
  INVALID_OPERATOR: AoudaValidationError,
1509
1513
  INVALID_COLUMN: AoudaValidationError,
1510
1514
  INVALID_VALUE: AoudaValidationError,
1515
+ CONSTRAINT_CHECK_VIOLATION: AoudaValidationError,
1516
+ TRANSFORM_DERIVED_READONLY: AoudaValidationError,
1517
+ TRANSFORM_ROUTE_UNMATCHED: AoudaValidationError,
1518
+ TRANSFORM_ROUTE_AMBIGUOUS: AoudaValidationError,
1511
1519
  UNSUPPORTED_VERSION: AoudaValidationError,
1512
1520
  MALFORMED_REQUEST: AoudaValidationError,
1513
1521
  INTERNAL_ERROR: AoudaServerError,
@@ -1533,7 +1541,12 @@ var ERROR_CODE_MAP = {
1533
1541
  AUTH_IDENTITY_INVALID: AoudaValidationError,
1534
1542
  AUTH_IDENTITY_NOT_FOUND: AoudaValidationError,
1535
1543
  BULK_LOAD_TRANSFORM_INTENT_REQUIRED: AoudaValidationError,
1536
- BULK_LOAD_TRANSFORM_INTENT_CONFLICT: AoudaValidationError
1544
+ BULK_LOAD_TRANSFORM_INTENT_CONFLICT: AoudaValidationError,
1545
+ TOKEN_MALFORMED: AoudaValidationError,
1546
+ TOKEN_FOREIGN_DATABASE: AoudaValidationError,
1547
+ TOKEN_EPOCH_SUPERSEDED: AoudaConflictError,
1548
+ TOKEN_UNSATISFIED: AoudaConflictError,
1549
+ TOKEN_FETCH_PRIMARY: AoudaApiError
1537
1550
  };
1538
1551
  function createComposedAbortController(...signals) {
1539
1552
  const controller = new AbortController();
@@ -1563,6 +1576,9 @@ function parseRetryAfterSeconds(header) {
1563
1576
  if (!Number.isFinite(n) || n < 0) return void 0;
1564
1577
  return n;
1565
1578
  }
1579
+ function nonEmptyRowErrors(rowErrors) {
1580
+ return Array.isArray(rowErrors) && rowErrors.length > 0 ? rowErrors : void 0;
1581
+ }
1566
1582
  function createApiError(statusCode, statusText, body, retryAfterHeader) {
1567
1583
  const message = body.error ?? `${statusCode} ${statusText}`;
1568
1584
  const code = body.code ?? "UNKNOWN";
@@ -1570,7 +1586,16 @@ function createApiError(statusCode, statusText, body, retryAfterHeader) {
1570
1586
  const requestId = body.requestId;
1571
1587
  const retryAfterSeconds = parseRetryAfterSeconds(retryAfterHeader ?? null);
1572
1588
  const Ctor = ERROR_CODE_MAP[code] ?? AoudaApiError;
1573
- return new Ctor(message, code, statusCode, details, requestId, retryAfterSeconds);
1589
+ return new Ctor(
1590
+ message,
1591
+ code,
1592
+ statusCode,
1593
+ details,
1594
+ requestId,
1595
+ retryAfterSeconds,
1596
+ body.token,
1597
+ nonEmptyRowErrors(body.rowErrors)
1598
+ );
1574
1599
  }
1575
1600
  var HttpTransport = class {
1576
1601
  constructor(options) {
@@ -1582,6 +1607,29 @@ var HttpTransport = class {
1582
1607
  ...options.defaultHeaders
1583
1608
  };
1584
1609
  this.abortController = new AbortController();
1610
+ this.database = options.database;
1611
+ this.store = options.consistencyTokenStore;
1612
+ }
1613
+ presentTokenHeaders(path4, headers) {
1614
+ if (this.store == null || this.database == null || isAuthPath(path4)) {
1615
+ return;
1616
+ }
1617
+ const token = this.store.get(this.database);
1618
+ if (token != null && token.length > 0) {
1619
+ headers[TOKEN_HEADER] = token;
1620
+ }
1621
+ }
1622
+ observeResponse(_path, response, bodyText, errorCode) {
1623
+ if (this.store == null || this.database == null) {
1624
+ return;
1625
+ }
1626
+ if (errorCode === "TOKEN_MALFORMED" || errorCode === "TOKEN_FOREIGN_DATABASE") {
1627
+ return;
1628
+ }
1629
+ const header = response.headers.get(TOKEN_HEADER);
1630
+ const bodyToken = readBodyToken(bodyText);
1631
+ const token = header != null && header.trim().length > 0 ? header : bodyToken;
1632
+ this.store.observe(this.database, token);
1585
1633
  }
1586
1634
  /**
1587
1635
  * Abort all in-flight and future requests (e.g. on client disconnect).
@@ -1619,6 +1667,7 @@ var HttpTransport = class {
1619
1667
  ...this.defaultHeaders,
1620
1668
  ...config.headers
1621
1669
  };
1670
+ this.presentTokenHeaders(config.path, headers);
1622
1671
  if (config.body !== void 0 && config.body !== null) {
1623
1672
  headers["Content-Type"] = CONTENT_TYPE_JSON2;
1624
1673
  }
@@ -1638,6 +1687,7 @@ var HttpTransport = class {
1638
1687
  const text = await response.text();
1639
1688
  if (config.allowStatuses?.includes(response.status) && text) {
1640
1689
  try {
1690
+ this.observeResponse(config.path, response, text);
1641
1691
  return JSON.parse(text);
1642
1692
  } catch {
1643
1693
  }
@@ -1651,15 +1701,20 @@ var HttpTransport = class {
1651
1701
  }
1652
1702
  if (response.status === 401) {
1653
1703
  const code = errorBody?.code ?? "AUTH_TOKEN_MISSING";
1704
+ this.observeResponse(config.path, response, text, code);
1654
1705
  throw new AoudaAuthenticationError(
1655
1706
  errorBody?.error ?? "Unauthorized",
1656
1707
  code,
1657
1708
  401,
1658
1709
  errorBody?.details,
1659
- errorBody?.requestId
1710
+ errorBody?.requestId,
1711
+ void 0,
1712
+ errorBody?.token,
1713
+ nonEmptyRowErrors(errorBody?.rowErrors)
1660
1714
  );
1661
1715
  }
1662
1716
  if (errorBody?.code != null) {
1717
+ this.observeResponse(config.path, response, text, errorBody.code);
1663
1718
  throw createApiError(
1664
1719
  response.status,
1665
1720
  response.statusText,
@@ -1667,6 +1722,7 @@ var HttpTransport = class {
1667
1722
  response.headers.get("Retry-After")
1668
1723
  );
1669
1724
  }
1725
+ this.observeResponse(config.path, response, text);
1670
1726
  throw new AoudaResponseError(
1671
1727
  errorBody?.error ?? `${response.status} ${response.statusText}`,
1672
1728
  response.status,
@@ -1674,6 +1730,7 @@ var HttpTransport = class {
1674
1730
  );
1675
1731
  }
1676
1732
  const responseText = await response.text();
1733
+ this.observeResponse(config.path, response, responseText);
1677
1734
  if (config.rawText) {
1678
1735
  return responseText;
1679
1736
  }
@@ -1734,6 +1791,7 @@ var HttpTransport = class {
1734
1791
  ...this.defaultHeaders,
1735
1792
  ...config.headers
1736
1793
  };
1794
+ this.presentTokenHeaders(config.path, headers);
1737
1795
  if (config.rawBodyStr === void 0 && config.body !== void 0 && config.body !== null) {
1738
1796
  headers["Content-Type"] = CONTENT_TYPE_JSON2;
1739
1797
  }
@@ -1750,6 +1808,7 @@ var HttpTransport = class {
1750
1808
  try {
1751
1809
  const response = await localNetworkFetch(url, init);
1752
1810
  if (response.ok || config.allowStatuses?.includes(response.status)) {
1811
+ this.observeResponse(config.path, response, void 0);
1753
1812
  return response;
1754
1813
  }
1755
1814
  const text = await response.text();
@@ -1762,15 +1821,20 @@ var HttpTransport = class {
1762
1821
  }
1763
1822
  if (response.status === 401) {
1764
1823
  const code = errorBody?.code ?? "AUTH_TOKEN_MISSING";
1824
+ this.observeResponse(config.path, response, text, code);
1765
1825
  throw new AoudaAuthenticationError(
1766
1826
  errorBody?.error ?? "Unauthorized",
1767
1827
  code,
1768
1828
  401,
1769
1829
  errorBody?.details,
1770
- errorBody?.requestId
1830
+ errorBody?.requestId,
1831
+ void 0,
1832
+ errorBody?.token,
1833
+ nonEmptyRowErrors(errorBody?.rowErrors)
1771
1834
  );
1772
1835
  }
1773
1836
  if (errorBody?.code != null) {
1837
+ this.observeResponse(config.path, response, text, errorBody.code);
1774
1838
  throw createApiError(
1775
1839
  response.status,
1776
1840
  response.statusText,
@@ -1778,6 +1842,7 @@ var HttpTransport = class {
1778
1842
  response.headers.get("Retry-After")
1779
1843
  );
1780
1844
  }
1845
+ this.observeResponse(config.path, response, text);
1781
1846
  throw new AoudaResponseError(
1782
1847
  errorBody?.error ?? `${response.status} ${response.statusText}`,
1783
1848
  response.status,
@@ -1932,6 +1997,47 @@ var HttpTransport = class {
1932
1997
  });
1933
1998
  }
1934
1999
  };
2000
+ function isAuthPath(path4) {
2001
+ let p = path4;
2002
+ const q = p.indexOf("?");
2003
+ if (q >= 0) {
2004
+ p = p.slice(0, q);
2005
+ }
2006
+ if (/^https?:\/\//i.test(p)) {
2007
+ try {
2008
+ p = new URL(p).pathname;
2009
+ } catch {
2010
+ }
2011
+ }
2012
+ const lower = p.toLowerCase();
2013
+ if (lower === "/api/auth" || lower.startsWith("/api/auth/")) {
2014
+ return true;
2015
+ }
2016
+ const prefix = "/api/databases/";
2017
+ if (!lower.startsWith(prefix)) {
2018
+ return false;
2019
+ }
2020
+ const rest = lower.slice(prefix.length);
2021
+ const slash = rest.indexOf("/");
2022
+ if (slash < 0) {
2023
+ return false;
2024
+ }
2025
+ const afterDb = rest.slice(slash + 1);
2026
+ return afterDb === "auth" || afterDb.startsWith("auth/");
2027
+ }
2028
+ function readBodyToken(bodyText) {
2029
+ if (bodyText == null || bodyText.trim().length === 0) {
2030
+ return void 0;
2031
+ }
2032
+ try {
2033
+ const parsed = JSON.parse(bodyText);
2034
+ if (parsed !== null && typeof parsed === "object" && "token" in parsed && typeof parsed.token === "string") {
2035
+ return parsed.token;
2036
+ }
2037
+ } catch {
2038
+ }
2039
+ return void 0;
2040
+ }
1935
2041
 
1936
2042
  // src/resilience/retry.ts
1937
2043
  var DEFAULT_MAX_RETRIES = 3;
@@ -2364,6 +2470,48 @@ function databasePath2(db) {
2364
2470
  return `/api/databases/${encodeURIComponent(db)}`;
2365
2471
  }
2366
2472
 
2473
+ // src/consistency-token-store.ts
2474
+ var MemoryConsistencyTokenStore = class {
2475
+ constructor() {
2476
+ this.tokens = /* @__PURE__ */ new Map();
2477
+ }
2478
+ get(database) {
2479
+ if (typeof database !== "string" || database.trim().length === 0) {
2480
+ throw new Error("database must be a non-empty string");
2481
+ }
2482
+ return this.tokens.get(normalizeKey(database));
2483
+ }
2484
+ observe(database, token) {
2485
+ if (typeof database !== "string" || database.trim().length === 0) {
2486
+ throw new Error("database must be a non-empty string");
2487
+ }
2488
+ if (token == null || token.trim().length === 0) {
2489
+ return;
2490
+ }
2491
+ const key = normalizeKey(database);
2492
+ const stored = this.tokens.get(key);
2493
+ if (stored === void 0 || compareOrdinal(token, stored) > 0) {
2494
+ this.tokens.set(key, token);
2495
+ }
2496
+ }
2497
+ };
2498
+ function compareOrdinal(left, right) {
2499
+ if (left === right) return 0;
2500
+ return left < right ? -1 : 1;
2501
+ }
2502
+ function maxToken(left, right) {
2503
+ if (left == null || left.length === 0) {
2504
+ return right == null || right.length === 0 ? void 0 : right;
2505
+ }
2506
+ if (right == null || right.length === 0) {
2507
+ return left;
2508
+ }
2509
+ return compareOrdinal(left, right) >= 0 ? left : right;
2510
+ }
2511
+ function normalizeKey(database) {
2512
+ return database.toLowerCase();
2513
+ }
2514
+
2367
2515
  // src/streaming/subscription.ts
2368
2516
  var AsyncEventQueue = class {
2369
2517
  constructor() {
@@ -2420,7 +2568,7 @@ var AsyncEventQueue = class {
2420
2568
  }
2421
2569
  };
2422
2570
  var TableSubscription = class {
2423
- constructor(transport, identity, options = {}, onWarnings) {
2571
+ constructor(transport, identity, options = {}, onWarnings, store, database) {
2424
2572
  this._queue = new AsyncEventQueue();
2425
2573
  this._active = true;
2426
2574
  this._started = false;
@@ -2436,6 +2584,11 @@ var TableSubscription = class {
2436
2584
  this._onError = options.onError;
2437
2585
  this._onWarnings = onWarnings;
2438
2586
  this._conflate = options.conflate;
2587
+ this._atLeast = options.atLeast;
2588
+ this._waitMs = options.waitMs;
2589
+ this._onExceeded = options.onExceeded;
2590
+ this._store = store;
2591
+ this._database = database;
2439
2592
  this._reconnectHandlerKey = `${this.id}::reconnect`;
2440
2593
  }
2441
2594
  get lastVersion() {
@@ -2500,7 +2653,7 @@ var TableSubscription = class {
2500
2653
  id: this.id
2501
2654
  };
2502
2655
  if (this._identity.kind === "named") {
2503
- message.hash = this._identity.hash;
2656
+ message.name = this._identity.name;
2504
2657
  if (this._identity.args !== void 0) {
2505
2658
  message.args = this._identity.args;
2506
2659
  }
@@ -2519,8 +2672,30 @@ var TableSubscription = class {
2519
2672
  if (this._conflate !== void 0) {
2520
2673
  message.conflate = this._conflate;
2521
2674
  }
2675
+ const pin = this._resolvePin();
2676
+ if (pin !== void 0) {
2677
+ message.at_least = pin;
2678
+ }
2679
+ if (this._waitMs !== void 0) {
2680
+ message.wait_ms = this._waitMs;
2681
+ }
2682
+ if (this._onExceeded !== void 0) {
2683
+ message.on_exceeded = this._onExceeded;
2684
+ }
2522
2685
  await this._transport.send(message);
2523
2686
  }
2687
+ _resolvePin() {
2688
+ if (this._atLeast !== void 0 && this._store != null && this._database != null) {
2689
+ this._store.observe(this._database, this._atLeast);
2690
+ }
2691
+ const stored = this._store != null && this._database != null ? this._store.get(this._database) : void 0;
2692
+ return maxToken(this._atLeast, stored);
2693
+ }
2694
+ _observeToken(token) {
2695
+ if (token !== void 0 && this._store != null && this._database != null) {
2696
+ this._store.observe(this._database, token);
2697
+ }
2698
+ }
2524
2699
  _handleMessage(message) {
2525
2700
  if (!this._active) {
2526
2701
  return;
@@ -2547,6 +2722,7 @@ var TableSubscription = class {
2547
2722
  }
2548
2723
  _handleSnapshotPage(message) {
2549
2724
  this._pendingSnapshotRows.push(...message.rows);
2725
+ this._observeToken(message.token);
2550
2726
  }
2551
2727
  _handleSnapshotComplete(message) {
2552
2728
  this._lastVersion = message.version;
@@ -2564,6 +2740,10 @@ var TableSubscription = class {
2564
2740
  if (message.total_matches !== void 0) {
2565
2741
  snapshot.totalMatches = message.total_matches;
2566
2742
  }
2743
+ if (message.token !== void 0) {
2744
+ snapshot.token = message.token;
2745
+ this._observeToken(message.token);
2746
+ }
2567
2747
  this._queue.push(snapshot);
2568
2748
  }
2569
2749
  async _handleGap(message) {
@@ -2589,6 +2769,10 @@ var TableSubscription = class {
2589
2769
  if (message.values_skipped !== void 0) {
2590
2770
  event.values_skipped = message.values_skipped;
2591
2771
  }
2772
+ if (message.token !== void 0) {
2773
+ event.token = message.token;
2774
+ this._observeToken(message.token);
2775
+ }
2592
2776
  this._onChange?.(event);
2593
2777
  this._queue.push(event);
2594
2778
  }
@@ -2736,8 +2920,11 @@ var TableWriteStream = class {
2736
2920
  resolve4?.();
2737
2921
  }
2738
2922
  _handleServerError(message) {
2923
+ const rowErrors = Array.isArray(message.errors) && message.errors.length > 0 ? message.errors : void 0;
2739
2924
  const error = new AoudaConnectionError(
2740
- `Write stream error (${message.code}): ${message.message}`
2925
+ `Write stream error (${message.code}): ${message.message}`,
2926
+ void 0,
2927
+ rowErrors
2741
2928
  );
2742
2929
  const openReject = this._openReject;
2743
2930
  this._openResolve = null;
@@ -2933,11 +3120,12 @@ var TableQuery = class _TableQuery {
2933
3120
  * @param state - Optional initial state (used for immutable chaining).
2934
3121
  * @internal Use `client.table()` to create queries.
2935
3122
  */
2936
- constructor(transport, tableName, database, state, getWebSocketTransport) {
3123
+ constructor(transport, tableName, database, state, getWebSocketTransport, store) {
2937
3124
  this.transport = transport;
2938
3125
  this.tableName = tableName;
2939
3126
  this.database = database;
2940
3127
  this.getWebSocketTransport = getWebSocketTransport;
3128
+ this.store = store;
2941
3129
  this.state = state ?? {
2942
3130
  predicates: [],
2943
3131
  groupClauses: [],
@@ -2953,6 +3141,16 @@ var TableQuery = class _TableQuery {
2953
3141
  isDistinct: false
2954
3142
  };
2955
3143
  }
3144
+ withState(state) {
3145
+ return new _TableQuery(
3146
+ this.transport,
3147
+ this.tableName,
3148
+ this.database,
3149
+ state,
3150
+ this.getWebSocketTransport,
3151
+ this.store
3152
+ );
3153
+ }
2956
3154
  where(column, operator, value) {
2957
3155
  const newPredicates = buildWherePredicates(column, operator, value);
2958
3156
  return new _TableQuery(
@@ -2963,7 +3161,8 @@ var TableQuery = class _TableQuery {
2963
3161
  ...this.state,
2964
3162
  predicates: [...this.state.predicates, ...newPredicates]
2965
3163
  },
2966
- this.getWebSocketTransport
3164
+ this.getWebSocketTransport,
3165
+ this.store
2967
3166
  );
2968
3167
  }
2969
3168
  /**
@@ -2982,7 +3181,7 @@ var TableQuery = class _TableQuery {
2982
3181
  return new _TableQuery(this.transport, this.tableName, this.database, {
2983
3182
  ...this.state,
2984
3183
  groupClauses: [...this.state.groupClauses, sub]
2985
- }, this.getWebSocketTransport);
3184
+ }, this.getWebSocketTransport, this.store);
2986
3185
  }
2987
3186
  /**
2988
3187
  * Sets the primary sort column for the query.
@@ -3007,7 +3206,7 @@ var TableQuery = class _TableQuery {
3007
3206
  return new _TableQuery(this.transport, this.tableName, this.database, {
3008
3207
  ...this.state,
3009
3208
  orderByClauses: [orderByClause]
3010
- }, this.getWebSocketTransport);
3209
+ }, this.getWebSocketTransport, this.store);
3011
3210
  }
3012
3211
  /**
3013
3212
  * Sets the primary sort column to descending order.
@@ -3053,7 +3252,7 @@ var TableQuery = class _TableQuery {
3053
3252
  return new _TableQuery(this.transport, this.tableName, this.database, {
3054
3253
  ...this.state,
3055
3254
  orderByClauses: [...this.state.orderByClauses, orderByClause]
3056
- }, this.getWebSocketTransport);
3255
+ }, this.getWebSocketTransport, this.store);
3057
3256
  }
3058
3257
  /**
3059
3258
  * Sets the maximum number of rows to return.
@@ -3070,7 +3269,7 @@ var TableQuery = class _TableQuery {
3070
3269
  return new _TableQuery(this.transport, this.tableName, this.database, {
3071
3270
  ...this.state,
3072
3271
  limitValue: count
3073
- }, this.getWebSocketTransport);
3272
+ }, this.getWebSocketTransport, this.store);
3074
3273
  }
3075
3274
  /**
3076
3275
  * Sets the number of rows to skip.
@@ -3087,7 +3286,7 @@ var TableQuery = class _TableQuery {
3087
3286
  return new _TableQuery(this.transport, this.tableName, this.database, {
3088
3287
  ...this.state,
3089
3288
  offsetValue: count
3090
- }, this.getWebSocketTransport);
3289
+ }, this.getWebSocketTransport, this.store);
3091
3290
  }
3092
3291
  /**
3093
3292
  * Requests cross-partition access for this query.
@@ -3102,10 +3301,21 @@ var TableQuery = class _TableQuery {
3102
3301
  return new _TableQuery(this.transport, this.tableName, this.database, {
3103
3302
  ...this.state,
3104
3303
  crossPartitionAccess: true
3105
- }, this.getWebSocketTransport);
3304
+ }, this.getWebSocketTransport, this.store);
3106
3305
  }
3107
3306
  /**
3108
- * Restricts the columns returned in the result.
3307
+ * Pin this query at at least this C-1 token. Observes the token into the
3308
+ * client store (I3, sticky) and presents it on execute via `X-Aouda-Token`.
3309
+ */
3310
+ atLeast(token) {
3311
+ if (typeof token !== "string" || token.trim().length === 0) {
3312
+ throw new Error("atLeast() requires a non-empty token");
3313
+ }
3314
+ this.store?.observe(this.database, token);
3315
+ return this.withState({ ...this.state, atLeast: token });
3316
+ }
3317
+ /**
3318
+ * Selects specific columns to return.
3109
3319
  * If not called, all columns are returned.
3110
3320
  *
3111
3321
  * When T is a specific row type, only keys of T are accepted as column names.
@@ -3127,7 +3337,7 @@ var TableQuery = class _TableQuery {
3127
3337
  return new _TableQuery(this.transport, this.tableName, this.database, {
3128
3338
  ...this.state,
3129
3339
  selectColumns: columns.length > 0 ? columns : null
3130
- }, this.getWebSocketTransport);
3340
+ }, this.getWebSocketTransport, this.store);
3131
3341
  }
3132
3342
  /**
3133
3343
  * Return only distinct (de-duplicated) rows for the given columns — SQL `SELECT DISTINCT`.
@@ -3157,7 +3367,7 @@ var TableQuery = class _TableQuery {
3157
3367
  ...this.state,
3158
3368
  selectColumns: columns,
3159
3369
  isDistinct: true
3160
- }, this.getWebSocketTransport);
3370
+ }, this.getWebSocketTransport, this.store);
3161
3371
  }
3162
3372
  /**
3163
3373
  * Adds server-side computed columns to the query result.
@@ -3166,6 +3376,12 @@ var TableQuery = class _TableQuery {
3166
3376
  * evaluated per row on the server. Computed columns are appended after any physical-column
3167
3377
  * `select()` projection.
3168
3378
  *
3379
+ * Result types are inferred by the server where the expression permits
3380
+ * (e.g. Int32 → `number`). Uninferable expressions use wire `"Unknown"` /
3381
+ * codegen `unknown`. Computed columns are always nullable. Named-query
3382
+ * `*Row` properties pick this up when regenerated against a post-S08 server.
3383
+ * See aouda-docs/guides/browser-tier-read-limits.md#selectexpr-result-types
3384
+ *
3169
3385
  * @param projections - One or more `{ alias, expr }` pairs.
3170
3386
  * @returns A new TableQuery with computed columns set.
3171
3387
  *
@@ -3186,7 +3402,7 @@ var TableQuery = class _TableQuery {
3186
3402
  return new _TableQuery(this.transport, this.tableName, this.database, {
3187
3403
  ...this.state,
3188
3404
  selectExprs: projections
3189
- }, this.getWebSocketTransport);
3405
+ }, this.getWebSocketTransport, this.store);
3190
3406
  }
3191
3407
  join(rightTable, leftColumnOrColumns, rightColumnOrColumns) {
3192
3408
  return this.addJoinClause(
@@ -3244,7 +3460,8 @@ var TableQuery = class _TableQuery {
3244
3460
  }
3245
3461
  ]
3246
3462
  },
3247
- this.getWebSocketTransport
3463
+ this.getWebSocketTransport,
3464
+ this.store
3248
3465
  );
3249
3466
  }
3250
3467
  /**
@@ -3280,7 +3497,8 @@ var TableQuery = class _TableQuery {
3280
3497
  ...this.state,
3281
3498
  groupByColumns: [...columns]
3282
3499
  },
3283
- this.getWebSocketTransport
3500
+ this.getWebSocketTransport,
3501
+ this.store
3284
3502
  );
3285
3503
  }
3286
3504
  /**
@@ -3314,8 +3532,11 @@ var TableQuery = class _TableQuery {
3314
3532
  onSnapshot: options.onSnapshot,
3315
3533
  onChange: options.onChange,
3316
3534
  onError: options.onError,
3317
- conflate: options.conflate
3318
- });
3535
+ conflate: options.conflate,
3536
+ atLeast: options.atLeast ?? this.state.atLeast,
3537
+ waitMs: options.waitMs,
3538
+ onExceeded: options.onExceeded
3539
+ }, void 0, this.store, this.database);
3319
3540
  subscription.start();
3320
3541
  return subscription;
3321
3542
  }
@@ -3430,7 +3651,8 @@ var TableQuery = class _TableQuery {
3430
3651
  ...this.state,
3431
3652
  joinClauses: [...this.state.joinClauses, joinClause]
3432
3653
  },
3433
- this.getWebSocketTransport
3654
+ this.getWebSocketTransport,
3655
+ this.store
3434
3656
  );
3435
3657
  }
3436
3658
  addAggregate(op, column) {
@@ -3451,7 +3673,8 @@ var TableQuery = class _TableQuery {
3451
3673
  }
3452
3674
  ]
3453
3675
  },
3454
- this.getWebSocketTransport
3676
+ this.getWebSocketTransport,
3677
+ this.store
3455
3678
  );
3456
3679
  }
3457
3680
  requireWebSocketTransport() {
@@ -3481,17 +3704,23 @@ var TableQuery = class _TableQuery {
3481
3704
  * ```
3482
3705
  */
3483
3706
  async execute() {
3707
+ if (this.state.atLeast) {
3708
+ this.store?.observe(this.database, this.state.atLeast);
3709
+ }
3484
3710
  const request = this.buildRequest();
3485
3711
  const path4 = `${databasePath2(this.database)}/query`;
3486
3712
  const response = await this.transport.post(path4, request);
3487
3713
  const rows = columnarToRows(response);
3488
3714
  const stats = response.stats;
3489
- return { rows, stats };
3715
+ return { rows, stats, token: response.token };
3490
3716
  }
3491
3717
  /**
3492
3718
  * Executes the query and returns the raw columnar JSON payload (no row-object conversion).
3493
3719
  */
3494
3720
  async toColumnar() {
3721
+ if (this.state.atLeast) {
3722
+ this.store?.observe(this.database, this.state.atLeast);
3723
+ }
3495
3724
  const request = this.buildRequest();
3496
3725
  const path4 = `${databasePath2(this.database)}/query`;
3497
3726
  return this.transport.post(path4, request);
@@ -3521,8 +3750,12 @@ var TableQuery = class _TableQuery {
3521
3750
  limitValue: 0,
3522
3751
  selectColumns: []
3523
3752
  },
3524
- this.getWebSocketTransport
3753
+ this.getWebSocketTransport,
3754
+ this.store
3525
3755
  );
3756
+ if (this.state.atLeast) {
3757
+ this.store?.observe(this.database, this.state.atLeast);
3758
+ }
3526
3759
  const request = countQuery.buildRequest();
3527
3760
  const path4 = `${databasePath2(this.database)}/query`;
3528
3761
  const response = await this.transport.post(path4, request);
@@ -3569,7 +3802,8 @@ var TableQuery = class _TableQuery {
3569
3802
  const response = await this.transport.post(path4, body);
3570
3803
  const result = {
3571
3804
  rowsInserted: response.rowsInserted,
3572
- executionMs: response.executionMs
3805
+ executionMs: response.executionMs,
3806
+ token: response.token
3573
3807
  };
3574
3808
  if (response.generatedValues !== void 0) {
3575
3809
  result.generatedValues = response.generatedValues;
@@ -3619,7 +3853,8 @@ var TableQuery = class _TableQuery {
3619
3853
  const response = await this.transport.post(path4, body);
3620
3854
  const result = {
3621
3855
  rowsInserted: response.rowsInserted,
3622
- executionMs: response.executionMs
3856
+ executionMs: response.executionMs,
3857
+ token: response.token
3623
3858
  };
3624
3859
  if (response.generatedValues !== void 0) {
3625
3860
  result.generatedValues = response.generatedValues;
@@ -3671,6 +3906,7 @@ var TableQuery = class _TableQuery {
3671
3906
  return {
3672
3907
  rowsAffected: response.rowsUpdated,
3673
3908
  executionMs: response.executionMs,
3909
+ token: response.token,
3674
3910
  ...response.rows ? { rows: columnarToRows(response.rows) } : {},
3675
3911
  ...response.rowsTruncated ? { rowsTruncated: true } : {}
3676
3912
  };
@@ -3721,6 +3957,7 @@ var TableQuery = class _TableQuery {
3721
3957
  rowsAffected: response.rowsDeleted,
3722
3958
  executionMs: response.executionMs,
3723
3959
  hasMore: response.hasMore,
3960
+ token: response.token,
3724
3961
  ...response.rows ? { rows: columnarToRows(response.rows) } : {},
3725
3962
  ...response.rowsTruncated ? { rowsTruncated: true } : {}
3726
3963
  };
@@ -3740,7 +3977,8 @@ var TableQuery = class _TableQuery {
3740
3977
  return {
3741
3978
  rowsAffected: response.rowsDeleted,
3742
3979
  executionMs: response.executionMs,
3743
- hasMore: false
3980
+ hasMore: false,
3981
+ token: response.token
3744
3982
  };
3745
3983
  }
3746
3984
  /**
@@ -3753,7 +3991,14 @@ var TableQuery = class _TableQuery {
3753
3991
  throw new Error("batch() requires a non-empty operations array");
3754
3992
  }
3755
3993
  const wireOperations = operations.map((op, index) => {
3756
- const base = new _TableQuery(this.transport, this.tableName, this.database);
3994
+ const base = new _TableQuery(
3995
+ this.transport,
3996
+ this.tableName,
3997
+ this.database,
3998
+ void 0,
3999
+ this.getWebSocketTransport,
4000
+ this.store
4001
+ );
3757
4002
  const scoped = op.where(base);
3758
4003
  const where = scoped.buildWhereClause();
3759
4004
  if (!where) {
@@ -5090,12 +5335,12 @@ function raiseDeprecationWarnings(sink, warnings) {
5090
5335
  continue;
5091
5336
  }
5092
5337
  const sunset = warning.sunsetAt != null ? ` sunsetAt=${warning.sunsetAt}` : "";
5093
- const hash = warning.hash != null && warning.hash.length > 0 ? ` hash=${warning.hash}` : "";
5338
+ const name = warning.name != null && warning.name.length > 0 ? ` name=${warning.name}` : "";
5094
5339
  sink({
5095
5340
  code: warning.code,
5096
- hash: warning.hash,
5341
+ name: warning.name,
5097
5342
  sunsetAt: warning.sunsetAt,
5098
- message: `${warning.code}:${hash}${sunset}`.trim()
5343
+ message: `${warning.code}:${name}${sunset}`.trim()
5099
5344
  });
5100
5345
  }
5101
5346
  }
@@ -5108,24 +5353,28 @@ function emptyStats() {
5108
5353
  };
5109
5354
  }
5110
5355
  var NamedQueriesApi = class {
5111
- constructor(transport, database, onWarning, getStreamingTransport) {
5356
+ constructor(transport, database, onWarning, getStreamingTransport, store) {
5112
5357
  this.transport = transport;
5113
5358
  this.database = database;
5114
5359
  this.onWarning = onWarning;
5115
5360
  this.getStreamingTransport = getStreamingTransport;
5361
+ this.store = store;
5116
5362
  }
5117
- async execute(hash, args, options) {
5118
- if (typeof hash !== "string" || hash.trim().length === 0) {
5119
- throw new Error("Named query hash must be a non-empty string");
5363
+ async execute(name, args, options) {
5364
+ if (typeof name !== "string" || name.trim().length === 0) {
5365
+ throw new Error("Named query name must be a non-empty string");
5120
5366
  }
5121
5367
  const prefix = databasePath2(this.database);
5122
- const path4 = `${prefix}/named-queries/${encodeURIComponent(hash)}/query?format=columnar`;
5368
+ const path4 = `${prefix}/named-queries/${encodeURIComponent(name)}/query?format=columnar`;
5123
5369
  const body = {
5124
5370
  args: args ?? {}
5125
5371
  };
5126
5372
  if (options?.orderByIndex !== void 0) {
5127
5373
  body.orderByIndex = options.orderByIndex;
5128
5374
  }
5375
+ if (options?.atLeast !== void 0) {
5376
+ this.store?.observe(this.database, options.atLeast);
5377
+ }
5129
5378
  const response = await this.transport.post(
5130
5379
  path4,
5131
5380
  body,
@@ -5137,7 +5386,8 @@ var NamedQueriesApi = class {
5137
5386
  rows,
5138
5387
  stats: response.stats,
5139
5388
  warnings: response.warnings,
5140
- totalMatches: response.totalMatches
5389
+ totalMatches: response.totalMatches,
5390
+ token: response.token
5141
5391
  };
5142
5392
  }
5143
5393
  async batch(items, options) {
@@ -5155,6 +5405,9 @@ var NamedQueriesApi = class {
5155
5405
  400
5156
5406
  );
5157
5407
  }
5408
+ if (options?.atLeast !== void 0) {
5409
+ this.store?.observe(this.database, options.atLeast);
5410
+ }
5158
5411
  const prefix = databasePath2(this.database);
5159
5412
  const path4 = `${prefix}/named-queries/batch?format=columnar`;
5160
5413
  const envelope = await this.transport.post(
@@ -5177,34 +5430,41 @@ var NamedQueriesApi = class {
5177
5430
  rowCount: slot.rowCount ?? 0,
5178
5431
  stats: slot.stats ?? emptyStats(),
5179
5432
  warnings: slot.warnings,
5180
- totalMatches: slot.totalMatches
5433
+ totalMatches: slot.totalMatches,
5434
+ token: slot.token
5181
5435
  };
5182
5436
  const result = {
5183
5437
  rows: columnarToRows(columnar),
5184
5438
  stats: columnar.stats,
5185
5439
  warnings: slot.warnings,
5186
- totalMatches: slot.totalMatches
5440
+ totalMatches: slot.totalMatches,
5441
+ token: slot.token
5187
5442
  };
5188
5443
  raiseDeprecationWarnings(this.onWarning, slot.warnings);
5189
5444
  return { isError: false, result };
5190
5445
  });
5191
5446
  }
5192
- subscribe(hash, args, options = {}) {
5193
- if (typeof hash !== "string" || hash.trim().length === 0) {
5194
- throw new Error("Named query hash must be a non-empty string");
5447
+ subscribe(name, args, options = {}) {
5448
+ if (typeof name !== "string" || name.trim().length === 0) {
5449
+ throw new Error("Named query name must be a non-empty string");
5195
5450
  }
5196
5451
  const subscription = new TableSubscription(
5197
5452
  this.getStreamingTransport(),
5198
- { kind: "named", hash, args, orderByIndex: options.orderByIndex },
5453
+ { kind: "named", name, args, orderByIndex: options.orderByIndex },
5199
5454
  {
5200
5455
  onSnapshot: options.onSnapshot,
5201
5456
  onChange: options.onChange,
5202
5457
  onError: options.onError,
5203
- conflate: options.conflate
5458
+ conflate: options.conflate,
5459
+ atLeast: options.atLeast,
5460
+ waitMs: options.waitMs,
5461
+ onExceeded: options.onExceeded
5204
5462
  },
5205
5463
  (warnings) => {
5206
5464
  raiseDeprecationWarnings(this.onWarning, warnings);
5207
- }
5465
+ },
5466
+ this.store,
5467
+ this.database
5208
5468
  );
5209
5469
  subscription.start();
5210
5470
  return subscription;
@@ -5216,12 +5476,12 @@ var NamedMutationsApi = class {
5216
5476
  this.database = database;
5217
5477
  this.onWarning = onWarning;
5218
5478
  }
5219
- async execute(hash, args) {
5220
- if (typeof hash !== "string" || hash.trim().length === 0) {
5221
- throw new Error("Named mutation hash must be a non-empty string");
5479
+ async execute(name, args) {
5480
+ if (typeof name !== "string" || name.trim().length === 0) {
5481
+ throw new Error("Named mutation name must be a non-empty string");
5222
5482
  }
5223
5483
  const prefix = databasePath2(this.database);
5224
- const path4 = `${prefix}/named-mutations/${encodeURIComponent(hash)}/execute`;
5484
+ const path4 = `${prefix}/named-mutations/${encodeURIComponent(name)}/execute`;
5225
5485
  const wire = await this.transport.post(path4, {
5226
5486
  args: args ?? {}
5227
5487
  });
@@ -5282,6 +5542,7 @@ var WebSocketTransport = class {
5282
5542
  // Serialized-send queue: each send appends to this tail.
5283
5543
  this._sendTail = Promise.resolve();
5284
5544
  this._lastVersion = 0;
5545
+ this._lastToken = null;
5285
5546
  this._reconnectAttempt = 0;
5286
5547
  this._disposed = false;
5287
5548
  this._missedPings = 0;
@@ -5299,6 +5560,7 @@ var WebSocketTransport = class {
5299
5560
  this._pingIntervalMs = options.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
5300
5561
  this._maxMissedHeartbeats = options.maxMissedHeartbeats ?? DEFAULT_MAX_MISSED_HEARTBEATS;
5301
5562
  this._enableCompression = options.enableCompression ?? true;
5563
+ this._store = options.consistencyTokenStore;
5302
5564
  this._wireMode = options.wireMode ?? "json";
5303
5565
  this._authHandler?.setOnAccessTokenRefreshed((token) => {
5304
5566
  if (this._handshakeComplete && this._ws?.readyState === WS_READY_STATE_OPEN) {
@@ -5307,10 +5569,14 @@ var WebSocketTransport = class {
5307
5569
  });
5308
5570
  }
5309
5571
  // ─── Public API ────────────────────────────────────────────────────────────
5310
- /** Latest version number from the most recent server `heartbeat` message. */
5572
+ /** Latest change-event sequence from the most recent server `heartbeat` message. */
5311
5573
  get lastVersion() {
5312
5574
  return this._lastVersion;
5313
5575
  }
5576
+ /** Consistency token from the most recent server `heartbeat` (ADR 0042 D-13). */
5577
+ get lastToken() {
5578
+ return this._lastToken;
5579
+ }
5314
5580
  /**
5315
5581
  * Registers a handler for incoming server messages with the given channel id.
5316
5582
  * Use `"__global__"` to receive broadcast messages that carry no `id`.
@@ -5498,6 +5764,10 @@ var WebSocketTransport = class {
5498
5764
  }
5499
5765
  case "heartbeat":
5500
5766
  this._lastVersion = msg.version;
5767
+ this._lastToken = msg.token ?? null;
5768
+ if (this._lastToken != null && this._lastToken.length > 0) {
5769
+ this._store?.observe(this._database, this._lastToken);
5770
+ }
5501
5771
  break;
5502
5772
  case "pong":
5503
5773
  this._missedPings = 0;
@@ -5663,16 +5933,21 @@ var LongPollTransport = class {
5663
5933
  this._connected = false;
5664
5934
  this._pollTask = null;
5665
5935
  this._lastVersion = 0;
5936
+ this._lastToken = null;
5666
5937
  this._serverUrl = serverUrl;
5667
5938
  this._database = database;
5668
5939
  this._authHandler = options.authHandler;
5669
5940
  this._onReconnected = options.onReconnected;
5670
5941
  this._waitMs = Math.max(1, options.waitMs ?? DEFAULT_WAIT_MS);
5671
5942
  this._fetch = options.fetchImpl ?? localNetworkFetch;
5943
+ this._store = options.consistencyTokenStore;
5672
5944
  }
5673
5945
  get lastVersion() {
5674
5946
  return this._lastVersion;
5675
5947
  }
5948
+ get lastToken() {
5949
+ return this._lastToken;
5950
+ }
5676
5951
  registerHandler(id, handler) {
5677
5952
  this._handlers.set(id, handler);
5678
5953
  }
@@ -5795,6 +6070,10 @@ var LongPollTransport = class {
5795
6070
  for (const msg of payload.messages) {
5796
6071
  if (msg.type === "heartbeat") {
5797
6072
  this._lastVersion = msg.version;
6073
+ this._lastToken = msg.token ?? null;
6074
+ if (this._lastToken != null && this._lastToken.length > 0) {
6075
+ this._store?.observe(this._database, this._lastToken);
6076
+ }
5798
6077
  }
5799
6078
  const id = "id" in msg ? msg.id : void 0;
5800
6079
  if (id !== void 0 && id !== null) {
@@ -5874,6 +6153,9 @@ var FallbackStreamingTransport = class {
5874
6153
  get lastVersion() {
5875
6154
  return this._active.lastVersion;
5876
6155
  }
6156
+ get lastToken() {
6157
+ return this._active.lastToken;
6158
+ }
5877
6159
  registerHandler(id, handler) {
5878
6160
  this._handlers.set(id, handler);
5879
6161
  this._active.registerHandler(id, handler);
@@ -5953,6 +6235,7 @@ var AoudaClient = class {
5953
6235
  this.baseUrl = normalizeBaseUrl(options.serverUrl);
5954
6236
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
5955
6237
  this.database = options.database.trim();
6238
+ this._store = options.consistencyTokenStore ?? new MemoryConsistencyTokenStore();
5956
6239
  if (!Number.isFinite(this.timeout) || this.timeout <= 0) {
5957
6240
  throw new Error("timeout must be a finite positive number");
5958
6241
  }
@@ -5987,7 +6270,9 @@ var AoudaClient = class {
5987
6270
  }
5988
6271
  const httpTransport = new HttpTransport({
5989
6272
  baseUrl: this.baseUrl,
5990
- timeout: this.timeout
6273
+ timeout: this.timeout,
6274
+ database: this.database,
6275
+ consistencyTokenStore: this._store
5991
6276
  });
5992
6277
  const activeAuth = options.serverAuth ?? options.appAuth;
5993
6278
  if (activeAuth) {
@@ -6047,7 +6332,8 @@ var AoudaClient = class {
6047
6332
  this.transport,
6048
6333
  this.database,
6049
6334
  onNamedArtifactWarning,
6050
- () => this._getOrCreateWebSocketTransport()
6335
+ () => this._getOrCreateWebSocketTransport(),
6336
+ this._store
6051
6337
  );
6052
6338
  this._namedMutations = new NamedMutationsApi(
6053
6339
  this.transport,
@@ -6146,7 +6432,8 @@ var AoudaClient = class {
6146
6432
  name,
6147
6433
  this.database,
6148
6434
  void 0,
6149
- () => this._getOrCreateWebSocketTransport()
6435
+ () => this._getOrCreateWebSocketTransport(),
6436
+ this._store
6150
6437
  );
6151
6438
  }
6152
6439
  /**
@@ -6198,13 +6485,26 @@ var AoudaClient = class {
6198
6485
  return this._materializedQueries;
6199
6486
  }
6200
6487
  /**
6201
- * Hash-only named-query execute and batch. Names are codegen aliases (D-5).
6488
+ * Named-query execute, read-only batch, and subscribe by unique schema name.
6202
6489
  */
6203
6490
  get namedQueries() {
6204
6491
  return this._namedQueries;
6205
6492
  }
6493
+ observeConsistencyToken(token) {
6494
+ this._store.observe(this.database, token);
6495
+ }
6496
+ getObservedConsistencyToken() {
6497
+ return this._store.get(this.database);
6498
+ }
6499
+ async getConsistencyToken() {
6500
+ const body = await this.transport.get(
6501
+ `/api/databases/${encodeURIComponent(this.database)}/token`
6502
+ );
6503
+ this._store.observe(this.database, body.token);
6504
+ return this._store.get(this.database) ?? body.token;
6505
+ }
6206
6506
  /**
6207
- * Hash-only named-mutation execute. No batch.
6507
+ * Named-mutation execute by unique schema name. No batch.
6208
6508
  */
6209
6509
  get namedMutations() {
6210
6510
  return this._namedMutations;
@@ -6331,13 +6631,15 @@ var AoudaClient = class {
6331
6631
  const primary = new WebSocketTransport(this.baseUrl, this.database, {
6332
6632
  authHandler: this._authHandler,
6333
6633
  enableCompression: this._streamingEnableCompression,
6334
- wireMode: this._streamingWireMode
6634
+ wireMode: this._streamingWireMode,
6635
+ consistencyTokenStore: this._store
6335
6636
  });
6336
6637
  if (this._streamingEnableLongPollFallback) {
6337
6638
  this._wsTransport = new FallbackStreamingTransport(primary, () => {
6338
6639
  return new LongPollTransport(this.baseUrl, this.database, {
6339
6640
  authHandler: this._authHandler,
6340
- waitMs: this._streamingLongPollWaitMs
6641
+ waitMs: this._streamingLongPollWaitMs,
6642
+ consistencyTokenStore: this._store
6341
6643
  });
6342
6644
  });
6343
6645
  } else {
@@ -6363,7 +6665,7 @@ Usage:
6363
6665
  npx @aouda/client schema <command> [options]
6364
6666
 
6365
6667
  Commands:
6366
- generate Fetch schema from Aouda server and output TypeScript types (tables + named query/mutation hashes)
6668
+ generate Fetch schema from Aouda server and output TypeScript types (tables + optional named query/mutation Args/Row)
6367
6669
  schema Schema management (diff, apply, export, validate, history, seed)
6368
6670
  diff Show migration plan (desired vs current)
6369
6671
  apply Apply schema (use --allow-destructive for drops; --dry-run to preview)