@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/README.md +1 -1
- package/dist/cli/index.cjs +377 -75
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +377 -75
- package/dist/cli/index.js.map +1 -1
- package/dist/{client-BCMP-Fge.d.cts → client-CsE8Vd6m.d.cts} +168 -24
- package/dist/{client-BCMP-Fge.d.ts → client-CsE8Vd6m.d.ts} +168 -24
- package/dist/index.cjs +382 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -10
- package/dist/index.d.ts +14 -10
- package/dist/index.js +379 -74
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.cjs
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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
|
-
*
|
|
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(
|
|
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
|
|
5338
|
+
const name = warning.name != null && warning.name.length > 0 ? ` name=${warning.name}` : "";
|
|
5094
5339
|
sink({
|
|
5095
5340
|
code: warning.code,
|
|
5096
|
-
|
|
5341
|
+
name: warning.name,
|
|
5097
5342
|
sunsetAt: warning.sunsetAt,
|
|
5098
|
-
message: `${warning.code}:${
|
|
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(
|
|
5118
|
-
if (typeof
|
|
5119
|
-
throw new Error("Named query
|
|
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(
|
|
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(
|
|
5193
|
-
if (typeof
|
|
5194
|
-
throw new Error("Named query
|
|
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",
|
|
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(
|
|
5220
|
-
if (typeof
|
|
5221
|
-
throw new Error("Named mutation
|
|
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(
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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)
|