@storecraft/database-sql-base 1.0.0 → 1.0.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 +30 -12
- package/index.js +172 -1
- package/{tsconfig.json → jsconfig.json} +1 -5
- package/migrate.js +25 -19
- package/migrations.mysql/00000_init_tables.js +6 -5
- package/migrations.postgres/00000_init_tables.js +15 -14
- package/migrations.shared/00001_seed_email_templates copy.js +262 -0
- package/migrations.shared/00001_seed_email_templates.js +5 -238
- package/migrations.sqlite/00000_init_tables.js +12 -12
- package/package.json +2 -4
- package/src/con.auth_users.js +5 -2
- package/src/con.collections.js +2 -2
- package/src/con.customers.js +2 -2
- package/src/con.discounts.js +3 -3
- package/src/con.discounts.utils.js +11 -11
- package/src/con.helpers.json.js +3 -3
- package/src/con.images.js +5 -5
- package/src/con.notifications.js +2 -2
- package/src/con.orders.js +19 -4
- package/src/con.posts.js +2 -2
- package/src/con.products.js +5 -5
- package/src/con.search.js +7 -7
- package/src/con.shared.experiment.js +723 -0
- package/src/con.shared.js +55 -21
- package/src/con.shipping.js +2 -2
- package/src/con.storefronts.js +2 -2
- package/src/con.tags.js +2 -2
- package/src/con.templates.js +22 -7
- package/src/utils.query.js +6 -6
- package/tests/runner.mssql-local.test.js +10 -6
- package/tests/runner.mysql-local.test.js +16 -63
- package/tests/runner.postgres-local.test.js +17 -62
- package/tests/runner.sqlite-local.test.js +15 -64
- package/tests/sandbox.test.js +15 -13
- package/types.public.d.ts +12 -4
- package/types.sql.tables.d.ts +4 -2
- package/driver.js +0 -190
- package/tests/query.cursor.test.js +0 -389
- package/tests/query.vql.test.js +0 -71
package/README.md
CHANGED
@@ -1,27 +1,45 @@
|
|
1
1
|
# Storecraft `SQL` driver
|
2
2
|
|
3
|
+
<div style="text-align:center">
|
4
|
+
<img src='https://storecraft.app/storecraft-color.svg'
|
5
|
+
width='90%' />
|
6
|
+
</div><hr/><br/>
|
7
|
+
|
3
8
|
Official `SQL` driver for `StoreCraft` with the dialects abstracted with `Kysely` or your own drivers.
|
4
9
|
|
10
|
+
```bash
|
11
|
+
npm i @storecraft/database-sql-base
|
12
|
+
```
|
13
|
+
|
5
14
|
## usage
|
6
15
|
|
7
16
|
```js
|
8
17
|
import 'dotenv/config';
|
9
18
|
import http from "node:http";
|
10
|
-
import { join } from "node:path";
|
11
|
-
import { homedir } from "node:os";
|
12
|
-
|
13
19
|
import { App } from '@storecraft/core'
|
14
|
-
import { NodePlatform } from '@storecraft/platform
|
15
|
-
import {
|
16
|
-
import {
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
import { NodePlatform } from '@storecraft/core/platform/node';
|
21
|
+
import { SQL } from '@storecraft/database-sql-base'
|
22
|
+
import { migrateToLatest } from '@storecraft/database-sql-base/migrate.js'
|
23
|
+
import { NodeLocalStorage } from '@storecraft/core/storage/node'
|
24
|
+
|
25
|
+
const app = new App(
|
26
|
+
{
|
27
|
+
auth_admins_emails: ['admin@sc.com'],
|
28
|
+
auth_secret_access_token: 'auth_secret_access_token',
|
29
|
+
auth_secret_refresh_token: 'auth_secret_refresh_token'
|
30
|
+
}
|
31
|
+
)
|
32
|
+
.withPlatform(new NodePlatform())
|
33
|
+
.withDatabase(
|
34
|
+
new SQL({
|
35
|
+
dialect: sqlite_dialect,
|
36
|
+
dialect_type: 'SQLITE'
|
37
|
+
})
|
38
|
+
)
|
39
|
+
.withStorage(new NodeLocalStorage('storage'))
|
23
40
|
|
24
41
|
await app.init();
|
42
|
+
await migrateToLatest(app.db, false);
|
25
43
|
|
26
44
|
const server = http.createServer(app.handler).listen(
|
27
45
|
8000,
|
package/index.js
CHANGED
@@ -1,3 +1,174 @@
|
|
1
|
-
|
1
|
+
import { App } from '@storecraft/core';
|
2
|
+
import { impl as auth_users } from './src/con.auth_users.js';
|
3
|
+
import { impl as collections } from './src/con.collections.js';
|
4
|
+
import { impl as customers } from './src/con.customers.js';
|
5
|
+
import { impl as discounts } from './src/con.discounts.js';
|
6
|
+
import { impl as images } from './src/con.images.js';
|
7
|
+
import { impl as notifications } from './src/con.notifications.js';
|
8
|
+
import { impl as orders } from './src/con.orders.js';
|
9
|
+
import { impl as posts } from './src/con.posts.js';
|
10
|
+
import { impl as products } from './src/con.products.js';
|
11
|
+
import { impl as shipping } from './src/con.shipping.js';
|
12
|
+
import { impl as storefronts } from './src/con.storefronts.js';
|
13
|
+
import { impl as tags } from './src/con.tags.js';
|
14
|
+
import { impl as templates } from './src/con.templates.js';
|
15
|
+
import { impl as search } from './src/con.search.js';
|
16
|
+
import { Kysely, ParseJSONResultsPlugin } from 'kysely'
|
17
|
+
import { SanitizePlugin } from './src/kysely.sanitize.plugin.js';
|
2
18
|
|
3
19
|
|
20
|
+
/**
|
21
|
+
* @param {any} b
|
22
|
+
* @param {string} msg
|
23
|
+
*/
|
24
|
+
const assert = (b, msg) => {
|
25
|
+
if(!Boolean(b)) throw new Error(msg);
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
* @typedef {import('./types.public.d.ts').Config} Config
|
30
|
+
* @typedef {import('./types.sql.tables.d.ts').Database} Database
|
31
|
+
* @typedef {import('kysely').Dialect} Dialect
|
32
|
+
* @typedef {import('@storecraft/core/database').db_driver} db_driver
|
33
|
+
*/
|
34
|
+
|
35
|
+
/**
|
36
|
+
* @implements {db_driver}
|
37
|
+
*/
|
38
|
+
export class SQL {
|
39
|
+
|
40
|
+
/** @type {boolean} */
|
41
|
+
#_is_ready;
|
42
|
+
|
43
|
+
/** @type {App<any, any, any>} */
|
44
|
+
#_app;
|
45
|
+
|
46
|
+
/** @type {Config} */
|
47
|
+
#_config;
|
48
|
+
|
49
|
+
/** @type {Kysely<Database>} */
|
50
|
+
#_client;
|
51
|
+
|
52
|
+
/** @type {db_driver["resources"]} */
|
53
|
+
#_resources;
|
54
|
+
|
55
|
+
/**
|
56
|
+
*
|
57
|
+
* @param {Config} [config] config
|
58
|
+
*/
|
59
|
+
constructor(config) {
|
60
|
+
this.#_is_ready = false;
|
61
|
+
this.#_config = config;
|
62
|
+
|
63
|
+
assert(
|
64
|
+
this.#_config.dialect,
|
65
|
+
'No Dialect found !'
|
66
|
+
);
|
67
|
+
|
68
|
+
assert(
|
69
|
+
this.#_config.dialect_type,
|
70
|
+
'No Dialect Type specified !'
|
71
|
+
);
|
72
|
+
|
73
|
+
this.#_client = new Kysely(
|
74
|
+
{
|
75
|
+
dialect: this.#_config.dialect,
|
76
|
+
plugins: [
|
77
|
+
new ParseJSONResultsPlugin(),
|
78
|
+
new SanitizePlugin()
|
79
|
+
]
|
80
|
+
}
|
81
|
+
);
|
82
|
+
}
|
83
|
+
|
84
|
+
throwIfNotReady() {
|
85
|
+
assert(
|
86
|
+
this.isReady,
|
87
|
+
'Database not ready !!! you need to `.init()` it'
|
88
|
+
);
|
89
|
+
}
|
90
|
+
|
91
|
+
/**
|
92
|
+
*
|
93
|
+
* @param {App<any, any, any>} app
|
94
|
+
*
|
95
|
+
*
|
96
|
+
* @returns {Promise<this>}
|
97
|
+
*/
|
98
|
+
async init(app) {
|
99
|
+
if(this.isReady)
|
100
|
+
return this;
|
101
|
+
|
102
|
+
this.#_app = app;
|
103
|
+
|
104
|
+
this.#_resources = {
|
105
|
+
auth_users: auth_users(this),
|
106
|
+
collections: collections(this),
|
107
|
+
customers: customers(this),
|
108
|
+
discounts: discounts(this),
|
109
|
+
images: images(this),
|
110
|
+
notifications: notifications(this),
|
111
|
+
orders: orders(this),
|
112
|
+
posts: posts(this),
|
113
|
+
products: products(this),
|
114
|
+
storefronts: storefronts(this),
|
115
|
+
tags: tags(this),
|
116
|
+
shipping_methods: shipping(this),
|
117
|
+
templates: templates(this),
|
118
|
+
search: search(this),
|
119
|
+
}
|
120
|
+
|
121
|
+
this.#_is_ready = true;
|
122
|
+
|
123
|
+
return this;
|
124
|
+
}
|
125
|
+
|
126
|
+
async disconnect() {
|
127
|
+
await this.client.destroy();
|
128
|
+
return true;
|
129
|
+
}
|
130
|
+
|
131
|
+
/**
|
132
|
+
* `database` resources
|
133
|
+
*/
|
134
|
+
get resources () {
|
135
|
+
return this.#_resources;
|
136
|
+
}
|
137
|
+
|
138
|
+
get name() {
|
139
|
+
return this?.config?.db_name ?? 'main';
|
140
|
+
}
|
141
|
+
|
142
|
+
get app() {
|
143
|
+
return this.#_app;
|
144
|
+
}
|
145
|
+
|
146
|
+
get client() {
|
147
|
+
return this.#_client;
|
148
|
+
}
|
149
|
+
|
150
|
+
get config() {
|
151
|
+
return this.#_config;
|
152
|
+
}
|
153
|
+
|
154
|
+
get isReady() {
|
155
|
+
return this.#_is_ready;
|
156
|
+
}
|
157
|
+
|
158
|
+
get dialectType() {
|
159
|
+
return this.#_config.dialect_type;
|
160
|
+
}
|
161
|
+
|
162
|
+
get isSqlite() {
|
163
|
+
return this.dialectType==='SQLITE';
|
164
|
+
}
|
165
|
+
|
166
|
+
get isPostgres() {
|
167
|
+
return this.dialectType==='POSTGRES';
|
168
|
+
}
|
169
|
+
|
170
|
+
get isMysql() {
|
171
|
+
return this.dialectType==='MYSQL';
|
172
|
+
}
|
173
|
+
|
174
|
+
}
|
@@ -1,11 +1,6 @@
|
|
1
1
|
{
|
2
|
-
"compileOnSave": false,
|
3
2
|
"compilerOptions": {
|
4
|
-
"noEmit": true,
|
5
|
-
"allowJs": true,
|
6
3
|
"checkJs": true,
|
7
|
-
"target": "ESNext",
|
8
|
-
"resolveJsonModule": true,
|
9
4
|
"moduleResolution": "NodeNext",
|
10
5
|
"module": "NodeNext",
|
11
6
|
"composite": true,
|
@@ -16,6 +11,7 @@
|
|
16
11
|
"migrations.sqlite/*",
|
17
12
|
"migrations.postgres/*",
|
18
13
|
"migrations.mysql/*",
|
14
|
+
"migrations.shared/*",
|
19
15
|
"tests/*.js"
|
20
16
|
]
|
21
17
|
}
|
package/migrate.js
CHANGED
@@ -29,31 +29,37 @@ export async function migrateToLatest(db_driver, destroy_db_upon_completion=true
|
|
29
29
|
|
30
30
|
current.driver = db_driver;
|
31
31
|
|
32
|
-
const migrator = new Migrator(
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
32
|
+
const migrator = new Migrator(
|
33
|
+
{
|
34
|
+
db,
|
35
|
+
provider: new FileMigrationProvider(
|
36
|
+
{
|
37
|
+
fs,
|
38
|
+
path,
|
39
|
+
migrationFolder: path.join(
|
40
|
+
__dirname,
|
41
|
+
`migrations.${db_driver.dialectType.toLowerCase()}`
|
42
|
+
),
|
43
|
+
}
|
40
44
|
),
|
41
|
-
}
|
42
|
-
|
45
|
+
}
|
46
|
+
);
|
43
47
|
|
44
48
|
const { error, results } = await migrator.migrateToLatest();
|
45
49
|
|
46
|
-
results?.forEach(
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
+
results?.forEach(
|
51
|
+
(it) => {
|
52
|
+
if (it.status === 'Success') {
|
53
|
+
console.log(
|
54
|
+
`migration "${it.migrationName}" was executed successfully`
|
50
55
|
);
|
51
|
-
|
52
|
-
|
53
|
-
|
56
|
+
} else if (it.status === 'Error') {
|
57
|
+
console.error(
|
58
|
+
`failed to execute migration "${it.migrationName}"`
|
54
59
|
);
|
60
|
+
}
|
55
61
|
}
|
56
|
-
|
62
|
+
);
|
57
63
|
|
58
64
|
if (error) {
|
59
65
|
console.error('failed to migrate')
|
@@ -62,5 +68,5 @@ export async function migrateToLatest(db_driver, destroy_db_upon_completion=true
|
|
62
68
|
}
|
63
69
|
|
64
70
|
if(destroy_db_upon_completion)
|
65
|
-
await db.destroy()
|
71
|
+
await db.destroy();
|
66
72
|
}
|
@@ -30,7 +30,7 @@ const add_base_columns = tb => {
|
|
30
30
|
const create_entity_to_value_table = (db, table_name) => {
|
31
31
|
|
32
32
|
return db.schema
|
33
|
-
.createTable(table_name)
|
33
|
+
.createTable(table_name).ifNotExists()
|
34
34
|
.addColumn('id', 'integer',
|
35
35
|
(col) => col.autoIncrement().primaryKey()
|
36
36
|
)
|
@@ -47,7 +47,7 @@ const create_entity_to_value_table = (db, table_name) => {
|
|
47
47
|
* @param {keyof Database} table_name
|
48
48
|
*/
|
49
49
|
const create_safe_table = (db, table_name) => {
|
50
|
-
return db.schema.createTable(table_name);
|
50
|
+
return db.schema.createTable(table_name).ifNotExists();
|
51
51
|
}
|
52
52
|
|
53
53
|
/**
|
@@ -56,7 +56,7 @@ const create_safe_table = (db, table_name) => {
|
|
56
56
|
* @param {keyof Database} table_name
|
57
57
|
*/
|
58
58
|
const drop_safe_table = (db, table_name) => {
|
59
|
-
return db.schema.dropTable(table_name).execute();
|
59
|
+
return db.schema.dropTable(table_name).ifExists().execute();
|
60
60
|
}
|
61
61
|
|
62
62
|
/**
|
@@ -94,7 +94,7 @@ const create_base_indexes = async (db, table_name, include_id=true, include_hand
|
|
94
94
|
* @param {keyof Pick<Database, 'entity_to_media' |
|
95
95
|
* 'entity_to_search_terms' | 'entity_to_tags_projections' |
|
96
96
|
* 'products_to_collections' | 'products_to_discounts' |
|
97
|
-
* 'products_to_variants' | 'storefronts_to_other'>} table_name
|
97
|
+
* 'products_to_variants' | 'storefronts_to_other' | 'products_to_related_products'>} table_name
|
98
98
|
*/
|
99
99
|
const create_entity_table_indexes = async (db, table_name) => {
|
100
100
|
await db.schema.createIndex(`index_${table_name}_entity_id`)
|
@@ -150,7 +150,8 @@ export async function up(db) {
|
|
150
150
|
let tb = create_safe_table(db, 'templates');
|
151
151
|
tb = add_base_columns(tb);
|
152
152
|
tb = tb.addColumn('title', 'text');
|
153
|
-
tb = tb.addColumn('
|
153
|
+
tb = tb.addColumn('template_html', 'text');
|
154
|
+
tb = tb.addColumn('template_text', 'text');
|
154
155
|
tb = tb.addColumn('reference_example_input', 'json');
|
155
156
|
await tb.execute();
|
156
157
|
await create_base_indexes(db, 'templates');
|
@@ -29,7 +29,7 @@ const add_base_columns = tb => {
|
|
29
29
|
const create_entity_to_value_table = (db, table_name) => {
|
30
30
|
|
31
31
|
return db.schema
|
32
|
-
.createTable(table_name)
|
32
|
+
.createTable(table_name).ifNotExists()
|
33
33
|
.addColumn('id', 'bigserial',
|
34
34
|
(col) => col.primaryKey()
|
35
35
|
)
|
@@ -46,7 +46,7 @@ const create_entity_to_value_table = (db, table_name) => {
|
|
46
46
|
* @param {keyof Database} table_name
|
47
47
|
*/
|
48
48
|
const create_safe_table = (db, table_name) => {
|
49
|
-
return db.schema.createTable(table_name);
|
49
|
+
return db.schema.createTable(table_name).ifNotExists();
|
50
50
|
}
|
51
51
|
|
52
52
|
/**
|
@@ -55,7 +55,7 @@ const create_safe_table = (db, table_name) => {
|
|
55
55
|
* @param {keyof Database} table_name
|
56
56
|
*/
|
57
57
|
const drop_safe_table = (db, table_name) => {
|
58
|
-
return db.schema.dropTable(table_name).execute();
|
58
|
+
return db.schema.dropTable(table_name).ifExists().execute();
|
59
59
|
}
|
60
60
|
|
61
61
|
/**
|
@@ -66,22 +66,22 @@ const drop_safe_table = (db, table_name) => {
|
|
66
66
|
*/
|
67
67
|
const create_base_indexes = async (db, table_name, include_id=true, include_handle=true) => {
|
68
68
|
if(include_id) {
|
69
|
-
await db.schema.createIndex(`index_${table_name}_id_updated_at_asc`)
|
69
|
+
await db.schema.createIndex(`index_${table_name}_id_updated_at_asc`).ifNotExists()
|
70
70
|
.on(table_name)
|
71
71
|
.columns(['id', 'updated_at asc'])
|
72
72
|
.execute();
|
73
|
-
await db.schema.createIndex(`index_${table_name}_id_updated_at_desc`)
|
73
|
+
await db.schema.createIndex(`index_${table_name}_id_updated_at_desc`).ifNotExists()
|
74
74
|
.on(table_name)
|
75
75
|
.columns(['id', 'updated_at desc'])
|
76
76
|
.execute();
|
77
77
|
}
|
78
78
|
|
79
79
|
if(include_handle) {
|
80
|
-
await db.schema.createIndex(`index_${table_name}_handle_updated_at_asc`)
|
80
|
+
await db.schema.createIndex(`index_${table_name}_handle_updated_at_asc`).ifNotExists()
|
81
81
|
.on(table_name)
|
82
82
|
.columns(['handle', 'updated_at asc'])
|
83
83
|
.execute();
|
84
|
-
await db.schema.createIndex(`index_${table_name}_handle_updated_at_desc`)
|
84
|
+
await db.schema.createIndex(`index_${table_name}_handle_updated_at_desc`).ifNotExists()
|
85
85
|
.on(table_name)
|
86
86
|
.columns(['handle', 'updated_at desc'])
|
87
87
|
.execute();
|
@@ -94,26 +94,26 @@ const create_base_indexes = async (db, table_name, include_id=true, include_hand
|
|
94
94
|
* @param {keyof Pick<Database, 'entity_to_media' |
|
95
95
|
* 'entity_to_search_terms' | 'entity_to_tags_projections' |
|
96
96
|
* 'products_to_collections' | 'products_to_discounts' |
|
97
|
-
* 'products_to_variants' | 'storefronts_to_other'>} table_name
|
97
|
+
* 'products_to_variants' | 'storefronts_to_other' | 'products_to_related_products'>} table_name
|
98
98
|
*/
|
99
99
|
const create_entity_table_indexes = async (db, table_name) => {
|
100
|
-
await db.schema.createIndex(`index_${table_name}_entity_id`)
|
100
|
+
await db.schema.createIndex(`index_${table_name}_entity_id`).ifNotExists()
|
101
101
|
.on(table_name)
|
102
102
|
.column('entity_id')
|
103
103
|
.execute();
|
104
|
-
await db.schema.createIndex(`index_${table_name}_entity_handle`)
|
104
|
+
await db.schema.createIndex(`index_${table_name}_entity_handle`).ifNotExists()
|
105
105
|
.on(table_name)
|
106
106
|
.column('entity_handle')
|
107
107
|
.execute();
|
108
|
-
await db.schema.createIndex(`index_${table_name}_value`)
|
108
|
+
await db.schema.createIndex(`index_${table_name}_value`).ifNotExists()
|
109
109
|
.on(table_name)
|
110
110
|
.column('value')
|
111
111
|
.execute();
|
112
|
-
await db.schema.createIndex(`index_${table_name}_reporter`)
|
112
|
+
await db.schema.createIndex(`index_${table_name}_reporter`).ifNotExists()
|
113
113
|
.on(table_name)
|
114
114
|
.column('reporter')
|
115
115
|
.execute();
|
116
|
-
await db.schema.createIndex(`index_${table_name}_context`)
|
116
|
+
await db.schema.createIndex(`index_${table_name}_context`).ifNotExists()
|
117
117
|
.on(table_name)
|
118
118
|
.column('context')
|
119
119
|
.execute();
|
@@ -148,7 +148,8 @@ export async function up(db) {
|
|
148
148
|
let tb = create_safe_table(db, 'templates');
|
149
149
|
tb = add_base_columns(tb);
|
150
150
|
tb = tb.addColumn('title', 'text');
|
151
|
-
tb = tb.addColumn('
|
151
|
+
tb = tb.addColumn('template_html', 'text');
|
152
|
+
tb = tb.addColumn('template_text', 'text');
|
152
153
|
tb = tb.addColumn('reference_example_input', 'json');
|
153
154
|
await tb.execute();
|
154
155
|
await create_base_indexes(db, 'templates');
|