@electric-sql/client 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +125 -7
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +65 -1
- package/dist/index.browser.mjs +3 -3
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.legacy-esm.js +123 -7
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +123 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +59 -3
- package/src/constants.ts +4 -0
- package/src/expression-compiler.ts +132 -0
- package/src/fetch.ts +16 -0
- package/src/index.ts +1 -0
- package/src/types.ts +28 -0
package/dist/index.mjs
CHANGED
|
@@ -467,6 +467,8 @@ var SUBSET_PARAM_LIMIT = `subset__limit`;
|
|
|
467
467
|
var SUBSET_PARAM_OFFSET = `subset__offset`;
|
|
468
468
|
var SUBSET_PARAM_ORDER_BY = `subset__order_by`;
|
|
469
469
|
var SUBSET_PARAM_WHERE_PARAMS = `subset__params`;
|
|
470
|
+
var SUBSET_PARAM_WHERE_EXPR = `subset__where_expr`;
|
|
471
|
+
var SUBSET_PARAM_ORDER_BY_EXPR = `subset__order_by_expr`;
|
|
470
472
|
var ELECTRIC_PROTOCOL_QUERY_PARAMS = [
|
|
471
473
|
LIVE_QUERY_PARAM,
|
|
472
474
|
LIVE_SSE_QUERY_PARAM,
|
|
@@ -479,7 +481,9 @@ var ELECTRIC_PROTOCOL_QUERY_PARAMS = [
|
|
|
479
481
|
SUBSET_PARAM_LIMIT,
|
|
480
482
|
SUBSET_PARAM_OFFSET,
|
|
481
483
|
SUBSET_PARAM_ORDER_BY,
|
|
482
|
-
SUBSET_PARAM_WHERE_PARAMS
|
|
484
|
+
SUBSET_PARAM_WHERE_PARAMS,
|
|
485
|
+
SUBSET_PARAM_WHERE_EXPR,
|
|
486
|
+
SUBSET_PARAM_ORDER_BY_EXPR
|
|
483
487
|
];
|
|
484
488
|
|
|
485
489
|
// src/fetch.ts
|
|
@@ -733,6 +737,13 @@ function getNextChunkUrl(url, res) {
|
|
|
733
737
|
if (!shapeHandle || !lastOffset || isUpToDate) return;
|
|
734
738
|
const nextUrl = new URL(url);
|
|
735
739
|
if (nextUrl.searchParams.has(LIVE_QUERY_PARAM)) return;
|
|
740
|
+
const expiredHandle = nextUrl.searchParams.get(EXPIRED_HANDLE_QUERY_PARAM);
|
|
741
|
+
if (expiredHandle && shapeHandle === expiredHandle) {
|
|
742
|
+
console.warn(
|
|
743
|
+
`[Electric] Received stale cached response with expired shape handle. This should not happen and indicates a proxy/CDN caching misconfiguration. The response contained handle "${shapeHandle}" which was previously marked as expired. Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key. Skipping prefetch to prevent infinite 409 loop.`
|
|
744
|
+
);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
736
747
|
nextUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, shapeHandle);
|
|
737
748
|
nextUrl.searchParams.set(OFFSET_QUERY_PARAM, lastOffset);
|
|
738
749
|
nextUrl.searchParams.sort();
|
|
@@ -759,6 +770,81 @@ function chainAborter(aborter, sourceSignal) {
|
|
|
759
770
|
function noop() {
|
|
760
771
|
}
|
|
761
772
|
|
|
773
|
+
// src/expression-compiler.ts
|
|
774
|
+
function compileExpression(expr, columnMapper) {
|
|
775
|
+
switch (expr.type) {
|
|
776
|
+
case `ref`: {
|
|
777
|
+
const mappedColumn = columnMapper ? columnMapper(expr.column) : expr.column;
|
|
778
|
+
return quoteIdentifier(mappedColumn);
|
|
779
|
+
}
|
|
780
|
+
case `val`:
|
|
781
|
+
return `$${expr.paramIndex}`;
|
|
782
|
+
case `func`:
|
|
783
|
+
return compileFunction(expr, columnMapper);
|
|
784
|
+
default: {
|
|
785
|
+
const _exhaustive = expr;
|
|
786
|
+
throw new Error(`Unknown expression type: ${JSON.stringify(_exhaustive)}`);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
function compileFunction(expr, columnMapper) {
|
|
791
|
+
const args = expr.args.map((arg) => compileExpression(arg, columnMapper));
|
|
792
|
+
switch (expr.name) {
|
|
793
|
+
// Binary comparison operators
|
|
794
|
+
case `eq`:
|
|
795
|
+
return `${args[0]} = ${args[1]}`;
|
|
796
|
+
case `gt`:
|
|
797
|
+
return `${args[0]} > ${args[1]}`;
|
|
798
|
+
case `gte`:
|
|
799
|
+
return `${args[0]} >= ${args[1]}`;
|
|
800
|
+
case `lt`:
|
|
801
|
+
return `${args[0]} < ${args[1]}`;
|
|
802
|
+
case `lte`:
|
|
803
|
+
return `${args[0]} <= ${args[1]}`;
|
|
804
|
+
// Logical operators
|
|
805
|
+
case `and`:
|
|
806
|
+
return args.map((a) => `(${a})`).join(` AND `);
|
|
807
|
+
case `or`:
|
|
808
|
+
return args.map((a) => `(${a})`).join(` OR `);
|
|
809
|
+
case `not`:
|
|
810
|
+
return `NOT (${args[0]})`;
|
|
811
|
+
// Special operators
|
|
812
|
+
case `in`:
|
|
813
|
+
return `${args[0]} = ANY(${args[1]})`;
|
|
814
|
+
case `like`:
|
|
815
|
+
return `${args[0]} LIKE ${args[1]}`;
|
|
816
|
+
case `ilike`:
|
|
817
|
+
return `${args[0]} ILIKE ${args[1]}`;
|
|
818
|
+
case `isNull`:
|
|
819
|
+
case `isUndefined`:
|
|
820
|
+
return `${args[0]} IS NULL`;
|
|
821
|
+
// String functions
|
|
822
|
+
case `upper`:
|
|
823
|
+
return `UPPER(${args[0]})`;
|
|
824
|
+
case `lower`:
|
|
825
|
+
return `LOWER(${args[0]})`;
|
|
826
|
+
case `length`:
|
|
827
|
+
return `LENGTH(${args[0]})`;
|
|
828
|
+
case `concat`:
|
|
829
|
+
return `CONCAT(${args.join(`, `)})`;
|
|
830
|
+
// Other functions
|
|
831
|
+
case `coalesce`:
|
|
832
|
+
return `COALESCE(${args.join(`, `)})`;
|
|
833
|
+
default:
|
|
834
|
+
throw new Error(`Unknown function: ${expr.name}`);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
function compileOrderBy(clauses, columnMapper) {
|
|
838
|
+
return clauses.map((clause) => {
|
|
839
|
+
const mappedColumn = columnMapper ? columnMapper(clause.column) : clause.column;
|
|
840
|
+
let sql = quoteIdentifier(mappedColumn);
|
|
841
|
+
if (clause.direction === `desc`) sql += ` DESC`;
|
|
842
|
+
if (clause.nulls === `first`) sql += ` NULLS FIRST`;
|
|
843
|
+
if (clause.nulls === `last`) sql += ` NULLS LAST`;
|
|
844
|
+
return sql;
|
|
845
|
+
}).join(`, `);
|
|
846
|
+
}
|
|
847
|
+
|
|
762
848
|
// src/client.ts
|
|
763
849
|
import {
|
|
764
850
|
fetchEventSource
|
|
@@ -1426,7 +1512,7 @@ requestShape_fn = async function() {
|
|
|
1426
1512
|
return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
1427
1513
|
};
|
|
1428
1514
|
constructUrl_fn = async function(url, resumingFromPause, subsetParams) {
|
|
1429
|
-
var _a, _b, _c, _d;
|
|
1515
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1430
1516
|
const [requestHeaders, params] = await Promise.all([
|
|
1431
1517
|
resolveHeaders(this.options.headers),
|
|
1432
1518
|
this.options.params ? toInternalParams(convertWhereParamsToObj(this.options.params)) : void 0
|
|
@@ -1471,10 +1557,20 @@ constructUrl_fn = async function(url, resumingFromPause, subsetParams) {
|
|
|
1471
1557
|
}
|
|
1472
1558
|
}
|
|
1473
1559
|
if (subsetParams) {
|
|
1474
|
-
if (subsetParams.
|
|
1560
|
+
if (subsetParams.whereExpr) {
|
|
1561
|
+
const compiledWhere = compileExpression(
|
|
1562
|
+
subsetParams.whereExpr,
|
|
1563
|
+
(_c = this.options.columnMapper) == null ? void 0 : _c.encode
|
|
1564
|
+
);
|
|
1565
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_WHERE, compiledWhere);
|
|
1566
|
+
fetchUrl.searchParams.set(
|
|
1567
|
+
SUBSET_PARAM_WHERE_EXPR,
|
|
1568
|
+
JSON.stringify(subsetParams.whereExpr)
|
|
1569
|
+
);
|
|
1570
|
+
} else if (subsetParams.where && typeof subsetParams.where === `string`) {
|
|
1475
1571
|
const encodedWhere = encodeWhereClause(
|
|
1476
1572
|
subsetParams.where,
|
|
1477
|
-
(
|
|
1573
|
+
(_d = this.options.columnMapper) == null ? void 0 : _d.encode
|
|
1478
1574
|
);
|
|
1479
1575
|
setQueryParam(fetchUrl, SUBSET_PARAM_WHERE, encodedWhere);
|
|
1480
1576
|
}
|
|
@@ -1487,10 +1583,20 @@ constructUrl_fn = async function(url, resumingFromPause, subsetParams) {
|
|
|
1487
1583
|
setQueryParam(fetchUrl, SUBSET_PARAM_LIMIT, subsetParams.limit);
|
|
1488
1584
|
if (subsetParams.offset)
|
|
1489
1585
|
setQueryParam(fetchUrl, SUBSET_PARAM_OFFSET, subsetParams.offset);
|
|
1490
|
-
if (subsetParams.
|
|
1586
|
+
if (subsetParams.orderByExpr) {
|
|
1587
|
+
const compiledOrderBy = compileOrderBy(
|
|
1588
|
+
subsetParams.orderByExpr,
|
|
1589
|
+
(_e = this.options.columnMapper) == null ? void 0 : _e.encode
|
|
1590
|
+
);
|
|
1591
|
+
setQueryParam(fetchUrl, SUBSET_PARAM_ORDER_BY, compiledOrderBy);
|
|
1592
|
+
fetchUrl.searchParams.set(
|
|
1593
|
+
SUBSET_PARAM_ORDER_BY_EXPR,
|
|
1594
|
+
JSON.stringify(subsetParams.orderByExpr)
|
|
1595
|
+
);
|
|
1596
|
+
} else if (subsetParams.orderBy && typeof subsetParams.orderBy === `string`) {
|
|
1491
1597
|
const encodedOrderBy = encodeWhereClause(
|
|
1492
1598
|
subsetParams.orderBy,
|
|
1493
|
-
(
|
|
1599
|
+
(_f = this.options.columnMapper) == null ? void 0 : _f.encode
|
|
1494
1600
|
);
|
|
1495
1601
|
setQueryParam(fetchUrl, SUBSET_PARAM_ORDER_BY, encodedOrderBy);
|
|
1496
1602
|
}
|
|
@@ -1541,7 +1647,15 @@ onInitialResponse_fn = async function(response) {
|
|
|
1541
1647
|
const { headers, status } = response;
|
|
1542
1648
|
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
1543
1649
|
if (shapeHandle) {
|
|
1544
|
-
|
|
1650
|
+
const shapeKey = __privateGet(this, _currentFetchUrl) ? canonicalShapeKey(__privateGet(this, _currentFetchUrl)) : null;
|
|
1651
|
+
const expiredHandle = shapeKey ? expiredShapesCache.getExpiredHandle(shapeKey) : null;
|
|
1652
|
+
if (shapeHandle !== expiredHandle) {
|
|
1653
|
+
__privateSet(this, _shapeHandle, shapeHandle);
|
|
1654
|
+
} else {
|
|
1655
|
+
console.warn(
|
|
1656
|
+
`[Electric] Received stale cached response with expired shape handle. This should not happen and indicates a proxy/CDN caching misconfiguration. The response contained handle "${shapeHandle}" which was previously marked as expired. Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key. Ignoring the stale handle and continuing with handle "${__privateGet(this, _shapeHandle)}".`
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1545
1659
|
}
|
|
1546
1660
|
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
1547
1661
|
if (lastOffset) {
|
|
@@ -2063,6 +2177,8 @@ export {
|
|
|
2063
2177
|
Shape,
|
|
2064
2178
|
ShapeStream,
|
|
2065
2179
|
camelToSnake,
|
|
2180
|
+
compileExpression,
|
|
2181
|
+
compileOrderBy,
|
|
2066
2182
|
createColumnMapper,
|
|
2067
2183
|
isChangeMessage,
|
|
2068
2184
|
isControlMessage,
|