@zm2231/kysely-powersync-dialect 0.1.0 → 0.1.1
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 +47 -7
- package/examples/local-powersync.md +7 -0
- package/examples/remote-writes.md +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
# @zm2231/kysely-powersync-dialect
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@zm2231/kysely-powersync-dialect)
|
|
4
|
+
[](package.json)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
3
7
|
A small Kysely dialect for PowerSync Node clients.
|
|
4
8
|
|
|
9
|
+
Status: initial `0.1.x` release. The core shape is usable, but the API may evolve before `1.0`.
|
|
10
|
+
|
|
5
11
|
PowerSync gives Node apps a local SQLite replica plus an upload queue. Kysely expects one database connection. This dialect splits the path:
|
|
6
12
|
|
|
7
13
|
- read queries use the local SQLite replica through `better-sqlite3`
|
|
@@ -9,7 +15,29 @@ PowerSync gives Node apps a local SQLite replica plus an upload queue. Kysely ex
|
|
|
9
15
|
- PowerSync credentials and uploads use explicit URLs by default
|
|
10
16
|
- auth, upload, read classification, and write routing can be replaced with hooks
|
|
11
17
|
|
|
12
|
-
This is intentionally only the dialect layer. It does not ship app
|
|
18
|
+
This is intentionally only the dialect layer. It does not ship app tables, migrations, sync rules, or schema engines.
|
|
19
|
+
|
|
20
|
+
## Why this exists
|
|
21
|
+
|
|
22
|
+
PowerSync's web SDK has Kysely-friendly patterns, but Node apps need a small bridge between Kysely's dialect API and PowerSync's local replica/write queue split. This package is that bridge.
|
|
23
|
+
|
|
24
|
+
## Compared to `@powersync/kysely-driver`
|
|
25
|
+
|
|
26
|
+
Use PowerSync's official Kysely driver when you are building against the web SDK it targets.
|
|
27
|
+
|
|
28
|
+
Use this package when you are running a Node PowerSync client and need Kysely to:
|
|
29
|
+
|
|
30
|
+
- read from the local SQLite replica
|
|
31
|
+
- send writes through the PowerSync Node client, or through your own HTTP write endpoint
|
|
32
|
+
- keep read/write routing explicit and replaceable
|
|
33
|
+
|
|
34
|
+
## Background
|
|
35
|
+
|
|
36
|
+
I built this at Cadence, where our team runs on top of the `pi` coding agent. We were standing up a multi-tenant CRM and team-OS, and Kysely was already the SQLite query builder we used everywhere. When we added PowerSync for multi-device sync, the friction was immediate: PowerSync's Node SDK gives you a local SQLite replica plus a write queue, which is exactly what you want for offline-first work, but Kysely's dialect API assumes one connection. The official `@powersync/kysely-driver` solves this for their web SDK; on the Node side, there was nothing.
|
|
37
|
+
|
|
38
|
+
So I wrote the bridge: reads go through `better-sqlite3` against the local replica, writes go through `PowerSyncDatabase.execute()`, or through a remote HTTP endpoint when the writer lives in another process. It ran internally for several months across multiple tenant domains before I extracted and published it.
|
|
39
|
+
|
|
40
|
+
Our schema engine, sync rules, app tables, and tenant routing all stayed in the private codebase. What you get here is just the dialect layer.
|
|
13
41
|
|
|
14
42
|
## Install
|
|
15
43
|
|
|
@@ -17,6 +45,22 @@ This is intentionally only the dialect layer. It does not ship app schemas, migr
|
|
|
17
45
|
npm install @zm2231/kysely-powersync-dialect kysely @powersync/common @powersync/node better-sqlite3
|
|
18
46
|
```
|
|
19
47
|
|
|
48
|
+
## 30-second usage
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
const writeDatabase = await createConnectedPowerSyncDatabase(config, schema);
|
|
52
|
+
const readDatabase = await openPowerSyncReadDatabase(config.db_path);
|
|
53
|
+
|
|
54
|
+
const db = new Kysely<DB>({
|
|
55
|
+
dialect: new PowerSyncDialect({ readDatabase, writeDatabase }),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await db.insertInto("todos").values({ id: "todo-1", title: "Ship it", done: 0 }).execute();
|
|
59
|
+
const todos = await db.selectFrom("todos").selectAll().execute();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`config`, `schema`, and `DB` are app-owned. See the examples for complete setup.
|
|
63
|
+
|
|
20
64
|
## Examples
|
|
21
65
|
|
|
22
66
|
- [Examples index](examples/README.md)
|
|
@@ -26,13 +70,9 @@ npm install @zm2231/kysely-powersync-dialect kysely @powersync/common @powersync
|
|
|
26
70
|
|
|
27
71
|
## Limitations
|
|
28
72
|
|
|
29
|
-
-
|
|
73
|
+
- Reads inside Kysely transactions hit the local replica. They do not see writes that PowerSync has queued but not yet checkpointed locally, so do not rely on read-your-write inside a transaction.
|
|
30
74
|
- Kysely transactions are read-only in this dialect. Writes inside a Kysely transaction throw instead of pretending the split read/write paths are atomic.
|
|
31
75
|
- Raw SQL read/write routing is conservative. Override `readQueryClassifier` when your app has SQL forms the default classifier should not decide.
|
|
32
76
|
- The package does not delete SQLite `-wal` or `-shm` sidecars. Those files can contain uncheckpointed data. Handle recovery explicitly in your app if you need it.
|
|
33
77
|
- Users provide their own PowerSync schema. No built-in tables are created for you.
|
|
34
|
-
- `RETURNING` rows are passed through when the
|
|
35
|
-
|
|
36
|
-
## Why this exists
|
|
37
|
-
|
|
38
|
-
PowerSync's web SDK has Kysely-friendly patterns, but Node apps need a small bridge between Kysely's dialect API and PowerSync's local replica/write queue split. This package is that bridge.
|
|
78
|
+
- `RETURNING` rows are passed through when the PowerSync SDK provides them. The Node SDK does not always return rows for writes, so if your code depends on `RETURNING`, test against your specific runtime.
|
|
@@ -15,12 +15,14 @@ interface DB {
|
|
|
15
15
|
todos: {
|
|
16
16
|
id: string;
|
|
17
17
|
title: string;
|
|
18
|
+
// SQLite stores booleans as 0/1.
|
|
18
19
|
done: number;
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
const schema = new Schema({
|
|
23
24
|
todos: new Table({
|
|
25
|
+
id: column.text,
|
|
24
26
|
title: column.text,
|
|
25
27
|
done: column.integer,
|
|
26
28
|
}),
|
|
@@ -44,6 +46,11 @@ const db = new Kysely<DB>({
|
|
|
44
46
|
}),
|
|
45
47
|
});
|
|
46
48
|
|
|
49
|
+
await db
|
|
50
|
+
.insertInto("todos")
|
|
51
|
+
.values({ id: "todo-1", title: "Ship it", done: 0 })
|
|
52
|
+
.execute();
|
|
53
|
+
|
|
47
54
|
const rows = await db.selectFrom("todos").selectAll().execute();
|
|
48
55
|
```
|
|
49
56
|
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
openPowerSyncReadDatabase,
|
|
10
10
|
} from "@zm2231/kysely-powersync-dialect";
|
|
11
11
|
|
|
12
|
+
const token = "replace-with-api-token";
|
|
13
|
+
|
|
12
14
|
const db = new Kysely({
|
|
13
15
|
dialect: new RemotePowerSyncDialect({
|
|
14
16
|
readDatabase: await openPowerSyncReadDatabase("./data/powersync.db"),
|