@storecraft/database-sqlite 1.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/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # Storecraft `SQLite` driver
2
+
3
+ <div style="text-align:center">
4
+ <img src='https://storecraft.app/storecraft-color.svg'
5
+ width='90%'' />
6
+ </div><hr/><br/>
7
+
8
+ Official `SQLite` driver for `StoreCraft` using `better-sqlite` package.
9
+
10
+ ```bash
11
+ npm i @storecraft/database-sqlite
12
+ ```
13
+
14
+ ## usage
15
+
16
+ ```js
17
+ import 'dotenv/config';
18
+ import http from "node:http";
19
+ import { join } from "node:path";
20
+ import { homedir } from "node:os";
21
+
22
+ import { App } from '@storecraft/core'
23
+ import { NodePlatform } from '@storecraft/platforms/node';
24
+ import { SQLite } from '@storecraft/database-sqlite'
25
+ import { migrateToLatest } from '@storecraft/database-sql-base/migrate.js'
26
+ import { NodeLocalStorage } from '@storecraft/storage-local/node'
27
+
28
+ const app = new App(
29
+ {
30
+ auth_admins_emails: ['admin@sc.com'],
31
+ auth_secret_access_token: 'auth_secret_access_token',
32
+ auth_secret_refresh_token: 'auth_secret_refresh_token'
33
+ }
34
+ )
35
+ .withPlatform(new NodePlatform())
36
+ .withDatabase(
37
+ new SQLite(
38
+ {
39
+ filename: join(homedir(), 'db.sqlite')
40
+ }
41
+ )
42
+ )
43
+ .withStorage(new NodeLocalStorage(join(homedir(), 'tomer')))
44
+
45
+ await app.init();
46
+ await migrateToLatest(app.db, false);
47
+
48
+ const server = http.createServer(app.handler).listen(
49
+ 8000,
50
+ () => {
51
+ console.log(`Server is running on http://localhost:8000`);
52
+ }
53
+ );
54
+
55
+ ```
56
+
57
+ ## Testing Locally
58
+
59
+ ### **SQLite**
60
+ 1. Simply `runner.sqlite-local.test.js`
61
+ ```bash
62
+ npm run test:sqlite
63
+ ```
64
+
65
+ ### **Postgres**
66
+ 1. First setup a `postgres` server
67
+ ```bash
68
+ docker pull postgres
69
+ docker run --name some-postgres -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=admin \
70
+ -e PGDATA=/var/lib/postgresql/data/pgdata \
71
+ -v $(pwd):/var/lib/postgresql/data \
72
+ -p 5432:5432 -d postgres
73
+ ```
74
+
75
+ 2. create Environment
76
+ create `.env` file with
77
+ ```bash
78
+ POSTGRES_USER='user'
79
+ POSTGRES_PASSWORD='password'
80
+ POSTGRES_PORT=5432
81
+ POSTGRES_HOST='localhost'
82
+ ```
83
+
84
+ 3. Run `runner.postgres-local.test.js`
85
+ ```bash
86
+ npm run test:postgres
87
+ ```
88
+
89
+ ### **MySQL**
90
+ 1. First setup a `mysql` server
91
+ ```zsh
92
+ docker pull mysql
93
+ docker run --name mysql \
94
+ -v $(pwd):/etc/mysql/conf.d \
95
+ -v /my/own/datadir:/var/lib/mysql \
96
+ -e MYSQL_ROOT_PASSWORD=admin -e MYSQL_ROOT_HOST=localhost \
97
+ -e MYSQL_DATABASE=main -e MYSQL_USER=admin -e MYSQL_PASSWORD=admin \
98
+ -p 8080:3306 -d mysql
99
+ ```
100
+
101
+ 2. create Environment
102
+ create `.env` file with
103
+ ```bash
104
+ MYSQL_USER='root'
105
+ MYSQL_ROOT_PASSWORD='password'
106
+ MYSQL_PORT=8080
107
+ MYSQL_HOST='localhost'
108
+ ```
109
+
110
+ 3. Run `runner.mysql-local.test.js`
111
+ ```bash
112
+ npm run test:mysql
113
+ ```
114
+
115
+ ### **MSSQL** (Currently NOT SUPPORTED, waiting for votes on that one)
116
+ Work in progress, i will probably not continue with this.
117
+
118
+ 1. First setup a `mysql` server
119
+ ```zsh
120
+ docker pull mcr.microsoft.com/mssql/server
121
+ # use this For OSX with M1 chips
122
+ docker pull mcr.microsoft.com/azure-sql-edge:latest
123
+ docker run --name some-mssql \
124
+ -e ACCEPT_EULA=Y -e MSSQL_SA_PASSWORD='Abcd1234!?' \
125
+ -v $(pwd):/var/opt/mssql \
126
+ -p 1433:1433 \
127
+ -d mcr.microsoft.com/azure-sql-edge:latest
128
+
129
+ ```
130
+
131
+ 2. create Environment
132
+ create `.env` file with
133
+ ```bash
134
+ MYSQL_USER='root'
135
+ MYSQL_ROOT_PASSWORD='password'
136
+ MYSQL_PORT=8080
137
+ MYSQL_HOST='localhost'
138
+ ```
139
+
140
+ 3. Run `runner.mysql-local.test.js`
141
+ ```bash
142
+ npm run test:mysql
143
+ ```
144
+
145
+
146
+ ```text
147
+ Author: Tomer Shalev <tomer.shalev@gmail.com>
148
+ ```
package/index.js ADDED
@@ -0,0 +1,30 @@
1
+ import { SQL } from '@storecraft/database-sql-base';
2
+ import { SqliteDialect } from 'kysely';
3
+ import BetterSQLite from 'better-sqlite3';
4
+
5
+
6
+ /**
7
+ * @description `better-sqlite` driver for storecraft
8
+ *
9
+ */
10
+ export class SQLite extends SQL {
11
+
12
+ /**
13
+ *
14
+ * @param {import('./types.public.d.ts').Config} [config] config
15
+ */
16
+ constructor(config) {
17
+ super(
18
+ {
19
+ dialect_type: 'SQLITE',
20
+ dialect: new SqliteDialect(
21
+ {
22
+ database: async () => new BetterSQLite(config.filename, config.options)
23
+ }
24
+ ),
25
+ }
26
+ );
27
+
28
+ }
29
+
30
+ }
package/migrate.js ADDED
@@ -0,0 +1,5 @@
1
+ export { migrateToLatest } from '@storecraft/database-sql-base/migrate.js';
2
+
3
+
4
+
5
+
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@storecraft/database-sqlite",
3
+ "version": "1.0.1",
4
+ "description": "Official SQLite Database driver for storecraft",
5
+ "license": "MIT",
6
+ "author": "Tomer Shalev (https://github.com/store-craft)",
7
+ "homepage": "https://github.com/store-craft/storecraft",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/store-craft/storecraft.git",
11
+ "directory": "packages/databases/database-sqlite"
12
+ },
13
+ "keywords": [
14
+ "commerce",
15
+ "dashboard",
16
+ "code",
17
+ "storecraft"
18
+ ],
19
+ "type": "module",
20
+ "main": "index.js",
21
+ "types": "types.public.d.ts",
22
+ "scripts": {
23
+ "test": "uvu -c",
24
+ "database-sqlite:test": "node ./tests/runner.test.js",
25
+ "database-sqlite:publish": "npm publish --access public"
26
+ },
27
+ "dependencies": {
28
+ "@storecraft/core": "^1.0.0",
29
+ "@storecraft/database-sql-base": "^1.0.0",
30
+ "@types/better-sqlite3": "^7.6.9",
31
+ "kysely": "^0.27.2"
32
+ },
33
+ "devDependencies": {
34
+ "@storecraft/platforms": "^1.0.0",
35
+ "@storecraft/test-runner": "^1.0.0",
36
+ "@types/node": "^20.11.0",
37
+ "dotenv": "^16.3.1",
38
+ "uvu": "^0.5.6"
39
+ }
40
+ }
@@ -0,0 +1,160 @@
1
+ import { SQL } from '../index.js'
2
+ import { sanitize_array } from './utils.funcs.js'
3
+ import { count_regular, delete_me, insert_search_of, insert_tags_of, regular_upsert_me,
4
+ where_id_or_handle_table,
5
+ with_media,
6
+ with_tags} from './con.shared.js'
7
+ import { query_to_eb, query_to_sort } from './utils.query.js';
8
+
9
+ /**
10
+ * @typedef {import('@storecraft/core/v-database').db_auth_users} db_col
11
+ */
12
+
13
+ export const table_name = 'auth_users';
14
+
15
+ /**
16
+ * @param {SQL} driver
17
+ * @returns {db_col["upsert"]}
18
+ */
19
+ const upsert = (driver) => {
20
+
21
+ return async (item, search_terms=[]) => {
22
+ const c = driver.client;
23
+ try {
24
+ const t = await c.transaction().execute(
25
+ async (trx) => {
26
+
27
+ await insert_tags_of(trx, item.tags, item.id, item.email, table_name);
28
+ await insert_search_of(trx, search_terms, item.id, item.email, table_name);
29
+
30
+ return await regular_upsert_me(trx, table_name, {
31
+ confirmed_mail: item.confirmed_mail ? 1 : 0,
32
+ email: item.email,
33
+ handle: item.email,
34
+ password: item.password,
35
+ created_at: item.created_at,
36
+ updated_at: item.updated_at,
37
+ id: item.id,
38
+ roles: JSON.stringify(item.roles),
39
+ });
40
+ }
41
+ );
42
+ return t.numInsertedOrUpdatedRows>0;
43
+ } catch(e) {
44
+ console.log(e);
45
+ return false;
46
+ }
47
+ return true;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * @param {SQL} driver
53
+ * @returns {db_col["get"]}
54
+ */
55
+ const get = (driver) => {
56
+ return (id_or_email, options) => {
57
+ return driver.client
58
+ .selectFrom(table_name)
59
+ .selectAll()
60
+ .where(where_id_or_handle_table(id_or_email))
61
+ .executeTakeFirst();
62
+ }
63
+ }
64
+
65
+
66
+ /**
67
+ * @param {SQL} driver
68
+ * @returns {db_col["getByEmail"]}
69
+ */
70
+ const getByEmail = (driver) => {
71
+ return (email) => {
72
+ return driver.client
73
+ .selectFrom('auth_users')
74
+ .selectAll().where('email', '=', email)
75
+ .executeTakeFirst();
76
+ }
77
+ }
78
+
79
+ /**
80
+ * @param {SQL} driver
81
+ * @returns {db_col["remove"]}
82
+ */
83
+ const remove = (driver) => {
84
+ return async (id_or_email) => {
85
+ try {
86
+ await driver.client.transaction().execute(
87
+ async (trx) => {
88
+
89
+ // entities
90
+ // delete me
91
+ await delete_me(trx, table_name, id_or_email);
92
+ }
93
+ );
94
+ } catch(e) {
95
+ console.log(e);
96
+ return false;
97
+ }
98
+ return true;
99
+ }
100
+ }
101
+ /**
102
+ * @param {SQL} driver
103
+ * @returns {db_col["removeByEmail"]}
104
+ */
105
+ const removeByEmail = (driver) => {
106
+ return async (email) => {
107
+ const r = await driver.client
108
+ .deleteFrom('auth_users')
109
+ .where('email', '=', email)
110
+ .executeTakeFirst();
111
+ return r.numDeletedRows>0;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * @param {SQL} driver
117
+ *
118
+ *
119
+ * @returns {db_col["list"]}
120
+ */
121
+ const list = (driver) => {
122
+ return async (query) => {
123
+
124
+ const items = await driver.client.selectFrom(table_name)
125
+ .selectAll()
126
+ .select(eb => [
127
+ with_tags(eb, eb.ref('auth_users.id'), driver.dialectType),
128
+ with_media(eb, eb.ref('auth_users.id'), driver.dialectType),
129
+ ])
130
+ .where(
131
+ (eb) => {
132
+ return query_to_eb(eb, query, table_name);
133
+ }
134
+ )
135
+ .orderBy(query_to_sort(query))
136
+ .limit(query.limitToLast ?? query.limit ?? 10)
137
+ .execute();
138
+
139
+ if(query.limitToLast) items.reverse();
140
+
141
+ return items;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * @param {SQL} driver
147
+ * @return {db_col}}
148
+ * */
149
+ export const impl = (driver) => {
150
+
151
+ return {
152
+ get: get(driver),
153
+ getByEmail: getByEmail(driver),
154
+ upsert: upsert(driver),
155
+ remove: remove(driver),
156
+ removeByEmail: removeByEmail(driver),
157
+ list: list(driver),
158
+ count: count_regular(driver, table_name),
159
+ }
160
+ }
@@ -0,0 +1,197 @@
1
+ import { SQL } from '../index.js'
2
+ import { report_document_media } from './con.images.js'
3
+ import { delete_entity_values_by_value_or_reporter, delete_me,
4
+ delete_media_of, delete_search_of, delete_tags_of,
5
+ insert_media_of, insert_search_of, insert_tags_of,
6
+ select_entity_ids_by_value_or_reporter,
7
+ regular_upsert_me, where_id_or_handle_table,
8
+ with_media, with_tags,
9
+ count_regular} from './con.shared.js'
10
+ import { sanitize_array } from './utils.funcs.js'
11
+ import { query_to_eb, query_to_sort } from './utils.query.js'
12
+
13
+
14
+ /**
15
+ * @typedef {import('@storecraft/core/v-database').db_collections} db_col
16
+ */
17
+ export const table_name = 'collections'
18
+
19
+ /**
20
+ * @param {SQL} driver
21
+ * @returns {db_col["upsert"]}
22
+ */
23
+ const upsert = (driver) => {
24
+ return async (item, search_terms) => {
25
+ const c = driver.client;
26
+ try {
27
+ const t = await driver.client.transaction().execute(
28
+ async (trx) => {
29
+
30
+ // entities
31
+ await insert_tags_of(trx, item.tags, item.id, item.handle, table_name);
32
+ await insert_search_of(trx, search_terms, item.id, item.handle, table_name);
33
+ await insert_media_of(trx, item.media, item.id, item.handle, table_name);
34
+ await report_document_media(driver)(item, trx);
35
+ // main
36
+ await regular_upsert_me(trx, table_name, {
37
+ created_at: item.created_at,
38
+ updated_at: item.updated_at,
39
+ id: item.id,
40
+ handle: item.handle,
41
+ active: item.active ? 1 : 0,
42
+ attributes: JSON.stringify(item.attributes),
43
+ description: item.description,
44
+ published: item.published,
45
+ title: item.title
46
+ });
47
+ }
48
+ );
49
+ } catch(e) {
50
+ console.log(e);
51
+ return false;
52
+ }
53
+ return true;
54
+ }
55
+ }
56
+
57
+
58
+ /**
59
+ * @param {SQL} driver
60
+ * @returns {db_col["get"]}
61
+ */
62
+ const get = (driver) => {
63
+ return (id_or_handle, options) => {
64
+
65
+ return driver.client
66
+ .selectFrom(table_name)
67
+ .selectAll('collections')
68
+ .select(eb => [
69
+ with_tags(eb, eb.ref('collections.id'), driver.dialectType),
70
+ with_media(eb, eb.ref('collections.id'), driver.dialectType)
71
+ ])
72
+ .where(where_id_or_handle_table(id_or_handle))
73
+ // .compile()
74
+ .executeTakeFirst();
75
+ }
76
+ }
77
+
78
+
79
+ /**
80
+ * @param {SQL} driver
81
+ * @returns {db_col["remove"]}
82
+ */
83
+ const remove = (driver) => {
84
+ return async (id_or_handle) => {
85
+ try {
86
+ await driver.client.transaction().execute(
87
+ async (trx) => {
88
+
89
+ // entities
90
+ await delete_tags_of(trx, id_or_handle);
91
+ await delete_search_of(trx, id_or_handle);
92
+ await delete_media_of(trx, id_or_handle);
93
+ // PRODUCTS -> COLLECTIONS
94
+ await delete_entity_values_by_value_or_reporter('products_to_collections')(
95
+ trx, id_or_handle, id_or_handle
96
+ );
97
+ // STOREFRONT => COLLECTIONS
98
+ await delete_entity_values_by_value_or_reporter('storefronts_to_other')(
99
+ trx, id_or_handle, id_or_handle
100
+ );
101
+
102
+ // delete me
103
+ await delete_me(trx, table_name, id_or_handle);
104
+ }
105
+ );
106
+
107
+ } catch(e) {
108
+ console.log(e);
109
+ return false;
110
+ }
111
+ return true;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * @param {SQL} driver
117
+ * @returns {db_col["list"]}
118
+ */
119
+ const list = (driver) => {
120
+ return async (query) => {
121
+
122
+ const items = await driver.client.selectFrom(table_name)
123
+ .selectAll()
124
+ .select(eb => [
125
+ with_tags(eb, eb.ref('collections.id'), driver.dialectType),
126
+ with_media(eb, eb.ref('collections.id'), driver.dialectType),
127
+ ])
128
+ .where(
129
+ (eb) => {
130
+ return query_to_eb(eb, query, table_name);
131
+ }
132
+ )
133
+ .orderBy(query_to_sort(query))
134
+ .limit(query.limitToLast ?? query.limit ?? 10)
135
+ .execute();
136
+
137
+ if(query.limitToLast) items.reverse();
138
+
139
+ return sanitize_array(items);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * @param {SQL} driver
145
+ * @returns {db_col["list_collection_products"]}
146
+ */
147
+ const list_collection_products = (driver) => {
148
+ return async (handle_or_id, query={}) => {
149
+
150
+ const items = await driver.client
151
+ .selectFrom('products')
152
+ .selectAll()
153
+ .select(eb => [
154
+ with_media(eb, eb.ref('products.id'), driver.dialectType),
155
+ with_tags(eb, eb.ref('products.id'), driver.dialectType),
156
+ ])
157
+ .where(
158
+ (eb) => eb.and(
159
+ [
160
+ query_to_eb(eb, query, 'products'),
161
+ eb('products.id', 'in',
162
+ eb => select_entity_ids_by_value_or_reporter( // select all the product ids by collection id
163
+ eb, 'products_to_collections', handle_or_id
164
+ )
165
+ )
166
+ ].filter(Boolean)
167
+ )
168
+ )
169
+ .orderBy(query_to_sort(query))
170
+ .limit(query.limitToLast ?? query.limit ?? 10)
171
+ .execute();
172
+
173
+ if(query.limitToLast) items.reverse();
174
+
175
+ return sanitize_array(items);
176
+ }
177
+ }
178
+
179
+ /**
180
+ * @param {SQL} driver
181
+ *
182
+ *
183
+ * @return {db_col}}
184
+ * */
185
+ export const impl = (driver) => {
186
+
187
+ return {
188
+
189
+ get: get(driver),
190
+ upsert: upsert(driver),
191
+ remove: remove(driver),
192
+ list: list(driver),
193
+ list_collection_products: list_collection_products(driver),
194
+ count: count_regular(driver, table_name),
195
+
196
+ }
197
+ }