@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
@@ -0,0 +1,723 @@
1
+ import { ExpressionWrapper, Kysely } from 'kysely'
2
+ import { jsonArrayFrom, stringArrayFrom } from './con.helpers.json.js'
3
+ import { SQL } from '../index.js';
4
+ import { query_to_eb } from './utils.query.js';
5
+
6
+ /**
7
+ * This file contains the shared commands without execute, but only returns
8
+ * array of query builders to mayne support batches and multi statements
9
+ */
10
+
11
+ /**
12
+ * @template ReturnType
13
+ * @typedef {(input: Kysely<import('../index.js').Database>) => ReturnType} Callback
14
+ */
15
+
16
+ /**
17
+ * @typedef {import('kysely').SelectQueryBuilder<import('../types.sql.tables.js').Database, keyof import('../types.sql.tables.js').Database> |
18
+ * import('kysely').DeleteQueryBuilder<import('../types.sql.tables.js').Database, keyof import('../types.sql.tables.js').Database, DeleteResult> |
19
+ * import('kysely').InsertQueryBuilder<import('../types.sql.tables.js').Database, keyof import('../types.sql.tables.js').Database, InsertResult> |
20
+ * import('kysely').UpdateQueryBuilder<import('../types.sql.tables.js').Database, keyof import('../types.sql.tables.js').Database>
21
+ * } Builder
22
+ */
23
+
24
+ /**
25
+ * @typedef {object} Executable
26
+ * @prop {(kysely?: Kysely) => Promise<any>} execute
27
+ * @prop {import('kysely').Compilable["compile"]} compile
28
+ */
29
+ /**
30
+ *
31
+ * @template {any[]} Input
32
+ *
33
+ * @param {SQL} driver
34
+ *
35
+ */
36
+ const trans_or_batch = (driver) => {
37
+ const prefers_batch = driver.config.prefers_multiple_statements_over_transaction;
38
+
39
+ if(prefers_batch) {
40
+ /**
41
+ *
42
+ */
43
+ return {
44
+ /**
45
+ * @param {Callback<Input>} callback
46
+ */
47
+ execute: async (callback) => {
48
+ const inputs = callback(driver.client);
49
+ /** @type {Executable[]} */
50
+ const inputs_2 = (inputs ?? []).flat(100).filter(Boolean);
51
+ const combined = sql.join(inputs_2, sql`;`);
52
+ const r = await combined.execute(driver.client);
53
+ return r;
54
+ }
55
+ }
56
+ }
57
+
58
+ return {
59
+ /**
60
+ *
61
+ * @param {Callback<Input>} callback
62
+ */
63
+ execute: async (callback) => {
64
+ const trans = await driver.client.transaction().execute(
65
+ async (trx) => {
66
+ const inputs = callback(trx);
67
+ /** @type {Executable[]} */
68
+ const inputs_2 = (inputs ?? []).flat(100).filter(Boolean);
69
+ const results = [];
70
+ for (const t of inputs_2) {
71
+ results.push(await t.execute())
72
+ }
73
+ return results;
74
+ }
75
+ );
76
+
77
+ }
78
+ }
79
+
80
+ }
81
+
82
+ /**
83
+ * @param {SQL} driver
84
+ * @param {keyof Database} table_name
85
+ *
86
+ * @returns {import('@storecraft/core/database').db_crud["count"]}
87
+ */
88
+ export const count_regular = (driver, table_name) => {
89
+ return async (query) => {
90
+
91
+ const result = await driver.client
92
+ .selectFrom(table_name)
93
+ .select(
94
+ (eb) => eb.fn.countAll().as('count')
95
+ )
96
+ .where(
97
+ (eb) => {
98
+ return query_to_eb(eb, query, table_name);
99
+ }
100
+ )
101
+ .executeTakeFirst();
102
+
103
+ return Number(result.count);
104
+ }
105
+ }
106
+
107
+ /**
108
+ *
109
+ * @param {string} id_or_handle
110
+ */
111
+ export const where_id_or_handle_entity = (id_or_handle) => {
112
+ /**
113
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
114
+ */
115
+ return (eb) => eb.or(
116
+ [
117
+ eb('entity_handle', '=', id_or_handle),
118
+ eb('entity_id', '=', id_or_handle),
119
+ ]
120
+ );
121
+ }
122
+
123
+ /**
124
+ *
125
+ * @param {string} id_or_handle
126
+ */
127
+ export const where_id_or_handle_table = (id_or_handle) => {
128
+ /**
129
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
130
+ */
131
+ return (eb) => eb.or(
132
+ [
133
+ eb('id', '=', id_or_handle),
134
+ eb('handle', '=', id_or_handle),
135
+ ]
136
+ );
137
+ }
138
+
139
+ /**
140
+ * @typedef { keyof Pick<Database,
141
+ * 'entity_to_media' | 'entity_to_search_terms'
142
+ * | 'entity_to_tags_projections' | 'products_to_collections'
143
+ * | 'products_to_discounts' | 'products_to_variants'
144
+ * | 'products_to_related_products' | 'storefronts_to_other'>
145
+ * } EntityTableKeys
146
+ */
147
+
148
+ /**
149
+ * helper to generate entity values delete
150
+ *
151
+ * @param {EntityTableKeys} entity_table_name
152
+ */
153
+ export const delete_entity_values_by_value_or_reporter = (entity_table_name) => {
154
+ /**
155
+ *
156
+ * @param {Kysely<Database>} trx
157
+ * @param {string} value delete by entity value
158
+ * @param {string} [reporter] delete by reporter
159
+ */
160
+ return (trx, value, reporter=undefined) => {
161
+
162
+ return trx.deleteFrom(entity_table_name).where(
163
+ eb => eb.or(
164
+ [
165
+ value && eb('value', '=', value),
166
+ reporter && eb('reporter', '=', reporter),
167
+ ].filter(Boolean)
168
+ )
169
+ )
170
+ //.executeTakeFirst();
171
+ }
172
+ }
173
+
174
+ /**
175
+ * helper to generate entity values delete
176
+ *
177
+ * @param {EntityTableKeys} entity_table_name
178
+ */
179
+ export const delete_entity_values_of_by_entity_id_or_handle =
180
+ (entity_table_name) => {
181
+ /**
182
+ *
183
+ * @param {Kysely<import('../index.js').Database>} trx
184
+ * @param {string} entity_id delete by id
185
+ * @param {string} [entity_handle=entity_id] delete by handle
186
+ */
187
+ return (trx, entity_id, entity_handle=undefined) => {
188
+ return trx.deleteFrom(entity_table_name).where(
189
+ eb => eb.or(
190
+ [
191
+ eb('entity_id', '=', entity_id),
192
+ eb('entity_handle', '=', entity_handle ?? entity_id),
193
+ ]
194
+ )
195
+ )
196
+ //.executeTakeFirst();
197
+ }
198
+ }
199
+
200
+ /**
201
+ * helper to generate entity values for simple tables
202
+ * @param {EntityTableKeys} entity_table_name
203
+ */
204
+ export const insert_entity_array_values_of = (entity_table_name) => {
205
+ /**
206
+ *
207
+ * @param {Kysely<Database>} trx
208
+ * @param {string[]} values values of the entity
209
+ * @param {string} item_id whom the tags belong to
210
+ * @param {string} [item_handle] whom the tags belong to
211
+ * @param {boolean} [delete_previous=true] if true and `reporter`,
212
+ * then will delete by reporter, otherwise by `item_id/item_handle`
213
+ * @param {string} [reporter=undefined] the reporter of the batch values
214
+ * (another segment technique)
215
+ * @param {string} [context=undefined] the context (another segment technique)
216
+ */
217
+ return (trx, values, item_id, item_handle, delete_previous=true,
218
+ reporter=undefined, context=undefined) => {
219
+
220
+ const queries = [];
221
+
222
+ if(delete_previous) {
223
+ if(reporter) {
224
+ queries.push(
225
+ delete_entity_values_by_value_or_reporter(entity_table_name)(
226
+ trx, undefined, reporter
227
+ )
228
+ )
229
+ } else {
230
+ queries.push(
231
+ delete_entity_values_of_by_entity_id_or_handle(entity_table_name)(
232
+ trx, item_id, item_handle
233
+ )
234
+ )
235
+ }
236
+ }
237
+
238
+ if(!values?.length)
239
+ return queries;
240
+
241
+
242
+ queries.push(
243
+ trx.insertInto(entity_table_name).values(
244
+ values.map(t => ({
245
+ entity_handle: item_handle,
246
+ entity_id: item_id,
247
+ value: t,
248
+ reporter,
249
+ context
250
+ })
251
+ )
252
+ )
253
+ );
254
+
255
+ //.executeTakeFirst();
256
+
257
+ return queries;
258
+ }
259
+ }
260
+
261
+ /**
262
+ * helper to generate entity values delete
263
+ *
264
+ * @param {EntityTableKeys} entity_table_name
265
+ */
266
+ export const insert_entity_values_of = (entity_table_name) => {
267
+ /**
268
+ *
269
+ * @param {Kysely<Database>} trx
270
+ * @param {{value: string, reporter: string}[]} values values of the entity
271
+ * @param {string} item_id whom the tags belong to
272
+ * @param {string} [item_handle] whom the tags belong to
273
+ * @param {string} [context=undefined] the context (another segment technique)
274
+ */
275
+ return (trx, values, item_id, item_handle, context=undefined) => {
276
+
277
+ if(!values?.length)
278
+ return undefined;
279
+
280
+ return trx.insertInto(entity_table_name).values(
281
+ values.map(t => (
282
+ {
283
+ entity_id: item_id,
284
+ entity_handle: item_handle,
285
+ value: t.value,
286
+ reporter: t.reporter,
287
+ context
288
+ }
289
+ )
290
+ )
291
+ )
292
+ //.executeTakeFirst();
293
+ }
294
+ }
295
+
296
+
297
+ /**
298
+ * Delete previous entities by `id/handle` and insert new ones
299
+ *
300
+ * @param {EntityTableKeys} entity_table
301
+ */
302
+ export const insert_entity_array_values_with_delete_of = (entity_table) => {
303
+ /**
304
+ * @param {Kysely<Database>} trx
305
+ * @param {string[]} values values of the entity
306
+ * @param {string} item_id entity id
307
+ * @param {string} [item_handle] entity handle
308
+ * @param {string} [context] context
309
+ */
310
+ return (trx, values, item_id, item_handle, context) => {
311
+ return insert_entity_array_values_of(entity_table)(
312
+ trx, values, item_id, item_handle, true, undefined, context
313
+ )
314
+ };
315
+ }
316
+
317
+ export const insert_tags_of = insert_entity_array_values_with_delete_of('entity_to_tags_projections');
318
+ export const insert_search_of = insert_entity_array_values_with_delete_of('entity_to_search_terms');
319
+ export const insert_media_of = insert_entity_array_values_with_delete_of('entity_to_media');
320
+
321
+ export const delete_tags_of = delete_entity_values_of_by_entity_id_or_handle('entity_to_tags_projections');
322
+ export const delete_search_of = delete_entity_values_of_by_entity_id_or_handle('entity_to_search_terms');
323
+ export const delete_media_of = delete_entity_values_of_by_entity_id_or_handle('entity_to_media');
324
+
325
+ /**
326
+ * @typedef {import('../index.js').Database} Database
327
+ */
328
+
329
+
330
+ /**
331
+ * @template {keyof Database} T
332
+ *
333
+ * @param {Kysely<Database>} trx
334
+ * @param {T} table_name
335
+ * @param {import('kysely').InsertObject<Database, T>} item values of the entity
336
+ *
337
+ */
338
+ export const regular_upsert_me = (trx, table_name, item) => {
339
+
340
+ const queries = [];
341
+
342
+ // TODO: maybe use only `id`
343
+ queries.push(
344
+ trx.deleteFrom(table_name).where(
345
+ eb => eb.or(
346
+ [
347
+ item.id && eb('id', '=', item.id),
348
+ item.handle && eb('handle', '=', item.handle),
349
+ ].filter(Boolean)
350
+ )
351
+ )
352
+ //.execute();
353
+ );
354
+
355
+ queries.push(
356
+ trx.insertInto(table_name).values(item)
357
+ //.executeTakeFirst()
358
+ );
359
+
360
+ return queries;
361
+ }
362
+
363
+
364
+ /**
365
+ *
366
+ * @param {Kysely<Database>} trx
367
+ * @param {keyof Database} table_name
368
+ * @param {string} id_or_handle
369
+ */
370
+ export const delete_me = (trx, table_name, id_or_handle) => {
371
+ // console.log('delete ', id_or_handle)
372
+ return trx.deleteFrom(table_name).where(
373
+ where_id_or_handle_table(id_or_handle)
374
+ );
375
+ //.executeTakeFirst();
376
+ }
377
+
378
+ /**
379
+ *
380
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
381
+ * @param {string | ExpressionWrapper<Database>} id_or_handle
382
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
383
+ */
384
+ export const with_tags = (eb, id_or_handle, sql_type) => {
385
+ return stringArrayFrom(
386
+ select_values_of_entity_by_entity_id_or_handle(
387
+ eb, 'entity_to_tags_projections', id_or_handle
388
+ ), sql_type
389
+ ).as('tags');
390
+ }
391
+
392
+ /**
393
+ *
394
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
395
+ * @param {string | ExpressionWrapper<Database>} id_or_handle
396
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
397
+ */
398
+ export const with_search = (eb, id_or_handle, sql_type) => {
399
+ return stringArrayFrom(
400
+ select_values_of_entity_by_entity_id_or_handle(
401
+ eb, 'entity_to_search_terms', id_or_handle
402
+ ), sql_type
403
+ ).as('search');
404
+ }
405
+
406
+ /**
407
+ *
408
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
409
+ * @param {string | ExpressionWrapper<Database>} id_or_handle
410
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
411
+ */
412
+ export const with_media = (eb, id_or_handle, sql_type) => {
413
+ return stringArrayFrom(
414
+ select_values_of_entity_by_entity_id_or_handle(
415
+ eb, 'entity_to_media', id_or_handle
416
+ ), sql_type
417
+ ).as('media');
418
+ }
419
+
420
+ /**
421
+ * helper to select base attributes
422
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
423
+ * @param {keyof Database} table
424
+ * @return {import('kysely').SelectQueryBuilder<Database, table>}
425
+ */
426
+ const select_base_from = (eb, table) => {
427
+ return [
428
+ 'active', 'attributes', 'created_at', 'updated_at',
429
+ 'description', 'handle', 'id'
430
+ ].map(k => `${table}.${k}`).reduce(
431
+ (p, c) => p.select(c), eb.selectFrom(table)
432
+ );
433
+ }
434
+
435
+ /**
436
+ * select as json array collections of a product
437
+ *
438
+ * @param {import('kysely').ExpressionBuilder<Database, 'products'>} eb
439
+ * @param {string | ExpressionWrapper<Database>} product_id_or_handle
440
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
441
+ */
442
+ export const products_with_collections = (eb, product_id_or_handle, sql_type) => {
443
+ return jsonArrayFrom(
444
+ select_base_from(eb, 'collections')
445
+ .select('collections.title')
446
+ .select('collections.published')
447
+ .select(eb => [
448
+ with_tags(eb, eb.ref('collections.id'), sql_type),
449
+ with_media(eb, eb.ref('collections.id'), sql_type),
450
+ ])
451
+ .where('collections.id', 'in',
452
+ eb => select_values_of_entity_by_entity_id_or_handle(
453
+ eb, 'products_to_collections', product_id_or_handle
454
+ )
455
+ ), sql_type
456
+ ).as('collections');
457
+ }
458
+
459
+ /**
460
+ * select as json array collections of a product
461
+ *
462
+ * @param {import('kysely').ExpressionBuilder<Database, 'products'>} eb
463
+ * @param {string | ExpressionWrapper<Database>} product_id_or_handle
464
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
465
+ */
466
+ export const products_with_discounts = (eb, product_id_or_handle, sql_type) => {
467
+ return jsonArrayFrom(
468
+ select_base_from(eb, 'discounts')
469
+ .select('discounts.title')
470
+ .select('discounts.published')
471
+ .select('discounts.application')
472
+ .select('discounts.info')
473
+ .select('discounts.priority')
474
+ .select(eb => [
475
+ with_tags(eb, eb.ref('discounts.id'), sql_type),
476
+ with_media(eb, eb.ref('discounts.id'), sql_type),
477
+ ])
478
+ .where('discounts.id', 'in',
479
+ eb => select_values_of_entity_by_entity_id_or_handle(
480
+ eb, 'products_to_discounts', product_id_or_handle
481
+ )
482
+ ), sql_type
483
+ ).as('discounts');
484
+ }
485
+
486
+ /**
487
+ * select as json array collections of a product
488
+ *
489
+ * @param {import('kysely').ExpressionBuilder<Database, 'products'>} eb
490
+ * @param {string | ExpressionWrapper<Database>} product_id_or_handle
491
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
492
+ */
493
+ export const products_with_variants = (eb, product_id_or_handle, sql_type) => {
494
+ return jsonArrayFrom(
495
+ select_base_from(eb, 'products')
496
+ .select('products.compare_at_price')
497
+ .select('products.parent_handle')
498
+ .select('products.parent_id')
499
+ .select('products.price')
500
+ .select('products.qty')
501
+ .select('products.title')
502
+ .select('products.variant_hint')
503
+ .select('products.variants_options')
504
+ .select('products.video')
505
+ .select(eb => [
506
+ with_tags(eb, eb.ref('products.id'), sql_type),
507
+ with_media(eb, eb.ref('products.id'), sql_type),
508
+ ])
509
+ .where('products.id', 'in',
510
+ eb => select_values_of_entity_by_entity_id_or_handle(
511
+ eb, 'products_to_variants', product_id_or_handle
512
+ )
513
+ ), sql_type
514
+ ).as('variants');
515
+ }
516
+
517
+ /**
518
+ * select as json array collections of a product
519
+ *
520
+ * @param {import('kysely').ExpressionBuilder<Database, 'products'>} eb
521
+ * @param {string | ExpressionWrapper<Database>} product_id_or_handle
522
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
523
+ */
524
+ export const products_with_related_products = (eb, product_id_or_handle, sql_type) => {
525
+ return jsonArrayFrom(
526
+ select_base_from(eb, 'products')
527
+ .select('products.compare_at_price')
528
+ .select('products.parent_handle')
529
+ .select('products.parent_id')
530
+ .select('products.price')
531
+ .select('products.qty')
532
+ .select('products.title')
533
+ .select('products.variant_hint')
534
+ .select('products.variants_options')
535
+ .select('products.video')
536
+ .select(eb => [
537
+ with_tags(eb, eb.ref('products.id'), sql_type),
538
+ with_media(eb, eb.ref('products.id'), sql_type),
539
+ ])
540
+ .where('products.id', 'in',
541
+ eb => select_values_of_entity_by_entity_id_or_handle(
542
+ eb, 'products_to_related_products', product_id_or_handle
543
+ )
544
+ ), sql_type
545
+ ).as('related_products');
546
+ }
547
+
548
+
549
+ /**
550
+ * select as json array collections of a product
551
+ *
552
+ * @param {import('kysely').ExpressionBuilder<Database, 'storefronts'>} eb
553
+ * @param {string | ExpressionWrapper<Database>} sf_id_or_handle
554
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
555
+ */
556
+ export const storefront_with_collections = (eb, sf_id_or_handle, sql_type) => {
557
+ return jsonArrayFrom(
558
+ select_base_from(eb, 'collections')
559
+ .select('collections.title')
560
+ .select('collections.published')
561
+ .select(eb => [
562
+ with_tags(eb, eb.ref('collections.id'), sql_type),
563
+ with_media(eb, eb.ref('collections.id'), sql_type),
564
+ ])
565
+ .where('collections.id', 'in',
566
+ eb => select_values_of_entity_by_entity_id_or_handle(
567
+ eb, 'storefronts_to_other', sf_id_or_handle
568
+ )
569
+ ), sql_type
570
+ ).as('collections');
571
+ }
572
+
573
+ /**
574
+ * select as json array collections of a product
575
+ *
576
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
577
+ * @param {string | ExpressionWrapper<Database>} sf_id_or_handle
578
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
579
+ */
580
+ export const storefront_with_products = (eb, sf_id_or_handle, sql_type) => {
581
+ return jsonArrayFrom(
582
+ select_base_from(eb, 'products')
583
+ .select('products.title')
584
+ .select('products.compare_at_price')
585
+ .select('products.parent_handle')
586
+ .select('products.parent_id')
587
+ .select('products.price')
588
+ .select('products.qty')
589
+ .select('products.variant_hint')
590
+ .select('products.variants_options')
591
+ .select('products.video')
592
+ .select(eb => [
593
+ with_tags(eb, eb.ref('products.id'), sql_type),
594
+ with_media(eb, eb.ref('products.id'), sql_type),
595
+ ])
596
+ .where('products.id', 'in',
597
+ eb => select_values_of_entity_by_entity_id_or_handle(
598
+ eb, 'storefronts_to_other', sf_id_or_handle
599
+ )
600
+ ), sql_type
601
+ ).as('products');
602
+ }
603
+
604
+ /**
605
+ * select as json array collections of a product
606
+ *
607
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
608
+ * @param {string | ExpressionWrapper<Database>} sf_id_or_handle
609
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
610
+ */
611
+ export const storefront_with_discounts = (eb, sf_id_or_handle, sql_type) => {
612
+ return jsonArrayFrom(
613
+ select_base_from(eb, 'discounts')
614
+ .select('discounts.application')
615
+ .select('discounts.info')
616
+ .select('discounts.priority')
617
+ .select('discounts.published')
618
+ .select('discounts.title')
619
+ .select(eb => [
620
+ with_tags(eb, eb.ref('discounts.id'), sql_type),
621
+ with_media(eb, eb.ref('discounts.id'), sql_type),
622
+ ])
623
+ .where('discounts.id', 'in',
624
+ eb => select_values_of_entity_by_entity_id_or_handle(
625
+ eb, 'storefronts_to_other', sf_id_or_handle
626
+ )
627
+ ), sql_type
628
+ ).as('discounts');
629
+ }
630
+
631
+ /**
632
+ * select as json array collections of a product
633
+ *
634
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
635
+ * @param {string | ExpressionWrapper<Database>} sf_id_or_handle
636
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
637
+ */
638
+ export const storefront_with_posts = (eb, sf_id_or_handle, sql_type) => {
639
+ return jsonArrayFrom(
640
+ select_base_from(eb, 'posts')
641
+ .select('posts.text')
642
+ .select('posts.title')
643
+ .select(eb => [
644
+ with_tags(eb, eb.ref('posts.id'), sql_type),
645
+ with_media(eb, eb.ref('posts.id'), sql_type),
646
+ ])
647
+ .where('posts.id', 'in',
648
+ eb => select_values_of_entity_by_entity_id_or_handle(
649
+ eb, 'storefronts_to_other', sf_id_or_handle
650
+ )
651
+ ), sql_type
652
+ ).as('posts');
653
+ }
654
+
655
+ /**
656
+ * select as json array collections of a product
657
+ *
658
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
659
+ * @param {string | ExpressionWrapper<Database>} sf_id_or_handle
660
+ * @param {import('../types.public.d.ts').SqlDialectType} sql_type
661
+ */
662
+ export const storefront_with_shipping = (eb, sf_id_or_handle, sql_type) => {
663
+ return jsonArrayFrom(
664
+ select_base_from(eb, 'shipping_methods')
665
+ .select('shipping_methods.price')
666
+ .select('shipping_methods.title')
667
+ .select(eb => [
668
+ with_tags(eb, eb.ref('shipping_methods.id'), sql_type),
669
+ with_media(eb, eb.ref('shipping_methods.id'), sql_type),
670
+ ])
671
+ .where('shipping_methods.id', 'in',
672
+ eb => select_values_of_entity_by_entity_id_or_handle(
673
+ eb, 'storefronts_to_other', sf_id_or_handle
674
+ )
675
+ ), sql_type
676
+ ).as('shipping_methods');
677
+ }
678
+
679
+ /**
680
+ * select all the entity values by entity id or handle
681
+ *
682
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
683
+ * @param {EntityTableKeys} entity_junction_table
684
+ * @param {string | ExpressionWrapper<Database>} entity_id_or_handle
685
+ */
686
+ export const select_values_of_entity_by_entity_id_or_handle = (
687
+ eb, entity_junction_table, entity_id_or_handle
688
+ ) => {
689
+ return eb
690
+ .selectFrom(entity_junction_table)
691
+ .select(`${entity_junction_table}.value`)
692
+ .where(eb2 => eb2.or(
693
+ [
694
+ eb2(`${entity_junction_table}.entity_id`, '=', entity_id_or_handle),
695
+ eb2(`${entity_junction_table}.entity_handle`, '=', entity_id_or_handle),
696
+ ]
697
+ )
698
+ )
699
+ .orderBy(`${entity_junction_table}.id`);
700
+ }
701
+
702
+ /**
703
+ * select the entity ids which are constrained by value or reporter
704
+ *
705
+ * @param {import('kysely').ExpressionBuilder<Database>} eb
706
+ * @param {EntityTableKeys} entity_junction_table
707
+ * @param {string | ExpressionWrapper<Database>} value
708
+ * @param {string | ExpressionWrapper<Database>} [reporter]
709
+ */
710
+ export const select_entity_ids_by_value_or_reporter =
711
+ (eb, entity_junction_table, value, reporter=undefined) => {
712
+ return eb
713
+ .selectFrom(entity_junction_table)
714
+ .select(`${entity_junction_table}.entity_id`)
715
+ .where(eb2 => eb2.or(
716
+ [
717
+ eb2(`${entity_junction_table}.value`, '=', value ?? reporter),
718
+ eb2(`${entity_junction_table}.reporter`, '=', reporter ?? value),
719
+ ]
720
+ )
721
+ )
722
+ .orderBy(`${entity_junction_table}.entity_id`);
723
+ }