@naturalcycles/db-lib 9.2.0 → 9.3.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.
Files changed (36) hide show
  1. package/dist/adapter/cachedb/cache.db.d.ts +11 -12
  2. package/dist/adapter/cachedb/cache.db.model.d.ts +2 -2
  3. package/dist/adapter/file/file.db.d.ts +12 -12
  4. package/dist/adapter/file/file.db.model.d.ts +2 -2
  5. package/dist/adapter/file/localFile.persistence.plugin.d.ts +3 -3
  6. package/dist/adapter/file/noop.persistence.plugin.d.ts +2 -2
  7. package/dist/adapter/inmemory/inMemory.db.d.ts +12 -12
  8. package/dist/adapter/inmemory/queryInMemory.d.ts +2 -2
  9. package/dist/base.common.db.d.ts +10 -10
  10. package/dist/common.db.d.ts +10 -10
  11. package/dist/commondao/common.dao.d.ts +26 -27
  12. package/dist/commondao/common.dao.js +12 -12
  13. package/dist/commondao/common.dao.model.d.ts +13 -13
  14. package/dist/db.model.d.ts +4 -4
  15. package/dist/query/dbQuery.d.ts +9 -9
  16. package/dist/testing/test.model.d.ts +3 -4
  17. package/dist/testing/test.model.js +1 -1
  18. package/dist/transaction/dbTransaction.util.d.ts +3 -3
  19. package/dist/validation/index.d.ts +2 -2
  20. package/package.json +1 -1
  21. package/src/adapter/cachedb/cache.db.model.ts +2 -2
  22. package/src/adapter/cachedb/cache.db.ts +18 -16
  23. package/src/adapter/file/file.db.model.ts +2 -2
  24. package/src/adapter/file/file.db.ts +17 -15
  25. package/src/adapter/file/localFile.persistence.plugin.ts +4 -4
  26. package/src/adapter/file/noop.persistence.plugin.ts +2 -2
  27. package/src/adapter/inmemory/inMemory.db.ts +20 -18
  28. package/src/adapter/inmemory/queryInMemory.ts +5 -2
  29. package/src/base.common.db.ts +20 -10
  30. package/src/common.db.ts +20 -13
  31. package/src/commondao/common.dao.model.ts +21 -15
  32. package/src/commondao/common.dao.ts +80 -69
  33. package/src/db.model.ts +4 -4
  34. package/src/query/dbQuery.ts +13 -11
  35. package/src/testing/test.model.ts +4 -6
  36. package/src/transaction/dbTransaction.util.ts +3 -3
package/src/common.db.ts CHANGED
@@ -1,4 +1,9 @@
1
- import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib'
1
+ import {
2
+ JsonSchemaObject,
3
+ JsonSchemaRootObject,
4
+ PartialObjectWithId,
5
+ Saved,
6
+ } from '@naturalcycles/js-lib'
2
7
  import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
3
8
  import {
4
9
  CommonDBCreateOptions,
@@ -64,13 +69,15 @@ export interface CommonDB {
64
69
  *
65
70
  * This is important for the code to rely on it, and it's verified by dbTest
66
71
  */
67
- getTableSchema: <ROW extends ObjectWithId>(table: string) => Promise<JsonSchemaRootObject<ROW>>
72
+ getTableSchema: <ROW extends PartialObjectWithId>(
73
+ table: string,
74
+ ) => Promise<JsonSchemaRootObject<ROW>>
68
75
 
69
76
  /**
70
77
  * Will do like `create table ...` for mysql.
71
78
  * Caution! dropIfExists defaults to false. If set to true - will actually DROP the table!
72
79
  */
73
- createTable: <ROW extends ObjectWithId>(
80
+ createTable: <ROW extends PartialObjectWithId>(
74
81
  table: string,
75
82
  schema: JsonSchemaObject<ROW>,
76
83
  opt?: CommonDBCreateOptions,
@@ -81,36 +88,36 @@ export interface CommonDB {
81
88
  * Order of items returned is not guaranteed to match order of ids.
82
89
  * (Such limitation exists because Datastore doesn't support it).
83
90
  */
84
- getByIds: <ROW extends ObjectWithId>(
91
+ getByIds: <ROW extends PartialObjectWithId>(
85
92
  table: string,
86
93
  ids: string[],
87
94
  opt?: CommonDBOptions,
88
- ) => Promise<ROW[]>
95
+ ) => Promise<Saved<ROW>[]>
89
96
 
90
97
  // QUERY
91
98
  /**
92
99
  * Order by 'id' is not supported by all implementations (for example, Datastore doesn't support it).
93
100
  */
94
- runQuery: <ROW extends ObjectWithId>(
101
+ runQuery: <ROW extends PartialObjectWithId>(
95
102
  q: DBQuery<ROW>,
96
103
  opt?: CommonDBOptions,
97
- ) => Promise<RunQueryResult<ROW>>
104
+ ) => Promise<RunQueryResult<Saved<ROW>>>
98
105
 
99
- runQueryCount: <ROW extends ObjectWithId>(
106
+ runQueryCount: <ROW extends PartialObjectWithId>(
100
107
  q: DBQuery<ROW>,
101
108
  opt?: CommonDBOptions,
102
109
  ) => Promise<number>
103
110
 
104
- streamQuery: <ROW extends ObjectWithId>(
111
+ streamQuery: <ROW extends PartialObjectWithId>(
105
112
  q: DBQuery<ROW>,
106
113
  opt?: CommonDBStreamOptions,
107
- ) => ReadableTyped<ROW>
114
+ ) => ReadableTyped<Saved<ROW>>
108
115
 
109
116
  // SAVE
110
117
  /**
111
118
  * rows can have missing ids only if DB supports auto-generating them (like mysql auto_increment).
112
119
  */
113
- saveBatch: <ROW extends Partial<ObjectWithId>>(
120
+ saveBatch: <ROW extends PartialObjectWithId>(
114
121
  table: string,
115
122
  rows: ROW[],
116
123
  opt?: CommonDBSaveOptions<ROW>,
@@ -127,7 +134,7 @@ export interface CommonDB {
127
134
  * Returns number of deleted items.
128
135
  * Not supported by all implementations (e.g Datastore will always return same number as number of ids).
129
136
  */
130
- deleteByQuery: <ROW extends ObjectWithId>(
137
+ deleteByQuery: <ROW extends PartialObjectWithId>(
131
138
  q: DBQuery<ROW>,
132
139
  opt?: CommonDBOptions,
133
140
  ) => Promise<number>
@@ -150,7 +157,7 @@ export interface CommonDB {
150
157
  *
151
158
  * Returns number of rows affected.
152
159
  */
153
- updateByQuery: <ROW extends ObjectWithId>(
160
+ updateByQuery: <ROW extends PartialObjectWithId>(
154
161
  q: DBQuery<ROW>,
155
162
  patch: DBPatch<ROW>,
156
163
  opt?: CommonDBOptions,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  CommonLogger,
3
3
  ErrorMode,
4
- ObjectWithId,
4
+ PartialObjectWithId,
5
5
  Promisable,
6
6
  Saved,
7
7
  ZodError,
@@ -18,7 +18,11 @@ import {
18
18
  import { CommonDB } from '../common.db'
19
19
  import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../db.model'
20
20
 
21
- export interface CommonDaoHooks<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId, TM> {
21
+ export interface CommonDaoHooks<
22
+ BM extends PartialObjectWithId,
23
+ DBM extends PartialObjectWithId,
24
+ TM,
25
+ > {
22
26
  /**
23
27
  * Allows to override the id generation function.
24
28
  * By default it uses `stringId` from nodejs-lib
@@ -59,7 +63,7 @@ export interface CommonDaoHooks<BM extends Partial<ObjectWithId>, DBM extends Ob
59
63
  */
60
64
  beforeDBMValidate: (dbm: Partial<DBM>) => Partial<DBM>
61
65
 
62
- beforeDBMToBM: (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>
66
+ beforeDBMToBM: (dbm: Saved<DBM>) => Partial<BM> | Promise<Partial<BM>>
63
67
  beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>
64
68
  beforeBMToTM: (bm: BM) => Partial<TM>
65
69
 
@@ -75,7 +79,7 @@ export interface CommonDaoHooks<BM extends Partial<ObjectWithId>, DBM extends Ob
75
79
  *
76
80
  * You can do validations as needed here and throw errors, they will be propagated.
77
81
  */
78
- afterLoad?: (dbm: DBM) => Promisable<DBM | null>
82
+ afterLoad?: (dbm: Saved<DBM>) => Promisable<Saved<DBM> | null>
79
83
 
80
84
  /**
81
85
  * Allows to access the DBM just before it's supposed to be saved to the DB.
@@ -90,7 +94,7 @@ export interface CommonDaoHooks<BM extends Partial<ObjectWithId>, DBM extends Ob
90
94
  *
91
95
  * You can do validations as needed here and throw errors, they will be propagated.
92
96
  */
93
- beforeSave?: (dbm: DBM) => Promisable<DBM | null>
97
+ beforeSave?: (dbm: Saved<DBM>) => Promisable<Saved<DBM> | null>
94
98
 
95
99
  /**
96
100
  * Called in:
@@ -101,7 +105,7 @@ export interface CommonDaoHooks<BM extends Partial<ObjectWithId>, DBM extends Ob
101
105
  * It still applies to BM "transitively", during dbmToBM
102
106
  * (e.g after loaded from the Database).
103
107
  */
104
- anonymize: (dbm: DBM) => DBM
108
+ anonymize: (dbm: Saved<DBM>) => Saved<DBM>
105
109
 
106
110
  /**
107
111
  * If hook is defined - allows to prevent or modify the error thrown.
@@ -132,8 +136,8 @@ export enum CommonDaoLogLevel {
132
136
  }
133
137
 
134
138
  export interface CommonDaoCfg<
135
- BM extends Partial<ObjectWithId>,
136
- DBM extends ObjectWithId = Saved<BM>,
139
+ BM extends PartialObjectWithId,
140
+ DBM extends PartialObjectWithId = BM,
137
141
  TM = BM,
138
142
  > {
139
143
  db: CommonDB
@@ -200,13 +204,13 @@ export interface CommonDaoCfg<
200
204
  * Defaults to true
201
205
  * Set to false to disable `created` field management.
202
206
  */
203
- created?: boolean
207
+ useCreatedProperty?: boolean
204
208
 
205
209
  /**
206
210
  * Defaults to true
207
211
  * Set to false to disable `updated` field management.
208
212
  */
209
- updated?: boolean
213
+ useUpdatedProperty?: boolean
210
214
 
211
215
  /**
212
216
  * Default is false.
@@ -276,8 +280,10 @@ export interface CommonDaoOptions extends CommonDBOptions {
276
280
  table?: string
277
281
  }
278
282
 
279
- export interface CommonDaoSaveOptions<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>
280
- extends CommonDaoSaveBatchOptions<DBM> {
283
+ export interface CommonDaoSaveOptions<
284
+ BM extends PartialObjectWithId,
285
+ DBM extends PartialObjectWithId,
286
+ > extends CommonDaoSaveBatchOptions<DBM> {
281
287
  /**
282
288
  * If provided - a check will be made.
283
289
  * If the object for saving equals to the object passed to `skipIfEquals` - save operation will be skipped.
@@ -292,7 +298,7 @@ export interface CommonDaoSaveOptions<BM extends Partial<ObjectWithId>, DBM exte
292
298
  /**
293
299
  * All properties default to undefined.
294
300
  */
295
- export interface CommonDaoSaveBatchOptions<DBM extends ObjectWithId>
301
+ export interface CommonDaoSaveBatchOptions<DBM extends PartialObjectWithId>
296
302
  extends CommonDaoOptions,
297
303
  CommonDBSaveOptions<DBM> {
298
304
  /**
@@ -307,10 +313,10 @@ export interface CommonDaoSaveBatchOptions<DBM extends ObjectWithId>
307
313
  ensureUniqueId?: boolean
308
314
  }
309
315
 
310
- export interface CommonDaoStreamDeleteOptions<DBM extends ObjectWithId>
316
+ export interface CommonDaoStreamDeleteOptions<DBM extends PartialObjectWithId>
311
317
  extends CommonDaoStreamOptions<DBM> {}
312
318
 
313
- export interface CommonDaoStreamSaveOptions<DBM extends ObjectWithId>
319
+ export interface CommonDaoStreamSaveOptions<DBM extends PartialObjectWithId>
314
320
  extends CommonDaoSaveBatchOptions<DBM>,
315
321
  CommonDaoStreamOptions<DBM> {}
316
322
 
@@ -18,11 +18,11 @@ import {
18
18
  JsonSchemaObject,
19
19
  JsonSchemaRootObject,
20
20
  ObjectWithId,
21
+ PartialObjectWithId,
21
22
  pMap,
22
23
  Saved,
23
24
  SKIP,
24
25
  UnixTimestampMillisNumber,
25
- Unsaved,
26
26
  ZodSchema,
27
27
  ZodValidationError,
28
28
  zSafeValidate,
@@ -43,7 +43,13 @@ import {
43
43
  writableVoid,
44
44
  } from '@naturalcycles/nodejs-lib'
45
45
  import { DBLibError } from '../cnst'
46
- import { DBModelType, DBPatch, DBTransaction, RunQueryResult } from '../db.model'
46
+ import {
47
+ CommonDBTransactionOptions,
48
+ DBModelType,
49
+ DBPatch,
50
+ DBTransaction,
51
+ RunQueryResult,
52
+ } from '../db.model'
47
53
  import { DBQuery, RunnableDBQuery } from '../query/dbQuery'
48
54
  import {
49
55
  CommonDaoCfg,
@@ -70,8 +76,8 @@ const isCI = !!process.env['CI']
70
76
  * TM = Transport model (optimized to be sent over the wire)
71
77
  */
72
78
  export class CommonDao<
73
- BM extends Partial<ObjectWithId>,
74
- DBM extends ObjectWithId = Saved<BM>,
79
+ BM extends PartialObjectWithId,
80
+ DBM extends PartialObjectWithId = BM,
75
81
  TM extends AnyObject = BM,
76
82
  > {
77
83
  constructor(public cfg: CommonDaoCfg<BM, DBM, TM>) {
@@ -82,8 +88,8 @@ export class CommonDao<
82
88
  logLevel: isGAE || isCI ? CommonDaoLogLevel.NONE : CommonDaoLogLevel.OPERATIONS,
83
89
  createId: true,
84
90
  assignGeneratedIds: false,
85
- created: true,
86
- updated: true,
91
+ useCreatedProperty: true,
92
+ useUpdatedProperty: true,
87
93
  logger: console,
88
94
  ...cfg,
89
95
  hooks: {
@@ -110,7 +116,7 @@ export class CommonDao<
110
116
  create(part: Partial<BM> = {}, opt: CommonDaoOptions = {}): Saved<BM> {
111
117
  const bm = this.cfg.hooks!.beforeCreate!(part)
112
118
  // First assignIdCreatedUpdated, then validate!
113
- this.assignIdCreatedUpdated(bm as any, opt)
119
+ this.assignIdCreatedUpdated(bm as BM, opt)
114
120
  return this.validateAndConvert(bm, this.cfg.bmSchema, DBModelType.BM, opt)
115
121
  }
116
122
 
@@ -148,17 +154,17 @@ export class CommonDao<
148
154
  id: string,
149
155
  part: Partial<BM> = {},
150
156
  opt?: CommonDaoOptions,
151
- ): Promise<DBM> {
157
+ ): Promise<Saved<DBM>> {
152
158
  const dbm = await this.getByIdAsDBM(id, opt)
153
159
  if (dbm) return dbm
154
160
 
155
- const bm: BM = this.create({ ...part, id }, opt) as any
161
+ const bm = this.create({ ...part, id }, opt)
156
162
  return await this.bmToDBM(bm, opt)
157
163
  }
158
164
 
159
165
  async getByIdAsDBM(id: undefined | null, opt?: CommonDaoOptions): Promise<null>
160
- async getByIdAsDBM(id?: string | null, opt?: CommonDaoOptions): Promise<DBM | null>
161
- async getByIdAsDBM(id?: string | null, opt: CommonDaoOptions = {}): Promise<DBM | null> {
166
+ async getByIdAsDBM(id?: string | null, opt?: CommonDaoOptions): Promise<Saved<DBM> | null>
167
+ async getByIdAsDBM(id?: string | null, opt: CommonDaoOptions = {}): Promise<Saved<DBM> | null> {
162
168
  if (!id) return null
163
169
  const op = `getByIdAsDBM(${id})`
164
170
  const table = opt.table || this.cfg.table
@@ -214,7 +220,7 @@ export class CommonDao<
214
220
  return bms
215
221
  }
216
222
 
217
- async getByIdsAsDBM(ids: string[], opt: CommonDaoOptions = {}): Promise<DBM[]> {
223
+ async getByIdsAsDBM(ids: string[], opt: CommonDaoOptions = {}): Promise<Saved<DBM>[]> {
218
224
  if (!ids.length) return []
219
225
  const op = `getByIdsAsDBM ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
220
226
  const table = opt.table || this.cfg.table
@@ -238,7 +244,7 @@ export class CommonDao<
238
244
  return r
239
245
  }
240
246
 
241
- async requireByIdAsDBM(id: string, opt: CommonDaoOptions = {}): Promise<DBM> {
247
+ async requireByIdAsDBM(id: string, opt: CommonDaoOptions = {}): Promise<Saved<DBM>> {
242
248
  const r = await this.getByIdAsDBM(id, opt)
243
249
  if (!r) {
244
250
  this.throwRequiredError(id, opt)
@@ -276,7 +282,7 @@ export class CommonDao<
276
282
  }
277
283
  }
278
284
 
279
- private async ensureUniqueId(table: string, dbm: DBM): Promise<void> {
285
+ private async ensureUniqueId(table: string, dbm: Saved<DBM>): Promise<void> {
280
286
  // todo: retry N times
281
287
  const existing = await this.cfg.db.getByIds<DBM>(table, [dbm.id])
282
288
  if (existing.length) {
@@ -360,7 +366,7 @@ export class CommonDao<
360
366
  }
361
367
  }
362
368
 
363
- async runQueryAsDBM(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<DBM[]> {
369
+ async runQueryAsDBM(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<Saved<DBM>[]> {
364
370
  const { rows } = await this.runQueryExtendedAsDBM(q, opt)
365
371
  return rows
366
372
  }
@@ -368,7 +374,7 @@ export class CommonDao<
368
374
  async runQueryExtendedAsDBM(
369
375
  q: DBQuery<DBM>,
370
376
  opt: CommonDaoOptions = {},
371
- ): Promise<RunQueryResult<DBM>> {
377
+ ): Promise<RunQueryResult<Saved<DBM>>> {
372
378
  q.table = opt.table || q.table
373
379
  const op = `runQueryAsDBM(${q.pretty()})`
374
380
  const started = this.logStarted(op, q.table)
@@ -442,7 +448,7 @@ export class CommonDao<
442
448
 
443
449
  await _pipeline([
444
450
  this.cfg.db.streamQuery<DBM>(q, opt),
445
- transformMap<DBM, Saved<BM>>(
451
+ transformMap<Saved<DBM>, Saved<BM>>(
446
452
  async dbm => {
447
453
  count++
448
454
  if (partialQuery || opt.raw) return dbm as any
@@ -542,7 +548,7 @@ export class CommonDao<
542
548
  return stream
543
549
  .on('error', err => stream.emit('error', err))
544
550
  .pipe(
545
- transformMap<any, DBM>(
551
+ transformMap<any, Saved<DBM>>(
546
552
  async dbm => {
547
553
  if (this.cfg.hooks!.afterLoad) {
548
554
  dbm = (await this.cfg.hooks!.afterLoad(dbm))!
@@ -587,7 +593,7 @@ export class CommonDao<
587
593
  // .pipe(transformMap<DBM, Saved<BM>>(dbm => this.dbmToBM(dbm, opt), safeOpt))
588
594
  .on('error', err => stream.emit('error', err))
589
595
  .pipe(
590
- transformMap<DBM, Saved<BM>>(
596
+ transformMap<Saved<DBM>, Saved<BM>>(
591
597
  async dbm => {
592
598
  if (this.cfg.hooks!.afterLoad) {
593
599
  dbm = (await this.cfg.hooks!.afterLoad(dbm))!
@@ -621,7 +627,7 @@ export class CommonDao<
621
627
  .streamQuery<DBM>(q.select(['id']), opt)
622
628
  .on('error', err => stream.emit('error', err))
623
629
  .pipe(
624
- transformMapSimple<DBM, string>(objectWithId => objectWithId.id, {
630
+ transformMapSimple<Saved<DBM>, string>(r => r.id, {
625
631
  errorMode: ErrorMode.SUPPRESS, // cause .pipe() cannot propagate errors
626
632
  }),
627
633
  )
@@ -643,9 +649,9 @@ export class CommonDao<
643
649
 
644
650
  await _pipeline([
645
651
  this.cfg.db.streamQuery<DBM>(q.select(['id']), opt),
646
- transformMapSimple<DBM, string>(objectWithId => {
652
+ transformMapSimple<Saved<DBM>, string>(r => {
647
653
  count++
648
- return objectWithId.id
654
+ return r.id
649
655
  }),
650
656
  transformMap<string, void>(mapper, {
651
657
  ...opt,
@@ -668,17 +674,19 @@ export class CommonDao<
668
674
  * Mutates!
669
675
  * "Returns", just to have a type of "Saved"
670
676
  */
671
- assignIdCreatedUpdated(obj: DBM, opt?: CommonDaoOptions): DBM
672
- assignIdCreatedUpdated(obj: BM, opt?: CommonDaoOptions): Saved<BM>
673
- assignIdCreatedUpdated(obj: Unsaved<BM>, opt?: CommonDaoOptions): Saved<BM>
674
- assignIdCreatedUpdated(obj: DBM | BM | Unsaved<BM>, opt: CommonDaoOptions = {}): DBM | Saved<BM> {
677
+ assignIdCreatedUpdated(obj: Partial<DBM>, opt?: CommonDaoOptions): Saved<Partial<DBM>>
678
+ assignIdCreatedUpdated(obj: Partial<BM>, opt?: CommonDaoOptions): Saved<Partial<BM>>
679
+ assignIdCreatedUpdated(
680
+ obj: Partial<DBM> | Partial<BM>,
681
+ opt: CommonDaoOptions = {},
682
+ ): Saved<Partial<DBM>> | Saved<Partial<BM>> {
675
683
  const now = Math.floor(Date.now() / 1000)
676
684
 
677
- if (this.cfg.created) {
685
+ if (this.cfg.useCreatedProperty) {
678
686
  ;(obj as any)['created'] ||= (obj as any)['updated'] || now
679
687
  }
680
688
 
681
- if (this.cfg.updated) {
689
+ if (this.cfg.useUpdatedProperty) {
682
690
  ;(obj as any)['updated'] =
683
691
  opt.preserveUpdatedCreated && (obj as any)['updated'] ? (obj as any)['updated'] : now
684
692
  }
@@ -694,7 +702,7 @@ export class CommonDao<
694
702
  /**
695
703
  * Mutates with id, created, updated
696
704
  */
697
- async save(bm: Unsaved<BM>, opt: CommonDaoSaveOptions<BM, DBM> = {}): Promise<Saved<BM>> {
705
+ async save(bm: BM, opt: CommonDaoSaveOptions<BM, DBM> = {}): Promise<Saved<BM>> {
698
706
  this.requireWriteAccess()
699
707
 
700
708
  if (opt.skipIfEquals && _deepJsonEquals(bm, opt.skipIfEquals)) {
@@ -704,7 +712,7 @@ export class CommonDao<
704
712
 
705
713
  const idWasGenerated = !bm.id && this.cfg.createId
706
714
  this.assignIdCreatedUpdated(bm, opt) // mutates
707
- let dbm = await this.bmToDBM(bm as BM, opt)
715
+ let dbm = await this.bmToDBM(bm, opt)
708
716
 
709
717
  if (this.cfg.hooks!.beforeSave) {
710
718
  dbm = (await this.cfg.hooks!.beforeSave(dbm))!
@@ -728,11 +736,11 @@ export class CommonDao<
728
736
  })
729
737
 
730
738
  if (assignGeneratedIds) {
731
- bm.id = dbm.id as any
739
+ bm.id = dbm.id
732
740
  }
733
741
 
734
742
  this.logSaveResult(started, op, table)
735
- return bm as any
743
+ return bm as Saved<BM>
736
744
  }
737
745
 
738
746
  /**
@@ -829,13 +837,13 @@ export class CommonDao<
829
837
  return await this.save(bm, opt)
830
838
  }
831
839
 
832
- async saveAsDBM(dbm: DBM, opt: CommonDaoSaveBatchOptions<DBM> = {}): Promise<DBM> {
840
+ async saveAsDBM(dbm: DBM, opt: CommonDaoSaveBatchOptions<DBM> = {}): Promise<Saved<DBM>> {
833
841
  this.requireWriteAccess()
834
842
  const table = opt.table || this.cfg.table
835
843
 
836
844
  // assigning id in case it misses the id
837
845
  // will override/set `updated` field, unless opts.preserveUpdated is set
838
- let row = dbm
846
+ let row = dbm as Saved<DBM>
839
847
  if (!opt.raw) {
840
848
  const idWasGenerated = !dbm.id && this.cfg.createId
841
849
  this.assignIdCreatedUpdated(dbm, opt) // mutates
@@ -852,7 +860,7 @@ export class CommonDao<
852
860
 
853
861
  if (this.cfg.hooks!.beforeSave) {
854
862
  row = (await this.cfg.hooks!.beforeSave(row))!
855
- if (row === null) return dbm
863
+ if (row === null) return dbm as Saved<DBM>
856
864
  }
857
865
 
858
866
  await this.cfg.db.saveBatch(table, [row], {
@@ -869,15 +877,12 @@ export class CommonDao<
869
877
  return row
870
878
  }
871
879
 
872
- async saveBatch(
873
- bms: Unsaved<BM>[],
874
- opt: CommonDaoSaveBatchOptions<DBM> = {},
875
- ): Promise<Saved<BM>[]> {
880
+ async saveBatch(bms: BM[], opt: CommonDaoSaveBatchOptions<DBM> = {}): Promise<Saved<BM>[]> {
876
881
  if (!bms.length) return []
877
882
  this.requireWriteAccess()
878
883
  const table = opt.table || this.cfg.table
879
884
  bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt))
880
- let dbms = await this.bmsToDBM(bms as BM[], opt)
885
+ let dbms = await this.bmsToDBM(bms, opt)
881
886
 
882
887
  if (this.cfg.hooks!.beforeSave && dbms.length) {
883
888
  dbms = (await pMap(dbms, async dbm => await this.cfg.hooks!.beforeSave!(dbm))).filter(
@@ -908,22 +913,25 @@ export class CommonDao<
908
913
  })
909
914
 
910
915
  if (assignGeneratedIds) {
911
- dbms.forEach((dbm, i) => (bms[i]!.id = dbm.id as any))
916
+ dbms.forEach((dbm, i) => (bms[i]!.id = dbm.id))
912
917
  }
913
918
 
914
919
  this.logSaveResult(started, op, table)
915
920
 
916
- return bms as any[]
921
+ return bms as Saved<BM>[]
917
922
  }
918
923
 
919
- async saveBatchAsDBM(dbms: DBM[], opt: CommonDaoSaveBatchOptions<DBM> = {}): Promise<DBM[]> {
924
+ async saveBatchAsDBM(
925
+ dbms: DBM[],
926
+ opt: CommonDaoSaveBatchOptions<DBM> = {},
927
+ ): Promise<Saved<DBM>[]> {
920
928
  if (!dbms.length) return []
921
929
  this.requireWriteAccess()
922
930
  const table = opt.table || this.cfg.table
923
- let rows = dbms
931
+ let rows = dbms as Saved<DBM>[]
924
932
  if (!opt.raw) {
925
933
  dbms.forEach(dbm => this.assignIdCreatedUpdated(dbm, opt)) // mutates
926
- rows = this.anyToDBMs(dbms, opt)
934
+ rows = this.anyToDBMs(dbms as Saved<DBM>[], opt)
927
935
  if (opt.ensureUniqueId) throw new AppError('ensureUniqueId is not supported in saveBatch')
928
936
  }
929
937
  if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
@@ -984,7 +992,7 @@ export class CommonDao<
984
992
  const { batchSize = 500, batchConcurrency = 16, errorMode } = opt
985
993
 
986
994
  return [
987
- transformMap<BM, DBM>(
995
+ transformMap<BM, Saved<DBM>>(
988
996
  async bm => {
989
997
  this.assignIdCreatedUpdated(bm, opt) // mutates
990
998
 
@@ -1077,7 +1085,7 @@ export class CommonDao<
1077
1085
 
1078
1086
  await _pipeline([
1079
1087
  this.cfg.db.streamQuery<DBM>(q.select(['id']), opt),
1080
- transformMapSimple<DBM, string>(objectWithId => objectWithId.id, {
1088
+ transformMapSimple<ObjectWithId, string>(r => r.id, {
1081
1089
  errorMode: ErrorMode.SUPPRESS,
1082
1090
  }),
1083
1091
  transformBuffer<string>({ batchSize }),
@@ -1141,13 +1149,13 @@ export class CommonDao<
1141
1149
  // CONVERSIONS
1142
1150
 
1143
1151
  async dbmToBM(_dbm: undefined, opt?: CommonDaoOptions): Promise<undefined>
1144
- async dbmToBM(_dbm?: DBM, opt?: CommonDaoOptions): Promise<Saved<BM>>
1145
- async dbmToBM(_dbm?: DBM, opt: CommonDaoOptions = {}): Promise<Saved<BM> | undefined> {
1152
+ async dbmToBM(_dbm?: Saved<DBM>, opt?: CommonDaoOptions): Promise<Saved<BM>>
1153
+ async dbmToBM(_dbm?: Saved<DBM>, opt: CommonDaoOptions = {}): Promise<Saved<BM> | undefined> {
1146
1154
  if (!_dbm) return
1147
1155
 
1148
1156
  // optimization: no need to run full joi DBM validation, cause BM validation will be run
1149
1157
  // const dbm = this.anyToDBM(_dbm, opt)
1150
- let dbm: DBM = { ..._dbm, ...this.cfg.hooks!.parseNaturalId!(_dbm.id) }
1158
+ let dbm: Saved<DBM> = { ..._dbm, ...this.cfg.hooks!.parseNaturalId!(_dbm.id) }
1151
1159
 
1152
1160
  if (opt.anonymize) {
1153
1161
  dbm = this.cfg.hooks!.anonymize!(dbm)
@@ -1161,7 +1169,7 @@ export class CommonDao<
1161
1169
  return this.validateAndConvert(bm, this.cfg.bmSchema, DBModelType.BM, opt)
1162
1170
  }
1163
1171
 
1164
- async dbmsToBM(dbms: DBM[], opt: CommonDaoOptions = {}): Promise<Saved<BM>[]> {
1172
+ async dbmsToBM(dbms: Saved<DBM>[], opt: CommonDaoOptions = {}): Promise<Saved<BM>[]> {
1165
1173
  return await pMap(dbms, async dbm => await this.dbmToBM(dbm, opt))
1166
1174
  }
1167
1175
 
@@ -1170,8 +1178,8 @@ export class CommonDao<
1170
1178
  * Returns DBM (new reference).
1171
1179
  */
1172
1180
  async bmToDBM(bm: undefined, opt?: CommonDaoOptions): Promise<undefined>
1173
- async bmToDBM(bm?: BM, opt?: CommonDaoOptions): Promise<DBM>
1174
- async bmToDBM(bm?: BM, opt?: CommonDaoOptions): Promise<DBM | undefined> {
1181
+ async bmToDBM(bm?: BM, opt?: CommonDaoOptions): Promise<Saved<DBM>>
1182
+ async bmToDBM(bm?: BM, opt?: CommonDaoOptions): Promise<Saved<DBM> | undefined> {
1175
1183
  if (bm === undefined) return
1176
1184
 
1177
1185
  // optimization: no need to run the BM validation, since DBM will be validated anyway
@@ -1190,14 +1198,14 @@ export class CommonDao<
1190
1198
  return this.validateAndConvert(dbm, this.cfg.dbmSchema, DBModelType.DBM, opt)
1191
1199
  }
1192
1200
 
1193
- async bmsToDBM(bms: BM[], opt: CommonDaoOptions = {}): Promise<DBM[]> {
1201
+ async bmsToDBM(bms: BM[], opt: CommonDaoOptions = {}): Promise<Saved<DBM>[]> {
1194
1202
  // try/catch?
1195
1203
  return await pMap(bms, async bm => await this.bmToDBM(bm, opt))
1196
1204
  }
1197
1205
 
1198
1206
  anyToDBM(dbm: undefined, opt?: CommonDaoOptions): undefined
1199
- anyToDBM(dbm?: any, opt?: CommonDaoOptions): DBM
1200
- anyToDBM(dbm?: DBM, opt: CommonDaoOptions = {}): DBM | undefined {
1207
+ anyToDBM(dbm?: any, opt?: CommonDaoOptions): Saved<DBM>
1208
+ anyToDBM(dbm?: Saved<DBM>, opt: CommonDaoOptions = {}): Saved<DBM> | undefined {
1201
1209
  if (!dbm) return
1202
1210
 
1203
1211
  // this shouldn't be happening on load! but should on save!
@@ -1213,7 +1221,7 @@ export class CommonDao<
1213
1221
  return this.validateAndConvert(dbm, this.cfg.dbmSchema, DBModelType.DBM, opt)
1214
1222
  }
1215
1223
 
1216
- anyToDBMs(entities: DBM[], opt: CommonDaoOptions = {}): DBM[] {
1224
+ anyToDBMs(entities: Saved<DBM>[], opt: CommonDaoOptions = {}): Saved<DBM>[] {
1217
1225
  return entities.map(entity => this.anyToDBM(entity, opt))
1218
1226
  }
1219
1227
 
@@ -1228,7 +1236,7 @@ export class CommonDao<
1228
1236
  // bm = this.validateAndConvert(bm, this.cfg.bmSchema, DBModelType.BM, opt)
1229
1237
 
1230
1238
  // BM > TM
1231
- const tm = this.cfg.hooks!.beforeBMToTM!(bm as any)
1239
+ const tm = this.cfg.hooks!.beforeBMToTM!(bm)
1232
1240
 
1233
1241
  // Validate/convert DBM
1234
1242
  return this.validateAndConvert(tm, this.cfg.tmSchema, DBModelType.TM, opt)
@@ -1321,7 +1329,7 @@ export class CommonDao<
1321
1329
 
1322
1330
  async createTable(schema: JsonSchemaObject<DBM>, opt?: CommonDaoCreateOptions): Promise<void> {
1323
1331
  this.requireWriteAccess()
1324
- await this.cfg.db.createTable(this.cfg.table, schema as any, opt)
1332
+ await this.cfg.db.createTable(this.cfg.table, schema, opt)
1325
1333
  }
1326
1334
 
1327
1335
  /**
@@ -1331,17 +1339,20 @@ export class CommonDao<
1331
1339
  await this.cfg.db.ping()
1332
1340
  }
1333
1341
 
1334
- async runInTransaction(fn: CommonDaoTransactionFn): Promise<void> {
1342
+ async runInTransaction(
1343
+ fn: CommonDaoTransactionFn,
1344
+ opt?: CommonDBTransactionOptions,
1345
+ ): Promise<void> {
1335
1346
  await this.cfg.db.runInTransaction(async tx => {
1336
1347
  const daoTx = new CommonDaoTransaction(tx, this.cfg.logger!)
1337
1348
 
1338
1349
  try {
1339
1350
  await fn(daoTx)
1340
1351
  } catch (err) {
1341
- await daoTx.rollback()
1352
+ await daoTx.rollback() // graceful rollback that "never throws"
1342
1353
  throw err
1343
1354
  }
1344
- })
1355
+ }, opt)
1345
1356
  }
1346
1357
 
1347
1358
  protected logResult(started: number, op: string, res: any, table: string): void {
@@ -1430,7 +1441,7 @@ export class CommonDaoTransaction {
1430
1441
  }
1431
1442
  }
1432
1443
 
1433
- async getById<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(
1444
+ async getById<BM extends PartialObjectWithId, DBM extends PartialObjectWithId>(
1434
1445
  dao: CommonDao<BM, DBM, any>,
1435
1446
  id?: string | null,
1436
1447
  opt?: CommonDaoOptions,
@@ -1439,7 +1450,7 @@ export class CommonDaoTransaction {
1439
1450
  return (await this.getByIds(dao, [id], opt))[0] || null
1440
1451
  }
1441
1452
 
1442
- async getByIds<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(
1453
+ async getByIds<BM extends PartialObjectWithId, DBM extends PartialObjectWithId>(
1443
1454
  dao: CommonDao<BM, DBM, any>,
1444
1455
  ids: string[],
1445
1456
  opt?: CommonDaoOptions,
@@ -1448,7 +1459,7 @@ export class CommonDaoTransaction {
1448
1459
  }
1449
1460
 
1450
1461
  // todo: Queries inside Transaction are not supported yet
1451
- // async runQuery<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(
1462
+ // async runQuery<BM extends PartialObjectWithId, DBM extends ObjectWithId>(
1452
1463
  // dao: CommonDao<BM, DBM, any>,
1453
1464
  // q: DBQuery<DBM>,
1454
1465
  // opt?: CommonDaoOptions,
@@ -1461,17 +1472,17 @@ export class CommonDaoTransaction {
1461
1472
  // }
1462
1473
  // }
1463
1474
 
1464
- async save<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(
1475
+ async save<BM extends PartialObjectWithId, DBM extends PartialObjectWithId>(
1465
1476
  dao: CommonDao<BM, DBM, any>,
1466
- bm: Unsaved<BM>,
1477
+ bm: BM,
1467
1478
  opt?: CommonDaoSaveBatchOptions<DBM>,
1468
1479
  ): Promise<Saved<BM>> {
1469
1480
  return (await this.saveBatch(dao, [bm], opt))[0]!
1470
1481
  }
1471
1482
 
1472
- async saveBatch<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(
1483
+ async saveBatch<BM extends PartialObjectWithId, DBM extends PartialObjectWithId>(
1473
1484
  dao: CommonDao<BM, DBM, any>,
1474
- bms: Unsaved<BM>[],
1485
+ bms: BM[],
1475
1486
  opt?: CommonDaoSaveBatchOptions<DBM>,
1476
1487
  ): Promise<Saved<BM>[]> {
1477
1488
  return await dao.saveBatch(bms, { ...opt, tx: this.tx })