@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.
Files changed (39) hide show
  1. package/README.md +30 -12
  2. package/index.js +172 -1
  3. package/{tsconfig.json → jsconfig.json} +1 -5
  4. package/migrate.js +25 -19
  5. package/migrations.mysql/00000_init_tables.js +6 -5
  6. package/migrations.postgres/00000_init_tables.js +15 -14
  7. package/migrations.shared/00001_seed_email_templates copy.js +262 -0
  8. package/migrations.shared/00001_seed_email_templates.js +5 -238
  9. package/migrations.sqlite/00000_init_tables.js +12 -12
  10. package/package.json +2 -4
  11. package/src/con.auth_users.js +5 -2
  12. package/src/con.collections.js +2 -2
  13. package/src/con.customers.js +2 -2
  14. package/src/con.discounts.js +3 -3
  15. package/src/con.discounts.utils.js +11 -11
  16. package/src/con.helpers.json.js +3 -3
  17. package/src/con.images.js +5 -5
  18. package/src/con.notifications.js +2 -2
  19. package/src/con.orders.js +19 -4
  20. package/src/con.posts.js +2 -2
  21. package/src/con.products.js +5 -5
  22. package/src/con.search.js +7 -7
  23. package/src/con.shared.experiment.js +723 -0
  24. package/src/con.shared.js +55 -21
  25. package/src/con.shipping.js +2 -2
  26. package/src/con.storefronts.js +2 -2
  27. package/src/con.tags.js +2 -2
  28. package/src/con.templates.js +22 -7
  29. package/src/utils.query.js +6 -6
  30. package/tests/runner.mssql-local.test.js +10 -6
  31. package/tests/runner.mysql-local.test.js +16 -63
  32. package/tests/runner.postgres-local.test.js +17 -62
  33. package/tests/runner.sqlite-local.test.js +15 -64
  34. package/tests/sandbox.test.js +15 -13
  35. package/types.public.d.ts +12 -4
  36. package/types.sql.tables.d.ts +4 -2
  37. package/driver.js +0 -190
  38. package/tests/query.cursor.test.js +0 -389
  39. 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-node'
15
- import { MongoDB } from '@storecraft/database-mongodb-node'
16
- import { NodeLocalStorage } from '@storecraft/storage-node-local'
17
-
18
- let app = new App(
19
- new NodePlatform(),
20
- new MongoDB({ db_name: 'prod', url: '<MONGO-URL>'}),
21
- new NodeLocalStorage(join(homedir(), 'tomer'))
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
- export * from './driver.js'
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
- db,
34
- provider: new FileMigrationProvider({
35
- fs,
36
- path,
37
- migrationFolder: path.join(
38
- __dirname,
39
- `migrations.${db_driver.dialectType.toLowerCase()}`
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((it) => {
47
- if (it.status === 'Success') {
48
- console.log(
49
- `migration "${it.migrationName}" was executed successfully`
50
+ results?.forEach(
51
+ (it) => {
52
+ if (it.status === 'Success') {
53
+ console.log(
54
+ `migration "${it.migrationName}" was executed successfully`
50
55
  );
51
- } else if (it.status === 'Error') {
52
- console.error(
53
- `failed to execute migration "${it.migrationName}"`
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('template', 'text');
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('template', 'text');
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');