@naturalcycles/db-lib 9.16.0 → 9.17.0

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.
@@ -11,19 +11,19 @@ import { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoPatchB
11
11
  * BM = Backend model (optimized for API access)
12
12
  * TM = Transport model (optimized to be sent over the wire)
13
13
  */
14
- export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
15
- cfg: CommonDaoCfg<BM, DBM>;
16
- constructor(cfg: CommonDaoCfg<BM, DBM>);
14
+ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> {
15
+ cfg: CommonDaoCfg<BM, DBM, ID>;
16
+ constructor(cfg: CommonDaoCfg<BM, DBM, ID>);
17
17
  create(part?: Partial<BM>, opt?: CommonDaoOptions): BM;
18
18
  getById(id: undefined | null, opt?: CommonDaoOptions): Promise<null>;
19
- getById(id?: string | null, opt?: CommonDaoOptions): Promise<BM | null>;
20
- getByIdOrEmpty(id: string, part?: Partial<BM>, opt?: CommonDaoOptions): Promise<BM>;
19
+ getById(id?: ID | null, opt?: CommonDaoOptions): Promise<BM | null>;
20
+ getByIdOrEmpty(id: ID, part?: Partial<BM>, opt?: CommonDaoOptions): Promise<BM>;
21
21
  getByIdAsDBM(id: undefined | null, opt?: CommonDaoOptions): Promise<null>;
22
- getByIdAsDBM(id?: string | null, opt?: CommonDaoOptions): Promise<DBM | null>;
23
- getByIds(ids: string[], opt?: CommonDaoOptions): Promise<BM[]>;
24
- getByIdsAsDBM(ids: string[], opt?: CommonDaoOptions): Promise<DBM[]>;
25
- requireById(id: string, opt?: CommonDaoOptions): Promise<BM>;
26
- requireByIdAsDBM(id: string, opt?: CommonDaoOptions): Promise<DBM>;
22
+ getByIdAsDBM(id?: ID | null, opt?: CommonDaoOptions): Promise<DBM | null>;
23
+ getByIds(ids: ID[], opt?: CommonDaoOptions): Promise<BM[]>;
24
+ getByIdsAsDBM(ids: ID[], opt?: CommonDaoOptions): Promise<DBM[]>;
25
+ requireById(id: ID, opt?: CommonDaoOptions): Promise<BM>;
26
+ requireByIdAsDBM(id: ID, opt?: CommonDaoOptions): Promise<DBM>;
27
27
  private throwRequiredError;
28
28
  /**
29
29
  * Throws if readOnly is true
@@ -40,7 +40,7 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
40
40
  /**
41
41
  * Pass `table` to override table
42
42
  */
43
- query(table?: string): RunnableDBQuery<BM, DBM>;
43
+ query(table?: string): RunnableDBQuery<BM, DBM, ID>;
44
44
  runQuery(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<BM[]>;
45
45
  runQuerySingleColumn<T = any>(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<T[]>;
46
46
  /**
@@ -69,9 +69,9 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
69
69
  * You can do `.pipe(transformNoOp)` to make it "valid again".
70
70
  */
71
71
  streamQuery(q: DBQuery<DBM>, opt?: CommonDaoStreamOptions<BM>): ReadableTyped<BM>;
72
- queryIds(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<string[]>;
73
- streamQueryIds(q: DBQuery<DBM>, opt?: CommonDaoStreamOptions<string>): ReadableTyped<string>;
74
- streamQueryIdsForEach(q: DBQuery<DBM>, mapper: AsyncMapper<string, void>, opt?: CommonDaoStreamForEachOptions<string>): Promise<void>;
72
+ queryIds(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<ID[]>;
73
+ streamQueryIds(q: DBQuery<DBM>, opt?: CommonDaoStreamOptions<ID>): ReadableTyped<ID>;
74
+ streamQueryIdsForEach(q: DBQuery<DBM>, mapper: AsyncMapper<ID, void>, opt?: CommonDaoStreamForEachOptions<ID>): Promise<void>;
75
75
  /**
76
76
  * Mutates!
77
77
  * "Returns", just to have a type of "Saved"
@@ -86,11 +86,11 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
86
86
  * 2. Applies the patch on top of loaded data.
87
87
  * 3. Saves (as fast as possible since the read) with the Patch applied, but only if the data has changed.
88
88
  */
89
- patchById(id: string, patch: Partial<BM>, opt?: CommonDaoPatchByIdOptions<DBM>): Promise<BM>;
89
+ patchById(id: ID, patch: Partial<BM>, opt?: CommonDaoPatchByIdOptions<DBM>): Promise<BM>;
90
90
  /**
91
91
  * Like patchById, but runs all operations within a Transaction.
92
92
  */
93
- patchByIdInTransaction(id: string, patch: Partial<BM>, opt?: CommonDaoPatchByIdOptions<DBM>): Promise<BM>;
93
+ patchByIdInTransaction(id: ID, patch: Partial<BM>, opt?: CommonDaoPatchByIdOptions<DBM>): Promise<BM>;
94
94
  /**
95
95
  * Same as patchById, but takes the whole object as input.
96
96
  * This "whole object" is mutated with the patch and returned.
@@ -119,16 +119,16 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
119
119
  /**
120
120
  * @returns number of deleted items
121
121
  */
122
- deleteById(id?: string | null, opt?: CommonDaoOptions): Promise<number>;
123
- deleteByIds(ids: string[], opt?: CommonDaoOptions): Promise<number>;
122
+ deleteById(id?: ID | null, opt?: CommonDaoOptions): Promise<number>;
123
+ deleteByIds(ids: ID[], opt?: CommonDaoOptions): Promise<number>;
124
124
  /**
125
125
  * Pass `chunkSize: number` (e.g 500) option to use Streaming: it will Stream the query, chunk by 500, and execute
126
126
  * `deleteByIds` for each chunk concurrently (infinite concurrency).
127
127
  * This is expected to be more memory-efficient way of deleting large number of rows.
128
128
  */
129
129
  deleteByQuery(q: DBQuery<DBM>, opt?: CommonDaoStreamDeleteOptions<DBM>): Promise<number>;
130
- updateById(id: string, patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
131
- updateByIds(ids: string[], patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
130
+ updateById(id: ID, patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
131
+ updateByIds(ids: ID[], patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
132
132
  updateByQuery(q: DBQuery<DBM>, patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
133
133
  dbmToBM(_dbm: undefined, opt?: CommonDaoOptions): Promise<undefined>;
134
134
  dbmToBM(_dbm?: DBM, opt?: CommonDaoOptions): Promise<BM>;
@@ -184,8 +184,8 @@ export declare class CommonDaoTransaction {
184
184
  * Perform a graceful rollback without throwing/re-throwing any error.
185
185
  */
186
186
  rollback(): Promise<void>;
187
- getById<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, id?: string | null, opt?: CommonDaoOptions): Promise<BM | null>;
188
- getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, ids: string[], opt?: CommonDaoOptions): Promise<BM[]>;
187
+ getById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, id?: ID | null, opt?: CommonDaoOptions): Promise<BM | null>;
188
+ getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, ids: ID[], opt?: CommonDaoOptions): Promise<BM[]>;
189
189
  save<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>;
190
190
  saveBatch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bms: Unsaved<BM>[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM[]>;
191
191
  /**
@@ -195,7 +195,7 @@ export declare class CommonDaoTransaction {
195
195
  *
196
196
  * So, this method is a rather simple convenience "Object.assign and then save".
197
197
  */
198
- patch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bm: BM, patch: Partial<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>;
199
- deleteById(dao: CommonDao<any>, id?: string | null, opt?: CommonDaoOptions): Promise<number>;
200
- deleteByIds(dao: CommonDao<any>, ids: string[], opt?: CommonDaoOptions): Promise<number>;
198
+ patch<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, bm: BM, patch: Partial<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>;
199
+ deleteById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, id?: ID | null, opt?: CommonDaoOptions): Promise<number>;
200
+ deleteByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, ids: ID[], opt?: CommonDaoOptions): Promise<number>;
201
201
  }
@@ -465,7 +465,8 @@ class CommonDao {
465
465
  obj.updated = opt.preserveUpdatedCreated && obj.updated ? obj.updated : now;
466
466
  }
467
467
  if (this.cfg.generateId) {
468
- obj.id ||= this.cfg.hooks.createNaturalId?.(obj) || this.cfg.hooks.createRandomId();
468
+ obj.id ||= (this.cfg.hooks.createNaturalId?.(obj) ||
469
+ this.cfg.hooks.createRandomId());
469
470
  }
470
471
  return obj;
471
472
  }
@@ -2,24 +2,24 @@ import { BaseDBEntity, CommonLogger, ErrorMode, Promisable, ZodError, ZodSchema
2
2
  import { AjvSchema, AjvValidationError, JoiValidationError, ObjectSchema, TransformLogProgressOptions, TransformMapOptions } from '@naturalcycles/nodejs-lib';
3
3
  import { CommonDB } from '../common.db';
4
4
  import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../db.model';
5
- export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity> {
5
+ export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']> {
6
6
  /**
7
7
  * Allows to override the id generation function.
8
8
  * By default it uses `stringId` from nodejs-lib
9
9
  * (which uses lowercase alphanumberic alphabet and the size of 16).
10
10
  */
11
- createRandomId: () => string;
11
+ createRandomId: () => ID;
12
12
  /**
13
13
  * createNaturalId hook is called (tried) first.
14
14
  * If it doesn't exist - createRandomId is called.
15
15
  */
16
- createNaturalId: (obj: DBM | BM) => string;
16
+ createNaturalId: (obj: DBM | BM) => ID;
17
17
  /**
18
18
  * It's a counter-part of `createNaturalId`.
19
19
  * Allows to provide a parser function to parse "natural id" into
20
20
  * DBM components (e.g accountId and some other property that is part of the id).
21
21
  */
22
- parseNaturalId: (id: string) => Partial<DBM>;
22
+ parseNaturalId: (id: ID) => Partial<DBM>;
23
23
  /**
24
24
  * It is called only on `dao.create` method.
25
25
  * Dao.create method is called in:
@@ -93,7 +93,7 @@ export declare enum CommonDaoLogLevel {
93
93
  */
94
94
  DATA_FULL = 30
95
95
  }
96
- export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
96
+ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> {
97
97
  db: CommonDB;
98
98
  table: string;
99
99
  /**
@@ -138,7 +138,7 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
138
138
  * @default false
139
139
  */
140
140
  logStarted?: boolean;
141
- hooks?: Partial<CommonDaoHooks<BM, DBM>>;
141
+ hooks?: Partial<CommonDaoHooks<BM, DBM, ID>>;
142
142
  /**
143
143
  * Defaults to true.
144
144
  * Set to false to disable auto-generation of `id`.
@@ -87,12 +87,12 @@ export declare class DBQuery<ROW extends ObjectWithId> {
87
87
  /**
88
88
  * DBQuery that has additional method to support Fluent API style.
89
89
  */
90
- export declare class RunnableDBQuery<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> extends DBQuery<DBM> {
91
- dao: CommonDao<BM, DBM>;
90
+ export declare class RunnableDBQuery<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> extends DBQuery<DBM> {
91
+ dao: CommonDao<BM, DBM, ID>;
92
92
  /**
93
93
  * Pass `table` to override table.
94
94
  */
95
- constructor(dao: CommonDao<BM, DBM>, table?: string);
95
+ constructor(dao: CommonDao<BM, DBM, ID>, table?: string);
96
96
  runQuery(opt?: CommonDaoOptions): Promise<BM[]>;
97
97
  runQuerySingleColumn<T = any>(opt?: CommonDaoOptions): Promise<T[]>;
98
98
  runQueryAsDBM(opt?: CommonDaoOptions): Promise<DBM[]>;
@@ -104,8 +104,8 @@ export declare class RunnableDBQuery<BM extends BaseDBEntity, DBM extends BaseDB
104
104
  streamQueryAsDBMForEach(mapper: AsyncMapper<DBM, void>, opt?: CommonDaoStreamForEachOptions<DBM>): Promise<void>;
105
105
  streamQuery(opt?: CommonDaoStreamOptions<BM>): ReadableTyped<BM>;
106
106
  streamQueryAsDBM(opt?: CommonDaoStreamOptions<DBM>): ReadableTyped<DBM>;
107
- queryIds(opt?: CommonDaoOptions): Promise<string[]>;
108
- streamQueryIds(opt?: CommonDaoStreamOptions<string>): ReadableTyped<string>;
109
- streamQueryIdsForEach(mapper: AsyncMapper<string, void>, opt?: CommonDaoStreamForEachOptions<string>): Promise<void>;
107
+ queryIds(opt?: CommonDaoOptions): Promise<ID[]>;
108
+ streamQueryIds(opt?: CommonDaoStreamOptions<ID>): ReadableTyped<ID>;
109
+ streamQueryIdsForEach(mapper: AsyncMapper<ID, void>, opt?: CommonDaoStreamForEachOptions<ID>): Promise<void>;
110
110
  deleteByQuery(opt?: CommonDaoStreamDeleteOptions<DBM>): Promise<number>;
111
111
  }
@@ -24,5 +24,5 @@ export declare class FakeDBTransaction implements DBTransaction {
24
24
  rollback(): Promise<void>;
25
25
  getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?: CommonDBOptions): Promise<ROW[]>;
26
26
  saveBatch<ROW extends ObjectWithId>(table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
27
- deleteByIds(table: string, ids: string[], opt?: CommonDBOptions | undefined): Promise<number>;
27
+ deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number>;
28
28
  }
package/package.json CHANGED
@@ -13,7 +13,6 @@
13
13
  "@naturalcycles/nodejs-lib": "^13.1.1"
14
14
  },
15
15
  "devDependencies": {
16
- "@biomejs/biome": "^1.8.3",
17
16
  "@naturalcycles/bench-lib": "^3.0.0",
18
17
  "@naturalcycles/dev-lib": "^15.4.1",
19
18
  "@types/node": "^22.1.0",
@@ -46,7 +45,7 @@
46
45
  "engines": {
47
46
  "node": ">=20.13"
48
47
  },
49
- "version": "9.16.0",
48
+ "version": "9.17.0",
50
49
  "description": "Lowest Common Denominator API to supported Databases",
51
50
  "keywords": [
52
51
  "db",
@@ -17,26 +17,26 @@ import {
17
17
  import { CommonDB } from '../common.db'
18
18
  import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../db.model'
19
19
 
20
- export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity> {
20
+ export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']> {
21
21
  /**
22
22
  * Allows to override the id generation function.
23
23
  * By default it uses `stringId` from nodejs-lib
24
24
  * (which uses lowercase alphanumberic alphabet and the size of 16).
25
25
  */
26
- createRandomId: () => string
26
+ createRandomId: () => ID
27
27
 
28
28
  /**
29
29
  * createNaturalId hook is called (tried) first.
30
30
  * If it doesn't exist - createRandomId is called.
31
31
  */
32
- createNaturalId: (obj: DBM | BM) => string
32
+ createNaturalId: (obj: DBM | BM) => ID
33
33
 
34
34
  /**
35
35
  * It's a counter-part of `createNaturalId`.
36
36
  * Allows to provide a parser function to parse "natural id" into
37
37
  * DBM components (e.g accountId and some other property that is part of the id).
38
38
  */
39
- parseNaturalId: (id: string) => Partial<DBM>
39
+ parseNaturalId: (id: ID) => Partial<DBM>
40
40
 
41
41
  /**
42
42
  * It is called only on `dao.create` method.
@@ -118,7 +118,11 @@ export enum CommonDaoLogLevel {
118
118
  DATA_FULL = 30,
119
119
  }
120
120
 
121
- export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
121
+ export interface CommonDaoCfg<
122
+ BM extends BaseDBEntity,
123
+ DBM extends BaseDBEntity = BM,
124
+ ID = BM['id'],
125
+ > {
122
126
  db: CommonDB
123
127
  table: string
124
128
 
@@ -174,7 +178,7 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
174
178
  logStarted?: boolean
175
179
 
176
180
  // Hooks are designed with inspiration from got/ky interface
177
- hooks?: Partial<CommonDaoHooks<BM, DBM>>
181
+ hooks?: Partial<CommonDaoHooks<BM, DBM, ID>>
178
182
 
179
183
  /**
180
184
  * Defaults to true.
@@ -72,8 +72,8 @@ const isCI = !!process.env['CI']
72
72
  * BM = Backend model (optimized for API access)
73
73
  * TM = Transport model (optimized to be sent over the wire)
74
74
  */
75
- export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
76
- constructor(public cfg: CommonDaoCfg<BM, DBM>) {
75
+ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> {
76
+ constructor(public cfg: CommonDaoCfg<BM, DBM, ID>) {
77
77
  this.cfg = {
78
78
  // Default is to NOT log in AppEngine and in CI,
79
79
  // otherwise to log Operations
@@ -93,11 +93,11 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
93
93
  anonymize: dbm => dbm,
94
94
  onValidationError: err => err,
95
95
  ...cfg.hooks,
96
- } satisfies Partial<CommonDaoHooks<BM, DBM>>,
96
+ } satisfies Partial<CommonDaoHooks<BM, DBM, ID>>,
97
97
  }
98
98
 
99
99
  if (this.cfg.generateId) {
100
- this.cfg.hooks!.createRandomId ||= () => stringId()
100
+ this.cfg.hooks!.createRandomId ||= () => stringId() as ID
101
101
  } else {
102
102
  delete this.cfg.hooks!.createRandomId
103
103
  }
@@ -113,14 +113,14 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
113
113
 
114
114
  // GET
115
115
  async getById(id: undefined | null, opt?: CommonDaoOptions): Promise<null>
116
- async getById(id?: string | null, opt?: CommonDaoOptions): Promise<BM | null>
117
- async getById(id?: string | null, opt: CommonDaoOptions = {}): Promise<BM | null> {
116
+ async getById(id?: ID | null, opt?: CommonDaoOptions): Promise<BM | null>
117
+ async getById(id?: ID | null, opt: CommonDaoOptions = {}): Promise<BM | null> {
118
118
  if (!id) return null
119
119
  const op = `getById(${id})`
120
120
  const table = opt.table || this.cfg.table
121
121
  const started = this.logStarted(op, table)
122
122
 
123
- let dbm = (await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id]))[0]
123
+ let dbm = (await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id as string]))[0]
124
124
  if (dbm && this.cfg.hooks!.afterLoad) {
125
125
  dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
126
126
  }
@@ -130,7 +130,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
130
130
  return bm || null
131
131
  }
132
132
 
133
- async getByIdOrEmpty(id: string, part: Partial<BM> = {}, opt?: CommonDaoOptions): Promise<BM> {
133
+ async getByIdOrEmpty(id: ID, part: Partial<BM> = {}, opt?: CommonDaoOptions): Promise<BM> {
134
134
  const bm = await this.getById(id, opt)
135
135
  if (bm) return bm
136
136
 
@@ -138,13 +138,13 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
138
138
  }
139
139
 
140
140
  async getByIdAsDBM(id: undefined | null, opt?: CommonDaoOptions): Promise<null>
141
- async getByIdAsDBM(id?: string | null, opt?: CommonDaoOptions): Promise<DBM | null>
142
- async getByIdAsDBM(id?: string | null, opt: CommonDaoOptions = {}): Promise<DBM | null> {
141
+ async getByIdAsDBM(id?: ID | null, opt?: CommonDaoOptions): Promise<DBM | null>
142
+ async getByIdAsDBM(id?: ID | null, opt: CommonDaoOptions = {}): Promise<DBM | null> {
143
143
  if (!id) return null
144
144
  const op = `getByIdAsDBM(${id})`
145
145
  const table = opt.table || this.cfg.table
146
146
  const started = this.logStarted(op, table)
147
- let [dbm] = await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id])
147
+ let [dbm] = await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id as string])
148
148
  if (dbm && this.cfg.hooks!.afterLoad) {
149
149
  dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
150
150
  }
@@ -154,12 +154,12 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
154
154
  return dbm || null
155
155
  }
156
156
 
157
- async getByIds(ids: string[], opt: CommonDaoOptions = {}): Promise<BM[]> {
157
+ async getByIds(ids: ID[], opt: CommonDaoOptions = {}): Promise<BM[]> {
158
158
  if (!ids.length) return []
159
159
  const op = `getByIds ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
160
160
  const table = opt.table || this.cfg.table
161
161
  const started = this.logStarted(op, table)
162
- let dbms = await (opt.tx || this.cfg.db).getByIds<DBM>(table, ids)
162
+ let dbms = await (opt.tx || this.cfg.db).getByIds<DBM>(table, ids as string[])
163
163
  if (this.cfg.hooks!.afterLoad && dbms.length) {
164
164
  dbms = (await pMap(dbms, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
165
165
  _isTruthy,
@@ -171,12 +171,12 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
171
171
  return bms
172
172
  }
173
173
 
174
- async getByIdsAsDBM(ids: string[], opt: CommonDaoOptions = {}): Promise<DBM[]> {
174
+ async getByIdsAsDBM(ids: ID[], opt: CommonDaoOptions = {}): Promise<DBM[]> {
175
175
  if (!ids.length) return []
176
176
  const op = `getByIdsAsDBM ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
177
177
  const table = opt.table || this.cfg.table
178
178
  const started = this.logStarted(op, table)
179
- let dbms = await (opt.tx || this.cfg.db).getByIds<DBM>(table, ids)
179
+ let dbms = await (opt.tx || this.cfg.db).getByIds<DBM>(table, ids as string[])
180
180
  if (this.cfg.hooks!.afterLoad && dbms.length) {
181
181
  dbms = (await pMap(dbms, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
182
182
  _isTruthy,
@@ -187,7 +187,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
187
187
  return dbms
188
188
  }
189
189
 
190
- async requireById(id: string, opt: CommonDaoOptions = {}): Promise<BM> {
190
+ async requireById(id: ID, opt: CommonDaoOptions = {}): Promise<BM> {
191
191
  const r = await this.getById(id, opt)
192
192
  if (!r) {
193
193
  this.throwRequiredError(id, opt)
@@ -195,7 +195,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
195
195
  return r
196
196
  }
197
197
 
198
- async requireByIdAsDBM(id: string, opt: CommonDaoOptions = {}): Promise<DBM> {
198
+ async requireByIdAsDBM(id: ID, opt: CommonDaoOptions = {}): Promise<DBM> {
199
199
  const r = await this.getByIdAsDBM(id, opt)
200
200
  if (!r) {
201
201
  this.throwRequiredError(id, opt)
@@ -203,7 +203,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
203
203
  return r
204
204
  }
205
205
 
206
- private throwRequiredError(id: string, opt: CommonDaoOptions): never {
206
+ private throwRequiredError(id: ID, opt: CommonDaoOptions): never {
207
207
  const table = opt.table || this.cfg.table
208
208
  throw new AppError(`DB row required, but not found in ${table}`, {
209
209
  table,
@@ -261,8 +261,8 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
261
261
  /**
262
262
  * Pass `table` to override table
263
263
  */
264
- query(table?: string): RunnableDBQuery<BM, DBM> {
265
- return new RunnableDBQuery<BM, DBM>(this, table)
264
+ query(table?: string): RunnableDBQuery<BM, DBM, ID> {
265
+ return new RunnableDBQuery<BM, DBM, ID>(this, table)
266
266
  }
267
267
 
268
268
  async runQuery(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<BM[]> {
@@ -546,23 +546,23 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
546
546
  )
547
547
  }
548
548
 
549
- async queryIds(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<string[]> {
549
+ async queryIds(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<ID[]> {
550
550
  this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
551
551
  q.table = opt.table || q.table
552
552
  const { rows } = await this.cfg.db.runQuery(q.select(['id']), opt)
553
- return rows.map(r => r.id)
553
+ return rows.map(r => r.id as ID)
554
554
  }
555
555
 
556
- streamQueryIds(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<string> = {}): ReadableTyped<string> {
556
+ streamQueryIds(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<ID> = {}): ReadableTyped<ID> {
557
557
  this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
558
558
  q.table = opt.table || q.table
559
559
  opt.errorMode ||= ErrorMode.SUPPRESS
560
560
 
561
561
  // Experimental: using `.map()`
562
- const stream: ReadableTyped<string> = this.cfg.db
562
+ const stream: ReadableTyped<ID> = this.cfg.db
563
563
  .streamQuery<DBM>(q.select(['id']), opt)
564
564
  // .on('error', err => stream.emit('error', err))
565
- .map((r: ObjectWithId) => r.id)
565
+ .map((r: ObjectWithId) => r.id as ID)
566
566
 
567
567
  // const stream: ReadableTyped<string> = this.cfg.db
568
568
  // .streamQuery<DBM>(q.select(['id']), opt)
@@ -578,8 +578,8 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
578
578
 
579
579
  async streamQueryIdsForEach(
580
580
  q: DBQuery<DBM>,
581
- mapper: AsyncMapper<string, void>,
582
- opt: CommonDaoStreamForEachOptions<string> = {},
581
+ mapper: AsyncMapper<ID, void>,
582
+ opt: CommonDaoStreamForEachOptions<ID> = {},
583
583
  ): Promise<void> {
584
584
  this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
585
585
  q.table = opt.table || q.table
@@ -594,7 +594,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
594
594
  count++
595
595
  return r.id
596
596
  }),
597
- transformMap<string, void>(mapper, {
597
+ transformMap<ID, void>(mapper, {
598
598
  ...opt,
599
599
  predicate: _passthroughPredicate,
600
600
  }),
@@ -627,7 +627,8 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
627
627
  }
628
628
 
629
629
  if (this.cfg.generateId) {
630
- obj.id ||= this.cfg.hooks!.createNaturalId?.(obj as any) || this.cfg.hooks!.createRandomId!()
630
+ obj.id ||= (this.cfg.hooks!.createNaturalId?.(obj as any) ||
631
+ this.cfg.hooks!.createRandomId!()) as T['id']
631
632
  }
632
633
 
633
634
  return obj as T
@@ -644,7 +645,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
644
645
  * 3. Saves (as fast as possible since the read) with the Patch applied, but only if the data has changed.
645
646
  */
646
647
  async patchById(
647
- id: string,
648
+ id: ID,
648
649
  patch: Partial<BM>,
649
650
  opt: CommonDaoPatchByIdOptions<DBM> = {},
650
651
  ): Promise<BM> {
@@ -681,7 +682,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
681
682
  * Like patchById, but runs all operations within a Transaction.
682
683
  */
683
684
  async patchByIdInTransaction(
684
- id: string,
685
+ id: ID,
685
686
  patch: Partial<BM>,
686
687
  opt?: CommonDaoPatchByIdOptions<DBM>,
687
688
  ): Promise<BM> {
@@ -716,7 +717,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
716
717
  }
717
718
  Object.assign(bm, patch)
718
719
  } else {
719
- const loaded = await this.getById(bm.id, opt)
720
+ const loaded = await this.getById(bm.id as ID, opt)
720
721
 
721
722
  if (loaded) {
722
723
  const loadedWithPatch: BM = {
@@ -999,19 +1000,19 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
999
1000
  /**
1000
1001
  * @returns number of deleted items
1001
1002
  */
1002
- async deleteById(id?: string | null, opt: CommonDaoOptions = {}): Promise<number> {
1003
+ async deleteById(id?: ID | null, opt: CommonDaoOptions = {}): Promise<number> {
1003
1004
  if (!id) return 0
1004
1005
  return await this.deleteByIds([id], opt)
1005
1006
  }
1006
1007
 
1007
- async deleteByIds(ids: string[], opt: CommonDaoOptions = {}): Promise<number> {
1008
+ async deleteByIds(ids: ID[], opt: CommonDaoOptions = {}): Promise<number> {
1008
1009
  if (!ids.length) return 0
1009
1010
  this.requireWriteAccess()
1010
1011
  this.requireObjectMutability(opt)
1011
1012
  const op = `deleteByIds(${ids.join(', ')})`
1012
1013
  const table = opt.table || this.cfg.table
1013
1014
  const started = this.logStarted(op, table)
1014
- const count = await (opt.tx || this.cfg.db).deleteByIds(table, ids, opt)
1015
+ const count = await (opt.tx || this.cfg.db).deleteByIds(table, ids as string[], opt)
1015
1016
  this.logSaveResult(started, op, table)
1016
1017
  return count
1017
1018
  }
@@ -1066,15 +1067,11 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
1066
1067
  return deleted
1067
1068
  }
1068
1069
 
1069
- async updateById(id: string, patch: DBPatch<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
1070
+ async updateById(id: ID, patch: DBPatch<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
1070
1071
  return await this.updateByQuery(this.query().filterEq('id', id), patch, opt)
1071
1072
  }
1072
1073
 
1073
- async updateByIds(
1074
- ids: string[],
1075
- patch: DBPatch<DBM>,
1076
- opt: CommonDaoOptions = {},
1077
- ): Promise<number> {
1074
+ async updateByIds(ids: ID[], patch: DBPatch<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
1078
1075
  if (!ids.length) return 0
1079
1076
  return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt)
1080
1077
  }
@@ -1104,7 +1101,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
1104
1101
 
1105
1102
  // optimization: no need to run full joi DBM validation, cause BM validation will be run
1106
1103
  // const dbm = this.anyToDBM(_dbm, opt)
1107
- let dbm: DBM = { ..._dbm, ...this.cfg.hooks!.parseNaturalId!(_dbm.id) }
1104
+ let dbm: DBM = { ..._dbm, ...this.cfg.hooks!.parseNaturalId!(_dbm.id as ID) }
1108
1105
 
1109
1106
  if (opt.anonymize) {
1110
1107
  dbm = this.cfg.hooks!.anonymize!(dbm)
@@ -1150,7 +1147,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
1150
1147
  // this shouldn't be happening on load! but should on save!
1151
1148
  // this.assignIdCreatedUpdated(dbm, opt)
1152
1149
 
1153
- dbm = { ...dbm, ...this.cfg.hooks!.parseNaturalId!(dbm.id) }
1150
+ dbm = { ...dbm, ...this.cfg.hooks!.parseNaturalId!(dbm.id as ID) }
1154
1151
 
1155
1152
  // todo: is this the right place?
1156
1153
  // todo: is anyToDBM even needed?
@@ -1375,17 +1372,17 @@ export class CommonDaoTransaction {
1375
1372
  }
1376
1373
  }
1377
1374
 
1378
- async getById<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
1379
- dao: CommonDao<BM, DBM>,
1380
- id?: string | null,
1375
+ async getById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
1376
+ dao: CommonDao<BM, DBM, ID>,
1377
+ id?: ID | null,
1381
1378
  opt?: CommonDaoOptions,
1382
1379
  ): Promise<BM | null> {
1383
1380
  return await dao.getById(id, { ...opt, tx: this.tx })
1384
1381
  }
1385
1382
 
1386
- async getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
1387
- dao: CommonDao<BM, DBM>,
1388
- ids: string[],
1383
+ async getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
1384
+ dao: CommonDao<BM, DBM, ID>,
1385
+ ids: ID[],
1389
1386
  opt?: CommonDaoOptions,
1390
1387
  ): Promise<BM[]> {
1391
1388
  return await dao.getByIds(ids, { ...opt, tx: this.tx })
@@ -1428,8 +1425,8 @@ export class CommonDaoTransaction {
1428
1425
  *
1429
1426
  * So, this method is a rather simple convenience "Object.assign and then save".
1430
1427
  */
1431
- async patch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
1432
- dao: CommonDao<BM, DBM>,
1428
+ async patch<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
1429
+ dao: CommonDao<BM, DBM, ID>,
1433
1430
  bm: BM,
1434
1431
  patch: Partial<BM>,
1435
1432
  opt?: CommonDaoSaveOptions<BM, DBM>,
@@ -1439,16 +1436,20 @@ export class CommonDaoTransaction {
1439
1436
  return await dao.save(bm, { ...opt, skipIfEquals, tx: this.tx })
1440
1437
  }
1441
1438
 
1442
- async deleteById(
1443
- dao: CommonDao<any>,
1444
- id?: string | null,
1439
+ async deleteById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
1440
+ dao: CommonDao<BM, DBM, ID>,
1441
+ id?: ID | null,
1445
1442
  opt?: CommonDaoOptions,
1446
1443
  ): Promise<number> {
1447
1444
  if (!id) return 0
1448
1445
  return await this.deleteByIds(dao, [id], opt)
1449
1446
  }
1450
1447
 
1451
- async deleteByIds(dao: CommonDao<any>, ids: string[], opt?: CommonDaoOptions): Promise<number> {
1448
+ async deleteByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
1449
+ dao: CommonDao<BM, DBM, ID>,
1450
+ ids: ID[],
1451
+ opt?: CommonDaoOptions,
1452
+ ): Promise<number> {
1452
1453
  return await dao.deleteByIds(ids, { ...opt, tx: this.tx })
1453
1454
  }
1454
1455
  }
@@ -193,7 +193,7 @@ export class DBQuery<ROW extends ObjectWithId> {
193
193
  }
194
194
 
195
195
  prettyConditions(): string[] {
196
- const tokens = []
196
+ const tokens: string[] = []
197
197
 
198
198
  // if (this.name) {
199
199
  // tokens.push(`"${this.name}"`)
@@ -240,12 +240,13 @@ export class DBQuery<ROW extends ObjectWithId> {
240
240
  export class RunnableDBQuery<
241
241
  BM extends BaseDBEntity,
242
242
  DBM extends BaseDBEntity = BM,
243
+ ID = BM['id'],
243
244
  > extends DBQuery<DBM> {
244
245
  /**
245
246
  * Pass `table` to override table.
246
247
  */
247
248
  constructor(
248
- public dao: CommonDao<BM, DBM>,
249
+ public dao: CommonDao<BM, DBM, ID>,
249
250
  table?: string,
250
251
  ) {
251
252
  super(table || dao.cfg.table)
@@ -301,17 +302,17 @@ export class RunnableDBQuery<
301
302
  return this.dao.streamQueryAsDBM(this, opt)
302
303
  }
303
304
 
304
- async queryIds(opt?: CommonDaoOptions): Promise<string[]> {
305
+ async queryIds(opt?: CommonDaoOptions): Promise<ID[]> {
305
306
  return await this.dao.queryIds(this, opt)
306
307
  }
307
308
 
308
- streamQueryIds(opt?: CommonDaoStreamOptions<string>): ReadableTyped<string> {
309
+ streamQueryIds(opt?: CommonDaoStreamOptions<ID>): ReadableTyped<ID> {
309
310
  return this.dao.streamQueryIds(this, opt)
310
311
  }
311
312
 
312
313
  async streamQueryIdsForEach(
313
- mapper: AsyncMapper<string, void>,
314
- opt?: CommonDaoStreamForEachOptions<string>,
314
+ mapper: AsyncMapper<ID, void>,
315
+ opt?: CommonDaoStreamForEachOptions<ID>,
315
316
  ): Promise<void> {
316
317
  await this.dao.streamQueryIdsForEach(this, mapper, opt)
317
318
  }
@@ -120,11 +120,7 @@ export class FakeDBTransaction implements DBTransaction {
120
120
  ): Promise<void> {
121
121
  await this.db.saveBatch(table, rows, opt)
122
122
  }
123
- async deleteByIds(
124
- table: string,
125
- ids: string[],
126
- opt?: CommonDBOptions | undefined,
127
- ): Promise<number> {
123
+ async deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number> {
128
124
  return await this.db.deleteByIds(table, ids, opt)
129
125
  }
130
126
  }