@tursodatabase/sync 0.1.5 → 0.2.0-pre.10

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,9 +1,9 @@
1
1
  <p align="center">
2
- <h1 align="center">Turso Sync for JavaScript</h1>
2
+ <h1 align="center">Turso Database for JavaScript in Node</h1>
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- <a title="JavaScript" target="_blank" href="https://www.npmjs.com/package/@tursodatabase/sync"><img alt="npm" src="https://img.shields.io/npm/v/@tursodatabase/sync"></a>
6
+ <a title="JavaScript" target="_blank" href="https://www.npmjs.com/package/@tursodatabase/database"><img alt="npm" src="https://img.shields.io/npm/v/@tursodatabase/database"></a>
7
7
  <a title="MIT" target="_blank" href="https://github.com/tursodatabase/turso/blob/main/LICENSE.md"><img src="http://img.shields.io/badge/license-MIT-orange.svg?style=flat-square"></a>
8
8
  </p>
9
9
  <p align="center">
@@ -14,40 +14,105 @@
14
14
 
15
15
  ## About
16
16
 
17
- This package is for syncing local Turso databases to the Turso Cloud and back.
17
+ This package is the Turso embedded database library for JavaScript in Node.
18
18
 
19
19
  > **⚠️ Warning:** This software is ALPHA, only use for development, testing, and experimentation. We are working to make it production ready, but do not use it for critical data right now.
20
20
 
21
+ ## Features
22
+
23
+ - **SQLite compatible:** SQLite query language and file format support ([status](https://github.com/tursodatabase/turso/blob/main/COMPAT.md)).
24
+ - **In-process**: No network overhead, runs directly in your Node.js process
25
+ - **TypeScript support**: Full TypeScript definitions included
26
+ - **Cross-platform**: Supports Linux (x86 and arm64), macOS, Windows (browser is supported in the separate package `@tursodatabase/database-browser` package)
27
+
21
28
  ## Installation
22
29
 
23
30
  ```bash
24
- npm install @tursodatabase/sync
31
+ npm install @tursodatabase/database
25
32
  ```
26
33
 
27
34
  ## Getting Started
28
35
 
29
- To sync a database hosted at [Turso Cloud](https://turso.tech):
36
+ ### In-Memory Database
37
+
38
+ ```javascript
39
+ import { connect } from '@tursodatabase/database';
40
+
41
+ // Create an in-memory database
42
+ const db = await connect(':memory:');
43
+
44
+ // Create a table
45
+ await db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)');
46
+
47
+ // Insert data
48
+ const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
49
+ await insert.run('Alice', 'alice@example.com');
50
+ await insert.run('Bob', 'bob@example.com');
51
+
52
+ // Query data
53
+ const users = await db.prepare('SELECT * FROM users').all();
54
+ console.log(users);
55
+ // Output: [
56
+ // { id: 1, name: 'Alice', email: 'alice@example.com' },
57
+ // { id: 2, name: 'Bob', email: 'bob@example.com' }
58
+ // ]
59
+ ```
60
+
61
+ ### File-Based Database
62
+
63
+ ```javascript
64
+ import { connect } from '@tursodatabase/database';
30
65
 
31
- ```js
32
- import { connect } from '@tursodatabase/sync';
66
+ // Create or open a database file
67
+ const db = await connect('my-database.db');
33
68
 
34
- const db = await connect({
35
- path: 'local.db', // path used as a prefix for local files created by sync-engine
36
- url: 'https://<db>.turso.io', // URL of the remote database: turso db show <db>
37
- authToken: '...', // auth token issued from the Turso Cloud: turso db tokens create <db>
38
- clientName: 'turso-sync-example' // arbitrary client name
69
+ // Create a table
70
+ await db.exec(`
71
+ CREATE TABLE IF NOT EXISTS posts (
72
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
73
+ title TEXT NOT NULL,
74
+ content TEXT,
75
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
76
+ )
77
+ `);
78
+
79
+ // Insert a post
80
+ const insertPost = db.prepare('INSERT INTO posts (title, content) VALUES (?, ?)');
81
+ const result = await insertPost.run('Hello World', 'This is my first blog post!');
82
+
83
+ console.log(`Inserted post with ID: ${result.lastInsertRowid}`);
84
+ ```
85
+
86
+ ### Transactions
87
+
88
+ ```javascript
89
+ import { connect } from '@tursodatabase/database';
90
+
91
+ const db = await connect('transactions.db');
92
+
93
+ // Using transactions for atomic operations
94
+ const transaction = db.transaction(async (users) => {
95
+ const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
96
+ for (const user of users) {
97
+ await insert.run(user.name, user.email);
98
+ }
39
99
  });
40
100
 
41
- // db has same functions as Database class from @tursodatabase/database package but adds few more methods for sync:
42
- await db.pull(); // pull changes from the remote
43
- await db.push(); // push changes to the remote
44
- await db.sync(); // pull & push changes
101
+ // Execute transaction
102
+ await transaction([
103
+ { name: 'Alice', email: 'alice@example.com' },
104
+ { name: 'Bob', email: 'bob@example.com' }
105
+ ]);
45
106
  ```
46
107
 
108
+ ## API Reference
109
+
110
+ For complete API documentation, see [JavaScript API Reference](../../../../docs/javascript-api-reference.md).
111
+
47
112
  ## Related Packages
48
113
 
49
- * The [@tursodatabase/database](https://www.npmjs.com/package/@tursodatabase/database) package provides the Turso in-memory database, compatible with SQLite.
50
114
  * The [@tursodatabase/serverless](https://www.npmjs.com/package/@tursodatabase/serverless) package provides a serverless driver with the same API.
115
+ * The [@tursodatabase/sync](https://www.npmjs.com/package/@tursodatabase/sync) package provides bidirectional sync between a local Turso database and Turso Cloud.
51
116
 
52
117
  ## License
53
118
 
@@ -57,4 +122,4 @@ This project is licensed under the [MIT license](../../LICENSE.md).
57
122
 
58
123
  - [GitHub Issues](https://github.com/tursodatabase/turso/issues)
59
124
  - [Documentation](https://docs.turso.tech)
60
- - [Discord Community](https://tur.so/discord)
125
+ - [Discord Community](https://tur.so/discord)
@@ -0,0 +1,43 @@
1
+ import { DatabasePromise } from "@tursodatabase/database-common";
2
+ import { DatabaseOpts, EncryptionOpts, DatabaseRowMutation, DatabaseRowStatement, DatabaseRowTransformResult, DatabaseStats } from "@tursodatabase/sync-common";
3
+ declare class Database extends DatabasePromise {
4
+ #private;
5
+ constructor(opts: DatabaseOpts);
6
+ /**
7
+ * connect database and initialize it in case of clean start
8
+ */
9
+ connect(): Promise<void>;
10
+ /**
11
+ * pull new changes from the remote database
12
+ * if {@link DatabaseOpts.longPollTimeoutMs} is set - then server will hold the connection open until either new changes will appear in the database or timeout occurs.
13
+ * @returns true if new changes were pulled from the remote
14
+ */
15
+ pull(): Promise<boolean>;
16
+ /**
17
+ * push new local changes to the remote database
18
+ * if {@link DatabaseOpts.transform} is set - then provided callback will be called for every mutation before sending it to the remote
19
+ */
20
+ push(): Promise<void>;
21
+ /**
22
+ * checkpoint WAL for local database
23
+ */
24
+ checkpoint(): Promise<void>;
25
+ /**
26
+ * @returns statistic of current local database
27
+ */
28
+ stats(): Promise<DatabaseStats>;
29
+ /**
30
+ * close the database
31
+ */
32
+ close(): Promise<void>;
33
+ }
34
+ /**
35
+ * Creates a new database connection asynchronously.
36
+ *
37
+ * @param {Object} opts - Options for database behavior.
38
+ * @returns {Promise<Database>} - A promise that resolves to a Database instance.
39
+ */
40
+ declare function connect(opts: DatabaseOpts): Promise<Database>;
41
+ export { connect, Database };
42
+ export type { DatabaseOpts, EncryptionOpts, DatabaseRowMutation, DatabaseRowStatement, DatabaseRowTransformResult };
43
+ //# sourceMappingURL=promise.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promise.d.ts","sourceRoot":"","sources":["../promise.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,OAAO,EAAmB,YAAY,EAAE,cAAc,EAAW,mBAAmB,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,aAAa,EAAoB,MAAM,4BAA4B,CAAC;AAwC5M,cAAM,QAAS,SAAQ,eAAe;;gBAKtB,IAAI,EAAE,YAAY;IAmC9B;;OAEG;IACY,OAAO;IAGtB;;;;OAIG;IACG,IAAI;IAQV;;;OAGG;IACG,IAAI;IAGV;;OAEG;IACG,UAAU;IAGhB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,aAAa,CAAC;IAGrC;;OAEG;IACY,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAGxC;AAED;;;;;GAKG;AACH,iBAAe,OAAO,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAI5D;AAED,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;AAC5B,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,CAAA"}
@@ -0,0 +1,137 @@
1
+ import { DatabasePromise } from "@tursodatabase/database-common";
2
+ import { run, SyncEngineGuards } from "@tursodatabase/sync-common";
3
+ import { SyncEngine } from "#index";
4
+ import { promises } from "node:fs";
5
+ let NodeIO = {
6
+ async read(path) {
7
+ try {
8
+ return await promises.readFile(path);
9
+ }
10
+ catch (error) {
11
+ if (error.code === 'ENOENT') {
12
+ return null;
13
+ }
14
+ throw error;
15
+ }
16
+ },
17
+ async write(path, data) {
18
+ const unix = Math.floor(Date.now() / 1000);
19
+ const nonce = Math.floor(Math.random() * 1000000000);
20
+ const tmp = `${path}.tmp.${unix}.${nonce}`;
21
+ await promises.writeFile(tmp, new Uint8Array(data));
22
+ try {
23
+ await promises.rename(tmp, path);
24
+ }
25
+ catch (err) {
26
+ await promises.unlink(tmp);
27
+ throw err;
28
+ }
29
+ }
30
+ };
31
+ function memoryIO() {
32
+ let values = new Map();
33
+ return {
34
+ async read(path) {
35
+ return values.get(path);
36
+ },
37
+ async write(path, data) {
38
+ values.set(path, data);
39
+ }
40
+ };
41
+ }
42
+ ;
43
+ class Database extends DatabasePromise {
44
+ #runOpts;
45
+ #engine;
46
+ #io;
47
+ #guards;
48
+ constructor(opts) {
49
+ const engine = new SyncEngine({
50
+ path: opts.path,
51
+ clientName: opts.clientName,
52
+ useTransform: opts.transform != null,
53
+ protocolVersion: "v1" /* SyncEngineProtocolVersion.V1 */,
54
+ longPollTimeoutMs: opts.longPollTimeoutMs,
55
+ tracing: opts.tracing,
56
+ });
57
+ super(engine.db());
58
+ let headers = typeof opts.authToken === "function" ? () => ({
59
+ ...(opts.authToken != null && { "Authorization": `Bearer ${opts.authToken()}` }),
60
+ ...(opts.encryption != null && {
61
+ "x-turso-encryption-key": opts.encryption.key,
62
+ "x-turso-encryption-cipher": opts.encryption.cipher,
63
+ })
64
+ }) : {
65
+ ...(opts.authToken != null && { "Authorization": `Bearer ${opts.authToken}` }),
66
+ ...(opts.encryption != null && {
67
+ "x-turso-encryption-key": opts.encryption.key,
68
+ "x-turso-encryption-cipher": opts.encryption.cipher,
69
+ })
70
+ };
71
+ this.#runOpts = {
72
+ url: opts.url,
73
+ headers: headers,
74
+ preemptionMs: 1,
75
+ transform: opts.transform,
76
+ };
77
+ this.#engine = engine;
78
+ this.#io = this.memory ? memoryIO() : NodeIO;
79
+ this.#guards = new SyncEngineGuards();
80
+ }
81
+ /**
82
+ * connect database and initialize it in case of clean start
83
+ */
84
+ async connect() {
85
+ await run(this.#runOpts, this.#io, this.#engine, this.#engine.connect());
86
+ }
87
+ /**
88
+ * pull new changes from the remote database
89
+ * if {@link DatabaseOpts.longPollTimeoutMs} is set - then server will hold the connection open until either new changes will appear in the database or timeout occurs.
90
+ * @returns true if new changes were pulled from the remote
91
+ */
92
+ async pull() {
93
+ const changes = await this.#guards.wait(async () => await run(this.#runOpts, this.#io, this.#engine, this.#engine.wait()));
94
+ if (changes.empty()) {
95
+ return false;
96
+ }
97
+ await this.#guards.apply(async () => await run(this.#runOpts, this.#io, this.#engine, this.#engine.apply(changes)));
98
+ return true;
99
+ }
100
+ /**
101
+ * push new local changes to the remote database
102
+ * if {@link DatabaseOpts.transform} is set - then provided callback will be called for every mutation before sending it to the remote
103
+ */
104
+ async push() {
105
+ await this.#guards.push(async () => await run(this.#runOpts, this.#io, this.#engine, this.#engine.push()));
106
+ }
107
+ /**
108
+ * checkpoint WAL for local database
109
+ */
110
+ async checkpoint() {
111
+ await this.#guards.checkpoint(async () => await run(this.#runOpts, this.#io, this.#engine, this.#engine.checkpoint()));
112
+ }
113
+ /**
114
+ * @returns statistic of current local database
115
+ */
116
+ async stats() {
117
+ return (await run(this.#runOpts, this.#io, this.#engine, this.#engine.stats()));
118
+ }
119
+ /**
120
+ * close the database
121
+ */
122
+ async close() {
123
+ this.#engine.close();
124
+ }
125
+ }
126
+ /**
127
+ * Creates a new database connection asynchronously.
128
+ *
129
+ * @param {Object} opts - Options for database behavior.
130
+ * @returns {Promise<Database>} - A promise that resolves to a Database instance.
131
+ */
132
+ async function connect(opts) {
133
+ const db = new Database(opts);
134
+ await db.connect();
135
+ return db;
136
+ }
137
+ export { connect, Database };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=promise.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promise.test.d.ts","sourceRoot":"","sources":["../promise.test.ts"],"names":[],"mappings":""}