xcraft-core-pickaxe 0.1.0 → 0.1.1

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
@@ -9,6 +9,7 @@ const {
9
9
  dateTime,
10
10
  any,
11
11
  value,
12
+ record,
12
13
  } = require('xcraft-core-stones');
13
14
  const $ = require('./operators.js');
14
15
  const {queryToSql} = require('./query-to-sql.js');
@@ -24,6 +25,7 @@ class TestUserShape {
24
25
  streetName = string;
25
26
  townName = string;
26
27
  };
28
+ skills = record(string, number);
27
29
  }
28
30
 
29
31
  const TestUserShape2 = {
@@ -110,21 +112,49 @@ example3: {
110
112
  console.log(queryToSql(builder.query));
111
113
  }
112
114
 
115
+ /**
116
+ * @template {AnyObjectShape} T
117
+ * @param {T} shape
118
+ * @returns {ScopedFromQuery<GetShape<T>>}
119
+ */
120
+ function queryAction(shape) {
121
+ const builder = new QueryBuilder()
122
+ .db('test_db')
123
+ .from('test_table', ActionShape(shape))
124
+ .scope((row) => row.field('action').get('payload').get('state'));
125
+ return /** @type {ScopedFromQuery<GetShape<T>>} */ (builder);
126
+ }
127
+
113
128
  example4: {
114
- /**
115
- * @template {AnyObjectShape} T
116
- * @param {T} shape
117
- * @returns {ScopedFromQuery<t<T>>}
118
- */
119
- function queryAction(shape) {
120
- const builder = new QueryBuilder()
121
- .db('test_db')
122
- .from('test_table', ActionShape(shape))
123
- .scope((row) => row.field('action').get('payload').get('state'));
124
- return /** @type {ScopedFromQuery<t<T>>} */ (builder);
125
- }
126
-
127
- const builder = queryAction(TestUserShape).fields(['firstname', 'age']);
129
+ const builder = queryAction(TestUserShape)
130
+ .fields(['firstname', 'age'])
131
+ .where((user) => user.get('address').get('streetName').eq('toto'))
132
+ .orderBy((user) => [user.get('age'), user.get('firstname')]);
133
+
134
+ console.log(queryToSql(builder.query));
135
+ }
136
+
137
+ example5: {
138
+ const builder = queryAction(TestUserShape)
139
+ .fields(['firstname', 'age'])
140
+ .where((user, $) =>
141
+ $.and(
142
+ user.get('mails').length.gt(0),
143
+ user.get('mails').some((mail) => mail.like('%@example.com'))
144
+ )
145
+ );
146
+
147
+ console.log(queryToSql(builder.query));
148
+ }
149
+
150
+ example6: {
151
+ const builder = queryAction(TestUserShape)
152
+ .fields(['firstname', 'age'])
153
+ .where((user, $) =>
154
+ user
155
+ .get('skills')
156
+ .some((value, key) => $.or(value.eq(42), key.eq('test')))
157
+ );
128
158
 
129
159
  console.log(queryToSql(builder.query));
130
160
  }
@@ -1,5 +1,14 @@
1
1
  // @ts-check
2
2
 
3
+ const {isAnyPick} = require('./picks.js');
4
+
5
+ /**
6
+ * @typedef {import("./operators.js").Operator} Operator
7
+ */
8
+ /**
9
+ * @typedef {import('./picks.js').AnyPick} AnyPick
10
+ */
11
+
3
12
  function escape(value) {
4
13
  if (typeof value === 'string') {
5
14
  return `'${value.replace(/'/g, "''")}'`;
@@ -9,6 +18,9 @@ function escape(value) {
9
18
 
10
19
  const operators = {
11
20
  value({value}, values) {
21
+ if (typeof value === 'boolean') {
22
+ return value ? 'TRUE' : 'FALSE';
23
+ }
12
24
  values.push(value);
13
25
  return '?';
14
26
  },
@@ -19,13 +31,22 @@ const operators = {
19
31
 
20
32
  field({field}, values) {
21
33
  // Note: field is not validated
22
- return `${field}`;
34
+ if (typeof field === 'string') {
35
+ return `${field}`;
36
+ }
37
+ return sql(field, values);
38
+ },
39
+
40
+ as({value, name}, values) {
41
+ return `${sql(value, values)} AS ${escape(name)}`;
23
42
  },
24
43
 
25
44
  get({value, path}, values) {
26
45
  // TODO: try values.push(path) => '?'
27
46
  // return `json_extract(${field}, ${sql(path, values)})`;
28
- return `json_extract(${sql(value)}, '$.' || ${escape(path.join('.'))})`;
47
+ return `json_extract(${sql(value, values)}, '$.' || ${escape(
48
+ path.join('.')
49
+ )})`;
29
50
  },
30
51
 
31
52
  not({value}, values) {
@@ -93,10 +114,14 @@ const operators = {
93
114
  .join(' OR ')})`;
94
115
  },
95
116
 
117
+ length({list}, values) {
118
+ return `json_array_length(${sql(list, values)})`;
119
+ },
120
+
96
121
  includes({list, value}, values) {
97
122
  return `EXISTS (
98
123
  SELECT *
99
- FROM json_each(${list})
124
+ FROM json_each(${sql(list, values)})
100
125
  WHERE json_each.value = ${sql(value, values)}
101
126
  )`;
102
127
  },
@@ -104,24 +129,55 @@ const operators = {
104
129
  some({list, condition}, values) {
105
130
  return `EXISTS (
106
131
  SELECT *
107
- FROM json_each(${list})
132
+ FROM json_each(${sql(list, values)})
108
133
  WHERE ${sql(condition, values)}
109
134
  )`;
110
135
  },
111
136
 
112
- someValue(_, values) {
137
+ eachValue(_, values) {
113
138
  return 'json_each.value';
114
139
  },
115
140
 
116
- keys() {},
141
+ eachKey(_, values) {
142
+ return 'json_each.key';
143
+ },
144
+
145
+ keys({obj}, values) {
146
+ return `(SELECT json_group_array(json_each.key) FROM json_each(${sql(
147
+ obj,
148
+ values
149
+ )}))`;
150
+ },
117
151
 
118
- length() {},
152
+ values({obj}, values) {
153
+ return `(SELECT json_group_array(json_each.value) FROM json_each(${sql(
154
+ obj,
155
+ values
156
+ )}))`;
157
+ },
119
158
  };
120
159
 
121
- function sql(operator, values) {
160
+ /**
161
+ * @param {Operator | AnyPick} operatorOrPick
162
+ * @returns {Operator}
163
+ */
164
+ function getOperator(operatorOrPick) {
165
+ if (isAnyPick(operatorOrPick)) {
166
+ return operatorOrPick.value;
167
+ }
168
+ return operatorOrPick;
169
+ }
170
+
171
+ /**
172
+ * @param {Operator | AnyPick} operatorOrPick
173
+ * @param {any[]} values
174
+ * @returns {string}
175
+ */
176
+ function sql(operatorOrPick, values) {
177
+ const operator = getOperator(operatorOrPick);
122
178
  const operatorName = operator.operator;
123
179
  if (!(operatorName in operators)) {
124
- throw new Error(`Unknown operator '${operator}'`);
180
+ throw new Error(`Unknown operator '${JSON.stringify(operator)}'`);
125
181
  }
126
182
  return operators[operatorName](operator, values);
127
183
  }
package/lib/operators.js CHANGED
@@ -36,6 +36,14 @@ const operators = {
36
36
  });
37
37
  },
38
38
 
39
+ as(value, name) {
40
+ return /** @type {const} */ ({
41
+ operator: 'as',
42
+ value,
43
+ name,
44
+ });
45
+ },
46
+
39
47
  get(value, path) {
40
48
  return /** @type {const} */ ({
41
49
  operator: 'get',
@@ -152,6 +160,13 @@ const operators = {
152
160
  });
153
161
  },
154
162
 
163
+ length(list) {
164
+ return /** @type {const} */ ({
165
+ operator: 'length',
166
+ list,
167
+ });
168
+ },
169
+
155
170
  includes(list, value) {
156
171
  value = op(value);
157
172
  return /** @type {const} */ ({
@@ -169,9 +184,29 @@ const operators = {
169
184
  });
170
185
  },
171
186
 
172
- someValue() {
187
+ eachValue() {
188
+ return /** @type {const} */ ({
189
+ operator: 'eachValue',
190
+ });
191
+ },
192
+
193
+ eachKey() {
194
+ return /** @type {const} */ ({
195
+ operator: 'eachKey',
196
+ });
197
+ },
198
+
199
+ keys(obj) {
200
+ return /** @type {const} */ ({
201
+ operator: 'keys',
202
+ obj,
203
+ });
204
+ },
205
+
206
+ values(obj) {
173
207
  return /** @type {const} */ ({
174
- operator: 'someValue',
208
+ operator: 'values',
209
+ obj,
175
210
  });
176
211
  },
177
212
  };
package/lib/picks.js CHANGED
@@ -6,6 +6,12 @@ const {
6
6
  ObjectType,
7
7
  Type,
8
8
  getTypeInstance,
9
+ array,
10
+ ObjectMapType,
11
+ RecordType,
12
+ number,
13
+ StringType,
14
+ string,
9
15
  } = require('xcraft-core-stones');
10
16
 
11
17
  const $o = require('./operators.js');
@@ -17,13 +23,6 @@ const $o = require('./operators.js');
17
23
  * @typedef {import('./operators.js').Operator} Operator
18
24
  */
19
25
 
20
- /**
21
- * @typedef {boolean | number | bigint | string | symbol | undefined | null} primitive
22
- */
23
- /**
24
- * @typedef {primitive | Function | Date | Error | RegExp} Builtin
25
- */
26
-
27
26
  /**
28
27
  * @typedef {(keyof any)} PathElement
29
28
  */
@@ -32,26 +31,29 @@ const $o = require('./operators.js');
32
31
  */
33
32
 
34
33
  /**
35
- * @typedef {{field: Operators["someValue"] | (keyof any), path: Path}} Context
34
+ * @template T
35
+ * @template {keyof T} K
36
+ * @typedef {Pick<T, K>[keyof Pick<T, K>]} SelectValues
36
37
  */
37
38
 
38
39
  /**
39
- * @template T
40
- * @typedef {0 extends (1 & T) ? true : never} IsAny
40
+ * @typedef {{
41
+ * field: (keyof any) | SelectValues<Operators, "eachValue" | "eachKey" | "keys" | "values" | "length">,
42
+ * path: Path
43
+ * }} Context
41
44
  */
42
45
 
43
46
  /**
44
- * @typedef {ValuePick<any> | ArrayPick<any> | ObjectPick<any>} AnyPick
47
+ * @typedef {ValuePick<any> | ArrayPick<any> | ObjectPick<any> | RecordPick<any,any>} AnyPick
45
48
  */
46
49
 
47
50
  /**
48
- * @template T
49
- * @typedef {true extends IsAny<T> ? AnyPick : [T] extends [Builtin] ? ValuePick<T> : [T] extends [Array<infer V>] ? ArrayPick<V> : [T] extends [{}] ? ObjectPick<T> : never} PickOf
51
+ * @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> : ValuePick<T>} PickOfType
50
53
  */
51
-
52
54
  /**
53
- * @template {{}} T
54
- * @typedef {ObjectType<{[K in keyof T] : Type<T[K]>}>} ObjectTypeOf
55
+ * @template {AnyTypeOrShape} T
56
+ * @typedef {PickOfType<GetType<T>>} PickOf
55
57
  */
56
58
 
57
59
  /**
@@ -67,16 +69,16 @@ function contextWithPath(context, path) {
67
69
  }
68
70
 
69
71
  /**
70
- * @template T
72
+ * @template {AnyTypeOrShape} T
71
73
  */
72
74
  class ValuePick {
73
- /** @type {Type<T>} */
75
+ /** @type {T} */
74
76
  #type;
75
77
  /** @type {Context} */
76
78
  #context;
77
79
 
78
80
  /**
79
- * @param {Type<T>} type
81
+ * @param {T} type
80
82
  * @param {Context} context
81
83
  */
82
84
  constructor(type, context) {
@@ -97,56 +99,56 @@ class ValuePick {
97
99
  }
98
100
 
99
101
  /**
100
- * @param {T | ValuePick<T>} value
102
+ * @param {t<T> | ValuePick<T>} value
101
103
  */
102
104
  eq(value) {
103
105
  return $o.eq(this.value, value);
104
106
  }
105
107
 
106
108
  /**
107
- * @param {T | ValuePick<T>} value
109
+ * @param {t<T> | ValuePick<T>} value
108
110
  */
109
111
  neq(value) {
110
112
  return $o.neq(this.value, value);
111
113
  }
112
114
 
113
115
  /**
114
- * @param {T | ValuePick<T>} value
116
+ * @param {t<T> | ValuePick<T>} value
115
117
  */
116
118
  gte(value) {
117
119
  return $o.gte(this.value, value);
118
120
  }
119
121
 
120
122
  /**
121
- * @param {T | ValuePick<T>} value
123
+ * @param {t<T> | ValuePick<T>} value
122
124
  */
123
125
  gt(value) {
124
126
  return $o.gt(this.value, value);
125
127
  }
126
128
 
127
129
  /**
128
- * @param {T | ValuePick<T>} value
130
+ * @param {t<T> | ValuePick<T>} value
129
131
  */
130
132
  lte(value) {
131
133
  return $o.lte(this.value, value);
132
134
  }
133
135
 
134
136
  /**
135
- * @param {T | ValuePick<T>} value
137
+ * @param {t<T> | ValuePick<T>} value
136
138
  */
137
139
  lt(value) {
138
140
  return $o.lt(this.value, value);
139
141
  }
140
142
 
141
143
  /**
142
- * @param {(T | ValuePick<T>)[]} list
144
+ * @param {(t<T> | ValuePick<T>)[]} list
143
145
  */
144
146
  in(list) {
145
147
  return $o.in(this.value, list);
146
148
  }
147
149
 
148
150
  /**
149
- * @param {string | ValuePick<string>} value
151
+ * @param {string | ValuePick<StringType>} value
150
152
  */
151
153
  like(value) {
152
154
  return $o.like(this.value, value);
@@ -154,16 +156,16 @@ class ValuePick {
154
156
  }
155
157
 
156
158
  /**
157
- * @template {{}} T
159
+ * @template {ObjectShape} T
158
160
  */
159
161
  class ObjectPick {
160
- /** @type {ObjectTypeOf<T>} */
162
+ /** @type {ObjectType<T>} */
161
163
  #type;
162
164
  /** @type {Context} */
163
165
  #context;
164
166
 
165
167
  /**
166
- * @param {ObjectTypeOf<T>} type
168
+ * @param {ObjectType<T>} type
167
169
  * @param {Context} context
168
170
  */
169
171
  constructor(type, context) {
@@ -189,24 +191,117 @@ class ObjectPick {
189
191
  * @returns {PickOf<T[K]>}
190
192
  */
191
193
  get(key) {
192
- return makeTypePick(
193
- getTypeInstance(this.#type.properties[key]),
194
+ return makePick(
195
+ this.#type.properties[key],
194
196
  contextWithPath(this.#context, key)
195
197
  );
196
198
  }
199
+
200
+ /**
201
+ * @param {t<T[keyof T]> | ValuePick<T[keyof T]>} value
202
+ */
203
+ includes(value) {
204
+ return $o.includes(this.value, value);
205
+ }
197
206
  }
198
207
 
199
208
  /**
200
- * @template T
209
+ * @template {AnyTypeOrShape} K
210
+ * @template {AnyTypeOrShape} V
211
+ */
212
+ class RecordPick {
213
+ /** @type {RecordType<K,V>} */
214
+ #type;
215
+ /** @type {Context} */
216
+ #context;
217
+
218
+ /**
219
+ * @param {RecordType<K,V>} type
220
+ * @param {Context} context
221
+ */
222
+ constructor(type, context) {
223
+ this.#type = type;
224
+ this.#context = context;
225
+ }
226
+
227
+ get value() {
228
+ const {field, path} = this.#context;
229
+ if (path.length > 0) {
230
+ return $o.get($o.field(field), path);
231
+ }
232
+ return $o.field(field);
233
+ }
234
+
235
+ get type() {
236
+ return this.#type;
237
+ }
238
+
239
+ /**
240
+ * @param {t<K>} key
241
+ * @returns {PickOf<V>}
242
+ */
243
+ get(key) {
244
+ return makePick(this.#type.valuesType, contextWithPath(this.#context, key));
245
+ }
246
+
247
+ /**
248
+ * @param {t<V> | ValuePick<V>} value
249
+ */
250
+ includes(value) {
251
+ return $o.includes(this.value, value);
252
+ }
253
+
254
+ /**
255
+ * @param {(value: PickOf<V>, key: PickOf<K>) => Operator} func
256
+ */
257
+ some(func) {
258
+ return $o.some(
259
+ this.value,
260
+ func(
261
+ makePick(this.#type.valuesType, {
262
+ field: $o.eachValue(),
263
+ path: [],
264
+ }),
265
+ makePick(this.#type.keysType, {
266
+ field: $o.eachKey(),
267
+ path: [],
268
+ })
269
+ )
270
+ );
271
+ }
272
+
273
+ /**
274
+ * @returns {ArrayPick<K>}
275
+ */
276
+ keys() {
277
+ return new ArrayPick(array(this.#type.keysType), {
278
+ field: $o.keys(this.value),
279
+ path: [],
280
+ });
281
+ }
282
+
283
+ /**
284
+ * @returns {ArrayPick<V>}
285
+ */
286
+ values() {
287
+ return new ArrayPick(array(this.#type.valuesType), {
288
+ field: $o.values(this.value),
289
+ path: [],
290
+ });
291
+ }
292
+ }
293
+
294
+ /**
295
+ * @template {AnyTypeOrShape} T
201
296
  */
202
297
  class ArrayPick {
203
- /** @type {ArrayType<Type<T>>} */
298
+ /** @type {ArrayType<T>} */
204
299
  #type;
205
300
  /** @type {Context} */
206
301
  #context;
207
302
 
208
303
  /**
209
- * @param {ArrayType<Type<T>>} type
304
+ * @param {ArrayType<T>} type
210
305
  * @param {Context} context
211
306
  */
212
307
  constructor(type, context) {
@@ -228,14 +323,21 @@ class ArrayPick {
228
323
  * @returns {PickOf<T>}
229
324
  */
230
325
  get(index) {
231
- return makeTypePick(
232
- getTypeInstance(this.#type.valuesType),
326
+ return makePick(
327
+ this.#type.valuesType,
233
328
  contextWithPath(this.#context, index)
234
329
  );
235
330
  }
236
331
 
332
+ get length() {
333
+ return new ValuePick(number, {
334
+ field: $o.length(this.value),
335
+ path: [],
336
+ });
337
+ }
338
+
237
339
  /**
238
- * @param {T | ValuePick<T>} value
340
+ * @param {t<T> | ValuePick<T>} value
239
341
  */
240
342
  includes(value) {
241
343
  return $o.includes(this.value, value);
@@ -248,8 +350,8 @@ class ArrayPick {
248
350
  return $o.some(
249
351
  this.value,
250
352
  func(
251
- makeTypePick(getTypeInstance(this.#type.valuesType), {
252
- field: $o.someValue(),
353
+ makePick(this.#type.valuesType, {
354
+ field: $o.eachValue(),
253
355
  path: [],
254
356
  })
255
357
  )
@@ -265,19 +367,20 @@ function isAnyPick(value) {
265
367
  return (
266
368
  value instanceof ValuePick ||
267
369
  value instanceof ObjectPick ||
370
+ value instanceof RecordPick ||
268
371
  value instanceof ArrayPick
269
372
  );
270
373
  }
271
374
 
272
375
  /**
273
- * @template {Record<string,any>} T
376
+ * @template {ObjectShape} T
274
377
  */
275
378
  class RowPick {
276
- /** @type {ObjectTypeOf<T>} */
379
+ /** @type {ObjectType<T>} */
277
380
  #type;
278
381
 
279
382
  /**
280
- * @param {ObjectTypeOf<T>} type
383
+ * @param {ObjectType<T>} type
281
384
  */
282
385
  constructor(type) {
283
386
  this.#type = type;
@@ -293,18 +396,15 @@ class RowPick {
293
396
  field: fieldName,
294
397
  path: [],
295
398
  };
296
- return makeTypePick(
297
- getTypeInstance(this.#type.properties[fieldName]),
298
- context
299
- );
399
+ return makePick(this.#type.properties[fieldName], context);
300
400
  }
301
401
  }
302
402
 
303
403
  /**
304
- * @template T
305
- * @param {Type<T>} type
404
+ * @template {Type} T
405
+ * @param {T} type
306
406
  * @param {Context} context
307
- * @returns {PickOf<T>}
407
+ * @returns {PickOfType<T>}
308
408
  */
309
409
  function makeTypePick(type, context) {
310
410
  if (type instanceof ArrayType) {
@@ -313,6 +413,13 @@ function makeTypePick(type, context) {
313
413
  if (type instanceof ObjectType) {
314
414
  return /** @type {any} */ (new ObjectPick(type, context));
315
415
  }
416
+ if (type instanceof ObjectMapType) {
417
+ const newType = new RecordType(string, type.valuesType);
418
+ return /** @type {any} */ (new RecordPick(newType, context));
419
+ }
420
+ if (type instanceof RecordType) {
421
+ return /** @type {any} */ (new RecordPick(type, context));
422
+ }
316
423
  return /** @type {any} */ (new ValuePick(type, context));
317
424
  }
318
425
 
@@ -320,7 +427,7 @@ function makeTypePick(type, context) {
320
427
  * @template {AnyTypeOrShape} T
321
428
  * @param {T} typeOrShape
322
429
  * @param {Context} context
323
- * @returns {PickOf<t<T>>}
430
+ * @returns {PickOf<T>}
324
431
  */
325
432
  function makePick(typeOrShape, context) {
326
433
  const type = getTypeInstance(typeOrShape);
@@ -328,8 +435,8 @@ function makePick(typeOrShape, context) {
328
435
  }
329
436
 
330
437
  /**
331
- * @template {Record<string,any>} T
332
- * @param {ObjectTypeOf<T>} type
438
+ * @template {ObjectShape} T
439
+ * @param {ObjectType<T>} type
333
440
  * @returns {RowPick<T>}
334
441
  */
335
442
  function rowPick(type) {
@@ -1,16 +1,18 @@
1
1
  // @ts-check
2
2
 
3
- const {ObjectType, getTypeInstance} = require('xcraft-core-stones');
3
+ const {ObjectType, ArrayType, toObjectType} = require('xcraft-core-stones');
4
4
  const operators = require('./operators.js');
5
5
  const {rowPick, ValuePick, RowPick, ObjectPick} = require('./picks.js');
6
6
  const {queryToSql} = require('./query-to-sql.js');
7
-
8
7
  /**
9
8
  * @typedef {import("./operators.js").Operator} Operator
10
9
  */
10
+ /**
11
+ * @typedef {import("./operators.js").Operators} Operators
12
+ */
11
13
 
12
14
  /**
13
- * @template T
15
+ * @template {AnyTypeOrShape} T
14
16
  * @typedef {import("./picks.js").PickOf<T>} PickOf
15
17
  */
16
18
  /**
@@ -19,17 +21,13 @@ const {queryToSql} = require('./query-to-sql.js');
19
21
  /**
20
22
  * @typedef {import("./picks.js").Path} Path
21
23
  */
22
- /**
23
- * @template {{}} T
24
- * @typedef {import("./picks.js").ObjectTypeOf<T>} ObjectTypeOf
25
- */
26
24
 
27
25
  /**
28
26
  * @typedef {{
29
27
  * db: string,
30
28
  * from: string,
31
29
  * scope?: ObjectPick<any>
32
- * select: AnyPick[] | Record<string, AnyPick> | AnyPick,
30
+ * select: Record<string, AnyPick>,
33
31
  * selectOneField?: boolean,
34
32
  * where?: Operator,
35
33
  * orderBy?: ValuePick<any> | ValuePick<any>[]
@@ -64,9 +62,73 @@ class FinalQuery {
64
62
  this.#queryParts = queryParts;
65
63
  }
66
64
 
65
+ #useRaw() {
66
+ return Boolean(this.#queryParts.selectOneField);
67
+ }
68
+
67
69
  #getStatement() {
68
70
  const {sql, values} = queryToSql(this.#queryParts);
69
- return this.#database.prepare(sql).bind(values);
71
+ return this.#database.prepare(sql).bind(values).raw(this.#useRaw());
72
+ }
73
+
74
+ #parseValue(value) {
75
+ return JSON.parse(value);
76
+ }
77
+
78
+ #identity(value) {
79
+ return value;
80
+ }
81
+
82
+ #getMapper(type) {
83
+ if (type instanceof ArrayType || type instanceof ObjectType) {
84
+ return this.#parseValue;
85
+ }
86
+ return this.#identity;
87
+ }
88
+
89
+ #getMappers() {
90
+ const select = this.#queryParts.select;
91
+ const mappers = Object.fromEntries(
92
+ Object.entries(select).map(([name, selectValue]) => [
93
+ name,
94
+ this.#getMapper(selectValue.type),
95
+ ])
96
+ );
97
+ return mappers;
98
+ }
99
+
100
+ #getRawMappers() {
101
+ const select = this.#queryParts.select;
102
+ const mappers = Object.values(select).map((selectValue) =>
103
+ this.#getMapper(selectValue.type)
104
+ );
105
+ return mappers;
106
+ }
107
+
108
+ #mapRow(row, mappers) {
109
+ for (const [name, value] of Object.entries(row)) {
110
+ row[name] = mappers[name](value);
111
+ }
112
+ return row;
113
+ }
114
+
115
+ #mapRawRow(row, mappers) {
116
+ for (const [i, value] of row.entries()) {
117
+ row[i] = mappers[i](value);
118
+ }
119
+ return row;
120
+ }
121
+
122
+ #getMapRow() {
123
+ if (this.#useRaw()) {
124
+ const mappers = this.#getRawMappers();
125
+ if (this.#queryParts.selectOneField) {
126
+ return (row) => (row ? this.#mapRawRow(row, mappers)[0] : row);
127
+ }
128
+ return (row) => row && this.#mapRawRow(row, mappers);
129
+ }
130
+ const mappers = this.#getMappers();
131
+ return (row) => row && this.#mapRow(row, mappers);
70
132
  }
71
133
 
72
134
  /**
@@ -80,39 +142,32 @@ class FinalQuery {
80
142
  * @returns {R}
81
143
  */
82
144
  get() {
83
- const result = this.#getStatement().get();
84
- if (result && this.#queryParts.selectOneField) {
85
- return Object.values(result)[0];
86
- }
87
- return result;
145
+ const mapRow = this.#getMapRow();
146
+ const row = this.#getStatement().get();
147
+ return mapRow(row);
88
148
  }
89
149
 
90
150
  /**
91
151
  * @returns {R[]}
92
152
  */
93
153
  all() {
94
- if (this.#queryParts.selectOneField) {
95
- return Array.from(this.#getStatement().raw().iterate(), (row) => row[0]);
96
- }
97
- return this.#getStatement().all();
154
+ const mapRow = this.#getMapRow();
155
+ return Array.from(this.#getStatement().iterate(), mapRow);
98
156
  }
99
157
 
100
158
  /**
101
159
  * @returns {Generator<R>}
102
160
  */
103
161
  *iterate() {
104
- if (this.#queryParts.selectOneField) {
105
- for (const row of this.#getStatement().raw().iterate()) {
106
- yield row[0];
107
- }
108
- return;
162
+ const mapRow = this.#getMapRow();
163
+ for (const row of this.#getStatement().iterate()) {
164
+ yield mapRow(row);
109
165
  }
110
- yield* this.#getStatement().iterate();
111
166
  }
112
167
  }
113
168
 
114
169
  /**
115
- * @template {Record<string, any>} T
170
+ * @template {ObjectShape} T
116
171
  * @template R
117
172
  * @extends {FinalQuery<R>}
118
173
  */
@@ -123,7 +178,7 @@ class ScopedSelectQuery extends FinalQuery {
123
178
 
124
179
  /**
125
180
  * @param {*} database
126
- * @param {ObjectTypeOf<T>} type
181
+ * @param {ObjectType<T>} type
127
182
  * @param {QueryParts<'db' | 'from' | 'select' | 'scope'>} queryParts
128
183
  */
129
184
  constructor(database, type, queryParts) {
@@ -161,7 +216,7 @@ class ScopedSelectQuery extends FinalQuery {
161
216
  }
162
217
 
163
218
  /**
164
- * @template {Record<string, any>} T
219
+ * @template {ObjectShape} T
165
220
  * @template R
166
221
  * @extends {FinalQuery<R>}
167
222
  */
@@ -172,7 +227,7 @@ class SelectQuery extends FinalQuery {
172
227
 
173
228
  /**
174
229
  * @param {*} database
175
- * @param {ObjectTypeOf<T>} type
230
+ * @param {ObjectType<T>} type
176
231
  * @param {QueryParts<'db' | 'from' | 'select'>} queryParts
177
232
  */
178
233
  constructor(database, type, queryParts) {
@@ -211,7 +266,7 @@ class SelectQuery extends FinalQuery {
211
266
  }
212
267
 
213
268
  /**
214
- * @template {Record<string, any>} T
269
+ * @template {ObjectShape} T
215
270
  */
216
271
  class ScopedFromQuery {
217
272
  #database;
@@ -220,7 +275,7 @@ class ScopedFromQuery {
220
275
 
221
276
  /**
222
277
  * @param {*} database
223
- * @param {ObjectTypeOf<T>} type
278
+ * @param {ObjectType<T>} type
224
279
  * @param {QueryParts<'db' | 'from' | 'scope'>} queryParts
225
280
  */
226
281
  constructor(database, type, queryParts) {
@@ -233,13 +288,13 @@ class ScopedFromQuery {
233
288
  * Select field
234
289
  * @template {keyof T} F
235
290
  * @param {F} value
236
- * @returns {ScopedSelectQuery<T, T[F]>}
291
+ * @returns {ScopedSelectQuery<T, t<T[F]>>}
237
292
  */
238
293
  field(value) {
239
294
  const scope = this.#queryParts.scope;
240
295
  const queryParts = {
241
296
  ...this.#queryParts,
242
- select: scope.get(value),
297
+ select: {[value]: scope.get(value)},
243
298
  selectOneField: true,
244
299
  };
245
300
  return new ScopedSelectQuery(this.#database, this.#type, queryParts);
@@ -249,26 +304,30 @@ class ScopedFromQuery {
249
304
  * Select fields
250
305
  * @template {(keyof T)[]} F
251
306
  * @param {F} values
252
- * @returns {ScopedSelectQuery<T, flatten<Pick<T, F[number]>>>}
307
+ * @returns {ScopedSelectQuery<T, t<Pick<T, F[number]>>>}
253
308
  */
254
309
  fields(values) {
255
310
  const scope = this.#queryParts.scope;
256
311
  const queryParts = {
257
312
  ...this.#queryParts,
258
- select: values.map((value) => scope.get(value)),
313
+ select: Object.fromEntries(
314
+ values.map((value) => [value, scope.get(value)])
315
+ ),
259
316
  };
260
317
  return new ScopedSelectQuery(this.#database, this.#type, queryParts);
261
318
  }
262
319
 
263
320
  /**
264
- * @template {Record<string, any>} R
265
- * @param {(obj: ObjectPick<T>) => {[K in keyof R]: PickOf<R[K]>}} fct
266
- * @returns {ScopedSelectQuery<T, R>}
321
+ * @template {Record<string, AnyPick>} R
322
+ * @param {(obj: ObjectPick<T>) => R} fct
323
+ * @returns {ScopedSelectQuery<T, t<{[K in keyof R]: R[K]["type"]}>>}
267
324
  */
268
325
  select(fct) {
269
326
  const scope = this.#queryParts.scope;
270
- const selectedFields = fct(scope);
271
- const queryParts = {...this.#queryParts, select: selectedFields};
327
+ const queryParts = {
328
+ ...this.#queryParts,
329
+ select: fct(scope),
330
+ };
272
331
  return new ScopedSelectQuery(this.#database, this.#type, queryParts);
273
332
  }
274
333
 
@@ -287,7 +346,7 @@ class ScopedFromQuery {
287
346
  }
288
347
 
289
348
  /**
290
- * @template {Record<string, any>} T
349
+ * @template {ObjectShape} T
291
350
  */
292
351
  class FromQuery {
293
352
  #database;
@@ -296,7 +355,7 @@ class FromQuery {
296
355
 
297
356
  /**
298
357
  * @param {*} database
299
- * @param {ObjectTypeOf<T>} type
358
+ * @param {ObjectType<T>} type
300
359
  * @param {QueryParts<'db' | 'from'>} queryParts
301
360
  */
302
361
  constructor(database, type, queryParts) {
@@ -306,7 +365,7 @@ class FromQuery {
306
365
  }
307
366
 
308
367
  /**
309
- * @template {{}} U
368
+ * @template {ObjectShape} U
310
369
  * @param {(obj: RowPick<T>) => ObjectPick<U>} fct
311
370
  * @returns {ScopedFromQuery<U>}
312
371
  */
@@ -323,25 +382,29 @@ class FromQuery {
323
382
  * Select fields
324
383
  * @template {(keyof T)[]} F
325
384
  * @param {F} fields
326
- * @returns {SelectQuery<T, flatten<Pick<T, F[number]>>>}
385
+ * @returns {SelectQuery<T, t<Pick<T, F[number]>>>}
327
386
  */
328
387
  fields(fields) {
329
388
  const row = rowPick(this.#type);
330
389
  const queryParts = {
331
390
  ...this.#queryParts,
332
- select: fields.map((fieldName) => row.field(fieldName)),
391
+ select: Object.fromEntries(
392
+ fields.map((fieldName) => [fieldName, row.field(fieldName)])
393
+ ),
333
394
  };
334
395
  return new SelectQuery(this.#database, this.#type, queryParts);
335
396
  }
336
397
 
337
398
  /**
338
- * @template {Record<string, any>} R
339
- * @param {(obj: RowPick<T>) => {[K in keyof R]: PickOf<R[K]>}} fct
340
- * @returns {SelectQuery<T, R>}
399
+ * @template {Record<string, AnyPick>} R
400
+ * @param {(obj: RowPick<T>) => R} fct
401
+ * @returns {SelectQuery<T, t<{[K in keyof R]: R[K]["type"]}>>}
341
402
  */
342
403
  select(fct) {
343
- const selectedFields = fct(rowPick(this.#type));
344
- const queryParts = {...this.#queryParts, select: selectedFields};
404
+ const queryParts = {
405
+ ...this.#queryParts,
406
+ select: fct(rowPick(this.#type)),
407
+ };
345
408
  return new SelectQuery(this.#database, this.#type, queryParts);
346
409
  }
347
410
 
@@ -378,15 +441,15 @@ class DbQuery {
378
441
  * @template {AnyObjectShape} T
379
442
  * @param {string} tableName
380
443
  * @param {T} shape
381
- * @returns {FromQuery<t<T>>}
444
+ * @returns {FromQuery<GetShape<T>>}
382
445
  */
383
446
  from(tableName, shape) {
384
- const type = getTypeInstance(shape);
447
+ const type = toObjectType(shape);
385
448
  const queryParts = {
386
449
  ...this.#queryParts,
387
450
  from: tableName,
388
451
  };
389
- return /** @type {any} */ (new FromQuery(this.#database, type, queryParts));
452
+ return new FromQuery(this.#database, type, queryParts);
390
453
  }
391
454
  }
392
455
 
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  const {sql} = require('./operator-to-sql.js');
7
- const {isAnyPick} = require('./picks.js');
8
7
 
9
8
  /**
10
9
  * @param {QueryObj["select"]} select
@@ -12,14 +11,8 @@ const {isAnyPick} = require('./picks.js');
12
11
  * @returns {string}
13
12
  */
14
13
  function selectFields(select, values) {
15
- if (Array.isArray(select)) {
16
- return select.map((rep) => sql(rep.value, values)).join(', ');
17
- }
18
- if (isAnyPick(select)) {
19
- return sql(select.value, values);
20
- }
21
14
  return Object.entries(select)
22
- .map(([name, rep]) => `${sql(rep.value, values)} AS ${name}`)
15
+ .map(([name, value]) => `${sql(value, values)} AS ${name}`)
23
16
  .join(', ');
24
17
  }
25
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xcraft-core-pickaxe",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Query builder",
5
5
  "main": "index.js",
6
6
  "scripts": {