@bibliotek/sqlite 0.0.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/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@bibliotek/sqlite",
3
+ "version": "0.0.1",
4
+ "description": "Sqlite3 database using async/await",
5
+ "type": "module",
6
+ "exports": "./sqlite.js",
7
+ "scripts": {
8
+ "test": "mocha"
9
+ },
10
+ "dependencies": {
11
+ "@bibliotek/synch": "^0.0.1",
12
+ "sqlite3": "^5.0.2"
13
+ },
14
+ "devDependencies": {
15
+ "expect.js": "^0.3.1",
16
+ "mocha": "^9.2.0",
17
+ "sinon": "^13.0.1"
18
+ }
19
+ }
package/sqlite.js ADDED
@@ -0,0 +1,146 @@
1
+ import Sqlite3 from "sqlite3";
2
+ import synch from "@bibliotek/synch";
3
+
4
+ export class Sqlite {
5
+ constructor(db) {
6
+ if (typeof db === "string") {
7
+ db = new Sqlite3.Database(db);
8
+ }
9
+
10
+ this.db = db;
11
+ }
12
+
13
+ static memory() {
14
+ return new Sqlite(":memory:");
15
+ }
16
+
17
+ static disk() {
18
+ return new Sqlite("");
19
+ }
20
+
21
+ static get Statement() {
22
+ return Statement;
23
+ }
24
+
25
+ async close() {
26
+ return new Promise((resolve, reject) => {
27
+ this.db.close(err => {
28
+ if (err) reject(err); else resolve();
29
+ });
30
+ });
31
+ }
32
+
33
+ async *each(sql) {
34
+ const buffer = [];
35
+ let done = false;
36
+ let sync = synch();
37
+
38
+ this.db.each(sql, record, complete);
39
+
40
+ while (!done) {
41
+ await sync;
42
+ if (done instanceof Error) throw done;
43
+ while (buffer.length) yield buffer.shift();
44
+ sync.reset();
45
+ };
46
+
47
+ function record(err, row) {
48
+ if (err) done = err; else buffer.push(row);
49
+ sync();
50
+ }
51
+
52
+ function complete() {
53
+ done = true;
54
+ sync();
55
+ }
56
+ }
57
+
58
+ async exec(sql) {
59
+ return new Promise((resolve, reject) => {
60
+ this.db.exec(sql, err => {
61
+ if (err) reject(err); else resolve();
62
+ });
63
+ });
64
+ }
65
+
66
+ async get(sql, ...params) {
67
+ return new Promise((resolve, reject) => {
68
+ this.db.get(sql, ...params, (err, row) => {
69
+ if (err) reject(err); else resolve(row);
70
+ });
71
+ });
72
+ }
73
+
74
+ prepare(sql) {
75
+ return new Statement(this.db.prepare(sql));
76
+ }
77
+
78
+ async run(sql) {
79
+ return new Promise((resolve, reject) => {
80
+ this.db.run(sql, err => {
81
+ if (err) reject(err); else resolve();
82
+ });
83
+ });
84
+ }
85
+ }
86
+
87
+ class Statement {
88
+ constructor(stmt) {
89
+ this.stmt = stmt;
90
+ }
91
+
92
+ async *each(...params) {
93
+ const buffer = [];
94
+ let done = false;
95
+ let sync = synch();
96
+
97
+ this.stmt.each(...params, record, complete);
98
+
99
+ while (!done) {
100
+ await sync;
101
+ if (done instanceof Error) throw done;
102
+ while (buffer.length) yield buffer.shift();
103
+ sync.reset();
104
+ }
105
+
106
+ function record(err, row) {
107
+ if (err) done = err; else buffer.push(row);
108
+ sync();
109
+ }
110
+
111
+ function complete() {
112
+ done = true;
113
+ sync();
114
+ }
115
+ }
116
+
117
+ async finalize() {
118
+ return new Promise((resolve, reject) => {
119
+ this.stmt.finalize(() => resolve());
120
+ });
121
+ }
122
+
123
+ async get(...params) {
124
+ return new Promise((resolve, reject) => {
125
+ this.stmt.get(...params, (err, row) => {
126
+ if (err) reject(err); else resolve(row);
127
+ });
128
+ });
129
+ }
130
+
131
+ async reset() {
132
+ return new Promise((resolve, reject) => {
133
+ this.stmt.reset(err => {
134
+ if (err) reject(err); else resolve();
135
+ });
136
+ });
137
+ }
138
+
139
+ async run(...params) {
140
+ return new Promise((resolve, reject) => {
141
+ this.stmt.run(...params, err => {
142
+ if (err) reject(err); else resolve();
143
+ });
144
+ });
145
+ }
146
+ }
@@ -0,0 +1,191 @@
1
+ import expect from "expect.js";
2
+ import sinon from "sinon";
3
+ import Sqlite3 from "sqlite3";
4
+ import {Sqlite} from "@bibliotek/sqlite";
5
+
6
+ describe("Sqlite", () => {
7
+ describe("constructor(db)", () => {
8
+ it("should initialize db from file path", async () => {
9
+ const db = new Sqlite(":memory:");
10
+ expect(db).to.be.a(Sqlite);
11
+ expect(db.db).to.be.a(Sqlite3.Database);
12
+ expect(db.db.filename).to.be(":memory:");
13
+ await db.close();
14
+ });
15
+
16
+ it("should use database if provided", async () => {
17
+ const sqlite = {};
18
+ const db = new Sqlite(sqlite);
19
+ expect(db.db).to.be(sqlite);
20
+ });
21
+ });
22
+
23
+ describe("Sqlite.memory()", () => {
24
+ it("should return in-memory DB", async () => {
25
+ const db = Sqlite.memory();
26
+ expect(db.db.filename).to.be(":memory:");
27
+ await db.close();
28
+ });
29
+ });
30
+
31
+ describe("Sqlite.disk()", () => {
32
+ it("should return on-disk temp DB", async () => {
33
+ const db = Sqlite.disk();
34
+ expect(db.db.filename).to.be("");
35
+ await db.close();
36
+ });
37
+ });
38
+
39
+ describe(".close()", () => {
40
+ it("should close the DB", async () => {
41
+ const db = {close: sinon.spy(fn => fn())};
42
+ const sqlite = new Sqlite(db);
43
+
44
+ await sqlite.close();
45
+ expect(db.close.calledOnce).to.be(true);
46
+ });
47
+ });
48
+
49
+ describe(".run(sql)", () => {
50
+ it("should run query on the DB", async () => {
51
+ const db = {run: sinon.spy((_, fn) => fn())};
52
+ const sqlite = new Sqlite(db);
53
+ const sql = "select 1";
54
+
55
+ await sqlite.run(sql);
56
+ expect(db.run.calledOnce).to.be(true);
57
+ expect(db.run.calledWith(sql)).to.be(true);
58
+ });
59
+ });
60
+
61
+ describe(".each(sql)", () => {
62
+ let sqlite;
63
+
64
+ beforeEach(async () => {
65
+ sqlite = Sqlite.memory();
66
+ await sqlite.run("create table t (id int, key text)");
67
+ await sqlite.run("insert into t values (1, 'foo')");
68
+ await sqlite.run("insert into t values (2, 'bar')");
69
+ });
70
+
71
+ it("should iterate over results", async () => {
72
+ const rows = [];
73
+
74
+ for await (const row of sqlite.each("select * from t order by id")) {
75
+ rows.push(row);
76
+ }
77
+
78
+ expect(rows.length).to.be(2);
79
+ expect(rows[0]).to.be.an("object");
80
+ expect(rows[0].id).to.be(1);
81
+ expect(rows[0].key).to.be("foo");
82
+ expect(rows[1]).to.be.an("object");
83
+ expect(rows[1].id).to.be(2);
84
+ expect(rows[1].key).to.be("bar");
85
+ });
86
+ });
87
+
88
+ describe(".get(sql)", () => {
89
+ let sqlite;
90
+
91
+ beforeEach(async () => {
92
+ sqlite = Sqlite.memory();
93
+ await sqlite.run("create table t (id int, key text)");
94
+ await sqlite.run("insert into t values (1, 'foo')");
95
+ await sqlite.run("insert into t values (2, 'bar')");
96
+ });
97
+
98
+ it("should return first row", async () => {
99
+ const row = await sqlite.get("select * from t order by id");
100
+
101
+ expect(row).to.be.an("object");
102
+ expect(row.id).to.be(1);
103
+ expect(row.key).to.be("foo");
104
+ });
105
+ });
106
+
107
+ describe(".prepare(sql)", () => {
108
+ it("should return a prepared statment", async () => {
109
+ const sqlite = Sqlite.memory();
110
+ await sqlite.run("create table t (i int, f text)");
111
+
112
+ const stmt = sqlite.prepare("select f from t where i = ?");
113
+
114
+ expect(stmt).to.be.a(Sqlite.Statement);
115
+ });
116
+ });
117
+ });
118
+
119
+ describe("Sqlite.Statement", () => {
120
+ let sqlite, insert, select;
121
+
122
+ beforeEach(async () => {
123
+ sqlite = Sqlite.memory();
124
+ await sqlite.run("create table t (id int, key text)");
125
+ insert = sqlite.prepare("insert into t values (?, ?)");
126
+ select = sqlite.prepare("select key from t where id between ? and ? order by id");
127
+ });
128
+
129
+ describe(".run(...params)", () => {
130
+ it("should execute statement using params", async () => {
131
+ await insert.run(1, "foo");
132
+ await insert.run(2, "bar");
133
+
134
+ const row = await sqlite.get("select count(*) c from t");
135
+ expect(row.c).to.be(2);
136
+ });
137
+ });
138
+
139
+ describe(".each(...params)", () => {
140
+ let sqlite;
141
+
142
+ beforeEach(async () => {
143
+ await insert.run(1, "foo");
144
+ await insert.run(2, "bar");
145
+ });
146
+
147
+ it("should execute statement and iterate over results", async () => {
148
+ const rows = [];
149
+
150
+ for await (const row of select.each(0, 5)) {
151
+ rows.push(row);
152
+ }
153
+
154
+ expect(rows.length).to.be(2);
155
+ expect(rows[0]).to.be.an("object");
156
+ expect(rows[0].key).to.be("foo");
157
+ expect(rows[1]).to.be.an("object");
158
+ expect(rows[1].key).to.be("bar");
159
+ });
160
+ });
161
+
162
+ describe(".finalize()", () => {
163
+ it("should finalize the statement", async () => {
164
+ insert.stmt = {finalize: sinon.spy(fn => fn())};
165
+ await insert.finalize();
166
+ expect(insert.stmt.finalize.calledOnce).to.be(true);
167
+ });
168
+ });
169
+
170
+ describe(".get(...params)", () => {
171
+ beforeEach(async () => {
172
+ await insert.run(1, "foo");
173
+ await insert.run(2, "bar");
174
+ });
175
+
176
+ it("should execute statement and return first row", async () => {
177
+ const row = await select.get(1, 5);
178
+
179
+ expect(row).to.be.an("object");
180
+ expect(row.key).to.be("foo");
181
+ });
182
+ });
183
+
184
+ describe(".reset()", () => {
185
+ it("should reset the statment", async () => {
186
+ insert.stmt = {reset: sinon.spy(fn => fn())};
187
+ await insert.reset();
188
+ expect(insert.stmt.reset.calledOnce).to.be(true);
189
+ });
190
+ });
191
+ });