xcraft-core-pickaxe 0.1.19 → 0.1.22

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/lib/examples.js CHANGED
@@ -87,7 +87,6 @@ example1: {
87
87
  example2: {
88
88
  const userIds = ['user@toto', 'user@tata'];
89
89
  const builder = new QueryBuilder()
90
- .db('test_db')
91
90
  .from('test_table', TestUserShape3)
92
91
  .fields(['firstname', 'age'])
93
92
  .where((user, $) =>
@@ -104,7 +103,6 @@ example2: {
104
103
 
105
104
  example3: {
106
105
  const builder = new QueryBuilder()
107
- .db('test_db')
108
106
  .from('test_table', ActionShape(TestUserShape))
109
107
  .scope((row) => row.field('action').get('payload').get('state'))
110
108
  .fields(['firstname', 'age']);
@@ -119,7 +117,6 @@ example3: {
119
117
  */
120
118
  function queryAction(shape) {
121
119
  const builder = new QueryBuilder()
122
- .db('test_db')
123
120
  .from('test_table', ActionShape(shape))
124
121
  .scope((row) => row.field('action').get('payload').get('state'));
125
122
  return /** @type {ScopedFromQuery<GetShape<T>>} */ (builder);
@@ -0,0 +1,21 @@
1
+ const joinOperators = /** @type {const} */ ([
2
+ 'join',
3
+ 'left join',
4
+ 'right join',
5
+ 'full join',
6
+ 'left outer join',
7
+ 'right outer join',
8
+ 'full outer join',
9
+ 'inner join',
10
+ 'cross join',
11
+ 'natural join',
12
+ 'natural left join',
13
+ 'natural right join',
14
+ 'natural full join',
15
+ 'natural left outer join',
16
+ 'natural right outer join',
17
+ 'natural full outer join',
18
+ 'natural inner join',
19
+ ]);
20
+
21
+ module.exports = {joinOperators};
@@ -32,9 +32,12 @@ const operators = {
32
32
  return 'NULL';
33
33
  },
34
34
 
35
- field({field}, context) {
35
+ field({tableName, field}, context) {
36
36
  // Note: field is not validated
37
37
  if (typeof field === 'string') {
38
+ if (context.useTableNames && tableName) {
39
+ return `${tableName}.${field}`;
40
+ }
38
41
  return `${field}`;
39
42
  }
40
43
  return sql(field, context);
@@ -78,12 +81,25 @@ const operators = {
78
81
  .join(' || ')})`;
79
82
  },
80
83
 
84
+ stringLength({value}, context) {
85
+ return `LENGTH(${sql(value, context)})`;
86
+ },
87
+
88
+ substr({value, start, length}, context) {
89
+ const valueSql = sql(value, context);
90
+ const startSql = sql(start, context);
91
+ const lengthSql = length !== undefined ? `, ${sql(length, context)}` : '';
92
+ return `SUBSTR(${valueSql}, ${startSql}${lengthSql})`;
93
+ },
94
+
81
95
  eq({a, b}, context) {
82
- return `${sql(a, context)} IS ${sql(b, context)}`;
96
+ const operator = context.equalOperator === '=' ? '=' : 'IS';
97
+ return `${sql(a, context)} ${operator} ${sql(b, context)}`;
83
98
  },
84
99
 
85
100
  neq({a, b}, context) {
86
- return `${sql(a, context)} IS NOT ${sql(b, context)}`;
101
+ const operator = context.equalOperator === '=' ? '<>' : 'IS NOT';
102
+ return `${sql(a, context)} ${operator} ${sql(b, context)}`;
87
103
  },
88
104
 
89
105
  gte({a, b}, context) {
@@ -264,6 +280,8 @@ function getOperator(operatorOrPick) {
264
280
  * @typedef {{
265
281
  * values: any[] | null,
266
282
  * scope?: any,
283
+ * useTableNames?: boolean,
284
+ * equalOperator: 'IS' | '=',
267
285
  * }} OperatorToSqlContext
268
286
  */
269
287
 
package/lib/operators.js CHANGED
@@ -41,9 +41,10 @@ const operators = {
41
41
  });
42
42
  },
43
43
 
44
- field(field) {
44
+ field(field, tableName) {
45
45
  return /** @type {const} */ ({
46
46
  operator: 'field',
47
+ tableName,
47
48
  field,
48
49
  });
49
50
  },
@@ -79,6 +80,28 @@ const operators = {
79
80
  });
80
81
  },
81
82
 
83
+ stringLength(value) {
84
+ value = op(value);
85
+ return /** @type {const} */ ({
86
+ operator: 'stringLength',
87
+ value,
88
+ });
89
+ },
90
+
91
+ substr(value, start, length) {
92
+ value = op(value);
93
+ start = op(start);
94
+ if (length !== undefined) {
95
+ length = op(length);
96
+ }
97
+ return /** @type {const} */ ({
98
+ operator: 'substr',
99
+ value,
100
+ start,
101
+ length,
102
+ });
103
+ },
104
+
82
105
  eq(a, b) {
83
106
  a = op(a);
84
107
  b = op(b);
@@ -383,6 +406,9 @@ const operators = {
383
406
  /**
384
407
  * @typedef {Operators["count"] | Operators["avg"] | Operators["max"] | Operators["min"] | Operators["sum"] | Operators["groupArray"]} Aggregator
385
408
  */
409
+ /**
410
+ * @typedef {Operators["eq"] | Operators["neq"] | Operators["and"] | Operators["or"] | Operators["not"] | Operators["gt"] | Operators["gte"] | Operators["lt"] | Operators["lte"] | Operators["in"] | Operators["includes"] | Operators["some"] | Operators["glob"] | Operators["like"] | Operators["match"]} BooleanOperator
411
+ */
386
412
  /**
387
413
  * @typedef {Operators["abs"] | Operators["plus"] | Operators["minus"]} MathOperator
388
414
  */
package/lib/picks.js CHANGED
@@ -13,6 +13,10 @@ const {
13
13
  StringType,
14
14
  string,
15
15
  NumberType,
16
+ EnumerationType,
17
+ toObjectType,
18
+ boolean,
19
+ BooleanType,
16
20
  } = require('xcraft-core-stones');
17
21
 
18
22
  const $o = require('./operators.js');
@@ -21,6 +25,7 @@ const $o = require('./operators.js');
21
25
  * @typedef {import('./operators.js').Operators} Operators
22
26
  * @typedef {import('./operators.js').Operator} Operator
23
27
  * @typedef {import('./operators.js').Aggregator} Aggregator
28
+ * @typedef {import("./operators.js").BooleanOperator} BooleanOperator
24
29
  */
25
30
 
26
31
  /**
@@ -30,6 +35,10 @@ const $o = require('./operators.js');
30
35
  * @typedef {PathElement[]} Path
31
36
  */
32
37
 
38
+ /**
39
+ * @typedef {ValuePick<BooleanType> | BooleanOperator} BooleanValue
40
+ */
41
+
33
42
  /**
34
43
  * @template T
35
44
  * @template {keyof T} K
@@ -39,7 +48,8 @@ const $o = require('./operators.js');
39
48
  /**
40
49
  * @typedef {{
41
50
  * field: (keyof any) | SelectValues<Operators, "eachValue" | "eachKey" | "keys" | "values" | "length">,
42
- * path: Path
51
+ * path: Path,
52
+ * tableName?: string
43
53
  * }} Context
44
54
  */
45
55
 
@@ -49,7 +59,7 @@ const $o = require('./operators.js');
49
59
 
50
60
  /**
51
61
  * @template {Type} T
52
- * @typedef { [T] extends [ArrayType<infer V>] ? ArrayPick<V> : [T] extends [ObjectType<infer S>] ? ObjectPick<S> : [T] extends [ObjectMapType<infer V>] ? RecordPick<StringType,V> : [T] extends [RecordType<infer K,infer V>] ? RecordPick<K,V> : [T] extends [NumberType] ? NumberPick : ValuePick<T>} PickOfType
62
+ * @typedef { [T] extends [ArrayType<infer V>] ? ArrayPick<V> : [T] extends [ObjectType<infer S>] ? ObjectPick<S> : [T] extends [ObjectMapType<infer V>] ? RecordPick<StringType,V> : [T] extends [RecordType<infer K,infer V>] ? RecordPick<K,V> : [T] extends [NumberType] ? NumberPick : [T] extends [StringType] ? StringPick : ValuePick<T>} PickOfType
53
63
  */
54
64
  /**
55
65
  * @template {AnyTypeOrShape} T
@@ -87,22 +97,33 @@ class ValuePick {
87
97
  }
88
98
 
89
99
  get value() {
90
- const {field, path} = this.#context;
100
+ const {tableName, field, path} = this.#context;
91
101
  if (path.length > 0) {
92
- return $o.get($o.field(field), path);
102
+ return $o.get($o.field(field, tableName), path);
93
103
  }
94
- return $o.field(field);
104
+ return $o.field(field, tableName);
95
105
  }
96
106
 
97
107
  get type() {
98
108
  return this.#type;
99
109
  }
100
110
 
111
+ /**
112
+ * @template {AnyObjectShape} T
113
+ * @param {T} shape
114
+ */
115
+ asObject(shape) {
116
+ return new ObjectPick(toObjectType(shape), this.#context);
117
+ }
118
+
101
119
  /**
102
120
  * @param {t<T> | ValuePick<T>} value
103
121
  */
104
122
  eq(value) {
105
- return $o.eq(this.value, value);
123
+ return new ValuePick(boolean, {
124
+ field: $o.eq(this.value, value),
125
+ path: [],
126
+ });
106
127
  }
107
128
 
108
129
  /**
@@ -147,6 +168,18 @@ class ValuePick {
147
168
  return $o.in(this.value, list);
148
169
  }
149
170
 
171
+ /**
172
+ * @param {any} value
173
+ */
174
+ match(value) {
175
+ return $o.match(this.value, value);
176
+ }
177
+ }
178
+
179
+ /**
180
+ * @extends {ValuePick<StringType>}
181
+ */
182
+ class StringPick extends ValuePick {
150
183
  /**
151
184
  * @param {string | ValuePick<StringType>} value
152
185
  */
@@ -161,11 +194,22 @@ class ValuePick {
161
194
  return $o.glob(this.value, value);
162
195
  }
163
196
 
197
+ get length() {
198
+ return new NumberPick(number, {
199
+ field: $o.stringLength(this.value),
200
+ path: [],
201
+ });
202
+ }
203
+
164
204
  /**
165
- * @param {any} value
205
+ * @param {number | ValuePick<NumberType>} start
206
+ * @param {number | ValuePick<NumberType>} [length]
166
207
  */
167
- match(value) {
168
- return $o.match(this.value, value);
208
+ substr(start, length) {
209
+ return new StringPick(string, {
210
+ field: $o.substr(this.value, start, length),
211
+ path: [],
212
+ });
169
213
  }
170
214
  }
171
215
 
@@ -174,21 +218,30 @@ class ValuePick {
174
218
  */
175
219
  class NumberPick extends ValuePick {
176
220
  abs() {
177
- return $o.abs(this.value);
221
+ return new NumberPick(number, {
222
+ field: $o.abs(this.value),
223
+ path: [],
224
+ });
178
225
  }
179
226
 
180
227
  /**
181
228
  * @param {number | ValuePick<NumberType>} value
182
229
  */
183
230
  plus(value) {
184
- return $o.plus(this.value, value);
231
+ return new NumberPick(number, {
232
+ field: $o.plus(this.value, value),
233
+ path: [],
234
+ });
185
235
  }
186
236
 
187
237
  /**
188
238
  * @param {number | ValuePick<NumberType>} value
189
239
  */
190
240
  minus(value) {
191
- return $o.minus(this.value, value);
241
+ return new NumberPick(number, {
242
+ field: $o.minus(this.value, value),
243
+ path: [],
244
+ });
192
245
  }
193
246
  }
194
247
 
@@ -211,17 +264,21 @@ class ObjectPick {
211
264
  }
212
265
 
213
266
  get value() {
214
- const {field, path} = this.#context;
267
+ const {tableName, field, path} = this.#context;
215
268
  if (path.length > 0) {
216
- return $o.get($o.field(field), path);
269
+ return $o.get($o.field(field, tableName), path);
217
270
  }
218
- return $o.field(field);
271
+ return $o.field(field, tableName);
219
272
  }
220
273
 
221
274
  get type() {
222
275
  return this.#type;
223
276
  }
224
277
 
278
+ toRowPick() {
279
+ return new RowPick(this.#type, this.#context);
280
+ }
281
+
225
282
  /**
226
283
  * @template {keyof T} K
227
284
  * @param {K} key
@@ -262,11 +319,11 @@ class RecordPick {
262
319
  }
263
320
 
264
321
  get value() {
265
- const {field, path} = this.#context;
322
+ const {tableName, field, path} = this.#context;
266
323
  if (path.length > 0) {
267
- return $o.get($o.field(field), path);
324
+ return $o.get($o.field(field, tableName), path);
268
325
  }
269
- return $o.field(field);
326
+ return $o.field(field, tableName);
270
327
  }
271
328
 
272
329
  get type() {
@@ -375,11 +432,11 @@ class ArrayPick {
375
432
  }
376
433
 
377
434
  get value() {
378
- const {field, path} = this.#context;
435
+ const {tableName, field, path} = this.#context;
379
436
  if (path.length > 0) {
380
- return $o.get($o.field(field), path);
437
+ return $o.get($o.field(field, tableName), path);
381
438
  }
382
- return $o.field(field);
439
+ return $o.field(field, tableName);
383
440
  }
384
441
 
385
442
  get type() {
@@ -412,7 +469,7 @@ class ArrayPick {
412
469
  }
413
470
 
414
471
  /**
415
- * @param {(value: PickOf<T>) => Operator} func
472
+ * @param {(value: PickOf<T>) => BooleanValue} func
416
473
  */
417
474
  some(func) {
418
475
  return $o.some(
@@ -470,12 +527,16 @@ function isAnyPick(value) {
470
527
  class RowPick {
471
528
  /** @type {ObjectType<T>} */
472
529
  #type;
530
+ /** @type {Partial<Context>} */
531
+ #context;
473
532
 
474
533
  /**
475
534
  * @param {ObjectType<T>} type
535
+ * @param {Partial<Context>} context
476
536
  */
477
- constructor(type) {
537
+ constructor(type, context) {
478
538
  this.#type = type;
539
+ this.#context = context;
479
540
  }
480
541
 
481
542
  /**
@@ -484,12 +545,29 @@ class RowPick {
484
545
  * @returns {PickOf<T[K]>}
485
546
  */
486
547
  field(fieldName) {
487
- const context = {
488
- field: fieldName,
489
- path: [],
490
- };
548
+ const context = this.#context.field
549
+ ? {
550
+ ...this.#context,
551
+ field: this.#context.field,
552
+ path: [...(this.#context.path ?? []), fieldName],
553
+ }
554
+ : {
555
+ ...this.#context,
556
+ field: fieldName,
557
+ path: [],
558
+ };
491
559
  return makePick(this.#type.properties[fieldName], context);
492
560
  }
561
+
562
+ /**
563
+ * Alias of field method to be symmetric with ObjectPick
564
+ * @template {keyof T} K
565
+ * @param {K} fieldName
566
+ * @returns {PickOf<T[K]>}
567
+ */
568
+ get(fieldName) {
569
+ return this.field(fieldName);
570
+ }
493
571
  }
494
572
 
495
573
  /**
@@ -515,6 +593,13 @@ function makeTypePick(type, context) {
515
593
  if (type instanceof NumberType) {
516
594
  return /** @type {any} */ (new NumberPick(type, context));
517
595
  }
596
+ if (
597
+ type instanceof StringType ||
598
+ (type instanceof EnumerationType &&
599
+ type.values.every((value) => typeof value === 'string'))
600
+ ) {
601
+ return /** @type {any} */ (new StringPick(type, context));
602
+ }
518
603
  return /** @type {any} */ (new ValuePick(type, context));
519
604
  }
520
605
 
@@ -532,10 +617,11 @@ function makePick(typeOrShape, context) {
532
617
  /**
533
618
  * @template {ObjectShape} T
534
619
  * @param {ObjectType<T>} type
620
+ * @param {string} [tableName]
535
621
  * @returns {RowPick<T>}
536
622
  */
537
- function rowPick(type) {
538
- return new RowPick(type);
623
+ function rowPick(type, tableName) {
624
+ return new RowPick(type, {tableName});
539
625
  }
540
626
 
541
627
  module.exports = {