@storecraft/database-sql-base 1.0.0

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 (49) hide show
  1. package/README.md +126 -0
  2. package/TODO.md +2 -0
  3. package/db-strategy.md +3 -0
  4. package/driver.js +190 -0
  5. package/index.js +3 -0
  6. package/migrate.js +66 -0
  7. package/migrations.mssql/00000_init_tables.js +268 -0
  8. package/migrations.mysql/00000_init_tables.js +372 -0
  9. package/migrations.mysql/00001_seed_email_templates.js +1 -0
  10. package/migrations.postgres/00000_init_tables.js +358 -0
  11. package/migrations.postgres/00001_seed_email_templates.js +1 -0
  12. package/migrations.shared/00001_seed_email_templates.js +260 -0
  13. package/migrations.sqlite/00000_init_tables.js +357 -0
  14. package/migrations.sqlite/00001_seed_email_templates.js +1 -0
  15. package/package.json +47 -0
  16. package/src/con.auth_users.js +159 -0
  17. package/src/con.collections.js +197 -0
  18. package/src/con.customers.js +202 -0
  19. package/src/con.discounts.js +225 -0
  20. package/src/con.discounts.utils.js +180 -0
  21. package/src/con.helpers.json.js +231 -0
  22. package/src/con.helpers.json.mssql.js +233 -0
  23. package/src/con.helpers.json.mysql.js +239 -0
  24. package/src/con.helpers.json.postgres.js +223 -0
  25. package/src/con.helpers.json.sqlite.js +263 -0
  26. package/src/con.images.js +230 -0
  27. package/src/con.notifications.js +149 -0
  28. package/src/con.orders.js +156 -0
  29. package/src/con.posts.js +147 -0
  30. package/src/con.products.js +497 -0
  31. package/src/con.search.js +148 -0
  32. package/src/con.shared.js +616 -0
  33. package/src/con.shipping.js +147 -0
  34. package/src/con.storefronts.js +301 -0
  35. package/src/con.tags.js +120 -0
  36. package/src/con.templates.js +133 -0
  37. package/src/kysely.sanitize.plugin.js +40 -0
  38. package/src/utils.funcs.js +77 -0
  39. package/src/utils.query.js +195 -0
  40. package/tests/query.cursor.test.js +389 -0
  41. package/tests/query.vql.test.js +71 -0
  42. package/tests/runner.mssql-local.test.js +118 -0
  43. package/tests/runner.mysql-local.test.js +101 -0
  44. package/tests/runner.postgres-local.test.js +99 -0
  45. package/tests/runner.sqlite-local.test.js +99 -0
  46. package/tests/sandbox.test.js +71 -0
  47. package/tsconfig.json +21 -0
  48. package/types.public.d.ts +19 -0
  49. package/types.sql.tables.d.ts +247 -0
package/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # Storecraft `SQL` driver
2
+
3
+ Official `SQL` driver for `StoreCraft` with the dialects abstracted with `Kysely` or your own drivers.
4
+
5
+ ## usage
6
+
7
+ ```js
8
+ import 'dotenv/config';
9
+ import http from "node:http";
10
+ import { join } from "node:path";
11
+ import { homedir } from "node:os";
12
+
13
+ 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
+ );
23
+
24
+ await app.init();
25
+
26
+ const server = http.createServer(app.handler).listen(
27
+ 8000,
28
+ () => {
29
+ console.log(`Server is running on http://localhost:8000`);
30
+ }
31
+ );
32
+
33
+ ```
34
+
35
+ ## Testing Locally
36
+
37
+ ### **SQLite**
38
+ 1. Simply `runner.sqlite-local.test.js`
39
+ ```bash
40
+ npm run test:sqlite
41
+ ```
42
+
43
+ ### **Postgres**
44
+ 1. First setup a `postgres` server
45
+ ```bash
46
+ docker pull postgres
47
+ docker run --name some-postgres -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=admin \
48
+ -e PGDATA=/var/lib/postgresql/data/pgdata \
49
+ -v $(pwd):/var/lib/postgresql/data \
50
+ -p 5432:5432 -d postgres
51
+ ```
52
+
53
+ 2. create Environment
54
+ create `.env` file with
55
+ ```bash
56
+ POSTGRES_USER='user'
57
+ POSTGRES_PASSWORD='password'
58
+ POSTGRES_PORT=5432
59
+ POSTGRES_HOST='localhost'
60
+ ```
61
+
62
+ 3. Run `runner.postgres-local.test.js`
63
+ ```bash
64
+ npm run test:postgres
65
+ ```
66
+
67
+ ### **MySQL**
68
+ 1. First setup a `mysql` server
69
+ ```zsh
70
+ docker pull mysql
71
+ docker run --name mysql \
72
+ -v $(pwd):/etc/mysql/conf.d \
73
+ -v /my/own/datadir:/var/lib/mysql \
74
+ -e MYSQL_ROOT_PASSWORD=admin -e MYSQL_ROOT_HOST=localhost \
75
+ -e MYSQL_DATABASE=main -e MYSQL_USER=admin -e MYSQL_PASSWORD=admin \
76
+ -p 8080:3306 -d mysql
77
+ ```
78
+
79
+ 2. create Environment
80
+ create `.env` file with
81
+ ```bash
82
+ MYSQL_USER='root'
83
+ MYSQL_ROOT_PASSWORD='password'
84
+ MYSQL_PORT=8080
85
+ MYSQL_HOST='localhost'
86
+ ```
87
+
88
+ 3. Run `runner.mysql-local.test.js`
89
+ ```bash
90
+ npm run test:mysql
91
+ ```
92
+
93
+ ### **MSSQL** (Currently NOT SUPPORTED, waiting for votes on that one)
94
+ Work in progress, i will probably not continue with this.
95
+
96
+ 1. First setup a `mysql` server
97
+ ```zsh
98
+ docker pull mcr.microsoft.com/mssql/server
99
+ # use this For OSX with M1 chips
100
+ docker pull mcr.microsoft.com/azure-sql-edge:latest
101
+ docker run --name some-mssql \
102
+ -e ACCEPT_EULA=Y -e MSSQL_SA_PASSWORD='Abcd1234!?' \
103
+ -v $(pwd):/var/opt/mssql \
104
+ -p 1433:1433 \
105
+ -d mcr.microsoft.com/azure-sql-edge:latest
106
+
107
+ ```
108
+
109
+ 2. create Environment
110
+ create `.env` file with
111
+ ```bash
112
+ MYSQL_USER='root'
113
+ MYSQL_ROOT_PASSWORD='password'
114
+ MYSQL_PORT=8080
115
+ MYSQL_HOST='localhost'
116
+ ```
117
+
118
+ 3. Run `runner.mysql-local.test.js`
119
+ ```bash
120
+ npm run test:mysql
121
+ ```
122
+
123
+
124
+ ```text
125
+ Author: Tomer Shalev <tomer.shalev@gmail.com>
126
+ ```
package/TODO.md ADDED
@@ -0,0 +1,2 @@
1
+ - seacrh terms (vql)
2
+ - TODO: test it
package/db-strategy.md ADDED
@@ -0,0 +1,3 @@
1
+ # SQL data modeling strategy
2
+
3
+ TODO
package/driver.js ADDED
@@ -0,0 +1,190 @@
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';
18
+
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.js').Config} Config
30
+ * @typedef {import('./types.sql.tables.js').Database} Database
31
+ * @typedef {import('kysely').Dialect} Dialect
32
+ * @typedef {import('@storecraft/core/v-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 {boolean} [destroy_db_upon_completion=false]
94
+ */
95
+ async migrateToLatest(destroy_db_upon_completion=false) {
96
+ this.throwIfNotReady();
97
+
98
+ const { migrateToLatest } = await import('./migrate.js');
99
+
100
+ await migrateToLatest(this, destroy_db_upon_completion);
101
+ };
102
+
103
+ /**
104
+ *
105
+ * @param {App<any, any, any>} app
106
+ *
107
+ *
108
+ * @returns {Promise<this>}
109
+ */
110
+ async init(app) {
111
+ if(this.isReady)
112
+ return this;
113
+
114
+ this.#_app = app;
115
+
116
+ this.#_resources = {
117
+ auth_users: auth_users(this),
118
+ collections: collections(this),
119
+ customers: customers(this),
120
+ discounts: discounts(this),
121
+ images: images(this),
122
+ notifications: notifications(this),
123
+ orders: orders(this),
124
+ posts: posts(this),
125
+ products: products(this),
126
+ storefronts: storefronts(this),
127
+ tags: tags(this),
128
+ shipping_methods: shipping(this),
129
+ templates: templates(this),
130
+ search: search(this),
131
+ }
132
+
133
+ this.#_is_ready = true;
134
+
135
+ return this;
136
+ }
137
+
138
+ async disconnect() {
139
+ await this.client.destroy();
140
+ return true;
141
+ }
142
+
143
+ /**
144
+ * `database` resources
145
+ */
146
+ get resources () {
147
+ return this.#_resources;
148
+ }
149
+
150
+ get name () {
151
+ return 'main';
152
+ }
153
+
154
+ get app() {
155
+ return this.#_app;
156
+ }
157
+
158
+ get client() {
159
+ return this.#_client;
160
+ }
161
+
162
+ get config() {
163
+ return this.#_config;
164
+ }
165
+
166
+ get isReady() {
167
+ return this.#_is_ready;
168
+ }
169
+
170
+ get dialectType() {
171
+ return this.#_config.dialect_type;
172
+ }
173
+
174
+ get isSqlite() {
175
+ return this.dialectType==='SQLITE';
176
+ }
177
+
178
+ get isPostgres() {
179
+ return this.dialectType==='POSTGRES';
180
+ }
181
+
182
+ get isMysql() {
183
+ return this.dialectType==='MYSQL';
184
+ }
185
+
186
+ get isMssql() {
187
+ return this.dialectType==='MSSQL';
188
+ }
189
+
190
+ }
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './driver.js'
2
+
3
+
package/migrate.js ADDED
@@ -0,0 +1,66 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import * as path from 'path'
3
+ import { promises as fs } from 'fs'
4
+ import {
5
+ Migrator,
6
+ FileMigrationProvider,
7
+ } from 'kysely'
8
+ import { SQL } from "./index.js";
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+
13
+ export const current = {
14
+ driver: undefined
15
+ }
16
+
17
+ /**
18
+ *
19
+ * @param {SQL} db_driver
20
+ * @param {boolean} [destroy_db_upon_completion=true]
21
+ */
22
+ export async function migrateToLatest(db_driver, destroy_db_upon_completion=true) {
23
+ if(!db_driver?.client)
24
+ throw new Error('No Kysely client found !!!');
25
+
26
+ console.log('Resolving migrations')
27
+
28
+ let db = db_driver.client;
29
+
30
+ current.driver = db_driver;
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()}`
40
+ ),
41
+ }),
42
+ })
43
+
44
+ const { error, results } = await migrator.migrateToLatest();
45
+
46
+ results?.forEach((it) => {
47
+ if (it.status === 'Success') {
48
+ console.log(
49
+ `migration "${it.migrationName}" was executed successfully`
50
+ );
51
+ } else if (it.status === 'Error') {
52
+ console.error(
53
+ `failed to execute migration "${it.migrationName}"`
54
+ );
55
+ }
56
+ })
57
+
58
+ if (error) {
59
+ console.error('failed to migrate')
60
+ console.error(error)
61
+ process.exit(1)
62
+ }
63
+
64
+ if(destroy_db_upon_completion)
65
+ await db.destroy()
66
+ }
@@ -0,0 +1,268 @@
1
+ import { CreateTableBuilder, Kysely } from 'kysely'
2
+
3
+ /**
4
+ * @typedef {import('../types.sql.tables.js').Database} Database
5
+ */
6
+
7
+ /**
8
+ * @template {string} TB
9
+ * @template {string} B
10
+ * @param {CreateTableBuilder<TB, B>} tb
11
+ */
12
+ const add_base_columns = tb => {
13
+ return tb
14
+ .addColumn('id', 'VARCHAR(256)', (col) =>
15
+ col.primaryKey()
16
+ )
17
+ .addColumn('handle', 'VARCHAR(256)', (col) => col.unique())
18
+ .addColumn('created_at', 'text')
19
+ .addColumn('updated_at', 'text')
20
+ .addColumn('attributes', 'VARCHAR(MAX)')
21
+ .addColumn('description', 'text')
22
+ .addColumn('active', 'integer')
23
+ }
24
+
25
+ /**
26
+ * @param {Kysely<Database>} db
27
+ * @param {keyof Database} table_name
28
+ */
29
+ const create_entity_to_value_table = (db, table_name) => {
30
+
31
+ return db.schema
32
+ .createTable(table_name)
33
+ .addColumn('id', 'integer',
34
+ (col) => col.autoIncrement().primaryKey()
35
+ )
36
+ .addColumn('entity_id', 'VARCHAR(256)', col => col.notNull())
37
+ .addColumn('entity_handle', 'VARCHAR(256)')
38
+ .addColumn('value', 'VARCHAR(256)')
39
+ .addColumn('reporter', 'VARCHAR(256)')
40
+ .addColumn('context', 'VARCHAR(256)')
41
+ }
42
+
43
+ /**
44
+ *
45
+ * @param {Kysely<Database>} db
46
+ * @param {keyof Database} table_name
47
+ */
48
+ const create_safe_table = (db, table_name) => {
49
+ return db.schema.createTable(table_name);
50
+ }
51
+
52
+ /**
53
+ *
54
+ * @param {Kysely<Database>} db
55
+ * @param {keyof Database} table_name
56
+ */
57
+ const drop_safe_table = (db, table_name) => {
58
+ return db.schema.dropTable(table_name).execute();
59
+ }
60
+
61
+ /**
62
+ *
63
+ * @param {Kysely<Database>} db
64
+ */
65
+ export async function up(db) {
66
+ { // auth_users
67
+ let tb = create_safe_table(db, 'auth_users');
68
+ tb = add_base_columns(tb);
69
+ tb = tb
70
+ .addColumn('email', 'VARCHAR(256)', (col) => col.unique())
71
+ .addColumn('password', 'text')
72
+ .addColumn('roles', 'VARCHAR(MAX)')
73
+ .addColumn('confirmed_mail', 'integer');
74
+ await tb.execute();
75
+ }
76
+
77
+ { // tags
78
+ let tb = create_safe_table(db, 'tags');
79
+ tb = add_base_columns(tb);
80
+ tb = tb.addColumn('values', 'VARCHAR(MAX)');
81
+ await tb.execute();
82
+ }
83
+
84
+ { // collections
85
+ let tb = create_safe_table(db, 'collections');
86
+ tb = add_base_columns(tb);
87
+ tb = tb
88
+ .addColumn('title', 'text')
89
+ .addColumn('published', 'text')
90
+ await tb.execute();
91
+ }
92
+
93
+ { // products
94
+ let tb = create_safe_table(db, 'products');
95
+ tb = add_base_columns(tb);
96
+ tb = tb
97
+ .addColumn('title', 'text')
98
+ .addColumn('video', 'text')
99
+ .addColumn('price', 'numeric')
100
+ .addColumn('compare_at_price', 'numeric')
101
+ .addColumn('qty', 'integer')
102
+ .addColumn('variants_options', 'VARCHAR(MAX)')
103
+ .addColumn('parent_handle', 'text')
104
+ .addColumn('parent_id', 'text')
105
+ .addColumn('variant_hint', 'VARCHAR(MAX)')
106
+ await tb.execute();
107
+ }
108
+
109
+ { // products_to_collections
110
+ let tb = create_entity_to_value_table(db, 'products_to_collections')
111
+ await tb.execute();
112
+ }
113
+
114
+ { // products_to_discounts
115
+ let tb = create_entity_to_value_table(db, 'products_to_discounts')
116
+ await tb.execute();
117
+ }
118
+
119
+ { // products_to_variants
120
+ let tb = create_entity_to_value_table(db, 'products_to_variants')
121
+ await tb.execute();
122
+ }
123
+
124
+ { // shipping_methods
125
+ let tb = create_safe_table(db, 'shipping_methods');
126
+ tb = add_base_columns(tb);
127
+ tb = tb.addColumn('title', 'text')
128
+ .addColumn('price', 'numeric')
129
+ await tb.execute();
130
+ }
131
+
132
+ { // posts
133
+ let tb = create_safe_table(db, 'posts');
134
+ tb = add_base_columns(tb);
135
+ tb = tb
136
+ .addColumn('title', 'text')
137
+ .addColumn('text', 'text')
138
+ await tb.execute();
139
+ }
140
+
141
+ { // customers
142
+ let tb = create_safe_table(db, 'customers');
143
+ tb = add_base_columns(tb);
144
+ tb = tb
145
+ .addColumn('email', 'VARCHAR(256)', (col) => col.unique())
146
+ .addColumn('auth_id', 'VARCHAR(256)', (col) => col.unique())
147
+ .addColumn('firstname', 'text')
148
+ .addColumn('lastname', 'text')
149
+ .addColumn('phone_number', 'text')
150
+ .addColumn('address', 'VARCHAR(MAX)')
151
+ await tb.execute();
152
+ }
153
+
154
+ { // orders
155
+ let tb = create_safe_table(db, 'orders');
156
+ tb = add_base_columns(tb);
157
+ tb = tb
158
+ .addColumn('contact', 'VARCHAR(MAX)')
159
+ .addColumn('address', 'VARCHAR(MAX)')
160
+ .addColumn('line_items', 'VARCHAR(MAX)')
161
+ .addColumn('notes', 'text')
162
+ .addColumn('shipping_method', 'VARCHAR(MAX)')
163
+ .addColumn('status', 'VARCHAR(MAX)')
164
+ .addColumn('pricing', 'VARCHAR(MAX)')
165
+ .addColumn('validation', 'VARCHAR(MAX)')
166
+ .addColumn('payment_gateway', 'VARCHAR(MAX)')
167
+ .addColumn('coupons', 'VARCHAR(MAX)')
168
+ .addColumn('_customer_id', 'text')
169
+ .addColumn('_customer_email', 'text')
170
+ .addColumn('_status_payment_id', 'integer')
171
+ .addColumn('_status_checkout_id', 'integer')
172
+ .addColumn('_status_fulfillment_id', 'integer')
173
+
174
+ await tb.execute();
175
+ }
176
+
177
+ { // storefronts
178
+ let tb = create_safe_table(db, 'storefronts');
179
+ tb = add_base_columns(tb);
180
+ tb = tb
181
+ .addColumn('title', 'text')
182
+ .addColumn('video', 'text')
183
+ .addColumn('published', 'text')
184
+ await tb.execute();
185
+ }
186
+
187
+ { // storefronts_to_other
188
+ let tb = create_entity_to_value_table(db, 'storefronts_to_other')
189
+ await tb.execute();
190
+ }
191
+
192
+ { // notifications
193
+ let tb = create_safe_table(db, 'notifications');
194
+ tb = add_base_columns(tb);
195
+ tb = tb
196
+ .addColumn('message', 'text')
197
+ .addColumn('author', 'text')
198
+ .addColumn('actions', 'VARCHAR(MAX)')
199
+ await tb.execute();
200
+ }
201
+
202
+ { // images
203
+ let tb = create_safe_table(db, 'images');
204
+ tb = add_base_columns(tb);
205
+ tb = tb
206
+ .addColumn('name', 'text')
207
+ .addColumn('url', 'text')
208
+ await tb.execute();
209
+ }
210
+
211
+ { // discounts
212
+ let tb = create_safe_table(db, 'discounts');
213
+ tb = add_base_columns(tb);
214
+ tb = tb
215
+ .addColumn('title', 'text')
216
+ .addColumn('published', 'text')
217
+ .addColumn('priority', 'integer')
218
+ .addColumn('info', 'json')
219
+ .addColumn('application', 'VARCHAR(MAX)')
220
+ .addColumn('_application_id', 'integer')
221
+ .addColumn('_discount_type_id', 'integer')
222
+ await tb.execute();
223
+ }
224
+
225
+ { // entity_to_tags_projections
226
+ let tb = create_entity_to_value_table(db, 'entity_to_tags_projections')
227
+ await tb.execute();
228
+ }
229
+
230
+ { // entity_to_search_terms
231
+ let tb = create_entity_to_value_table(db, 'entity_to_search_terms')
232
+ await tb.execute();
233
+ }
234
+
235
+ { // entity_to_media
236
+ let tb = create_entity_to_value_table(db, 'entity_to_media')
237
+ await tb.execute();
238
+ }
239
+
240
+ }
241
+
242
+ /**
243
+ *
244
+ * @param {Kysely<Database>} db
245
+ */
246
+ export async function down(db) {
247
+ await Promise.all([
248
+ drop_safe_table(db, 'auth_users'),
249
+ drop_safe_table(db, 'tags'),
250
+ drop_safe_table(db, 'collections'),
251
+ drop_safe_table(db, 'customers'),
252
+ drop_safe_table(db, 'discounts'),
253
+ drop_safe_table(db, 'images'),
254
+ drop_safe_table(db, 'notifications'),
255
+ drop_safe_table(db, 'orders'),
256
+ drop_safe_table(db, 'posts'),
257
+ drop_safe_table(db, 'shipping_methods'),
258
+ drop_safe_table(db, 'products'),
259
+ drop_safe_table(db, 'products_to_collections'),
260
+ drop_safe_table(db, 'products_to_discounts'),
261
+ drop_safe_table(db, 'products_to_variants'),
262
+ drop_safe_table(db, 'storefronts'),
263
+ drop_safe_table(db, 'storefronts_to_other'),
264
+ drop_safe_table(db, 'entity_to_media'),
265
+ drop_safe_table(db, 'entity_to_search_terms'),
266
+ drop_safe_table(db, 'entity_to_tags_projections'),
267
+ ]);
268
+ }