Skip to content

Commit b8875a2

Browse files
authored
Fix Kiira docs snippet issues (#4526)
## Summary - fix TypeScript client README snippets so they pass Kiira - export FetchBackoffAbortError from @electric-sql/client to match documented API - add Kiira grouping metadata and fix high-signal docs snippets in sync docs ## Verification - npx kiira check --entry packages/typescript-client/README.md --static --raw - cd packages/typescript-client && pnpm exec tsc -p tsconfig.build.json Note: package-level `pnpm --filter @electric-sql/client typecheck` currently fails in this fresh worktree on existing test/dependency typing issues (uuid/pg/fast-check declarations and Vitest mock typings), unrelated to this change.
1 parent 4640704 commit b8875a2

8 files changed

Lines changed: 73 additions & 45 deletions

File tree

.changeset/curly-pears-smell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@electric-sql/client": patch
3+
---
4+
5+
Export `FetchBackoffAbortError` from the package entrypoint to match the documented error handling API.

packages/typescript-client/README.md

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ The client exports a `ShapeStream` class for getting updates to shapes on a row-
4848
```tsx
4949
import { ShapeStream } from '@electric-sql/client'
5050

51+
const BASE_URL = 'http://localhost:3000'
52+
5153
// Passes subscribers rows as they're inserted, updated, or deleted
5254
const stream = new ShapeStream({
5355
url: `${BASE_URL}/v1/shape`,
@@ -80,11 +82,13 @@ stream.subscribe((messages) => {
8082
```tsx
8183
import { ShapeStream, Shape } from '@electric-sql/client'
8284

85+
const BASE_URL = 'http://localhost:3000'
86+
8387
const stream = new ShapeStream({
8488
url: `${BASE_URL}/v1/shape`,
8589
params: {
86-
table: `foo`
87-
}
90+
table: `foo`,
91+
},
8892
})
8993
const shape = new Shape(stream)
9094

@@ -94,7 +98,7 @@ await shape.rows
9498
// passes subscribers shape data when the shape updates
9599
shape.subscribe(({ rows }) => {
96100
// rows is an array of the latest value of each row in a shape.
97-
}
101+
})
98102
```
99103

100104
### Error Handling
@@ -105,7 +109,13 @@ The ShapeStream provides robust error handling with automatic retry support:
105109

106110
The `onError` handler gives you full control over error recovery:
107111

108-
```typescript
112+
```typescript group=readme
113+
import { ShapeStream, FetchError } from '@electric-sql/client'
114+
115+
const BASE_URL = 'http://localhost:3000'
116+
const getRefreshedToken = () => 'refreshed-token'
117+
const fallbackUserId = 'fallback-user-id'
118+
109119
const stream = new ShapeStream({
110120
url: `${BASE_URL}/v1/shape`,
111121
params: { table: `foo` },
@@ -158,15 +168,22 @@ const stream = new ShapeStream({
158168
The handler supports async operations:
159169

160170
```typescript
161-
onError: async (error) => {
162-
if (error instanceof FetchError && error.status === 401) {
163-
// Perform async token refresh
164-
const newToken = await refreshAuthToken()
165-
return {
166-
headers: { Authorization: `Bearer ${newToken}` },
171+
import { FetchError } from '@electric-sql/client'
172+
173+
const refreshAuthToken = async () => 'refreshed-token'
174+
175+
const options = {
176+
onError: async (error: Error) => {
177+
if (error instanceof FetchError && error.status === 401) {
178+
// Perform async token refresh
179+
const newToken = await refreshAuthToken()
180+
return {
181+
headers: { Authorization: `Bearer ${newToken}` },
182+
}
167183
}
168-
}
169-
return {} // Retry other errors
184+
185+
return {} // Retry other errors
186+
},
170187
}
171188
```
172189

@@ -178,7 +195,7 @@ onError: async (error) => {
178195

179196
Individual subscribers can handle errors specific to their subscription:
180197

181-
```typescript
198+
```typescript group=readme
182199
stream.subscribe(
183200
(messages) => {
184201
// Handle messages

packages/typescript-client/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export {
66
isControlMessage,
77
isVisibleInSnapshot,
88
} from './helpers'
9-
export { FetchError } from './error'
9+
export { FetchError, FetchBackoffAbortError } from './error'
1010
export { type BackoffOptions, BackoffDefaults } from './fetch'
1111
export { ELECTRIC_PROTOCOL_QUERY_PARAMS } from './constants'
1212
export {

website/docs/sync/api/clients/typescript.md

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ The `subscribe` method allows you to receive updates whenever the shape changes.
623623
1. A message handler callback (required)
624624
2. An error handler callback (optional)
625625

626-
```typescript
626+
```typescript group=typescript-1
627627
const stream = new ShapeStream({
628628
url: 'http://localhost:3000/v1/shape',
629629
params: {
@@ -651,7 +651,7 @@ To stop receiving updates, you can either:
651651
- Unsubscribe a specific subscription using the function returned by `subscribe`
652652
- Unsubscribe all subscriptions using `unsubscribeAll()`
653653

654-
```typescript
654+
```typescript group=typescript-1
655655
// Store the unsubscribe function
656656
const unsubscribe = stream.subscribe((messages) => {
657657
console.log('Received messages:', messages)
@@ -673,15 +673,17 @@ The ShapeStream provides robust error handling with automatic retry support thro
673673
The `onError` option provides powerful error recovery with automatic retry support:
674674

675675
```typescript
676-
onError?: ShapeStreamErrorHandler
676+
type RetryOpts = {
677+
params?: Record<string, string | string[]>
678+
headers?: Record<string, string>
679+
}
677680

678681
type ShapeStreamErrorHandler = (
679682
error: Error
680683
) => void | RetryOpts | Promise<void | RetryOpts>
681684

682-
type RetryOpts = {
683-
params?: ParamsRecord
684-
headers?: Record<string, string>
685+
interface ShapeStreamOptions {
686+
onError?: ShapeStreamErrorHandler
685687
}
686688
```
687689

@@ -941,7 +943,7 @@ GET requests (the default) with subset parameters in the URL can fail with `414
941943

942944
To avoid this, use POST requests by setting `subsetMethod: 'POST'` on the stream:
943945

944-
```typescript
946+
```typescript group=typescript-2
945947
const stream = new ShapeStream({
946948
url: 'http://localhost:3000/v1/shape',
947949
params: { table: 'items' },
@@ -952,7 +954,7 @@ const stream = new ShapeStream({
952954

953955
Or override per-request with `method: 'POST'`:
954956

955-
```typescript
957+
```typescript group=typescript-2
956958
const { metadata, data } = await stream.requestSnapshot({
957959
where: "status = 'active'",
958960
method: 'POST', // Use POST body instead of query parameters
@@ -980,15 +982,15 @@ The snapshot data is automatically injected into the subscribed message stream w
980982

981983
A `snapshot-end` control message is added after the snapshot data to mark its boundary:
982984

983-
```typescript
985+
```json
984986
{
985-
headers: {
986-
control: "snapshot-end",
987-
xmin: "1234",
988-
xmax: "1240",
989-
xip_list: ["1235", "1237"],
990-
snapshot_mark: 42,
991-
database_lsn: "0/12345678"
987+
"headers": {
988+
"control": "snapshot-end",
989+
"xmin": "1234",
990+
"xmax": "1240",
991+
"xip_list": ["1235", "1237"],
992+
"snapshot_mark": 42,
993+
"database_lsn": "0/12345678"
992994
}
993995
}
994996
```

website/docs/sync/guides/shapes.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,14 @@ Electric has preview support for subqueries in where clauses, allowing you to fi
127127

128128
For example, you can sync only users who belong to a specific organization:
129129

130-
```ts
130+
```ts group=shapes-1
131131
import { electricCollectionOptions } from '@tanstack/electric-db-collection'
132132
import { createCollection } from '@tanstack/react-db'
133133

134134
const usersCollection = createCollection(
135135
electricCollectionOptions({
136136
id: 'org-users',
137+
getKey: (row: { id: string }) => row.id,
137138
shapeOptions: {
138139
url: 'http://localhost:3000/v1/shape',
139140
params: {
@@ -148,10 +149,11 @@ const usersCollection = createCollection(
148149

149150
Or combine subqueries with boolean logic to express more realistic access rules:
150151

151-
```ts
152+
```ts group=shapes-1
152153
const documentsCollection = createCollection(
153154
electricCollectionOptions({
154155
id: 'visible-documents',
156+
getKey: (row: { id: string }) => row.id,
155157
shapeOptions: {
156158
url: 'http://localhost:3000/v1/shape',
157159
params: {
@@ -298,7 +300,7 @@ npm i @electric-sql/client
298300

299301
Instantiate a `ShapeStream` and materialise into a `Shape`:
300302

301-
```ts
303+
```ts group=shapes-2
302304
import { ShapeStream, Shape } from '@electric-sql/client'
303305

304306
const stream = new ShapeStream({
@@ -315,7 +317,7 @@ await shape.rows
315317

316318
You can register a callback to be notified whenever the shape data changes:
317319

318-
```ts
320+
```ts group=shapes-2
319321
shape.subscribe(({ rows }) => {
320322
// rows is an array of the latest value of each row in a shape.
321323
})
@@ -329,13 +331,14 @@ See the [Quickstart](/docs/sync/quickstart) and [HTTP API](/docs/sync/api/http)
329331

330332
Or with [TanStack DB](/sync/tanstack-db), you can sync shapes into collections:
331333

332-
```ts
334+
```ts group=shapes-3
333335
import { electricCollectionOptions } from '@tanstack/electric-db-collection'
334336
import { createCollection } from '@tanstack/react-db'
335337

336338
const todosCollection = createCollection(
337339
electricCollectionOptions({
338340
id: 'todos',
341+
getKey: (row: { id: string }) => row.id,
339342
shapeOptions: {
340343
url: 'http://localhost:3000/v1/shape',
341344
params: {
@@ -348,7 +351,7 @@ const todosCollection = createCollection(
348351

349352
Then query your collections with live queries:
350353

351-
```tsx
354+
```tsx group=shapes-3
352355
import { useLiveQuery, eq } from '@tanstack/react-db'
353356

354357
const Todos = () => {
@@ -428,13 +431,14 @@ See the [TypeScript client docs](/docs/sync/api/clients/typescript#requesting-su
428431

429432
Or with [TanStack DB](/sync/tanstack-db), you can use [query-driven sync](https://tanstack.com/blog/tanstack-db-0.5-query-driven-sync) where your live queries define what data to sync:
430433

431-
```ts
434+
```ts group=shapes-4
432435
import { electricCollectionOptions } from '@tanstack/electric-db-collection'
433436
import { createCollection } from '@tanstack/react-db'
434437

435438
const itemsCollection = createCollection(
436439
electricCollectionOptions({
437440
id: 'items',
441+
getKey: (row: { id: string }) => row.id,
438442
shapeOptions: {
439443
url: 'http://localhost:3000/v1/shape',
440444
params: {
@@ -448,7 +452,7 @@ const itemsCollection = createCollection(
448452

449453
Then query data progressively with live queries:
450454

451-
```tsx
455+
```tsx group=shapes-4
452456
import { useLiveQuery, eq } from '@tanstack/react-db'
453457

454458
const ItemsList = ({ priority }: { priority: string }) => {

website/docs/sync/guides/sharding.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ This works well when:
195195

196196
For more control, route requests through a proxy that determines the shard server-side. This hides sharding complexity from clients.
197197

198-
```typescript
198+
```typescript group=sharding
199199
// proxy/server.ts
200200
import express from 'express'
201201

@@ -409,7 +409,7 @@ function getShardId(userId: string): number {
409409

410410
Sharding works naturally with Electric's [auth patterns](/docs/sync/guides/auth). Your proxy can handle both shard routing and authorization:
411411

412-
```typescript
412+
```typescript group=sharding
413413
app.get('/v1/shape', async (req, res) => {
414414
// 1. Authenticate
415415
const user = await validateToken(req.headers.authorization)
@@ -445,7 +445,7 @@ app.get('/v1/shape', async (req, res) => {
445445

446446
Monitor all Electric instances for a complete view of your sharded deployment:
447447

448-
```typescript
448+
```typescript group=sharding
449449
// Health check aggregator
450450
async function checkAllShards(): Promise<ShardHealth[]> {
451451
const checks = Object.entries(SHARD_URLS).map(async ([shardId, url]) => {

website/docs/sync/integrations/react.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const MyComponent = () => {
6565

6666
For development, you can connect directly to Electric:
6767

68-
```tsx
68+
```tsx group=react
6969
// ⚠️ Development only - exposes database structure
7070
import { useShape } from '@electric-sql/react'
7171

@@ -93,7 +93,7 @@ const MyComponent = () => {
9393

9494
You can also include additional PostgreSQL-specific parameters:
9595

96-
```tsx
96+
```tsx group=react
9797
const MyFilteredComponent = () => {
9898
const { isLoading, data } = useShape<{ id: number; title: string }>({
9999
url: `http://localhost:3000/v1/shape`,

website/docs/sync/stacks.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ Having a Postgres database is as simple as:
360360
npm install @electric-sql/pglite
361361
```
362362

363-
```ts
363+
```ts group=stacks
364364
import { PGlite } from '@electric-sql/pglite'
365365

366366
const db = new PGlite()
@@ -376,7 +376,7 @@ PGlite is a Postgres database that runs inside your dev environment. With it, yo
376376

377377
Electric can be used to hydrate data into a PGlite instance using the [sync plugin](https://pglite.dev/docs/sync):
378378

379-
```ts
379+
```ts group=stacks
380380
import { electricSync } from '@electric-sql/pglite-sync'
381381

382382
const pg = await PGlite.create({

0 commit comments

Comments
 (0)