@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 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 First**: SQL is the best way to interact with data.
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 best choice.
21
+ concurrent, and custom use cases, sqlite is the simple, correct choice.
20
22
 
21
- 4. **Security**: Inline SQL is insecure, hard to maintain, and error-prone.
23
+ 4. **Security**: Inline SQL is insecure, unmaintainable, and error-prone.
22
24
 
23
- 5. **Separation**: SQL code should be in separate SQL files rather than
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
- ## Solution
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` directory to your project and include as many `.sql` files as you
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
- Sqlrite automatically loads only two types of code, "transactions," and
35
- "prepared statements." Transactions can contain multiple statements, and are
36
- best for operations like creating tables, views, and indexes. Prepared
37
- Statements are best for the queries you will be running.
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
- -- TX: createEmployeeTable
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
- -- PS: addEmployee
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
- -- PS: getTopEmployee
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 sqlrite from "sqlrite";
93
+ import SqlRite from "@possumtech/sqlrite";
62
94
 
63
- const sql = new sqlrite();
95
+ const sql = new SqlRite();
64
96
 
65
- (async () => {
66
- await sql.createEmployeeTable();
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
- await sql.addEmployee.run({ name: "John", position: "CEO", salary: 99999 });
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
- const employee = await sql.getTopEmployee.get();
104
+ console.log(`The highest paid employee is ${employee.name}.`);
74
105
 
75
- console.log(employee.name);
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
- Additional arguments to the
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.1",
3
+ "version": "0.1.2",
4
4
  "description": "SQL Done Right",
5
- "keywords": ["node", "sqlite", "sql", "orm", "database"],
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": "sqlrite.js",
23
+ "main": "SqlRite.js",
18
24
  "scripts": {
19
- "test": "node ./test/test.js"
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();