tanstack-db-pglite 1.3.5 → 1.3.6

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 CHANGED
@@ -1,25 +1,34 @@
1
1
  # tanstack-db-pglite
2
2
 
3
- A seamless integration between [TanStack DB](https://tanstack.com/db) and [PGLite](https://github.com/electric-sql/pglite) with [Drizzle ORM](https://orm.drizzle.team/) for browser-based database management.
3
+ A seamless integration between [TanStack DB](https://tanstack.com/db) and [PGLite](https://github.com/electric-sql/pglite) with optional [Drizzle ORM](https://orm.drizzle.team/) support for browser-based database management.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install tanstack-db-pglite @tanstack/db drizzle-orm @electric-sql/pglite
8
+ npm install tanstack-db-pglite @tanstack/db @electric-sql/pglite
9
9
  ```
10
10
 
11
- > **Note:** `@tanstack/db` and `drizzle-orm` are peer dependencies and must be installed separately.
11
+ > **Note:** `@tanstack/db` and `@electric-sql/pglite` are peer dependencies. `drizzle-orm` is an optional peer dependency required only when using `drizzleCollectionOptions`.
12
12
 
13
- ## Quick Start
13
+ ## APIs
14
+
15
+ This package exports two collection option creators:
16
+
17
+ - `drizzleCollectionOptions` — uses Drizzle ORM on top of PGlite
18
+ - `sqlCollectionOptions` — uses raw SQL queries directly on PGlite
19
+
20
+ Both follow the TanStack DB [collection options creator](https://tanstack.com/db/latest/docs/guides/collection-options-creator) pattern.
21
+
22
+ ## Quick Start (Drizzle)
14
23
 
15
24
  ```typescript
16
- import { PGLite } from '@electric-sql/pglite'
25
+ import { PGlite } from '@electric-sql/pglite'
17
26
  import { createCollection } from '@tanstack/react-db'
18
27
  import { drizzle } from 'drizzle-orm/pglite'
19
28
  import { drizzleCollectionOptions } from 'tanstack-db-pglite'
20
29
  import { chats } from '~/drizzle'
21
30
 
22
- const pglite = new PGLite()
31
+ const pglite = new PGlite()
23
32
  const db = drizzle(pglite)
24
33
 
25
34
  export const chatsCollection = createCollection(drizzleCollectionOptions({
@@ -27,26 +36,23 @@ export const chatsCollection = createCollection(drizzleCollectionOptions({
27
36
  table: chats,
28
37
  primaryColumn: chats.id,
29
38
  prepare: async () => {
30
- // Prepare your database before starting the collection (e.g., run migrations)
31
39
  await waitForMigrations()
32
40
  },
33
- sync: async ({ collection, write }) => {
34
- // Send some data to your backend to sync and receive the response
35
- const sync = await syncWithCloud(
36
- collection.toArray.map(c => ({
37
- id: c.id,
38
- updatedAt: c.updatedAt
39
- }))
40
- )
41
-
42
- sync.forEach((item) => {
43
- if (item.type === 'delete') {
44
- write({ type: 'delete', value: collection.get(item.value)! })
45
- }
46
- else {
47
- write(item)
48
- }
49
- })
41
+ sync: async ({ begin, commit, write, markReady }) => {
42
+ const eventSource = new EventSource('/api/chats/sync')
43
+
44
+ eventSource.onmessage = (event) => {
45
+ const item = JSON.parse(event.data)
46
+ begin()
47
+ write(item)
48
+ commit()
49
+ }
50
+
51
+ eventSource.addEventListener('ready', () => markReady())
52
+
53
+ return () => {
54
+ eventSource.close()
55
+ }
50
56
  },
51
57
  onInsert: async (params) => {
52
58
  await saveInCloud(params)
@@ -59,3 +65,110 @@ export const chatsCollection = createCollection(drizzleCollectionOptions({
59
65
  },
60
66
  }))
61
67
  ```
68
+
69
+ ## Quick Start (Raw SQL)
70
+
71
+ ```typescript
72
+ import { PGlite } from '@electric-sql/pglite'
73
+ import { createCollection } from '@tanstack/react-db'
74
+ import { sqlCollectionOptions } from 'tanstack-db-pglite'
75
+ import { z } from 'zod'
76
+
77
+ const pglite = new PGlite()
78
+
79
+ const chatSchema = z.object({
80
+ id: z.string(),
81
+ name: z.string(),
82
+ updatedAt: z.string(),
83
+ })
84
+
85
+ export const chatsCollection = createCollection(sqlCollectionOptions({
86
+ db: pglite,
87
+ tableName: 'chats',
88
+ primaryKeyColumn: 'id',
89
+ schema: chatSchema,
90
+ prepare: async () => {
91
+ await pglite.query(`
92
+ CREATE TABLE IF NOT EXISTS chats (
93
+ id TEXT PRIMARY KEY,
94
+ name TEXT NOT NULL,
95
+ "updatedAt" TEXT NOT NULL
96
+ )
97
+ `)
98
+ },
99
+ sync: async ({ begin, commit, write, markReady }) => {
100
+ const eventSource = new EventSource('/api/chats/sync')
101
+
102
+ eventSource.onmessage = (event) => {
103
+ const item = JSON.parse(event.data)
104
+ begin()
105
+ write(item)
106
+ commit()
107
+ }
108
+
109
+ eventSource.addEventListener('ready', () => markReady())
110
+
111
+ return () => {
112
+ eventSource.close()
113
+ }
114
+ },
115
+ }))
116
+ ```
117
+
118
+ ## Options
119
+
120
+ ### Common Options
121
+
122
+ | Option | Type | Description |
123
+ |--------|------|-------------|
124
+ | `startSync` | `boolean` | Whether to run the `sync` callback automatically on startup. Defaults to `true`. When `false`, use `collection.utils.runSync()` to trigger manually. |
125
+ | `prepare` | `() => Promise<unknown> \| unknown` | Runs before the initial data load (e.g., run migrations). |
126
+ | `sync` | `(params) => Promise<(() => void) \| void>` | Sync callback receiving `write`, `markReady`, `collection`, and `metadata`. Return a cleanup function to close subscriptions. |
127
+ | `rowUpdateMode` | `'partial' \| 'full'` | Whether sync updates contain partial changes or full row replacements. |
128
+ | `onInsert` | `(params) => Promise<void>` | Called when a row is inserted optimistically. Persist to your backend here. |
129
+ | `onUpdate` | `(params) => Promise<void>` | Called when a row is updated optimistically. Persist to your backend here. |
130
+ | `onDelete` | `(params) => Promise<void>` | Called when a row is deleted optimistically. Persist to your backend here. |
131
+
132
+ ### `drizzleCollectionOptions` Specific
133
+
134
+ | Option | Type | Description |
135
+ |--------|------|-------------|
136
+ | `db` | `PgliteDatabase` | Drizzle PGlite database instance. |
137
+ | `table` | `PgTable` | Drizzle table definition. |
138
+ | `primaryColumn` | `IndexColumn` | The primary key column from the table. |
139
+
140
+ ### `sqlCollectionOptions` Specific
141
+
142
+ | Option | Type | Description |
143
+ |--------|------|-------------|
144
+ | `db` | `PGlite \| PGliteWorker` | PGlite instance (or worker). |
145
+ | `tableName` | `string` | SQL table name. |
146
+ | `primaryKeyColumn` | `string` | Name of the primary key column. |
147
+ | `schema` | `StandardSchemaV1` | A Standard Schema (e.g., Zod) for the row type. |
148
+ | `getKey` | `(row) => string` | Custom key extractor. Defaults to `row[primaryKeyColumn]`. |
149
+
150
+ ## Utilities
151
+
152
+ Both adapters expose a `utils` object on the collection:
153
+
154
+ ```typescript
155
+ // Manually trigger sync (cleans up any previous sync, then re-syncs)
156
+ await chatsCollection.utils.runSync()
157
+ ```
158
+
159
+ This is useful when `startSync: false` and you want to control when sync starts (e.g., after authentication).
160
+
161
+ ## Sync Callback
162
+
163
+ The `sync` callback receives:
164
+
165
+ - **`write(message)`** — writes a change to both PGlite and the TanStack DB collection. Accepts `{ type: 'insert', value }`, `{ type: 'update', value }`, or `{ type: 'delete', key }`.
166
+ - **`markReady()`** — signals that the initial data is loaded and the collection is ready for queries. Must be called once.
167
+ - **`collection`** — reference to the collection instance.
168
+ - **`metadata`** — persisted sync metadata API for storing resume tokens, cursors, etc.
169
+
170
+ Return a cleanup function to close long-lived connections (WebSocket, EventSource, etc.) when the collection is destroyed or `runSync()` is called again.
171
+
172
+ ## License
173
+
174
+ MIT
package/dist/drizzle.d.ts CHANGED
@@ -19,7 +19,7 @@ export declare function drizzleCollectionOptions<Table extends PgTable>({ startS
19
19
  table: Table;
20
20
  primaryColumn: IndexColumn;
21
21
  rowUpdateMode?: 'partial' | 'full';
22
- sync?: (params: Pick<SyncParams<Table>, 'write' | 'collection' | 'markReady' | 'metadata'>) => Promise<(() => void) | void>;
22
+ sync?: (params: SyncParams<Table>) => Promise<(() => void) | void>;
23
23
  prepare?: () => Promise<unknown> | unknown;
24
24
  onInsert?: (params: InsertMutationFnParams<Table['$inferSelect'], string>) => Promise<void>;
25
25
  onUpdate?: (params: UpdateMutationFnParams<Table['$inferSelect'], string>) => Promise<void>;
package/dist/drizzle.js CHANGED
@@ -89,12 +89,13 @@ export function drizzleCollectionOptions({ startSync = true, ...config }) {
89
89
  const key = 'key' in message ? message.key : params.collection.getKeyFromItem(message.value);
90
90
  await onDrizzleDelete([key]);
91
91
  }
92
- params.begin();
93
92
  params.write(message);
94
- params.commit();
95
93
  },
96
94
  collection: params.collection,
97
95
  markReady: params.markReady,
96
+ begin: params.begin,
97
+ commit: params.commit,
98
+ truncate: params.truncate,
98
99
  ...(params.metadata && { metadata: params.metadata }),
99
100
  });
100
101
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tanstack-db-pglite",
3
- "version": "1.3.5",
3
+ "version": "1.3.6",
4
4
  "author": "Valerii Strilets",
5
5
  "license": "MIT",
6
6
  "repository": {