@electric-sql/client 1.4.2 → 1.5.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 +67 -18
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +34 -0
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +34 -0
- package/dist/index.legacy-esm.js +67 -18
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +67 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +93 -19
- package/src/types.ts +10 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@electric-sql/client",
|
|
3
3
|
"description": "Postgres everywhere - your data, in sync, wherever you need it.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.5.0",
|
|
5
5
|
"author": "ElectricSQL team and contributors.",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/electric-sql/electric/issues"
|
package/src/client.ts
CHANGED
|
@@ -429,6 +429,27 @@ export interface ShapeStreamOptions<T = never> {
|
|
|
429
429
|
* ```
|
|
430
430
|
*/
|
|
431
431
|
onError?: ShapeStreamErrorHandler
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* HTTP method to use for subset snapshot requests (`requestSnapshot`/`fetchSnapshot`).
|
|
435
|
+
*
|
|
436
|
+
* - `'GET'` (default): Sends subset params as URL query parameters. May fail with
|
|
437
|
+
* HTTP 414 errors for large queries with many parameters.
|
|
438
|
+
* - `'POST'`: Sends subset params in request body as JSON. Recommended for queries
|
|
439
|
+
* with large parameter lists (e.g., `WHERE id = ANY($1)` with hundreds of IDs).
|
|
440
|
+
*
|
|
441
|
+
* This can be overridden per-request by passing `method` in the subset params.
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```typescript
|
|
445
|
+
* const stream = new ShapeStream({
|
|
446
|
+
* url: 'http://localhost:3000/v1/shape',
|
|
447
|
+
* params: { table: 'items' },
|
|
448
|
+
* subsetMethod: 'POST', // Use POST for all subset requests
|
|
449
|
+
* })
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
452
|
+
subsetMethod?: `GET` | `POST`
|
|
432
453
|
}
|
|
433
454
|
|
|
434
455
|
export interface ShapeStreamInterface<T extends Row<unknown> = Row> {
|
|
@@ -1632,6 +1653,10 @@ export class ShapeStream<T extends Row<unknown> = Row>
|
|
|
1632
1653
|
* Fetch a snapshot for subset of data.
|
|
1633
1654
|
* Returns the metadata and the data, but does not inject it into the subscribed data stream.
|
|
1634
1655
|
*
|
|
1656
|
+
* By default, uses GET to send subset parameters as query parameters. This may hit URL length
|
|
1657
|
+
* limits (HTTP 414) with large WHERE clauses or many parameters. Set `method: 'POST'` or use
|
|
1658
|
+
* `subsetMethod: 'POST'` on the stream to send parameters in the request body instead.
|
|
1659
|
+
*
|
|
1635
1660
|
* @param opts - The options for the snapshot request.
|
|
1636
1661
|
* @returns The metadata and the data for the snapshot.
|
|
1637
1662
|
*/
|
|
@@ -1639,27 +1664,35 @@ export class ShapeStream<T extends Row<unknown> = Row>
|
|
|
1639
1664
|
metadata: SnapshotMetadata
|
|
1640
1665
|
data: Array<ChangeMessage<T>>
|
|
1641
1666
|
}> {
|
|
1642
|
-
const
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1667
|
+
const method = opts.method ?? this.options.subsetMethod ?? `GET`
|
|
1668
|
+
const usePost = method === `POST`
|
|
1669
|
+
|
|
1670
|
+
let fetchUrl: URL
|
|
1671
|
+
let fetchOptions: RequestInit
|
|
1672
|
+
|
|
1673
|
+
if (usePost) {
|
|
1674
|
+
const result = await this.#constructUrl(this.options.url, true)
|
|
1675
|
+
fetchUrl = result.fetchUrl
|
|
1676
|
+
fetchOptions = {
|
|
1677
|
+
method: `POST`,
|
|
1678
|
+
headers: {
|
|
1679
|
+
...result.requestHeaders,
|
|
1680
|
+
'Content-Type': `application/json`,
|
|
1681
|
+
},
|
|
1682
|
+
body: JSON.stringify(this.#buildSubsetBody(opts)),
|
|
1683
|
+
}
|
|
1684
|
+
} else {
|
|
1685
|
+
const result = await this.#constructUrl(this.options.url, true, opts)
|
|
1686
|
+
fetchUrl = result.fetchUrl
|
|
1687
|
+
fetchOptions = { headers: result.requestHeaders }
|
|
1688
|
+
}
|
|
1647
1689
|
|
|
1648
|
-
const response = await this.#fetchClient(fetchUrl.toString(),
|
|
1649
|
-
headers: requestHeaders,
|
|
1650
|
-
})
|
|
1690
|
+
const response = await this.#fetchClient(fetchUrl.toString(), fetchOptions)
|
|
1651
1691
|
|
|
1652
1692
|
if (!response.ok) {
|
|
1653
|
-
throw
|
|
1654
|
-
response.status,
|
|
1655
|
-
undefined,
|
|
1656
|
-
undefined,
|
|
1657
|
-
Object.fromEntries([...response.headers.entries()]),
|
|
1658
|
-
fetchUrl.toString()
|
|
1659
|
-
)
|
|
1693
|
+
throw await FetchError.fromResponse(response, fetchUrl.toString())
|
|
1660
1694
|
}
|
|
1661
1695
|
|
|
1662
|
-
// Use schema from stream if available, otherwise extract from response header
|
|
1663
1696
|
const schema: Schema =
|
|
1664
1697
|
this.#schema ??
|
|
1665
1698
|
getSchemaFromHeaders(response.headers, {
|
|
@@ -1673,10 +1706,51 @@ export class ShapeStream<T extends Row<unknown> = Row>
|
|
|
1673
1706
|
schema
|
|
1674
1707
|
)
|
|
1675
1708
|
|
|
1676
|
-
return {
|
|
1677
|
-
|
|
1678
|
-
|
|
1709
|
+
return { metadata, data }
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
#buildSubsetBody(opts: SubsetParams): Record<string, unknown> {
|
|
1713
|
+
const body: Record<string, unknown> = {}
|
|
1714
|
+
|
|
1715
|
+
if (opts.whereExpr) {
|
|
1716
|
+
body.where = compileExpression(
|
|
1717
|
+
opts.whereExpr,
|
|
1718
|
+
this.options.columnMapper?.encode
|
|
1719
|
+
)
|
|
1720
|
+
body.where_expr = opts.whereExpr
|
|
1721
|
+
} else if (opts.where && typeof opts.where === `string`) {
|
|
1722
|
+
body.where = encodeWhereClause(
|
|
1723
|
+
opts.where,
|
|
1724
|
+
this.options.columnMapper?.encode
|
|
1725
|
+
)
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
if (opts.params) {
|
|
1729
|
+
body.params = opts.params
|
|
1679
1730
|
}
|
|
1731
|
+
|
|
1732
|
+
if (opts.limit !== undefined) {
|
|
1733
|
+
body.limit = opts.limit
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
if (opts.offset !== undefined) {
|
|
1737
|
+
body.offset = opts.offset
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
if (opts.orderByExpr) {
|
|
1741
|
+
body.order_by = compileOrderBy(
|
|
1742
|
+
opts.orderByExpr,
|
|
1743
|
+
this.options.columnMapper?.encode
|
|
1744
|
+
)
|
|
1745
|
+
body.order_by_expr = opts.orderByExpr
|
|
1746
|
+
} else if (opts.orderBy && typeof opts.orderBy === `string`) {
|
|
1747
|
+
body.order_by = encodeWhereClause(
|
|
1748
|
+
opts.orderBy,
|
|
1749
|
+
this.options.columnMapper?.encode
|
|
1750
|
+
)
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
return body
|
|
1680
1754
|
}
|
|
1681
1755
|
}
|
|
1682
1756
|
|
package/src/types.ts
CHANGED
|
@@ -102,6 +102,16 @@ export type SubsetParams = {
|
|
|
102
102
|
whereExpr?: SerializedExpression
|
|
103
103
|
/** Structured ORDER BY clauses (preferred when available) */
|
|
104
104
|
orderByExpr?: SerializedOrderByClause[]
|
|
105
|
+
/**
|
|
106
|
+
* HTTP method to use for the request. Overrides `subsetMethod` from ShapeStreamOptions.
|
|
107
|
+
* - `GET` (default): Sends subset params as query parameters. May fail with 414 errors
|
|
108
|
+
* for large queries.
|
|
109
|
+
* - `POST`: Sends subset params in request body as JSON. Recommended to avoid URL
|
|
110
|
+
* length limits with large WHERE clauses or many parameters.
|
|
111
|
+
*
|
|
112
|
+
* In Electric 2.0, GET will be deprecated and only POST will be supported.
|
|
113
|
+
*/
|
|
114
|
+
method?: `GET` | `POST`
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
export type ControlMessage = {
|