@storecraft/database-sql-base 1.0.10 → 1.0.12

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.
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @import { ReplaceValuesOfKeys } from './utils.types.js'
3
+ */
4
+
1
5
  export const isDef = v => v!==undefined && v!==null;
2
6
  export const isUndef = v => !isDef(v);
3
7
 
@@ -19,14 +23,13 @@ export const delete_keys = (...keys) => {
19
23
  }
20
24
  }
21
25
 
26
+
27
+
22
28
  /**
23
29
  * Sanitize null/undefined valued keys
24
- *
25
- *
26
- * @template {Record<string, any>} T
27
- *
28
- *
30
+ * @template T
29
31
  * @param {T} o
32
+ * @return {ReplaceValuesOfKeys<T, 'active' | 'confirmed_mail', boolean>}
30
33
  */
31
34
  export const sanitize = o => {
32
35
  for (const key in o) {
@@ -37,12 +40,19 @@ export const sanitize = o => {
37
40
  continue;
38
41
  }
39
42
  if(key==='active') {
43
+ // @ts-ignore
44
+ o[key] = Boolean(value);
45
+ }
46
+ else if(key==='confirmed_mail') {
47
+ // @ts-ignore
40
48
  o[key] = Boolean(value);
41
49
  }
42
50
  else if(key==='price') {
51
+ // @ts-ignore
43
52
  o[key] = parseFloat(value);
44
53
  }
45
54
  else if(key==='compare_at_price') {
55
+ // @ts-ignore
46
56
  o[key] = parseFloat(value);
47
57
  }
48
58
  else if(typeof value === 'object') {
@@ -50,6 +60,7 @@ export const sanitize = o => {
50
60
  }
51
61
  }
52
62
 
63
+ // @ts-ignore
53
64
  return o;
54
65
  }
55
66
 
@@ -62,10 +73,11 @@ export const sanitize = o => {
62
73
  * @param {T[]} arr
63
74
  */
64
75
  export const sanitize_array = arr => {
76
+ const xx = []
65
77
  for(const p of arr) {
66
- sanitize(p);
78
+ xx.push(sanitize(p));
67
79
  }
68
- return arr;
80
+ return xx;
69
81
  }
70
82
 
71
83
  /**
@@ -1,5 +1,8 @@
1
1
  /**
2
- * @typedef {import("../index.js").Database} Database
2
+ * @import { ApiQuery, Cursor } from '@storecraft/core/api'
3
+ * @import { VQL } from '@storecraft/core/vql'
4
+ * @import { Database } from '../types.sql.tables.js'
5
+ * @import { ExpressionBuilder } from 'kysely'
3
6
  */
4
7
 
5
8
  import { parse } from "@storecraft/core/vql";
@@ -12,8 +15,8 @@ import { parse } from "@storecraft/core/vql";
12
15
  * 3. (a1, a2, a3) > (b1, b2, b3) ==> (a1 > b1) || (a1=b1 & a2>b2) || (a1=b1 & a2=b2 & a3>b3)
13
16
  * 4. (a1, a2, a3) >= (b1, b2, b3) ==> (a1 > b1) || (a1=b1 & a2>b2) || (a1=b1 & a2=b2 & a3>=b3)
14
17
  *
15
- * @param {import("kysely").ExpressionBuilder<Database>} eb
16
- * @param {import("@storecraft/core/api").Cursor} c
18
+ * @param {ExpressionBuilder<Database>} eb
19
+ * @param {Cursor} c
17
20
  * @param {'>' | '>=' | '<' | '<='} relation
18
21
  * @param {(x: [k: string, v: any]) => [k: string, v: any]} transformer Your chance to change key and value
19
22
  */
@@ -72,9 +75,10 @@ export const query_cursor_to_eb = (eb, c, relation, transformer=(x)=>x) => {
72
75
 
73
76
 
74
77
  /**
75
- * @param {import("kysely").ExpressionBuilder<Database>} eb
76
- * @param {import("@storecraft/core/vql").VQL.Node} node
77
- * @param {keyof Database} table_name
78
+ * @template {keyof Database} T
79
+ * @param {ExpressionBuilder<Database>} eb
80
+ * @param {VQL.Node} node
81
+ * @param {T} table_name
78
82
  */
79
83
  export const query_vql_node_to_eb = (eb, node, table_name) => {
80
84
  if(node.op==='LEAF') {
@@ -119,8 +123,8 @@ export const query_vql_node_to_eb = (eb, node, table_name) => {
119
123
  }
120
124
 
121
125
  /**
122
- * @param {import("kysely").ExpressionBuilder<Database>} eb
123
- * @param {import("@storecraft/core/vql").VQL.Node} root
126
+ * @param {ExpressionBuilder<Database>} eb
127
+ * @param {VQL.Node} root
124
128
  * @param {keyof Database} table_name
125
129
  */
126
130
  export const query_vql_to_eb = (eb, root, table_name) => {
@@ -146,9 +150,10 @@ const transform_boolean_to_0_or_1 = (kv) => {
146
150
  /**
147
151
  * Convert an API Query into mongo dialect, also sanitize.
148
152
  *
153
+ * @template {any} [T=any]
149
154
  *
150
- * @param {import("kysely").ExpressionBuilder<Database>} eb
151
- * @param {import("@storecraft/core/api").ApiQuery} q
155
+ * @param {ExpressionBuilder<Database>} eb
156
+ * @param {ApiQuery<T>} q
152
157
  * @param {keyof Database} table_name
153
158
  *
154
159
  */
@@ -189,12 +194,22 @@ const SIGN = {
189
194
  '-1': 'desc'
190
195
  }
191
196
 
197
+ // export type DirectedOrderByStringReference<DB, TB extends keyof DB, O> = `${StringReference<DB, TB> | (keyof O & string)} ${OrderByDirection}`;
198
+
199
+ /**
200
+ * @import {DirectedOrderByStringReference} from './utils.types.js'
201
+ */
202
+ // OE extends OrderByExpression<DB, TB, O>
192
203
  /**
193
204
  * Convert an API Query into mongo dialect, also sanitize.
205
+ * @template {Record<string, any>} [Type=Record<string, any>]
206
+ * @template {keyof Database} [Table=keyof Database]
194
207
  *
195
- * @param {import("@storecraft/core/api").ApiQuery} q
208
+ * @param {ApiQuery<Type>} q
209
+ * @param {Table} table
210
+ * @returns {DirectedOrderByStringReference<Database, Table, Database[Table]>[]}
196
211
  */
197
- export const query_to_sort = (q={}) => {
212
+ export const query_to_sort = (q={}, table) => {
198
213
  // const sort_sign = q.order === 'asc' ? 'asc' : 'desc';
199
214
  // `reverse_sign=-1` means we need to reverse because of `limitToLast`
200
215
  const reverse_sign = (q.limitToLast && !q.limit) ? -1 : 1;
@@ -202,9 +217,11 @@ export const query_to_sort = (q={}) => {
202
217
  const sort_sign = (asc ? 1 : -1) * reverse_sign;
203
218
 
204
219
  // compute sort fields and order
205
- const sort = (q.sortBy?.length ? q.sortBy : ['updated_at', 'id']).map(
220
+ const keys = q.sortBy?.length ? q.sortBy : ['updated_at', 'id'];
221
+ const sort = keys.map(
206
222
  s => `${s} ${SIGN[sort_sign]}`
207
223
  )
208
-
224
+ // it's too complicated to map each ket to table column.
225
+ // kysely was designed to do this in place
209
226
  return sort;
210
227
  }
@@ -0,0 +1,17 @@
1
+ import { StringReference } from "kysely";
2
+
3
+ export type NoActive<T> = T extends {active:number} ? Omit<T, 'active'> & {active: boolean} : T;
4
+ export type ReplaceValues<T, Find extends any=any, ReplaceWith extends any=any> = {
5
+ [K in keyof T]: T[K] extends Find ? ReplaceWith : T[K]
6
+ };
7
+
8
+ export type ReplaceValuesOfKeys<T, Find extends keyof T=keyof T, ReplaceWith extends any=any> = {
9
+ [K in keyof T]: K extends Find ? ReplaceWith : T[K]
10
+ };
11
+
12
+ export type NO<T> = {
13
+ [P in keyof T]: T[P]
14
+ }
15
+
16
+ export type OrderByDirection = 'asc' | 'desc';
17
+ export type DirectedOrderByStringReference<DB, TB extends keyof DB, O> = `${StringReference<DB, TB> | (keyof O & string)} ${OrderByDirection}`;
package/types.public.d.ts CHANGED
@@ -7,7 +7,7 @@ export type SqlDialectType = 'SQLITE' | 'POSTGRES' | 'MYSQL';
7
7
  /**
8
8
  * The Storecraft SQL config
9
9
  */
10
- export type Config = {
10
+ export type Config<DialectType extends Dialect = Dialect> = {
11
11
  /**
12
12
  * @description Database name
13
13
  *
@@ -18,7 +18,7 @@ export type Config = {
18
18
  /**
19
19
  * @description The `Kysely` dialect
20
20
  */
21
- dialect: Dialect,
21
+ dialect: DialectType,
22
22
 
23
23
  /**
24
24
  * @description The type of the sql dialect `SQLITE`, `POSTGRES`, `MYSQL`
@@ -240,7 +240,7 @@ export interface DiscountsTable extends Base {
240
240
  /** details and filters of the discount */
241
241
  info: JSONColumnType<DiscountInfo>;
242
242
  /** discount application (automatic and coupons) */
243
- application: JSONColumnType<DiscountApplicationEnum>;
243
+ application: JSONColumnType<DiscountApplicationEnum[keyof DiscountApplicationEnum]>;
244
244
  /** internal usage, the application type id */
245
245
  _application_id: number;
246
246
  /** internal usage, the discount type id */
@@ -1,268 +0,0 @@
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
- }