leoric 1.13.1 → 1.13.5

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/History.md CHANGED
@@ -1,3 +1,45 @@
1
+ 1.13.5 / 2021-10-26
2
+ ===================
3
+
4
+ ## What's Changed
5
+ * docs: enhance aggregation query types & fix raw query result type by @cyjake in https://github.com/cyjake/leoric/pull/208
6
+
7
+
8
+ **Full Changelog**: https://github.com/cyjake/leoric/compare/v1.13.4...v1.13.5
9
+
10
+ 1.13.4 / 2021-10-25
11
+ ===================
12
+
13
+ ## What's Changed
14
+ * docs: spell & model methods should be generic by @cyjake in https://github.com/cyjake/leoric/pull/206
15
+ * docs: enhance query options, instance type, and toJSON() result type by @cyjake in https://github.com/cyjake/leoric/pull/207
16
+
17
+ This version brings correct (and hopefully better) typescript definitions, with the dts checked continuously at test/types tests. With this version, users that have model types correctly pinned at Bone will get code completion including class fields. Such as:
18
+
19
+ ![image](https://user-images.githubusercontent.com/252317/138683240-98ee9e79-4b3e-449c-bc95-a449d457d64f.png)
20
+
21
+ **Full Changelog**: https://github.com/cyjake/leoric/compare/v1.13.3...v1.13.4
22
+
23
+ 1.13.3 / 2021-10-21
24
+ ===================
25
+
26
+ ## What's Changed
27
+ * refactor: persist edge cases of type casting in integration tests by @cyjake in https://github.com/cyjake/leoric/pull/202
28
+ * docs: renaming attributes by @cyjake in https://github.com/cyjake/leoric/pull/203
29
+ * fix: JSON.uncast(string) should not serialize twice by @cyjake in https://github.com/cyjake/leoric/pull/205
30
+
31
+
32
+ **Full Changelog**: https://github.com/cyjake/leoric/compare/v1.13.2...v1.13.3
33
+
34
+ 1.13.2 / 2021-10-18
35
+ ===================
36
+
37
+ ## What's Changed
38
+ * fix: attribute.uncast([]) and realm.connect with synchronized models by @cyjake in https://github.com/cyjake/leoric/pull/201
39
+
40
+
41
+ **Full Changelog**: https://github.com/cyjake/leoric/compare/v1.13.1...v1.13.2
42
+
1
43
  1.13.1 / 2021-10-18
2
44
  ===================
3
45
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leoric",
3
- "version": "1.13.1",
3
+ "version": "1.13.5",
4
4
  "description": "JavaScript Object-relational mapping alchemy",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
@@ -20,7 +20,8 @@
20
20
  "test:mysql2": "./test/start.sh test/integration/mysql2.test.js",
21
21
  "test:postgres": "./test/start.sh test/integration/postgres.test.js",
22
22
  "test:sqlite": "./test/start.sh test/integration/sqlite.test.js",
23
- "test:dts": "./test/start.sh test/types/dts.test.js",
23
+ "test:sqlcipher": "./test/start.sh test/integration/sqlcipher.test.js",
24
+ "test:dts": "./test/start.sh dts",
24
25
  "test:coverage": "nyc ./test/start.sh && nyc report --reporter=lcov",
25
26
  "lint": "eslint ./",
26
27
  "lint:fix": "eslint . --fix"
@@ -58,8 +59,8 @@
58
59
  "@babel/eslint-parser": "^7.14.7",
59
60
  "@cara/minami": "^1.2.3",
60
61
  "@journeyapps/sqlcipher": "^5.2.0",
62
+ "@types/mocha": "^9.0.0",
61
63
  "@types/node": "^16.10.1",
62
- "coffee": "^5.4.0",
63
64
  "dayjs": "^1.10.3",
64
65
  "eslint": "^7.20.0",
65
66
  "expect.js": "^0.3.1",
@@ -71,6 +72,6 @@
71
72
  "pg": "^8.5.1",
72
73
  "sinon": "^10.0.0",
73
74
  "sqlite3": "^5.0.2",
74
- "typescript": "^4.4.3"
75
+ "typescript": "^4.4.4"
75
76
  }
76
77
  }
@@ -154,7 +154,10 @@ module.exports = Bone => {
154
154
  }
155
155
 
156
156
  static scope(name, ...args) {
157
- class ScopeClass extends this {};
157
+ const parentName = this.name;
158
+ class ScopeClass extends this {
159
+ static name = parentName;
160
+ };
158
161
  ScopeClass.setScope(name, ...args);
159
162
  return ScopeClass;
160
163
  }
package/src/bone.js CHANGED
@@ -820,7 +820,7 @@ class Bone {
820
820
  * restore rows
821
821
  * @param {Object} conditions query conditions
822
822
  * @param {Object?} opts query options
823
- * @returns
823
+ * @returns {Spell}
824
824
  */
825
825
  static restore(conditions, opts = {}) {
826
826
  const { deletedAt } = this.timestamps;
package/src/data_types.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const util = require('util');
3
4
  const invokable = require('./utils/invokable');
4
5
 
5
6
  /**
@@ -66,6 +67,15 @@ class DataType {
66
67
  }
67
68
  }
68
69
 
70
+ /**
71
+ * Check if params is instance of DataType or not
72
+ * @param {*} params
73
+ * @returns {boolean}
74
+ */
75
+ static is(params) {
76
+ return params instanceof DataType;
77
+ }
78
+
69
79
  /**
70
80
  * cast raw data returned from data packet into js type
71
81
  */
@@ -181,7 +191,10 @@ class INTEGER extends DataType {
181
191
  }
182
192
 
183
193
  uncast(value) {
184
- if (typeof value === 'string') return parseInt(value, 10);
194
+ const originValue = value;
195
+ if (value == null) return value;
196
+ if (typeof value === 'string') value = parseInt(value, 10);
197
+ if (isNaN(value)) throw new Error(util.format('invalid integer: %s', originValue));
185
198
  return value;
186
199
  }
187
200
  }
@@ -227,27 +240,31 @@ class DATE extends DataType {
227
240
  }
228
241
 
229
242
  uncast(value) {
243
+ const originValue = value;
244
+
230
245
  if (value == null) return value;
231
246
  if (typeof value.toDate === 'function') {
232
247
  value = value.toDate();
233
248
  }
234
249
 
250
+ // @deprecated
251
+ // vaguely standard date formats such as 2021-10-15 15:50:02,548
252
+ if (typeof value === 'string' && rDateFormat.test(value)) {
253
+ value = new Date(`${value.replace(' ', 'T').replace(',', '.')}Z`);
254
+ }
255
+
256
+ // 1634611135776
257
+ // '2021-10-15T08:38:43.877Z'
258
+ if (!(value instanceof Date)) value = new Date(value);
259
+ if (isNaN(value)) throw new Error(util.format('invalid date: %s', originValue));
260
+
235
261
  const { precision } = this;
236
- if (value instanceof Date && precision < 3) {
262
+ if (precision < 3) {
237
263
  const result = new Date(value);
238
264
  result.setMilliseconds(result.getMilliseconds() % (10 ** precision));
239
265
  return result;
240
266
  }
241
-
242
- if (typeof value === 'string') {
243
- // vaguely standard date formats such as 2021-10-15 15:50:02,548
244
- if (rDateFormat.test(value)) {
245
- return new Date(`${value.replace(' ', 'T').replace(',', '.')}Z`);
246
- }
247
- // Date.parse('2021-10-15T08:38:43.877Z')
248
- return new Date(value);
249
- }
250
- return value instanceof Date ? value : new Date(value);
267
+ return value;
251
268
  }
252
269
  }
253
270
 
@@ -261,12 +278,29 @@ class DATEONLY extends DataType {
261
278
  return this.dataType.toUpperCase();
262
279
  }
263
280
 
281
+ cast(value) {
282
+ if (value == null) return value;
283
+ if (value instanceof Date) return value;
284
+ return new Date(value);
285
+ }
286
+
264
287
  uncast(value) {
288
+ const originValue = value;
289
+
265
290
  if (value == null) return value;
266
291
  if (typeof value.toDate === 'function') {
267
292
  value = value.toDate();
268
293
  }
269
294
 
295
+ // @deprecated
296
+ // vaguely standard date formats such as 2021-10-15 15:50:02,548
297
+ if (typeof value === 'string' && rDateFormat.test(value)) {
298
+ value = new Date(`${value.replace(' ', 'T').replace(',', '.')}Z`);
299
+ }
300
+
301
+ if (!(value instanceof Date)) value = new Date(value);
302
+ if (isNaN(value)) throw new Error(util.format('invalid date: %s', originValue));;
303
+
270
304
  return new Date(value.getFullYear(), value.getMonth(), value.getDate());
271
305
  }
272
306
  }
@@ -86,7 +86,7 @@ class Attribute {
86
86
 
87
87
  // { foo: STRING }
88
88
  // { foo: STRING(255) }
89
- if (typeof params === 'function' || params instanceof DataTypes) {
89
+ if (typeof params === 'function' || DataTypes.is(params)) {
90
90
  params = { type: params };
91
91
  }
92
92
  const type = createType(DataTypes, params);
@@ -127,7 +127,9 @@ class Attribute {
127
127
  }
128
128
 
129
129
  uncast(value) {
130
- if (Array.isArray(value)) return value.map(entry => this.type.uncast(entry));
130
+ if (Array.isArray(value) && this.jsType !== JSON) {
131
+ return value.map(entry => this.type.uncast(entry));
132
+ }
131
133
  return this.type.uncast(value);
132
134
  }
133
135
  }
@@ -9,6 +9,7 @@ const { escapeId, escape } = require('./sqlstring');
9
9
  const schema = require('./schema');
10
10
  const spellbook = require('./spellbook');
11
11
  const Pool = require('./pool');
12
+
12
13
  class SqliteDriver extends AbstractDriver {
13
14
  constructor(opts = {}) {
14
15
  super(opts);
@@ -197,16 +197,18 @@ function coerceLiteral(spell, ast) {
197
197
  const { args } = ast;
198
198
  const firstArg = args[0];
199
199
 
200
- if (firstArg.type === 'id') {
201
- const model = findModel(spell, firstArg.qualifiers);
202
- const attribute = model && model.attributeMap[firstArg.value];
203
-
204
- if (attribute) {
205
- for (const arg of args.slice(1)) {
206
- if (arg.type === 'literal') {
207
- arg.value = attribute.uncast(arg.value);
208
- }
209
- }
200
+ if (firstArg.type !== 'id') return;
201
+
202
+ const model = findModel(spell, firstArg.qualifiers);
203
+ const attribute = model && model.attributes[firstArg.value];
204
+
205
+ if (!attribute) return;
206
+
207
+ for (const arg of args.slice(1)) {
208
+ if (arg.type === 'literal') {
209
+ // { params: { $like: '%foo%' } }
210
+ if (attribute.jsType === JSON && typeof arg.value === 'string') continue;
211
+ arg.value = attribute.uncast(arg.value);
210
212
  }
211
213
  }
212
214
  }
package/src/realm.js CHANGED
@@ -91,6 +91,10 @@ class Realm {
91
91
  const Spine = createSpine(opts);
92
92
  const models = {};
93
93
 
94
+ if (Array.isArray(opts.models)) {
95
+ for (const model of opts.models) models[model.name] = model;
96
+ }
97
+
94
98
  const driver = new (findDriver(dialect))({
95
99
  client,
96
100
  database,
@@ -130,8 +134,11 @@ class Realm {
130
134
  models = Object.values(this.models);
131
135
  }
132
136
 
137
+ // models could be connected already if cached
138
+ models = models.filter(model => !model.synchronized);
139
+
133
140
  if (models.length > 0) {
134
- await loadModels(this.Bone, models.filter(model => !model.synchronized), this.options);
141
+ await loadModels(this.Bone, models, this.options);
135
142
  }
136
143
  this.connected = true;
137
144
  return this.Bone;
package/src/spell.js CHANGED
@@ -65,7 +65,7 @@ function parseSelect(spell, ...names) {
65
65
  * @param {Spell} spell
66
66
  * @param {Object} obj - key-value pairs of attributes
67
67
  * @param {boolean} strict - check attribute exist or not
68
- * @returns
68
+ * @returns {Object}
69
69
  */
70
70
  function formatValueSet(spell, obj, strict = true) {
71
71
  const { Model, silent = false, command } = spell;
package/types/index.d.ts CHANGED
@@ -44,85 +44,89 @@ interface SpellOptions {
44
44
  rowCount: 0;
45
45
  }
46
46
 
47
- type SpellFactory = (spell: Spell) => Promise<null | number | Collection<Bone> | ResultSet>;
48
-
49
47
  type OrderOptions = { [name: string]: 'desc' | 'asc' };
50
48
 
51
49
  type SetOptions = { [key: string]: Literal };
52
50
 
53
51
  type WithOptions = {
54
- [qualifier: string]: { select: string[], throughRelation: string }
52
+ [qualifier: string]: { select: string | string[], throughRelation?: string }
55
53
  }
56
54
 
57
- declare class Spell {
58
- constructor(Model: Bone, factory: SpellFactory, opts: SpellOptions);
55
+ declare class Spell<T extends typeof Bone, U = InstanceType<T> | Collection<InstanceType<T>> | ResultSet | number | null> extends Promise<U> {
56
+ constructor(Model: T, opts: SpellOptions);
59
57
 
60
- select(...names: string[]): Spell & Promise<Bone>;
61
- insert(opts: SetOptions): Spell & Promise<number>;
62
- update(opts: SetOptions): Spell & Promise<number>;
63
- upsert(opts: SetOptions): Spell & Promise<number>;
64
- delete(): Spell & Promise<number>;
58
+ select(...names: Array<string | RawSql>): Spell<T, U>;
59
+ insert(opts: SetOptions): Spell<T, number>;
60
+ update(opts: SetOptions): Spell<T, number>;
61
+ upsert(opts: SetOptions): Spell<T, number>;
62
+ delete(): Spell<T, number>;
65
63
 
66
- from(table: string | Spell): Spell & Promise<Bone>;
64
+ from(table: string | Spell<T>): Spell<T, U>;
67
65
 
68
- with(opts: WithOptions): Spell & Promise<Bone>;
69
- with(...qualifiers: string[]): Spell & Promise<Bone>;
66
+ with(opts: WithOptions): Spell<T, U>;
67
+ with(...qualifiers: string[]): Spell<T, U>;
70
68
 
71
- join(Model: Bone, onConditions: string, ...values: Literal[]): Spell & Promise<Bone>;
72
- join(Model: Bone, onConditions: WhereConditions): Spell & Promise<Bone>;
69
+ join<V extends typeof Bone>(Model: V, onConditions: string, ...values: Literal[]): Spell<T, U>;
70
+ join<V extends typeof Bone>(Model: V, onConditions: WhereConditions<T>): Spell<T, U>;
73
71
 
74
- where(conditions: string, ...values: Literal[]): Spell & Promise<Bone>;
75
- where(conditions: WhereConditions): Spell & Promise<Bone>;
72
+ $where(conditions: WhereConditions<T>): this;
73
+ $where(conditions: string, ...values: Literal[]): this;
74
+ where(conditions: WhereConditions<T>): Spell<T, U>;
75
+ where(conditions: string, ...values: Literal[]): Spell<T, U>;
76
76
 
77
- orWhere(conditions: string, ...values: Literal[]): Spell & Promise<Bone>;
78
- orWhere(conditions: WhereConditions): Spell & Promise<Bone>;
77
+ orWhere(conditions: WhereConditions<T>): Spell<T, U>;
78
+ orWhere(conditions: string, ...values: Literal[]): Spell<T, U>;
79
79
 
80
- group(...names: string[]): Spell & Promise<ResultSet>;
80
+ group(...names: Array<string | RawSql>): Spell<T, ResultSet>;
81
81
 
82
- having(conditions: string, ...values: Literal[]): Spell & Promise<ResultSet>;
83
- having(conditions: WhereConditions): Spell & Promise<ResultSet>;
82
+ having(conditions: string, ...values: Literal[]): Spell<T, ResultSet>;
83
+ having(conditions: WhereConditions<T>): Spell<T, ResultSet>;
84
84
 
85
- orHaving(conditions: string, ...values: Literal[]): Spell & Promise<ResultSet>;
86
- orHaving(conditions: WhereConditions): Spell & Promise<ResultSet>;
85
+ orHaving(conditions: string, ...values: Literal[]): Spell<T, ResultSet>;
86
+ orHaving(conditions: WhereConditions<T>): Spell<T, ResultSet>;
87
87
 
88
- order(name: string, order?: 'desc' | 'asc'): Spell & Promise<Bone>;
89
- order(opts: OrderOptions): Spell & Promise<Bone>;
88
+ order(name: string, order?: 'desc' | 'asc'): Spell<T, U>;
89
+ order(opts: OrderOptions): Spell<T, U>;
90
90
 
91
- offset(skip: number): Spell & Promise<Bone>;
92
- limit(skip: number): Spell & Promise<Bone>;
91
+ offset(skip: number): Spell<T, U>;
92
+ limit(skip: number): Spell<T, U>;
93
93
 
94
- count(name?: string): Spell & Promise<ResultSet>;
95
- average(name?: string): Spell & Promise<ResultSet>;
96
- minimum(name?: string): Spell & Promise<ResultSet>;
97
- maximum(name?: string): Spell & Promise<ResultSet>;
98
- sum(name?: string): Spell & Promise<ResultSet>;
94
+ count(name?: string): Spell<T, Extract<U, ResultSet | number>>;
95
+ average(name?: string): Spell<T, Extract<U, ResultSet | number>>;
96
+ minimum(name?: string): Spell<T, Extract<U, ResultSet | number>>;
97
+ maximum(name?: string): Spell<T, Extract<U, ResultSet | number>>;
98
+ sum(name?: string): Spell<T, Extract<U, ResultSet | number>>;
99
99
 
100
- batch(size?: number): AsyncIterable<Bone>;
100
+ batch(size?: number): AsyncIterable<T>;
101
101
 
102
102
  toSqlString(): string;
103
103
  toString(): string;
104
104
  }
105
105
 
106
- type Literal = null | boolean | number | string | Date | JSON | ArrayBuffer;
106
+ type Literal = null | undefined | boolean | number | string | Date | object | ArrayBuffer;
107
107
 
108
108
  type OperatorCondition = {
109
109
  [key in '$eq' | '$ne']?: Literal;
110
110
  } & {
111
111
  [key in '$in' | '$nin' | '$notIn']?: Literal[] | Set<Literal>;
112
112
  } & {
113
- [key in '$like' | '$notLike']?: Literal[];
113
+ [key in '$like' | '$notLike']?: string;
114
114
  } & {
115
115
  [key in '$gt' | '$gte' | '$lt' | '$lte']?: number;
116
116
  } & {
117
117
  [key in '$between' | '$notBetween']?: [number, number] | [Date, Date];
118
118
  };
119
119
 
120
- interface WhereConditions {
121
- [key: string]: Literal | Literal[] | OperatorCondition;
120
+ type WhereConditions<T extends typeof Bone> = {
121
+ [Property in keyof T['attributes']]?: Literal | Literal[] | OperatorCondition;
122
+ }
123
+
124
+ type Values<T extends typeof Bone> = {
125
+ [Property in keyof T['attributes']]?: Literal;
122
126
  }
123
127
 
124
- interface Values {
125
- [key: string]: Literal;
128
+ type InstanceValues<T> = {
129
+ [Property in keyof Extract<T, Literal>]?: Extract<T, Literal>[Property]
126
130
  }
127
131
 
128
132
  declare class DataType {}
@@ -148,10 +152,17 @@ interface RelateOptions {
148
152
  }
149
153
 
150
154
  interface QueryOptions {
151
- validate?: boolean,
152
- individualHooks?: boolean,
153
- hooks?: boolean,
154
- paranoid?: boolean,
155
+ validate?: boolean;
156
+ individualHooks?: boolean;
157
+ hooks?: boolean;
158
+ paranoid?: boolean;
159
+ }
160
+
161
+ interface QueryResult {
162
+ insertId?: number;
163
+ affectedRows?: number;
164
+ rows?: Array<Record<string, Literal>>,
165
+ fields?: Array<{ table: string, name: string }>,
155
166
  }
156
167
 
157
168
  interface Connection {
@@ -160,9 +171,8 @@ interface Connection {
160
171
  */
161
172
  query(
162
173
  query: string,
163
- values: Array<Literal>,
164
- callback: (err: Error|null, results: ResultSet, fields: Array<string>) => void
165
- ): void;
174
+ values: Array<Literal | Literal[]>,
175
+ ): Promise<QueryResult>;
166
176
  }
167
177
 
168
178
  declare class Pool {
@@ -188,19 +198,19 @@ declare class Driver {
188
198
  /**
189
199
  * Grab a connection and query the database
190
200
  */
191
- query(sql: string, values: Array<Literal>): ResultSet;
201
+ query(sql: string, values?: Array<Literal | Literal[]>): Promise<QueryResult>;
192
202
  }
193
203
 
194
- type ResultSet = Values[] | { [qualifier: string]: Values }[]
204
+ type ResultSet = {
205
+ [key: string]: Literal
206
+ };
195
207
 
196
- declare class Collection<Bone> extends Array<Bone> {
208
+ declare class Collection<T extends Bone> extends Array<T> {
197
209
  save(): Promise<void>;
198
210
  toJSON(): Object[];
199
211
  toObject(): Object[];
200
212
  }
201
213
 
202
- type Query = Spell & Promise<Collection<Bone>>;
203
-
204
214
  export class Bone {
205
215
  /**
206
216
  * The connection pool of the specified client, with few `Leoric_` prefixed methods extended to eliminate client differences.
@@ -218,7 +228,7 @@ export class Bone {
218
228
  static model: { [key: string]: Bone };
219
229
 
220
230
  /**
221
- * The table name of current model, which needs to be specified by the model subclass.
231
+ * The table name of the model, which needs to be specified by the model subclass.
222
232
  */
223
233
  static table: string;
224
234
 
@@ -228,10 +238,15 @@ export class Bone {
228
238
  static tableAlias: string;
229
239
 
230
240
  /**
231
- * The primary key of current model, defaults to `id`.
241
+ * The primary key of the model, defaults to `id`.
232
242
  */
233
243
  static primaryKey: string;
234
244
 
245
+ /**
246
+ * The primary column of the table, defaults to `id`. This is {@link Bone.primaryKey} in snake case.
247
+ */
248
+ static primaryColumn: string;
249
+
235
250
  /**
236
251
  * The attribute definitions of the model.
237
252
  */
@@ -260,7 +275,7 @@ export class Bone {
260
275
  * @param conditions query conditions
261
276
  * @param opts query options
262
277
  */
263
- static restore(conditions: Object, opts?: QueryOptions): Spell & Promise<number>;
278
+ static restore<T extends typeof Bone>(this: T, conditions: Object, opts?: QueryOptions): Spell<T, number>;
264
279
 
265
280
  /**
266
281
  * Override attribute metadata
@@ -288,7 +303,7 @@ export class Bone {
288
303
  * @example
289
304
  * Bone.create({ foo: 1, bar: 'baz' })
290
305
  */
291
- static create(values: Values, options?: QueryOptions): Promise<Bone>;
306
+ static create<T extends typeof Bone>(this: T, values: Values<T>, options?: QueryOptions): Promise<InstanceType<T>>;
292
307
 
293
308
  /**
294
309
  * INSERT or UPDATE rows
@@ -297,12 +312,12 @@ export class Bone {
297
312
  * @param values values
298
313
  * @param opt query options
299
314
  */
300
- static upsert(values: Object, options?: QueryOptions): Spell & Promise<number>;
315
+ static upsert<T extends typeof Bone>(this: T, values: Object, options?: QueryOptions): Spell<T, number>;
301
316
 
302
317
  /**
303
318
  * Batch INSERT
304
319
  */
305
- static bulkCreate(records: Array<Record<string, Literal>>, options?: QueryOptions): Promise<Array<Bone>>;
320
+ static bulkCreate<T extends typeof Bone>(this: T, records: Array<Record<string, Literal>>, options?: QueryOptions): Promise<Array<InstanceType<T>>>;
306
321
 
307
322
  /**
308
323
  * SELECT rows
@@ -310,21 +325,21 @@ export class Bone {
310
325
  * Bone.find('foo = ?', 1)
311
326
  * Bone.find({ foo: { $eq: 1 } })
312
327
  */
313
- static find(whereConditions: string, ...values: Literal[]): Query;
314
- static find(whereConditions: WhereConditions): Query;
315
- static find(): Query;
328
+ static find<T extends typeof Bone>(this: T, whereConditions: WhereConditions<T>): Spell<T, Collection<InstanceType<T>>>;
329
+ static find<T extends typeof Bone>(this: T, whereConditions: string, ...values: Literal[]): Spell<T, Collection<InstanceType<T>>>;
330
+ static find<T extends typeof Bone>(this: T, ): Spell<T, Collection<InstanceType<T>>>;
316
331
 
317
332
  /**
318
333
  * SELECT all rows. In production, when the table is at large, it is not recommended to access records in this way. To iterate over all records, {@link Bone.batch} shall be considered as the better alternative. For tables with soft delete enabled, which means they've got `deletedAt` attribute, use {@link Bone.unscoped} to discard the default scope.
319
334
  */
320
- static all: Query;
335
+ static all: Spell<typeof Bone, Collection<Bone>>;
321
336
 
322
337
  /**
323
338
  * Discard all the applied scopes.
324
339
  * @example
325
340
  * Bone.all.unscoped // includes soft deleted rows
326
341
  */
327
- static unscoped: Query;
342
+ static unscoped: Spell<typeof Bone>;
328
343
 
329
344
  /**
330
345
  * SELECT rows LIMIT 1. Besides limiting the results to one rows, the type of the return value is different from {@link Bone.find} too. If no results were found, {@link Bone.findOne} returns null. If results were found, it returns the found record instead of wrapping them as a collection.
@@ -332,9 +347,9 @@ export class Bone {
332
347
  * Bone.findOne('foo = ?', 1)
333
348
  * Bone.findOne({ foo: { $eq: 1 } })
334
349
  */
335
- static findOne(whereConditions: string, ...values: Literal[]): Spell & Promise<Bone | null>;
336
- static findOne(whereConditions: WhereConditions): Spell & Promise<Bone | null>;
337
- static findOne(): Spell & Promise<Bone | null>;
350
+ static findOne<T extends typeof Bone>(this: T, whereConditions: WhereConditions<T>): Spell<T, InstanceType<T> | null>;
351
+ static findOne<T extends typeof Bone>(this: T, whereConditions: string, ...values: Literal[]): Spell<T, InstanceType<T> | null>;
352
+ static findOne<T extends typeof Bone>(this: T, ): Spell<T, InstanceType<T> | null>;
338
353
 
339
354
  /**
340
355
  * SELECT rows OFFSET index LIMIT 1
@@ -342,24 +357,24 @@ export class Bone {
342
357
  * Bone.get(8)
343
358
  * Bone.find({ foo: { $gt: 1 } }).get(42)
344
359
  */
345
- static get(index: number): Spell & Promise<Bone | null>;
360
+ static get<T extends typeof Bone>(this: T, index: number): Spell<T, InstanceType<T> | null>;
346
361
 
347
362
  /**
348
363
  * SELECT rows ORDER BY id ASC LIMIT 1
349
364
  */
350
- static first: Spell & Promise<Bone | null>;
365
+ static first: Spell<typeof Bone, Bone | null>;
351
366
 
352
367
  /**
353
368
  * SELECT rows ORDER BY id DESC LIMIT 1
354
369
  */
355
- static last: Spell & Promise<Bone | null>;
370
+ static last: Spell<typeof Bone, Bone | null>;
356
371
 
357
372
  /**
358
373
  * Short of `Bone.find().with(...names)`
359
374
  * @example
360
375
  * Post.include('author', 'comments').where('posts.id = ?', 1)
361
376
  */
362
- static include(...names: string[]) : Query;
377
+ static include<T extends typeof Bone>(this: T, ...names: string[]) : Spell<T>;
363
378
 
364
379
  /**
365
380
  * Whitelist SELECT fields by names or filter function
@@ -370,16 +385,16 @@ export class Bone {
370
385
  * Bone.select('MONTH(date), foo + 1')
371
386
  * Bone.select(name => name !== foo)
372
387
  */
373
- static select(...names: string[]): Query;
374
- static select(filter: (name: string) => boolean): Query;
388
+ static select<T extends typeof Bone>(this: T, ...names: string[]): Spell<T>;
389
+ static select<T extends typeof Bone>(this: T, filter: (name: string) => boolean): Spell<T>;
375
390
 
376
391
  /**
377
392
  * JOIN arbitrary models with given ON conditions
378
393
  * @example
379
394
  * Bone.join(Muscle, 'bones.id == muscles.boneId')
380
395
  */
381
- static join(Model: Bone, onConditions: string, ...values: Literal[]): Query;
382
- static join(Model: Bone, onConditions: WhereConditions): Query;
396
+ static join<T extends typeof Bone>(this: T, Model: Bone, onConditions: string, ...values: Literal[]): Spell<T, Collection<InstanceType<T>>>;
397
+ static join<T extends typeof Bone>(this: T, Model: Bone, onConditions: WhereConditions<T>): Spell<T, Collection<InstanceType<T>>>;
383
398
 
384
399
  /**
385
400
  * Set WHERE conditions
@@ -387,8 +402,8 @@ export class Bone {
387
402
  * Bone.where('foo = ?', 1)
388
403
  * Bone.where({ foo: { $eq: 1 } })
389
404
  */
390
- static where(whereConditions: string, ...values: Literal[]): Query;
391
- static where(whereConditions: WhereConditions): Query;
405
+ static where<T extends typeof Bone>(this: T, whereConditions: string, ...values: Literal[]): Spell<T, Collection<InstanceType<T>>>;
406
+ static where<T extends typeof Bone>(this: T, whereConditions: WhereConditions<T>): Spell<T, Collection<InstanceType<T>>>;
392
407
 
393
408
  /**
394
409
  * Set GROUP fields
@@ -396,7 +411,7 @@ export class Bone {
396
411
  * Bone.group('foo')
397
412
  * Bone.group('MONTH(createdAt)')
398
413
  */
399
- static group(...names: string[]): Spell & Promise<ResultSet>;
414
+ static group<T extends typeof Bone>(this: T, ...names: string[]): Spell<T, ResultSet>;
400
415
 
401
416
  /**
402
417
  * Set ORDER fields
@@ -405,24 +420,24 @@ export class Bone {
405
420
  * Bone.order('foo', 'desc')
406
421
  * Bone.order({ foo: 'desc' })
407
422
  */
408
- static order(name: string, order?: 'desc' | 'asc'): Query;
409
- static order(opts: OrderOptions): Query;
423
+ static order<T extends typeof Bone>(this: T, name: string, order?: 'desc' | 'asc'): Spell<T>;
424
+ static order<T extends typeof Bone>(this: T, opts: OrderOptions): Spell<T>;
410
425
 
411
- static count(name?: string): Spell & Promise<ResultSet>;
412
- static average(name?: string): Spell & Promise<ResultSet>;
413
- static minimum(name?: string): Spell & Promise<ResultSet>;
414
- static maximum(name?: string): Spell & Promise<ResultSet>;
415
- static sum(name?: string): Spell & Promise<ResultSet>;
426
+ static count<T extends typeof Bone>(this: T, name?: string): Spell<T, ResultSet | number>;
427
+ static average<T extends typeof Bone>(this: T, name?: string): Spell<T, ResultSet | number>;
428
+ static minimum<T extends typeof Bone>(this: T, name?: string): Spell<T, ResultSet | number>;
429
+ static maximum<T extends typeof Bone>(this: T, name?: string): Spell<T, ResultSet | number>;
430
+ static sum<T extends typeof Bone>(this: T, name?: string): Spell<T, ResultSet | number>;
416
431
 
417
432
  /**
418
433
  * UPDATE rows.
419
434
  */
420
- static update(whereConditions: WhereConditions, values?: Object, opts?: QueryOptions): Spell & Promise<number>;
435
+ static update<T extends typeof Bone>(this: T, whereConditions: WhereConditions<T>, values?: Object, opts?: QueryOptions): Spell<T, number>;
421
436
 
422
437
  /**
423
438
  * Remove rows. If soft delete is applied, an UPDATE query is performed instead of DELETing records directly. Set `forceDelete` to true to force a `DELETE` query.
424
439
  */
425
- static remove(whereConditions: WhereConditions, forceDelete?: boolean, opt?: QueryOptions): Spell & Promise<number>;
440
+ static remove<T extends typeof Bone>(this: T, whereConditions: WhereConditions<T>, forceDelete?: boolean, opt?: QueryOptions): Spell<T, number>;
426
441
 
427
442
  /**
428
443
  * Grabs a connection and starts a transaction process. Both GeneratorFunction and AsyncFunction are acceptable. If GeneratorFunction is used, the connection of the transaction process will be passed around automatically.
@@ -445,7 +460,7 @@ export class Bone {
445
460
  */
446
461
  static truncate(): Promise<void>;
447
462
 
448
- constructor(values: Values);
463
+ constructor(values: { [key: string]: Literal });
449
464
 
450
465
  /**
451
466
  * Get or set attribute value. Getting the value of unset attribute gives an error.
@@ -511,8 +526,8 @@ export class Bone {
511
526
  * bone.remove(true) // => DELETE FROM ... WHERE ...
512
527
  * bone.remove(true, { hooks: false })
513
528
  */
514
- remove(forceDelete?: boolean): Spell & Promise<number>;
515
- remove(forceDelete?: boolean, opts?: QueryOptions): Spell & Promise<number>;
529
+ remove(forceDelete?: boolean): Promise<number>;
530
+ remove(forceDelete?: boolean, opts?: QueryOptions): Promise<number>;
516
531
 
517
532
  /**
518
533
  * update or insert record.
@@ -521,31 +536,31 @@ export class Bone {
521
536
  * bone.upsert({ hooks: false })
522
537
  * @param opts queryOptions
523
538
  */
524
- upsert(opts?: QueryOptions): Spell & Promise<number>;
539
+ upsert(opts?: QueryOptions): Promise<number>;
525
540
 
526
541
  /**
527
542
  * update rows
528
543
  * @param changes data changes
529
544
  * @param opts query options
530
545
  */
531
- update(changes: Object, opts?: QueryOptions): Spell & Promise<number>;
546
+ update(changes: Object, opts?: QueryOptions): Promise<number>;
532
547
 
533
548
  /**
534
549
  * create instance
535
550
  * @param opts query options
536
551
  */
537
- create(opts?: QueryOptions): Spell & Promise<Bone>;
552
+ create(opts?: QueryOptions): Promise<this>;
538
553
 
539
554
  /**
540
555
  * reload instance
541
556
  */
542
- reload(): Promise<Bone>;
557
+ reload(): Promise<this>;
543
558
 
544
559
  /**
545
560
  * restore data
546
561
  * @param opts query options
547
562
  */
548
- restore(opts?: QueryOptions): Promise<Bone>;
563
+ restore(opts?: QueryOptions): Promise<this>;
549
564
 
550
565
  /**
551
566
  * Gets called when `JSON.stringify(instance)` is invoked.
@@ -556,7 +571,7 @@ export class Bone {
556
571
  * post.toJSON() // => { id: 1, ... }
557
572
  * @return {Object}
558
573
  */
559
- toJSON(): Record<string, any>;
574
+ toJSON(): InstanceValues<this>;
560
575
 
561
576
  /**
562
577
  * This is the loyal twin of {@link Bone#toJSON} because when generating the result object, the raw values of attributes are used, instead of the values returned by custom getters (if any).
@@ -567,7 +582,7 @@ export class Bone {
567
582
  * post.toObject() // => { id: 1, ... }
568
583
  * @return {Object}
569
584
  */
570
- toObject(): Record<string, Literal>;
585
+ toObject(): InstanceValues<this>;
571
586
  }
572
587
 
573
588
  interface ConnectOptions {
@@ -576,7 +591,7 @@ interface ConnectOptions {
576
591
  host?: string;
577
592
  user?: string;
578
593
  database: string;
579
- models?: string | Bone[];
594
+ models?: string | (typeof Bone)[];
580
595
  }
581
596
 
582
597
  interface InitOptions {
@@ -596,7 +611,7 @@ type RawSql = {
596
611
  };
597
612
 
598
613
  interface RawQueryOptions {
599
- replacements?: Values;
614
+ replacements?: { [key:string]: Literal | Literal[] };
600
615
  model: Bone;
601
616
  connection: Connection;
602
617
  }