@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 CHANGED
@@ -1,7 +1,13 @@
1
1
  # @zm2231/kysely-powersync-dialect
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@zm2231/kysely-powersync-dialect.svg)](https://www.npmjs.com/package/@zm2231/kysely-powersync-dialect)
4
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D20.18.1-339933)](package.json)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](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 schemas, migrations, CRM tables, sync rules, or schema engines.
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
- - The dialect is designed for PowerSync's local-first model. Reads inside Kysely transactions still read from the local replica, so they are useful for read-only transactional code but should not be treated as read-your-write guarantees after queued PowerSync writes.
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 underlying PowerSync SDK returns rows. If your PowerSync runtime does not return rows for a write, Kysely receives an empty row list.
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"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zm2231/kysely-powersync-dialect",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A Kysely dialect for PowerSync Node clients backed by a local SQLite read replica.",
5
5
  "type": "module",
6
6
  "license": "MIT",