@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.js
CHANGED
|
@@ -392,7 +392,7 @@ import { fileURLToPath } from "url";
|
|
|
392
392
|
// package.json
|
|
393
393
|
var package_default = {
|
|
394
394
|
name: "@aouda/client",
|
|
395
|
-
version: "0.1.
|
|
395
|
+
version: "0.1.16",
|
|
396
396
|
description: "Official TypeScript/JavaScript client library for Aouda",
|
|
397
397
|
type: "module",
|
|
398
398
|
main: "./dist/index.cjs",
|
|
@@ -485,9 +485,10 @@ var AoudaError = class extends Error {
|
|
|
485
485
|
}
|
|
486
486
|
};
|
|
487
487
|
var AoudaConnectionError = class extends AoudaError {
|
|
488
|
-
constructor(message, cause) {
|
|
488
|
+
constructor(message, cause, rowErrors) {
|
|
489
489
|
super(message);
|
|
490
490
|
this.cause = cause;
|
|
491
|
+
this.rowErrors = rowErrors;
|
|
491
492
|
this.name = "AoudaConnectionError";
|
|
492
493
|
}
|
|
493
494
|
};
|
|
@@ -507,43 +508,45 @@ var AoudaResponseError = class extends AoudaError {
|
|
|
507
508
|
}
|
|
508
509
|
};
|
|
509
510
|
var AoudaApiError = class extends AoudaError {
|
|
510
|
-
constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
|
|
511
|
+
constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
|
|
511
512
|
super(message);
|
|
512
513
|
this.code = code;
|
|
513
514
|
this.statusCode = statusCode;
|
|
514
515
|
this.details = details;
|
|
515
516
|
this.requestId = requestId;
|
|
516
517
|
this.retryAfterSeconds = retryAfterSeconds;
|
|
518
|
+
this.token = token;
|
|
519
|
+
this.rowErrors = rowErrors;
|
|
517
520
|
this.name = "AoudaApiError";
|
|
518
521
|
}
|
|
519
522
|
};
|
|
520
523
|
var AoudaNotFoundError = class extends AoudaApiError {
|
|
521
|
-
constructor(message, code, statusCode, details, requestId) {
|
|
522
|
-
super(message, code, statusCode, details, requestId);
|
|
524
|
+
constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
|
|
525
|
+
super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
|
|
523
526
|
this.name = "AoudaNotFoundError";
|
|
524
527
|
}
|
|
525
528
|
};
|
|
526
529
|
var AoudaConflictError = class extends AoudaApiError {
|
|
527
|
-
constructor(message, code, statusCode, details, requestId) {
|
|
528
|
-
super(message, code, statusCode, details, requestId);
|
|
530
|
+
constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
|
|
531
|
+
super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
|
|
529
532
|
this.name = "AoudaConflictError";
|
|
530
533
|
}
|
|
531
534
|
};
|
|
532
535
|
var AoudaValidationError = class extends AoudaApiError {
|
|
533
|
-
constructor(message, code, statusCode, details, requestId) {
|
|
534
|
-
super(message, code, statusCode, details, requestId);
|
|
536
|
+
constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
|
|
537
|
+
super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
|
|
535
538
|
this.name = "AoudaValidationError";
|
|
536
539
|
}
|
|
537
540
|
};
|
|
538
541
|
var AoudaServerError = class extends AoudaApiError {
|
|
539
|
-
constructor(message, code, statusCode, details, requestId, retryAfterSeconds) {
|
|
540
|
-
super(message, code, statusCode, details, requestId, retryAfterSeconds);
|
|
542
|
+
constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
|
|
543
|
+
super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
|
|
541
544
|
this.name = "AoudaServerError";
|
|
542
545
|
}
|
|
543
546
|
};
|
|
544
547
|
var AoudaAuthenticationError = class extends AoudaApiError {
|
|
545
|
-
constructor(message, code, statusCode, details, requestId) {
|
|
546
|
-
super(message, code, statusCode, details, requestId);
|
|
548
|
+
constructor(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors) {
|
|
549
|
+
super(message, code, statusCode, details, requestId, retryAfterSeconds, token, rowErrors);
|
|
547
550
|
this.name = "AoudaAuthenticationError";
|
|
548
551
|
}
|
|
549
552
|
};
|
|
@@ -1339,7 +1342,7 @@ function buildHandle(jobId, commitResp, wasResumed, transport, database, signal)
|
|
|
1339
1342
|
rowsDurablyCommitted: commitResp.rowsLoaded,
|
|
1340
1343
|
segmentsCreated: commitResp.segmentsCreated,
|
|
1341
1344
|
committedAtUtc: commitResp.committedAtUtc,
|
|
1342
|
-
|
|
1345
|
+
token: commitResp.token,
|
|
1343
1346
|
writeConcernSatisfied: commitResp.writeConcernAchieved ?? "acknowledged",
|
|
1344
1347
|
writeConcernTimedOut: commitResp.writeConcernTimedOut,
|
|
1345
1348
|
wasResumed,
|
|
@@ -1465,6 +1468,7 @@ var PROTOCOL_VERSION_HEADER = "X-Aouda-Protocol-Version";
|
|
|
1465
1468
|
var PROTOCOL_VERSION = "1";
|
|
1466
1469
|
var CONTENT_TYPE_JSON2 = "application/json";
|
|
1467
1470
|
var REQUEST_ID_HEADER2 = "X-Request-Id";
|
|
1471
|
+
var TOKEN_HEADER = "X-Aouda-Token";
|
|
1468
1472
|
var ERROR_CODE_MAP = {
|
|
1469
1473
|
TABLE_NOT_FOUND: AoudaNotFoundError,
|
|
1470
1474
|
COLUMN_NOT_FOUND: AoudaNotFoundError,
|
|
@@ -1479,6 +1483,10 @@ var ERROR_CODE_MAP = {
|
|
|
1479
1483
|
INVALID_OPERATOR: AoudaValidationError,
|
|
1480
1484
|
INVALID_COLUMN: AoudaValidationError,
|
|
1481
1485
|
INVALID_VALUE: AoudaValidationError,
|
|
1486
|
+
CONSTRAINT_CHECK_VIOLATION: AoudaValidationError,
|
|
1487
|
+
TRANSFORM_DERIVED_READONLY: AoudaValidationError,
|
|
1488
|
+
TRANSFORM_ROUTE_UNMATCHED: AoudaValidationError,
|
|
1489
|
+
TRANSFORM_ROUTE_AMBIGUOUS: AoudaValidationError,
|
|
1482
1490
|
UNSUPPORTED_VERSION: AoudaValidationError,
|
|
1483
1491
|
MALFORMED_REQUEST: AoudaValidationError,
|
|
1484
1492
|
INTERNAL_ERROR: AoudaServerError,
|
|
@@ -1504,7 +1512,12 @@ var ERROR_CODE_MAP = {
|
|
|
1504
1512
|
AUTH_IDENTITY_INVALID: AoudaValidationError,
|
|
1505
1513
|
AUTH_IDENTITY_NOT_FOUND: AoudaValidationError,
|
|
1506
1514
|
BULK_LOAD_TRANSFORM_INTENT_REQUIRED: AoudaValidationError,
|
|
1507
|
-
BULK_LOAD_TRANSFORM_INTENT_CONFLICT: AoudaValidationError
|
|
1515
|
+
BULK_LOAD_TRANSFORM_INTENT_CONFLICT: AoudaValidationError,
|
|
1516
|
+
TOKEN_MALFORMED: AoudaValidationError,
|
|
1517
|
+
TOKEN_FOREIGN_DATABASE: AoudaValidationError,
|
|
1518
|
+
TOKEN_EPOCH_SUPERSEDED: AoudaConflictError,
|
|
1519
|
+
TOKEN_UNSATISFIED: AoudaConflictError,
|
|
1520
|
+
TOKEN_FETCH_PRIMARY: AoudaApiError
|
|
1508
1521
|
};
|
|
1509
1522
|
function createComposedAbortController(...signals) {
|
|
1510
1523
|
const controller = new AbortController();
|
|
@@ -1534,6 +1547,9 @@ function parseRetryAfterSeconds(header) {
|
|
|
1534
1547
|
if (!Number.isFinite(n) || n < 0) return void 0;
|
|
1535
1548
|
return n;
|
|
1536
1549
|
}
|
|
1550
|
+
function nonEmptyRowErrors(rowErrors) {
|
|
1551
|
+
return Array.isArray(rowErrors) && rowErrors.length > 0 ? rowErrors : void 0;
|
|
1552
|
+
}
|
|
1537
1553
|
function createApiError(statusCode, statusText, body, retryAfterHeader) {
|
|
1538
1554
|
const message = body.error ?? `${statusCode} ${statusText}`;
|
|
1539
1555
|
const code = body.code ?? "UNKNOWN";
|
|
@@ -1541,7 +1557,16 @@ function createApiError(statusCode, statusText, body, retryAfterHeader) {
|
|
|
1541
1557
|
const requestId = body.requestId;
|
|
1542
1558
|
const retryAfterSeconds = parseRetryAfterSeconds(retryAfterHeader ?? null);
|
|
1543
1559
|
const Ctor = ERROR_CODE_MAP[code] ?? AoudaApiError;
|
|
1544
|
-
return new Ctor(
|
|
1560
|
+
return new Ctor(
|
|
1561
|
+
message,
|
|
1562
|
+
code,
|
|
1563
|
+
statusCode,
|
|
1564
|
+
details,
|
|
1565
|
+
requestId,
|
|
1566
|
+
retryAfterSeconds,
|
|
1567
|
+
body.token,
|
|
1568
|
+
nonEmptyRowErrors(body.rowErrors)
|
|
1569
|
+
);
|
|
1545
1570
|
}
|
|
1546
1571
|
var HttpTransport = class {
|
|
1547
1572
|
constructor(options) {
|
|
@@ -1553,6 +1578,29 @@ var HttpTransport = class {
|
|
|
1553
1578
|
...options.defaultHeaders
|
|
1554
1579
|
};
|
|
1555
1580
|
this.abortController = new AbortController();
|
|
1581
|
+
this.database = options.database;
|
|
1582
|
+
this.store = options.consistencyTokenStore;
|
|
1583
|
+
}
|
|
1584
|
+
presentTokenHeaders(path4, headers) {
|
|
1585
|
+
if (this.store == null || this.database == null || isAuthPath(path4)) {
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
const token = this.store.get(this.database);
|
|
1589
|
+
if (token != null && token.length > 0) {
|
|
1590
|
+
headers[TOKEN_HEADER] = token;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
observeResponse(_path, response, bodyText, errorCode) {
|
|
1594
|
+
if (this.store == null || this.database == null) {
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
if (errorCode === "TOKEN_MALFORMED" || errorCode === "TOKEN_FOREIGN_DATABASE") {
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
const header = response.headers.get(TOKEN_HEADER);
|
|
1601
|
+
const bodyToken = readBodyToken(bodyText);
|
|
1602
|
+
const token = header != null && header.trim().length > 0 ? header : bodyToken;
|
|
1603
|
+
this.store.observe(this.database, token);
|
|
1556
1604
|
}
|
|
1557
1605
|
/**
|
|
1558
1606
|
* Abort all in-flight and future requests (e.g. on client disconnect).
|
|
@@ -1590,6 +1638,7 @@ var HttpTransport = class {
|
|
|
1590
1638
|
...this.defaultHeaders,
|
|
1591
1639
|
...config.headers
|
|
1592
1640
|
};
|
|
1641
|
+
this.presentTokenHeaders(config.path, headers);
|
|
1593
1642
|
if (config.body !== void 0 && config.body !== null) {
|
|
1594
1643
|
headers["Content-Type"] = CONTENT_TYPE_JSON2;
|
|
1595
1644
|
}
|
|
@@ -1609,6 +1658,7 @@ var HttpTransport = class {
|
|
|
1609
1658
|
const text = await response.text();
|
|
1610
1659
|
if (config.allowStatuses?.includes(response.status) && text) {
|
|
1611
1660
|
try {
|
|
1661
|
+
this.observeResponse(config.path, response, text);
|
|
1612
1662
|
return JSON.parse(text);
|
|
1613
1663
|
} catch {
|
|
1614
1664
|
}
|
|
@@ -1622,15 +1672,20 @@ var HttpTransport = class {
|
|
|
1622
1672
|
}
|
|
1623
1673
|
if (response.status === 401) {
|
|
1624
1674
|
const code = errorBody?.code ?? "AUTH_TOKEN_MISSING";
|
|
1675
|
+
this.observeResponse(config.path, response, text, code);
|
|
1625
1676
|
throw new AoudaAuthenticationError(
|
|
1626
1677
|
errorBody?.error ?? "Unauthorized",
|
|
1627
1678
|
code,
|
|
1628
1679
|
401,
|
|
1629
1680
|
errorBody?.details,
|
|
1630
|
-
errorBody?.requestId
|
|
1681
|
+
errorBody?.requestId,
|
|
1682
|
+
void 0,
|
|
1683
|
+
errorBody?.token,
|
|
1684
|
+
nonEmptyRowErrors(errorBody?.rowErrors)
|
|
1631
1685
|
);
|
|
1632
1686
|
}
|
|
1633
1687
|
if (errorBody?.code != null) {
|
|
1688
|
+
this.observeResponse(config.path, response, text, errorBody.code);
|
|
1634
1689
|
throw createApiError(
|
|
1635
1690
|
response.status,
|
|
1636
1691
|
response.statusText,
|
|
@@ -1638,6 +1693,7 @@ var HttpTransport = class {
|
|
|
1638
1693
|
response.headers.get("Retry-After")
|
|
1639
1694
|
);
|
|
1640
1695
|
}
|
|
1696
|
+
this.observeResponse(config.path, response, text);
|
|
1641
1697
|
throw new AoudaResponseError(
|
|
1642
1698
|
errorBody?.error ?? `${response.status} ${response.statusText}`,
|
|
1643
1699
|
response.status,
|
|
@@ -1645,6 +1701,7 @@ var HttpTransport = class {
|
|
|
1645
1701
|
);
|
|
1646
1702
|
}
|
|
1647
1703
|
const responseText = await response.text();
|
|
1704
|
+
this.observeResponse(config.path, response, responseText);
|
|
1648
1705
|
if (config.rawText) {
|
|
1649
1706
|
return responseText;
|
|
1650
1707
|
}
|
|
@@ -1705,6 +1762,7 @@ var HttpTransport = class {
|
|
|
1705
1762
|
...this.defaultHeaders,
|
|
1706
1763
|
...config.headers
|
|
1707
1764
|
};
|
|
1765
|
+
this.presentTokenHeaders(config.path, headers);
|
|
1708
1766
|
if (config.rawBodyStr === void 0 && config.body !== void 0 && config.body !== null) {
|
|
1709
1767
|
headers["Content-Type"] = CONTENT_TYPE_JSON2;
|
|
1710
1768
|
}
|
|
@@ -1721,6 +1779,7 @@ var HttpTransport = class {
|
|
|
1721
1779
|
try {
|
|
1722
1780
|
const response = await localNetworkFetch(url, init);
|
|
1723
1781
|
if (response.ok || config.allowStatuses?.includes(response.status)) {
|
|
1782
|
+
this.observeResponse(config.path, response, void 0);
|
|
1724
1783
|
return response;
|
|
1725
1784
|
}
|
|
1726
1785
|
const text = await response.text();
|
|
@@ -1733,15 +1792,20 @@ var HttpTransport = class {
|
|
|
1733
1792
|
}
|
|
1734
1793
|
if (response.status === 401) {
|
|
1735
1794
|
const code = errorBody?.code ?? "AUTH_TOKEN_MISSING";
|
|
1795
|
+
this.observeResponse(config.path, response, text, code);
|
|
1736
1796
|
throw new AoudaAuthenticationError(
|
|
1737
1797
|
errorBody?.error ?? "Unauthorized",
|
|
1738
1798
|
code,
|
|
1739
1799
|
401,
|
|
1740
1800
|
errorBody?.details,
|
|
1741
|
-
errorBody?.requestId
|
|
1801
|
+
errorBody?.requestId,
|
|
1802
|
+
void 0,
|
|
1803
|
+
errorBody?.token,
|
|
1804
|
+
nonEmptyRowErrors(errorBody?.rowErrors)
|
|
1742
1805
|
);
|
|
1743
1806
|
}
|
|
1744
1807
|
if (errorBody?.code != null) {
|
|
1808
|
+
this.observeResponse(config.path, response, text, errorBody.code);
|
|
1745
1809
|
throw createApiError(
|
|
1746
1810
|
response.status,
|
|
1747
1811
|
response.statusText,
|
|
@@ -1749,6 +1813,7 @@ var HttpTransport = class {
|
|
|
1749
1813
|
response.headers.get("Retry-After")
|
|
1750
1814
|
);
|
|
1751
1815
|
}
|
|
1816
|
+
this.observeResponse(config.path, response, text);
|
|
1752
1817
|
throw new AoudaResponseError(
|
|
1753
1818
|
errorBody?.error ?? `${response.status} ${response.statusText}`,
|
|
1754
1819
|
response.status,
|
|
@@ -1903,6 +1968,47 @@ var HttpTransport = class {
|
|
|
1903
1968
|
});
|
|
1904
1969
|
}
|
|
1905
1970
|
};
|
|
1971
|
+
function isAuthPath(path4) {
|
|
1972
|
+
let p = path4;
|
|
1973
|
+
const q = p.indexOf("?");
|
|
1974
|
+
if (q >= 0) {
|
|
1975
|
+
p = p.slice(0, q);
|
|
1976
|
+
}
|
|
1977
|
+
if (/^https?:\/\//i.test(p)) {
|
|
1978
|
+
try {
|
|
1979
|
+
p = new URL(p).pathname;
|
|
1980
|
+
} catch {
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
const lower = p.toLowerCase();
|
|
1984
|
+
if (lower === "/api/auth" || lower.startsWith("/api/auth/")) {
|
|
1985
|
+
return true;
|
|
1986
|
+
}
|
|
1987
|
+
const prefix = "/api/databases/";
|
|
1988
|
+
if (!lower.startsWith(prefix)) {
|
|
1989
|
+
return false;
|
|
1990
|
+
}
|
|
1991
|
+
const rest = lower.slice(prefix.length);
|
|
1992
|
+
const slash = rest.indexOf("/");
|
|
1993
|
+
if (slash < 0) {
|
|
1994
|
+
return false;
|
|
1995
|
+
}
|
|
1996
|
+
const afterDb = rest.slice(slash + 1);
|
|
1997
|
+
return afterDb === "auth" || afterDb.startsWith("auth/");
|
|
1998
|
+
}
|
|
1999
|
+
function readBodyToken(bodyText) {
|
|
2000
|
+
if (bodyText == null || bodyText.trim().length === 0) {
|
|
2001
|
+
return void 0;
|
|
2002
|
+
}
|
|
2003
|
+
try {
|
|
2004
|
+
const parsed = JSON.parse(bodyText);
|
|
2005
|
+
if (parsed !== null && typeof parsed === "object" && "token" in parsed && typeof parsed.token === "string") {
|
|
2006
|
+
return parsed.token;
|
|
2007
|
+
}
|
|
2008
|
+
} catch {
|
|
2009
|
+
}
|
|
2010
|
+
return void 0;
|
|
2011
|
+
}
|
|
1906
2012
|
|
|
1907
2013
|
// src/resilience/retry.ts
|
|
1908
2014
|
var DEFAULT_MAX_RETRIES = 3;
|
|
@@ -2335,6 +2441,48 @@ function databasePath2(db) {
|
|
|
2335
2441
|
return `/api/databases/${encodeURIComponent(db)}`;
|
|
2336
2442
|
}
|
|
2337
2443
|
|
|
2444
|
+
// src/consistency-token-store.ts
|
|
2445
|
+
var MemoryConsistencyTokenStore = class {
|
|
2446
|
+
constructor() {
|
|
2447
|
+
this.tokens = /* @__PURE__ */ new Map();
|
|
2448
|
+
}
|
|
2449
|
+
get(database) {
|
|
2450
|
+
if (typeof database !== "string" || database.trim().length === 0) {
|
|
2451
|
+
throw new Error("database must be a non-empty string");
|
|
2452
|
+
}
|
|
2453
|
+
return this.tokens.get(normalizeKey(database));
|
|
2454
|
+
}
|
|
2455
|
+
observe(database, token) {
|
|
2456
|
+
if (typeof database !== "string" || database.trim().length === 0) {
|
|
2457
|
+
throw new Error("database must be a non-empty string");
|
|
2458
|
+
}
|
|
2459
|
+
if (token == null || token.trim().length === 0) {
|
|
2460
|
+
return;
|
|
2461
|
+
}
|
|
2462
|
+
const key = normalizeKey(database);
|
|
2463
|
+
const stored = this.tokens.get(key);
|
|
2464
|
+
if (stored === void 0 || compareOrdinal(token, stored) > 0) {
|
|
2465
|
+
this.tokens.set(key, token);
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
};
|
|
2469
|
+
function compareOrdinal(left, right) {
|
|
2470
|
+
if (left === right) return 0;
|
|
2471
|
+
return left < right ? -1 : 1;
|
|
2472
|
+
}
|
|
2473
|
+
function maxToken(left, right) {
|
|
2474
|
+
if (left == null || left.length === 0) {
|
|
2475
|
+
return right == null || right.length === 0 ? void 0 : right;
|
|
2476
|
+
}
|
|
2477
|
+
if (right == null || right.length === 0) {
|
|
2478
|
+
return left;
|
|
2479
|
+
}
|
|
2480
|
+
return compareOrdinal(left, right) >= 0 ? left : right;
|
|
2481
|
+
}
|
|
2482
|
+
function normalizeKey(database) {
|
|
2483
|
+
return database.toLowerCase();
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2338
2486
|
// src/streaming/subscription.ts
|
|
2339
2487
|
var AsyncEventQueue = class {
|
|
2340
2488
|
constructor() {
|
|
@@ -2391,7 +2539,7 @@ var AsyncEventQueue = class {
|
|
|
2391
2539
|
}
|
|
2392
2540
|
};
|
|
2393
2541
|
var TableSubscription = class {
|
|
2394
|
-
constructor(transport, identity, options = {}, onWarnings) {
|
|
2542
|
+
constructor(transport, identity, options = {}, onWarnings, store, database) {
|
|
2395
2543
|
this._queue = new AsyncEventQueue();
|
|
2396
2544
|
this._active = true;
|
|
2397
2545
|
this._started = false;
|
|
@@ -2407,6 +2555,11 @@ var TableSubscription = class {
|
|
|
2407
2555
|
this._onError = options.onError;
|
|
2408
2556
|
this._onWarnings = onWarnings;
|
|
2409
2557
|
this._conflate = options.conflate;
|
|
2558
|
+
this._atLeast = options.atLeast;
|
|
2559
|
+
this._waitMs = options.waitMs;
|
|
2560
|
+
this._onExceeded = options.onExceeded;
|
|
2561
|
+
this._store = store;
|
|
2562
|
+
this._database = database;
|
|
2410
2563
|
this._reconnectHandlerKey = `${this.id}::reconnect`;
|
|
2411
2564
|
}
|
|
2412
2565
|
get lastVersion() {
|
|
@@ -2471,7 +2624,7 @@ var TableSubscription = class {
|
|
|
2471
2624
|
id: this.id
|
|
2472
2625
|
};
|
|
2473
2626
|
if (this._identity.kind === "named") {
|
|
2474
|
-
message.
|
|
2627
|
+
message.name = this._identity.name;
|
|
2475
2628
|
if (this._identity.args !== void 0) {
|
|
2476
2629
|
message.args = this._identity.args;
|
|
2477
2630
|
}
|
|
@@ -2490,8 +2643,30 @@ var TableSubscription = class {
|
|
|
2490
2643
|
if (this._conflate !== void 0) {
|
|
2491
2644
|
message.conflate = this._conflate;
|
|
2492
2645
|
}
|
|
2646
|
+
const pin = this._resolvePin();
|
|
2647
|
+
if (pin !== void 0) {
|
|
2648
|
+
message.at_least = pin;
|
|
2649
|
+
}
|
|
2650
|
+
if (this._waitMs !== void 0) {
|
|
2651
|
+
message.wait_ms = this._waitMs;
|
|
2652
|
+
}
|
|
2653
|
+
if (this._onExceeded !== void 0) {
|
|
2654
|
+
message.on_exceeded = this._onExceeded;
|
|
2655
|
+
}
|
|
2493
2656
|
await this._transport.send(message);
|
|
2494
2657
|
}
|
|
2658
|
+
_resolvePin() {
|
|
2659
|
+
if (this._atLeast !== void 0 && this._store != null && this._database != null) {
|
|
2660
|
+
this._store.observe(this._database, this._atLeast);
|
|
2661
|
+
}
|
|
2662
|
+
const stored = this._store != null && this._database != null ? this._store.get(this._database) : void 0;
|
|
2663
|
+
return maxToken(this._atLeast, stored);
|
|
2664
|
+
}
|
|
2665
|
+
_observeToken(token) {
|
|
2666
|
+
if (token !== void 0 && this._store != null && this._database != null) {
|
|
2667
|
+
this._store.observe(this._database, token);
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2495
2670
|
_handleMessage(message) {
|
|
2496
2671
|
if (!this._active) {
|
|
2497
2672
|
return;
|
|
@@ -2518,6 +2693,7 @@ var TableSubscription = class {
|
|
|
2518
2693
|
}
|
|
2519
2694
|
_handleSnapshotPage(message) {
|
|
2520
2695
|
this._pendingSnapshotRows.push(...message.rows);
|
|
2696
|
+
this._observeToken(message.token);
|
|
2521
2697
|
}
|
|
2522
2698
|
_handleSnapshotComplete(message) {
|
|
2523
2699
|
this._lastVersion = message.version;
|
|
@@ -2535,6 +2711,10 @@ var TableSubscription = class {
|
|
|
2535
2711
|
if (message.total_matches !== void 0) {
|
|
2536
2712
|
snapshot.totalMatches = message.total_matches;
|
|
2537
2713
|
}
|
|
2714
|
+
if (message.token !== void 0) {
|
|
2715
|
+
snapshot.token = message.token;
|
|
2716
|
+
this._observeToken(message.token);
|
|
2717
|
+
}
|
|
2538
2718
|
this._queue.push(snapshot);
|
|
2539
2719
|
}
|
|
2540
2720
|
async _handleGap(message) {
|
|
@@ -2560,6 +2740,10 @@ var TableSubscription = class {
|
|
|
2560
2740
|
if (message.values_skipped !== void 0) {
|
|
2561
2741
|
event.values_skipped = message.values_skipped;
|
|
2562
2742
|
}
|
|
2743
|
+
if (message.token !== void 0) {
|
|
2744
|
+
event.token = message.token;
|
|
2745
|
+
this._observeToken(message.token);
|
|
2746
|
+
}
|
|
2563
2747
|
this._onChange?.(event);
|
|
2564
2748
|
this._queue.push(event);
|
|
2565
2749
|
}
|
|
@@ -2707,8 +2891,11 @@ var TableWriteStream = class {
|
|
|
2707
2891
|
resolve4?.();
|
|
2708
2892
|
}
|
|
2709
2893
|
_handleServerError(message) {
|
|
2894
|
+
const rowErrors = Array.isArray(message.errors) && message.errors.length > 0 ? message.errors : void 0;
|
|
2710
2895
|
const error = new AoudaConnectionError(
|
|
2711
|
-
`Write stream error (${message.code}): ${message.message}
|
|
2896
|
+
`Write stream error (${message.code}): ${message.message}`,
|
|
2897
|
+
void 0,
|
|
2898
|
+
rowErrors
|
|
2712
2899
|
);
|
|
2713
2900
|
const openReject = this._openReject;
|
|
2714
2901
|
this._openResolve = null;
|
|
@@ -2904,11 +3091,12 @@ var TableQuery = class _TableQuery {
|
|
|
2904
3091
|
* @param state - Optional initial state (used for immutable chaining).
|
|
2905
3092
|
* @internal Use `client.table()` to create queries.
|
|
2906
3093
|
*/
|
|
2907
|
-
constructor(transport, tableName, database, state, getWebSocketTransport) {
|
|
3094
|
+
constructor(transport, tableName, database, state, getWebSocketTransport, store) {
|
|
2908
3095
|
this.transport = transport;
|
|
2909
3096
|
this.tableName = tableName;
|
|
2910
3097
|
this.database = database;
|
|
2911
3098
|
this.getWebSocketTransport = getWebSocketTransport;
|
|
3099
|
+
this.store = store;
|
|
2912
3100
|
this.state = state ?? {
|
|
2913
3101
|
predicates: [],
|
|
2914
3102
|
groupClauses: [],
|
|
@@ -2924,6 +3112,16 @@ var TableQuery = class _TableQuery {
|
|
|
2924
3112
|
isDistinct: false
|
|
2925
3113
|
};
|
|
2926
3114
|
}
|
|
3115
|
+
withState(state) {
|
|
3116
|
+
return new _TableQuery(
|
|
3117
|
+
this.transport,
|
|
3118
|
+
this.tableName,
|
|
3119
|
+
this.database,
|
|
3120
|
+
state,
|
|
3121
|
+
this.getWebSocketTransport,
|
|
3122
|
+
this.store
|
|
3123
|
+
);
|
|
3124
|
+
}
|
|
2927
3125
|
where(column, operator, value) {
|
|
2928
3126
|
const newPredicates = buildWherePredicates(column, operator, value);
|
|
2929
3127
|
return new _TableQuery(
|
|
@@ -2934,7 +3132,8 @@ var TableQuery = class _TableQuery {
|
|
|
2934
3132
|
...this.state,
|
|
2935
3133
|
predicates: [...this.state.predicates, ...newPredicates]
|
|
2936
3134
|
},
|
|
2937
|
-
this.getWebSocketTransport
|
|
3135
|
+
this.getWebSocketTransport,
|
|
3136
|
+
this.store
|
|
2938
3137
|
);
|
|
2939
3138
|
}
|
|
2940
3139
|
/**
|
|
@@ -2953,7 +3152,7 @@ var TableQuery = class _TableQuery {
|
|
|
2953
3152
|
return new _TableQuery(this.transport, this.tableName, this.database, {
|
|
2954
3153
|
...this.state,
|
|
2955
3154
|
groupClauses: [...this.state.groupClauses, sub]
|
|
2956
|
-
}, this.getWebSocketTransport);
|
|
3155
|
+
}, this.getWebSocketTransport, this.store);
|
|
2957
3156
|
}
|
|
2958
3157
|
/**
|
|
2959
3158
|
* Sets the primary sort column for the query.
|
|
@@ -2978,7 +3177,7 @@ var TableQuery = class _TableQuery {
|
|
|
2978
3177
|
return new _TableQuery(this.transport, this.tableName, this.database, {
|
|
2979
3178
|
...this.state,
|
|
2980
3179
|
orderByClauses: [orderByClause]
|
|
2981
|
-
}, this.getWebSocketTransport);
|
|
3180
|
+
}, this.getWebSocketTransport, this.store);
|
|
2982
3181
|
}
|
|
2983
3182
|
/**
|
|
2984
3183
|
* Sets the primary sort column to descending order.
|
|
@@ -3024,7 +3223,7 @@ var TableQuery = class _TableQuery {
|
|
|
3024
3223
|
return new _TableQuery(this.transport, this.tableName, this.database, {
|
|
3025
3224
|
...this.state,
|
|
3026
3225
|
orderByClauses: [...this.state.orderByClauses, orderByClause]
|
|
3027
|
-
}, this.getWebSocketTransport);
|
|
3226
|
+
}, this.getWebSocketTransport, this.store);
|
|
3028
3227
|
}
|
|
3029
3228
|
/**
|
|
3030
3229
|
* Sets the maximum number of rows to return.
|
|
@@ -3041,7 +3240,7 @@ var TableQuery = class _TableQuery {
|
|
|
3041
3240
|
return new _TableQuery(this.transport, this.tableName, this.database, {
|
|
3042
3241
|
...this.state,
|
|
3043
3242
|
limitValue: count
|
|
3044
|
-
}, this.getWebSocketTransport);
|
|
3243
|
+
}, this.getWebSocketTransport, this.store);
|
|
3045
3244
|
}
|
|
3046
3245
|
/**
|
|
3047
3246
|
* Sets the number of rows to skip.
|
|
@@ -3058,7 +3257,7 @@ var TableQuery = class _TableQuery {
|
|
|
3058
3257
|
return new _TableQuery(this.transport, this.tableName, this.database, {
|
|
3059
3258
|
...this.state,
|
|
3060
3259
|
offsetValue: count
|
|
3061
|
-
}, this.getWebSocketTransport);
|
|
3260
|
+
}, this.getWebSocketTransport, this.store);
|
|
3062
3261
|
}
|
|
3063
3262
|
/**
|
|
3064
3263
|
* Requests cross-partition access for this query.
|
|
@@ -3073,10 +3272,21 @@ var TableQuery = class _TableQuery {
|
|
|
3073
3272
|
return new _TableQuery(this.transport, this.tableName, this.database, {
|
|
3074
3273
|
...this.state,
|
|
3075
3274
|
crossPartitionAccess: true
|
|
3076
|
-
}, this.getWebSocketTransport);
|
|
3275
|
+
}, this.getWebSocketTransport, this.store);
|
|
3077
3276
|
}
|
|
3078
3277
|
/**
|
|
3079
|
-
*
|
|
3278
|
+
* Pin this query at at least this C-1 token. Observes the token into the
|
|
3279
|
+
* client store (I3, sticky) and presents it on execute via `X-Aouda-Token`.
|
|
3280
|
+
*/
|
|
3281
|
+
atLeast(token) {
|
|
3282
|
+
if (typeof token !== "string" || token.trim().length === 0) {
|
|
3283
|
+
throw new Error("atLeast() requires a non-empty token");
|
|
3284
|
+
}
|
|
3285
|
+
this.store?.observe(this.database, token);
|
|
3286
|
+
return this.withState({ ...this.state, atLeast: token });
|
|
3287
|
+
}
|
|
3288
|
+
/**
|
|
3289
|
+
* Selects specific columns to return.
|
|
3080
3290
|
* If not called, all columns are returned.
|
|
3081
3291
|
*
|
|
3082
3292
|
* When T is a specific row type, only keys of T are accepted as column names.
|
|
@@ -3098,7 +3308,7 @@ var TableQuery = class _TableQuery {
|
|
|
3098
3308
|
return new _TableQuery(this.transport, this.tableName, this.database, {
|
|
3099
3309
|
...this.state,
|
|
3100
3310
|
selectColumns: columns.length > 0 ? columns : null
|
|
3101
|
-
}, this.getWebSocketTransport);
|
|
3311
|
+
}, this.getWebSocketTransport, this.store);
|
|
3102
3312
|
}
|
|
3103
3313
|
/**
|
|
3104
3314
|
* Return only distinct (de-duplicated) rows for the given columns — SQL `SELECT DISTINCT`.
|
|
@@ -3128,7 +3338,7 @@ var TableQuery = class _TableQuery {
|
|
|
3128
3338
|
...this.state,
|
|
3129
3339
|
selectColumns: columns,
|
|
3130
3340
|
isDistinct: true
|
|
3131
|
-
}, this.getWebSocketTransport);
|
|
3341
|
+
}, this.getWebSocketTransport, this.store);
|
|
3132
3342
|
}
|
|
3133
3343
|
/**
|
|
3134
3344
|
* Adds server-side computed columns to the query result.
|
|
@@ -3137,6 +3347,12 @@ var TableQuery = class _TableQuery {
|
|
|
3137
3347
|
* evaluated per row on the server. Computed columns are appended after any physical-column
|
|
3138
3348
|
* `select()` projection.
|
|
3139
3349
|
*
|
|
3350
|
+
* Result types are inferred by the server where the expression permits
|
|
3351
|
+
* (e.g. Int32 → `number`). Uninferable expressions use wire `"Unknown"` /
|
|
3352
|
+
* codegen `unknown`. Computed columns are always nullable. Named-query
|
|
3353
|
+
* `*Row` properties pick this up when regenerated against a post-S08 server.
|
|
3354
|
+
* See aouda-docs/guides/browser-tier-read-limits.md#selectexpr-result-types
|
|
3355
|
+
*
|
|
3140
3356
|
* @param projections - One or more `{ alias, expr }` pairs.
|
|
3141
3357
|
* @returns A new TableQuery with computed columns set.
|
|
3142
3358
|
*
|
|
@@ -3157,7 +3373,7 @@ var TableQuery = class _TableQuery {
|
|
|
3157
3373
|
return new _TableQuery(this.transport, this.tableName, this.database, {
|
|
3158
3374
|
...this.state,
|
|
3159
3375
|
selectExprs: projections
|
|
3160
|
-
}, this.getWebSocketTransport);
|
|
3376
|
+
}, this.getWebSocketTransport, this.store);
|
|
3161
3377
|
}
|
|
3162
3378
|
join(rightTable, leftColumnOrColumns, rightColumnOrColumns) {
|
|
3163
3379
|
return this.addJoinClause(
|
|
@@ -3215,7 +3431,8 @@ var TableQuery = class _TableQuery {
|
|
|
3215
3431
|
}
|
|
3216
3432
|
]
|
|
3217
3433
|
},
|
|
3218
|
-
this.getWebSocketTransport
|
|
3434
|
+
this.getWebSocketTransport,
|
|
3435
|
+
this.store
|
|
3219
3436
|
);
|
|
3220
3437
|
}
|
|
3221
3438
|
/**
|
|
@@ -3251,7 +3468,8 @@ var TableQuery = class _TableQuery {
|
|
|
3251
3468
|
...this.state,
|
|
3252
3469
|
groupByColumns: [...columns]
|
|
3253
3470
|
},
|
|
3254
|
-
this.getWebSocketTransport
|
|
3471
|
+
this.getWebSocketTransport,
|
|
3472
|
+
this.store
|
|
3255
3473
|
);
|
|
3256
3474
|
}
|
|
3257
3475
|
/**
|
|
@@ -3285,8 +3503,11 @@ var TableQuery = class _TableQuery {
|
|
|
3285
3503
|
onSnapshot: options.onSnapshot,
|
|
3286
3504
|
onChange: options.onChange,
|
|
3287
3505
|
onError: options.onError,
|
|
3288
|
-
conflate: options.conflate
|
|
3289
|
-
|
|
3506
|
+
conflate: options.conflate,
|
|
3507
|
+
atLeast: options.atLeast ?? this.state.atLeast,
|
|
3508
|
+
waitMs: options.waitMs,
|
|
3509
|
+
onExceeded: options.onExceeded
|
|
3510
|
+
}, void 0, this.store, this.database);
|
|
3290
3511
|
subscription.start();
|
|
3291
3512
|
return subscription;
|
|
3292
3513
|
}
|
|
@@ -3401,7 +3622,8 @@ var TableQuery = class _TableQuery {
|
|
|
3401
3622
|
...this.state,
|
|
3402
3623
|
joinClauses: [...this.state.joinClauses, joinClause]
|
|
3403
3624
|
},
|
|
3404
|
-
this.getWebSocketTransport
|
|
3625
|
+
this.getWebSocketTransport,
|
|
3626
|
+
this.store
|
|
3405
3627
|
);
|
|
3406
3628
|
}
|
|
3407
3629
|
addAggregate(op, column) {
|
|
@@ -3422,7 +3644,8 @@ var TableQuery = class _TableQuery {
|
|
|
3422
3644
|
}
|
|
3423
3645
|
]
|
|
3424
3646
|
},
|
|
3425
|
-
this.getWebSocketTransport
|
|
3647
|
+
this.getWebSocketTransport,
|
|
3648
|
+
this.store
|
|
3426
3649
|
);
|
|
3427
3650
|
}
|
|
3428
3651
|
requireWebSocketTransport() {
|
|
@@ -3452,17 +3675,23 @@ var TableQuery = class _TableQuery {
|
|
|
3452
3675
|
* ```
|
|
3453
3676
|
*/
|
|
3454
3677
|
async execute() {
|
|
3678
|
+
if (this.state.atLeast) {
|
|
3679
|
+
this.store?.observe(this.database, this.state.atLeast);
|
|
3680
|
+
}
|
|
3455
3681
|
const request = this.buildRequest();
|
|
3456
3682
|
const path4 = `${databasePath2(this.database)}/query`;
|
|
3457
3683
|
const response = await this.transport.post(path4, request);
|
|
3458
3684
|
const rows = columnarToRows(response);
|
|
3459
3685
|
const stats = response.stats;
|
|
3460
|
-
return { rows, stats };
|
|
3686
|
+
return { rows, stats, token: response.token };
|
|
3461
3687
|
}
|
|
3462
3688
|
/**
|
|
3463
3689
|
* Executes the query and returns the raw columnar JSON payload (no row-object conversion).
|
|
3464
3690
|
*/
|
|
3465
3691
|
async toColumnar() {
|
|
3692
|
+
if (this.state.atLeast) {
|
|
3693
|
+
this.store?.observe(this.database, this.state.atLeast);
|
|
3694
|
+
}
|
|
3466
3695
|
const request = this.buildRequest();
|
|
3467
3696
|
const path4 = `${databasePath2(this.database)}/query`;
|
|
3468
3697
|
return this.transport.post(path4, request);
|
|
@@ -3492,8 +3721,12 @@ var TableQuery = class _TableQuery {
|
|
|
3492
3721
|
limitValue: 0,
|
|
3493
3722
|
selectColumns: []
|
|
3494
3723
|
},
|
|
3495
|
-
this.getWebSocketTransport
|
|
3724
|
+
this.getWebSocketTransport,
|
|
3725
|
+
this.store
|
|
3496
3726
|
);
|
|
3727
|
+
if (this.state.atLeast) {
|
|
3728
|
+
this.store?.observe(this.database, this.state.atLeast);
|
|
3729
|
+
}
|
|
3497
3730
|
const request = countQuery.buildRequest();
|
|
3498
3731
|
const path4 = `${databasePath2(this.database)}/query`;
|
|
3499
3732
|
const response = await this.transport.post(path4, request);
|
|
@@ -3540,7 +3773,8 @@ var TableQuery = class _TableQuery {
|
|
|
3540
3773
|
const response = await this.transport.post(path4, body);
|
|
3541
3774
|
const result = {
|
|
3542
3775
|
rowsInserted: response.rowsInserted,
|
|
3543
|
-
executionMs: response.executionMs
|
|
3776
|
+
executionMs: response.executionMs,
|
|
3777
|
+
token: response.token
|
|
3544
3778
|
};
|
|
3545
3779
|
if (response.generatedValues !== void 0) {
|
|
3546
3780
|
result.generatedValues = response.generatedValues;
|
|
@@ -3590,7 +3824,8 @@ var TableQuery = class _TableQuery {
|
|
|
3590
3824
|
const response = await this.transport.post(path4, body);
|
|
3591
3825
|
const result = {
|
|
3592
3826
|
rowsInserted: response.rowsInserted,
|
|
3593
|
-
executionMs: response.executionMs
|
|
3827
|
+
executionMs: response.executionMs,
|
|
3828
|
+
token: response.token
|
|
3594
3829
|
};
|
|
3595
3830
|
if (response.generatedValues !== void 0) {
|
|
3596
3831
|
result.generatedValues = response.generatedValues;
|
|
@@ -3642,6 +3877,7 @@ var TableQuery = class _TableQuery {
|
|
|
3642
3877
|
return {
|
|
3643
3878
|
rowsAffected: response.rowsUpdated,
|
|
3644
3879
|
executionMs: response.executionMs,
|
|
3880
|
+
token: response.token,
|
|
3645
3881
|
...response.rows ? { rows: columnarToRows(response.rows) } : {},
|
|
3646
3882
|
...response.rowsTruncated ? { rowsTruncated: true } : {}
|
|
3647
3883
|
};
|
|
@@ -3692,6 +3928,7 @@ var TableQuery = class _TableQuery {
|
|
|
3692
3928
|
rowsAffected: response.rowsDeleted,
|
|
3693
3929
|
executionMs: response.executionMs,
|
|
3694
3930
|
hasMore: response.hasMore,
|
|
3931
|
+
token: response.token,
|
|
3695
3932
|
...response.rows ? { rows: columnarToRows(response.rows) } : {},
|
|
3696
3933
|
...response.rowsTruncated ? { rowsTruncated: true } : {}
|
|
3697
3934
|
};
|
|
@@ -3711,7 +3948,8 @@ var TableQuery = class _TableQuery {
|
|
|
3711
3948
|
return {
|
|
3712
3949
|
rowsAffected: response.rowsDeleted,
|
|
3713
3950
|
executionMs: response.executionMs,
|
|
3714
|
-
hasMore: false
|
|
3951
|
+
hasMore: false,
|
|
3952
|
+
token: response.token
|
|
3715
3953
|
};
|
|
3716
3954
|
}
|
|
3717
3955
|
/**
|
|
@@ -3724,7 +3962,14 @@ var TableQuery = class _TableQuery {
|
|
|
3724
3962
|
throw new Error("batch() requires a non-empty operations array");
|
|
3725
3963
|
}
|
|
3726
3964
|
const wireOperations = operations.map((op, index) => {
|
|
3727
|
-
const base = new _TableQuery(
|
|
3965
|
+
const base = new _TableQuery(
|
|
3966
|
+
this.transport,
|
|
3967
|
+
this.tableName,
|
|
3968
|
+
this.database,
|
|
3969
|
+
void 0,
|
|
3970
|
+
this.getWebSocketTransport,
|
|
3971
|
+
this.store
|
|
3972
|
+
);
|
|
3728
3973
|
const scoped = op.where(base);
|
|
3729
3974
|
const where = scoped.buildWhereClause();
|
|
3730
3975
|
if (!where) {
|
|
@@ -5061,12 +5306,12 @@ function raiseDeprecationWarnings(sink, warnings) {
|
|
|
5061
5306
|
continue;
|
|
5062
5307
|
}
|
|
5063
5308
|
const sunset = warning.sunsetAt != null ? ` sunsetAt=${warning.sunsetAt}` : "";
|
|
5064
|
-
const
|
|
5309
|
+
const name = warning.name != null && warning.name.length > 0 ? ` name=${warning.name}` : "";
|
|
5065
5310
|
sink({
|
|
5066
5311
|
code: warning.code,
|
|
5067
|
-
|
|
5312
|
+
name: warning.name,
|
|
5068
5313
|
sunsetAt: warning.sunsetAt,
|
|
5069
|
-
message: `${warning.code}:${
|
|
5314
|
+
message: `${warning.code}:${name}${sunset}`.trim()
|
|
5070
5315
|
});
|
|
5071
5316
|
}
|
|
5072
5317
|
}
|
|
@@ -5079,24 +5324,28 @@ function emptyStats() {
|
|
|
5079
5324
|
};
|
|
5080
5325
|
}
|
|
5081
5326
|
var NamedQueriesApi = class {
|
|
5082
|
-
constructor(transport, database, onWarning, getStreamingTransport) {
|
|
5327
|
+
constructor(transport, database, onWarning, getStreamingTransport, store) {
|
|
5083
5328
|
this.transport = transport;
|
|
5084
5329
|
this.database = database;
|
|
5085
5330
|
this.onWarning = onWarning;
|
|
5086
5331
|
this.getStreamingTransport = getStreamingTransport;
|
|
5332
|
+
this.store = store;
|
|
5087
5333
|
}
|
|
5088
|
-
async execute(
|
|
5089
|
-
if (typeof
|
|
5090
|
-
throw new Error("Named query
|
|
5334
|
+
async execute(name, args, options) {
|
|
5335
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
5336
|
+
throw new Error("Named query name must be a non-empty string");
|
|
5091
5337
|
}
|
|
5092
5338
|
const prefix = databasePath2(this.database);
|
|
5093
|
-
const path4 = `${prefix}/named-queries/${encodeURIComponent(
|
|
5339
|
+
const path4 = `${prefix}/named-queries/${encodeURIComponent(name)}/query?format=columnar`;
|
|
5094
5340
|
const body = {
|
|
5095
5341
|
args: args ?? {}
|
|
5096
5342
|
};
|
|
5097
5343
|
if (options?.orderByIndex !== void 0) {
|
|
5098
5344
|
body.orderByIndex = options.orderByIndex;
|
|
5099
5345
|
}
|
|
5346
|
+
if (options?.atLeast !== void 0) {
|
|
5347
|
+
this.store?.observe(this.database, options.atLeast);
|
|
5348
|
+
}
|
|
5100
5349
|
const response = await this.transport.post(
|
|
5101
5350
|
path4,
|
|
5102
5351
|
body,
|
|
@@ -5108,7 +5357,8 @@ var NamedQueriesApi = class {
|
|
|
5108
5357
|
rows,
|
|
5109
5358
|
stats: response.stats,
|
|
5110
5359
|
warnings: response.warnings,
|
|
5111
|
-
totalMatches: response.totalMatches
|
|
5360
|
+
totalMatches: response.totalMatches,
|
|
5361
|
+
token: response.token
|
|
5112
5362
|
};
|
|
5113
5363
|
}
|
|
5114
5364
|
async batch(items, options) {
|
|
@@ -5126,6 +5376,9 @@ var NamedQueriesApi = class {
|
|
|
5126
5376
|
400
|
|
5127
5377
|
);
|
|
5128
5378
|
}
|
|
5379
|
+
if (options?.atLeast !== void 0) {
|
|
5380
|
+
this.store?.observe(this.database, options.atLeast);
|
|
5381
|
+
}
|
|
5129
5382
|
const prefix = databasePath2(this.database);
|
|
5130
5383
|
const path4 = `${prefix}/named-queries/batch?format=columnar`;
|
|
5131
5384
|
const envelope = await this.transport.post(
|
|
@@ -5148,34 +5401,41 @@ var NamedQueriesApi = class {
|
|
|
5148
5401
|
rowCount: slot.rowCount ?? 0,
|
|
5149
5402
|
stats: slot.stats ?? emptyStats(),
|
|
5150
5403
|
warnings: slot.warnings,
|
|
5151
|
-
totalMatches: slot.totalMatches
|
|
5404
|
+
totalMatches: slot.totalMatches,
|
|
5405
|
+
token: slot.token
|
|
5152
5406
|
};
|
|
5153
5407
|
const result = {
|
|
5154
5408
|
rows: columnarToRows(columnar),
|
|
5155
5409
|
stats: columnar.stats,
|
|
5156
5410
|
warnings: slot.warnings,
|
|
5157
|
-
totalMatches: slot.totalMatches
|
|
5411
|
+
totalMatches: slot.totalMatches,
|
|
5412
|
+
token: slot.token
|
|
5158
5413
|
};
|
|
5159
5414
|
raiseDeprecationWarnings(this.onWarning, slot.warnings);
|
|
5160
5415
|
return { isError: false, result };
|
|
5161
5416
|
});
|
|
5162
5417
|
}
|
|
5163
|
-
subscribe(
|
|
5164
|
-
if (typeof
|
|
5165
|
-
throw new Error("Named query
|
|
5418
|
+
subscribe(name, args, options = {}) {
|
|
5419
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
5420
|
+
throw new Error("Named query name must be a non-empty string");
|
|
5166
5421
|
}
|
|
5167
5422
|
const subscription = new TableSubscription(
|
|
5168
5423
|
this.getStreamingTransport(),
|
|
5169
|
-
{ kind: "named",
|
|
5424
|
+
{ kind: "named", name, args, orderByIndex: options.orderByIndex },
|
|
5170
5425
|
{
|
|
5171
5426
|
onSnapshot: options.onSnapshot,
|
|
5172
5427
|
onChange: options.onChange,
|
|
5173
5428
|
onError: options.onError,
|
|
5174
|
-
conflate: options.conflate
|
|
5429
|
+
conflate: options.conflate,
|
|
5430
|
+
atLeast: options.atLeast,
|
|
5431
|
+
waitMs: options.waitMs,
|
|
5432
|
+
onExceeded: options.onExceeded
|
|
5175
5433
|
},
|
|
5176
5434
|
(warnings) => {
|
|
5177
5435
|
raiseDeprecationWarnings(this.onWarning, warnings);
|
|
5178
|
-
}
|
|
5436
|
+
},
|
|
5437
|
+
this.store,
|
|
5438
|
+
this.database
|
|
5179
5439
|
);
|
|
5180
5440
|
subscription.start();
|
|
5181
5441
|
return subscription;
|
|
@@ -5187,12 +5447,12 @@ var NamedMutationsApi = class {
|
|
|
5187
5447
|
this.database = database;
|
|
5188
5448
|
this.onWarning = onWarning;
|
|
5189
5449
|
}
|
|
5190
|
-
async execute(
|
|
5191
|
-
if (typeof
|
|
5192
|
-
throw new Error("Named mutation
|
|
5450
|
+
async execute(name, args) {
|
|
5451
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
5452
|
+
throw new Error("Named mutation name must be a non-empty string");
|
|
5193
5453
|
}
|
|
5194
5454
|
const prefix = databasePath2(this.database);
|
|
5195
|
-
const path4 = `${prefix}/named-mutations/${encodeURIComponent(
|
|
5455
|
+
const path4 = `${prefix}/named-mutations/${encodeURIComponent(name)}/execute`;
|
|
5196
5456
|
const wire = await this.transport.post(path4, {
|
|
5197
5457
|
args: args ?? {}
|
|
5198
5458
|
});
|
|
@@ -5253,6 +5513,7 @@ var WebSocketTransport = class {
|
|
|
5253
5513
|
// Serialized-send queue: each send appends to this tail.
|
|
5254
5514
|
this._sendTail = Promise.resolve();
|
|
5255
5515
|
this._lastVersion = 0;
|
|
5516
|
+
this._lastToken = null;
|
|
5256
5517
|
this._reconnectAttempt = 0;
|
|
5257
5518
|
this._disposed = false;
|
|
5258
5519
|
this._missedPings = 0;
|
|
@@ -5270,6 +5531,7 @@ var WebSocketTransport = class {
|
|
|
5270
5531
|
this._pingIntervalMs = options.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
|
|
5271
5532
|
this._maxMissedHeartbeats = options.maxMissedHeartbeats ?? DEFAULT_MAX_MISSED_HEARTBEATS;
|
|
5272
5533
|
this._enableCompression = options.enableCompression ?? true;
|
|
5534
|
+
this._store = options.consistencyTokenStore;
|
|
5273
5535
|
this._wireMode = options.wireMode ?? "json";
|
|
5274
5536
|
this._authHandler?.setOnAccessTokenRefreshed((token) => {
|
|
5275
5537
|
if (this._handshakeComplete && this._ws?.readyState === WS_READY_STATE_OPEN) {
|
|
@@ -5278,10 +5540,14 @@ var WebSocketTransport = class {
|
|
|
5278
5540
|
});
|
|
5279
5541
|
}
|
|
5280
5542
|
// ─── Public API ────────────────────────────────────────────────────────────
|
|
5281
|
-
/** Latest
|
|
5543
|
+
/** Latest change-event sequence from the most recent server `heartbeat` message. */
|
|
5282
5544
|
get lastVersion() {
|
|
5283
5545
|
return this._lastVersion;
|
|
5284
5546
|
}
|
|
5547
|
+
/** Consistency token from the most recent server `heartbeat` (ADR 0042 D-13). */
|
|
5548
|
+
get lastToken() {
|
|
5549
|
+
return this._lastToken;
|
|
5550
|
+
}
|
|
5285
5551
|
/**
|
|
5286
5552
|
* Registers a handler for incoming server messages with the given channel id.
|
|
5287
5553
|
* Use `"__global__"` to receive broadcast messages that carry no `id`.
|
|
@@ -5469,6 +5735,10 @@ var WebSocketTransport = class {
|
|
|
5469
5735
|
}
|
|
5470
5736
|
case "heartbeat":
|
|
5471
5737
|
this._lastVersion = msg.version;
|
|
5738
|
+
this._lastToken = msg.token ?? null;
|
|
5739
|
+
if (this._lastToken != null && this._lastToken.length > 0) {
|
|
5740
|
+
this._store?.observe(this._database, this._lastToken);
|
|
5741
|
+
}
|
|
5472
5742
|
break;
|
|
5473
5743
|
case "pong":
|
|
5474
5744
|
this._missedPings = 0;
|
|
@@ -5634,16 +5904,21 @@ var LongPollTransport = class {
|
|
|
5634
5904
|
this._connected = false;
|
|
5635
5905
|
this._pollTask = null;
|
|
5636
5906
|
this._lastVersion = 0;
|
|
5907
|
+
this._lastToken = null;
|
|
5637
5908
|
this._serverUrl = serverUrl;
|
|
5638
5909
|
this._database = database;
|
|
5639
5910
|
this._authHandler = options.authHandler;
|
|
5640
5911
|
this._onReconnected = options.onReconnected;
|
|
5641
5912
|
this._waitMs = Math.max(1, options.waitMs ?? DEFAULT_WAIT_MS);
|
|
5642
5913
|
this._fetch = options.fetchImpl ?? localNetworkFetch;
|
|
5914
|
+
this._store = options.consistencyTokenStore;
|
|
5643
5915
|
}
|
|
5644
5916
|
get lastVersion() {
|
|
5645
5917
|
return this._lastVersion;
|
|
5646
5918
|
}
|
|
5919
|
+
get lastToken() {
|
|
5920
|
+
return this._lastToken;
|
|
5921
|
+
}
|
|
5647
5922
|
registerHandler(id, handler) {
|
|
5648
5923
|
this._handlers.set(id, handler);
|
|
5649
5924
|
}
|
|
@@ -5766,6 +6041,10 @@ var LongPollTransport = class {
|
|
|
5766
6041
|
for (const msg of payload.messages) {
|
|
5767
6042
|
if (msg.type === "heartbeat") {
|
|
5768
6043
|
this._lastVersion = msg.version;
|
|
6044
|
+
this._lastToken = msg.token ?? null;
|
|
6045
|
+
if (this._lastToken != null && this._lastToken.length > 0) {
|
|
6046
|
+
this._store?.observe(this._database, this._lastToken);
|
|
6047
|
+
}
|
|
5769
6048
|
}
|
|
5770
6049
|
const id = "id" in msg ? msg.id : void 0;
|
|
5771
6050
|
if (id !== void 0 && id !== null) {
|
|
@@ -5845,6 +6124,9 @@ var FallbackStreamingTransport = class {
|
|
|
5845
6124
|
get lastVersion() {
|
|
5846
6125
|
return this._active.lastVersion;
|
|
5847
6126
|
}
|
|
6127
|
+
get lastToken() {
|
|
6128
|
+
return this._active.lastToken;
|
|
6129
|
+
}
|
|
5848
6130
|
registerHandler(id, handler) {
|
|
5849
6131
|
this._handlers.set(id, handler);
|
|
5850
6132
|
this._active.registerHandler(id, handler);
|
|
@@ -5924,6 +6206,7 @@ var AoudaClient = class {
|
|
|
5924
6206
|
this.baseUrl = normalizeBaseUrl(options.serverUrl);
|
|
5925
6207
|
this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
5926
6208
|
this.database = options.database.trim();
|
|
6209
|
+
this._store = options.consistencyTokenStore ?? new MemoryConsistencyTokenStore();
|
|
5927
6210
|
if (!Number.isFinite(this.timeout) || this.timeout <= 0) {
|
|
5928
6211
|
throw new Error("timeout must be a finite positive number");
|
|
5929
6212
|
}
|
|
@@ -5958,7 +6241,9 @@ var AoudaClient = class {
|
|
|
5958
6241
|
}
|
|
5959
6242
|
const httpTransport = new HttpTransport({
|
|
5960
6243
|
baseUrl: this.baseUrl,
|
|
5961
|
-
timeout: this.timeout
|
|
6244
|
+
timeout: this.timeout,
|
|
6245
|
+
database: this.database,
|
|
6246
|
+
consistencyTokenStore: this._store
|
|
5962
6247
|
});
|
|
5963
6248
|
const activeAuth = options.serverAuth ?? options.appAuth;
|
|
5964
6249
|
if (activeAuth) {
|
|
@@ -6018,7 +6303,8 @@ var AoudaClient = class {
|
|
|
6018
6303
|
this.transport,
|
|
6019
6304
|
this.database,
|
|
6020
6305
|
onNamedArtifactWarning,
|
|
6021
|
-
() => this._getOrCreateWebSocketTransport()
|
|
6306
|
+
() => this._getOrCreateWebSocketTransport(),
|
|
6307
|
+
this._store
|
|
6022
6308
|
);
|
|
6023
6309
|
this._namedMutations = new NamedMutationsApi(
|
|
6024
6310
|
this.transport,
|
|
@@ -6117,7 +6403,8 @@ var AoudaClient = class {
|
|
|
6117
6403
|
name,
|
|
6118
6404
|
this.database,
|
|
6119
6405
|
void 0,
|
|
6120
|
-
() => this._getOrCreateWebSocketTransport()
|
|
6406
|
+
() => this._getOrCreateWebSocketTransport(),
|
|
6407
|
+
this._store
|
|
6121
6408
|
);
|
|
6122
6409
|
}
|
|
6123
6410
|
/**
|
|
@@ -6169,13 +6456,26 @@ var AoudaClient = class {
|
|
|
6169
6456
|
return this._materializedQueries;
|
|
6170
6457
|
}
|
|
6171
6458
|
/**
|
|
6172
|
-
*
|
|
6459
|
+
* Named-query execute, read-only batch, and subscribe by unique schema name.
|
|
6173
6460
|
*/
|
|
6174
6461
|
get namedQueries() {
|
|
6175
6462
|
return this._namedQueries;
|
|
6176
6463
|
}
|
|
6464
|
+
observeConsistencyToken(token) {
|
|
6465
|
+
this._store.observe(this.database, token);
|
|
6466
|
+
}
|
|
6467
|
+
getObservedConsistencyToken() {
|
|
6468
|
+
return this._store.get(this.database);
|
|
6469
|
+
}
|
|
6470
|
+
async getConsistencyToken() {
|
|
6471
|
+
const body = await this.transport.get(
|
|
6472
|
+
`/api/databases/${encodeURIComponent(this.database)}/token`
|
|
6473
|
+
);
|
|
6474
|
+
this._store.observe(this.database, body.token);
|
|
6475
|
+
return this._store.get(this.database) ?? body.token;
|
|
6476
|
+
}
|
|
6177
6477
|
/**
|
|
6178
|
-
*
|
|
6478
|
+
* Named-mutation execute by unique schema name. No batch.
|
|
6179
6479
|
*/
|
|
6180
6480
|
get namedMutations() {
|
|
6181
6481
|
return this._namedMutations;
|
|
@@ -6302,13 +6602,15 @@ var AoudaClient = class {
|
|
|
6302
6602
|
const primary = new WebSocketTransport(this.baseUrl, this.database, {
|
|
6303
6603
|
authHandler: this._authHandler,
|
|
6304
6604
|
enableCompression: this._streamingEnableCompression,
|
|
6305
|
-
wireMode: this._streamingWireMode
|
|
6605
|
+
wireMode: this._streamingWireMode,
|
|
6606
|
+
consistencyTokenStore: this._store
|
|
6306
6607
|
});
|
|
6307
6608
|
if (this._streamingEnableLongPollFallback) {
|
|
6308
6609
|
this._wsTransport = new FallbackStreamingTransport(primary, () => {
|
|
6309
6610
|
return new LongPollTransport(this.baseUrl, this.database, {
|
|
6310
6611
|
authHandler: this._authHandler,
|
|
6311
|
-
waitMs: this._streamingLongPollWaitMs
|
|
6612
|
+
waitMs: this._streamingLongPollWaitMs,
|
|
6613
|
+
consistencyTokenStore: this._store
|
|
6312
6614
|
});
|
|
6313
6615
|
});
|
|
6314
6616
|
} else {
|
|
@@ -6333,7 +6635,7 @@ Usage:
|
|
|
6333
6635
|
npx @aouda/client schema <command> [options]
|
|
6334
6636
|
|
|
6335
6637
|
Commands:
|
|
6336
|
-
generate Fetch schema from Aouda server and output TypeScript types (tables + named query/mutation
|
|
6638
|
+
generate Fetch schema from Aouda server and output TypeScript types (tables + optional named query/mutation Args/Row)
|
|
6337
6639
|
schema Schema management (diff, apply, export, validate, history, seed)
|
|
6338
6640
|
diff Show migration plan (desired vs current)
|
|
6339
6641
|
apply Apply schema (use --allow-destructive for drops; --dry-run to preview)
|