@possumtech/sqlrite 0.1.1 → 0.1.2
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 +59 -31
- package/SqlRite.js +37 -0
- package/biome.json +30 -0
- package/package.json +11 -4
- package/sql/test.sql +25 -0
- package/test/test.js +17 -0
package/README.md
CHANGED
|
@@ -5,41 +5,64 @@ SQL Done Right
|
|
|
5
5
|
## About sqlrite
|
|
6
6
|
|
|
7
7
|
The sqlrite package is a modern node module that delivers an opinionated
|
|
8
|
-
alternative to ORMs.
|
|
8
|
+
alternative to ORMs. It is a thin wrapper around the
|
|
9
|
+
[native sqlite module](https://nodejs.org/api/sqlite.html), which
|
|
10
|
+
enables one to separate SQL code from Javascript code.
|
|
9
11
|
|
|
10
12
|
## Opinions
|
|
11
13
|
|
|
12
|
-
1. **SQL
|
|
14
|
+
1. **SQL Supremacy**: Your application is data, and SQL is the best interface.
|
|
13
15
|
|
|
14
16
|
2. **Standards**: Node is the standard for server-side web apps, and it now
|
|
15
17
|
contains a native sqlite module. Sqlite is the standard for SQL.
|
|
16
18
|
|
|
17
19
|
3. **Simplicity**: It takes as much time to master an ORM as it would take to
|
|
18
20
|
just master SQL, and with worse performance. For all but the most distributed,
|
|
19
|
-
concurrent, and custom use cases, sqlite is the
|
|
21
|
+
concurrent, and custom use cases, sqlite is the simple, correct choice.
|
|
20
22
|
|
|
21
|
-
4. **Security**: Inline SQL is insecure,
|
|
23
|
+
4. **Security**: Inline SQL is insecure, unmaintainable, and error-prone.
|
|
22
24
|
|
|
23
|
-
5. **
|
|
25
|
+
5. **Speed**: By enforcing the use of prepared statements, sqlrite ensures that
|
|
26
|
+
your queries are compiled and cached by the sqlite engine.
|
|
27
|
+
|
|
28
|
+
6. **Separation**: SQL code should be in separate SQL files rather than
|
|
24
29
|
scattered throughout your JS codebase.
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
7. **Size**: By relying on the native sqlite module, and nothing else, sqlrite
|
|
32
|
+
won't bloat your project with unnecessary dependencies. This thing is *tiny*.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
27
35
|
|
|
28
36
|
**SQL**
|
|
29
37
|
|
|
30
|
-
Add a `sql`
|
|
38
|
+
Add a `sql` folder to your project and include as many `.sql` files as you
|
|
31
39
|
wish, with whatever folder structure you like. Sqlrite will automatically load
|
|
32
40
|
them all.
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
| Syntax | Name | Description |
|
|
43
|
+
|---------------------|------------------------|------------------------|
|
|
44
|
+
| `-- INIT: txName` | Executed Transaction | Executed transaction |
|
|
45
|
+
| `-- EXEC: txName` | Executable Transaction | Executable transaction |
|
|
46
|
+
| `-- PREP: stmtName` | Prepared Statements | Prepared statement |
|
|
47
|
+
|
|
48
|
+
There are three types of "chunk" one can add to a `.sql` file:
|
|
49
|
+
|
|
50
|
+
1. **INIT**: A transaction that is executed when the database is first created.
|
|
51
|
+
This is where you should create your tables, for example.
|
|
52
|
+
|
|
53
|
+
2. **EXEC**: A transaction that can be executed at any time. For example,
|
|
54
|
+
dropping a table. This is where you should put your transactions that are
|
|
55
|
+
not prepared statements, like maintaining your database.
|
|
56
|
+
|
|
57
|
+
3. **PREP**: A prepared statement that can be executed at any time. This is
|
|
58
|
+
where you should put your queries.
|
|
38
59
|
|
|
39
60
|
**Example SQL File**
|
|
40
61
|
|
|
41
62
|
```sql
|
|
42
|
-
--
|
|
63
|
+
-- INIT: createEmployeeTable
|
|
64
|
+
BEGIN TRANSACTION;
|
|
65
|
+
|
|
43
66
|
CREATE TABLE IF NOT EXISTS employees (
|
|
44
67
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
45
68
|
name TEXT NOT NULL,
|
|
@@ -47,42 +70,49 @@ CREATE TABLE IF NOT EXISTS employees (
|
|
|
47
70
|
salary REAL NOT NULL
|
|
48
71
|
);
|
|
49
72
|
|
|
50
|
-
|
|
73
|
+
END TRANSACTION;
|
|
74
|
+
|
|
75
|
+
-- EXEC: deleteTable
|
|
76
|
+
BEGIN TRANSACTION;
|
|
77
|
+
|
|
78
|
+
DROP TABLE IF EXISTS employees;
|
|
79
|
+
|
|
80
|
+
END TRANSACTION;
|
|
81
|
+
|
|
82
|
+
-- PREP: addEmployee
|
|
51
83
|
INSERT INTO employees (name, position, salary)
|
|
52
84
|
VALUES ($name, $position, $salary);
|
|
53
85
|
|
|
54
|
-
--
|
|
86
|
+
-- PREP: getHighestPaidEmployee
|
|
55
87
|
SELECT name FROM employees ORDER BY salary DESC LIMIT 1;
|
|
56
88
|
```
|
|
57
89
|
|
|
58
90
|
**Example Node File**
|
|
59
91
|
|
|
60
92
|
```js
|
|
61
|
-
import
|
|
93
|
+
import SqlRite from "@possumtech/sqlrite";
|
|
62
94
|
|
|
63
|
-
const sql = new
|
|
95
|
+
const sql = new SqlRite();
|
|
64
96
|
|
|
65
|
-
(
|
|
66
|
-
|
|
97
|
+
sql.addEmployee.run({ name: "John", position: "CEO", salary: 99999 });
|
|
98
|
+
sql.addEmployee.run({ name: "Jane", position: "COO", salary: 49998 });
|
|
99
|
+
sql.addEmployee.run({ name: "Jack", position: "CFO", salary: 49997 });
|
|
100
|
+
sql.addEmployee.run({ name: "Jill", position: "CIO", salary: 49996 });
|
|
67
101
|
|
|
68
|
-
|
|
69
|
-
await sql.addEmployee.run({ name: "Jane", position: "COO", salary: 49998 });
|
|
70
|
-
await sql.addEmployee.run({ name: "Jack", position: "CFO", salary: 49998 });
|
|
71
|
-
await sql.addEmployee.run({ name: "Jill", position: "CIO", salary: 49998 });
|
|
102
|
+
const employee = sql.getHighestPaidEmployee.get();
|
|
72
103
|
|
|
73
|
-
|
|
104
|
+
console.log(`The highest paid employee is ${employee.name}.`);
|
|
74
105
|
|
|
75
|
-
|
|
76
|
-
})();
|
|
77
|
-
```
|
|
106
|
+
sql.deleteTable();
|
|
78
107
|
|
|
108
|
+
```
|
|
79
109
|
|
|
80
110
|
## Installation
|
|
81
111
|
|
|
82
112
|
Navigate to your project directory and run the following command:
|
|
83
113
|
|
|
84
114
|
```bash
|
|
85
|
-
npm install sqlrite
|
|
115
|
+
npm install @possumtech/sqlrite
|
|
86
116
|
```
|
|
87
117
|
|
|
88
118
|
## Configuration
|
|
@@ -98,7 +128,5 @@ const sql = new sqlrite(options = {
|
|
|
98
128
|
dir: "./sql",
|
|
99
129
|
});
|
|
100
130
|
```
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
[native sqlite module](https://nodejs.org/api/sqlite.html) can be passed as
|
|
104
|
-
well.
|
|
131
|
+
Additional arguments will be passed to the options object of the native sqlite
|
|
132
|
+
module.
|
package/SqlRite.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { DatabaseSync } from "node:sqlite";
|
|
3
|
+
|
|
4
|
+
export default class SqlRite {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
const defaults = {
|
|
7
|
+
path: ":memory:",
|
|
8
|
+
dir: "sql/",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const merged = { ...defaults, ...options };
|
|
12
|
+
|
|
13
|
+
const db = new DatabaseSync(merged.path, merged);
|
|
14
|
+
|
|
15
|
+
const { dir } = merged;
|
|
16
|
+
const files = fs.readdirSync(dir);
|
|
17
|
+
const code = files.map((f) => fs.readFileSync(dir + f, "utf8")).join("");
|
|
18
|
+
|
|
19
|
+
const chunks =
|
|
20
|
+
/-- (?<chunk>(?<type>INIT|EXEC|PREP): (?<name>\w+)\n(?<sql>.*?))($|(?=-- (INIT|EXEC|PREP):))/gs;
|
|
21
|
+
|
|
22
|
+
for (const chunk of code.matchAll(chunks)) {
|
|
23
|
+
const { type, name, sql } = chunk.groups;
|
|
24
|
+
switch (type) {
|
|
25
|
+
case "INIT":
|
|
26
|
+
db.exec(sql);
|
|
27
|
+
break;
|
|
28
|
+
case "EXEC":
|
|
29
|
+
this[name] = () => db.exec(sql);
|
|
30
|
+
break;
|
|
31
|
+
case "PREP":
|
|
32
|
+
this[name] = db.prepare(sql);
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
package/biome.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": false,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": false
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"ignoreUnknown": false,
|
|
10
|
+
"ignore": []
|
|
11
|
+
},
|
|
12
|
+
"formatter": {
|
|
13
|
+
"enabled": true,
|
|
14
|
+
"indentStyle": "tab"
|
|
15
|
+
},
|
|
16
|
+
"organizeImports": {
|
|
17
|
+
"enabled": true
|
|
18
|
+
},
|
|
19
|
+
"linter": {
|
|
20
|
+
"enabled": true,
|
|
21
|
+
"rules": {
|
|
22
|
+
"recommended": true
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"javascript": {
|
|
26
|
+
"formatter": {
|
|
27
|
+
"quoteStyle": "double"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@possumtech/sqlrite",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "SQL Done Right",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"node",
|
|
7
|
+
"sqlite",
|
|
8
|
+
"sql",
|
|
9
|
+
"orm",
|
|
10
|
+
"database"
|
|
11
|
+
],
|
|
6
12
|
"homepage": "https://github.com/possumtech/sqlrite#readme",
|
|
7
13
|
"bugs": {
|
|
8
14
|
"url": "https://github.com/possumtech/sqlrite/issues"
|
|
@@ -14,8 +20,9 @@
|
|
|
14
20
|
"license": "MIT",
|
|
15
21
|
"author": "@wikitopian",
|
|
16
22
|
"type": "module",
|
|
17
|
-
"main": "
|
|
23
|
+
"main": "SqlRite.js",
|
|
18
24
|
"scripts": {
|
|
19
|
-
"
|
|
25
|
+
"syntax": "npx @biomejs/biome check",
|
|
26
|
+
"test": "node --test"
|
|
20
27
|
}
|
|
21
28
|
}
|
package/sql/test.sql
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- INIT: createEmployeeTable
|
|
2
|
+
BEGIN TRANSACTION;
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS employees (
|
|
5
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6
|
+
name TEXT NOT NULL,
|
|
7
|
+
position TEXT NOT NULL,
|
|
8
|
+
salary REAL NOT NULL
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
END TRANSACTION;
|
|
12
|
+
|
|
13
|
+
-- EXEC: deleteTable
|
|
14
|
+
BEGIN TRANSACTION;
|
|
15
|
+
|
|
16
|
+
DROP TABLE IF EXISTS employees;
|
|
17
|
+
|
|
18
|
+
END TRANSACTION;
|
|
19
|
+
|
|
20
|
+
-- PREP: addEmployee
|
|
21
|
+
INSERT INTO employees (name, position, salary)
|
|
22
|
+
VALUES ($name, $position, $salary);
|
|
23
|
+
|
|
24
|
+
-- PREP: getHighestPaidEmployee
|
|
25
|
+
SELECT name FROM employees ORDER BY salary DESC LIMIT 1;
|
package/test/test.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import SqlRite from "../SqlRite.js";
|
|
3
|
+
|
|
4
|
+
const sql = new SqlRite();
|
|
5
|
+
|
|
6
|
+
sql.addEmployee.run({ name: "John", position: "CEO", salary: 99999 });
|
|
7
|
+
sql.addEmployee.run({ name: "Jane", position: "COO", salary: 49998 });
|
|
8
|
+
sql.addEmployee.run({ name: "Jack", position: "CFO", salary: 49997 });
|
|
9
|
+
sql.addEmployee.run({ name: "Jill", position: "CIO", salary: 49996 });
|
|
10
|
+
|
|
11
|
+
const employee = sql.getHighestPaidEmployee.get();
|
|
12
|
+
|
|
13
|
+
assert(employee?.name === "John", "The highest paid employee should be John");
|
|
14
|
+
|
|
15
|
+
console.log(`The highest paid employee is ${employee.name}.`);
|
|
16
|
+
|
|
17
|
+
sql.deleteTable();
|