@naturalcycles/db-lib 10.45.2 → 10.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -123,16 +123,16 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
123
123
  * @experimental
124
124
  */
125
125
  incrementBatch(prop: keyof DBM, incrementMap: StringMap<number>, opt?: CommonDaoOptions): Promise<StringMap<number>>;
126
- dbmToBM(_dbm: undefined, opt?: CommonDaoOptions): Promise<null>;
127
- dbmToBM(_dbm?: DBM, opt?: CommonDaoOptions): Promise<BM>;
128
- dbmsToBM(dbms: DBM[], opt?: CommonDaoOptions): Promise<BM[]>;
126
+ dbmToBM(_dbm: undefined, opt?: CommonDaoOptions): null;
127
+ dbmToBM(_dbm?: DBM, opt?: CommonDaoOptions): BM;
128
+ dbmsToBM(dbms: DBM[], opt?: CommonDaoOptions): BM[];
129
129
  /**
130
130
  * Mutates object with properties: id, created, updated.
131
131
  * Returns DBM (new reference).
132
132
  */
133
- bmToDBM(bm: undefined, opt?: CommonDaoOptions): Promise<null>;
134
- bmToDBM(bm?: BM, opt?: CommonDaoOptions): Promise<DBM>;
135
- bmsToDBM(bms: BM[], opt?: CommonDaoOptions): Promise<DBM[]>;
133
+ bmToDBM(bm: undefined, opt?: CommonDaoOptions): null;
134
+ bmToDBM(bm?: BM, opt?: CommonDaoOptions): DBM;
135
+ bmsToDBM(bms: BM[], opt?: CommonDaoOptions): DBM[];
136
136
  /**
137
137
  * Converts a DBM to storage format, applying compression if configured.
138
138
  *
@@ -164,9 +164,9 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
164
164
  storageRowsToDBMs(rows: ObjectWithId[]): Promise<DBM[]>;
165
165
  private compress;
166
166
  private decompress;
167
- anyToDBM(dbm: undefined, opt?: CommonDaoOptions): Promise<null>;
168
- anyToDBM(dbm?: any, opt?: CommonDaoOptions): Promise<DBM>;
169
- anyToDBMs(rows: DBM[], opt?: CommonDaoOptions): Promise<DBM[]>;
167
+ anyToDBM(dbm: undefined, opt?: CommonDaoOptions): null;
168
+ anyToDBM(dbm?: any, opt?: CommonDaoOptions): DBM;
169
+ anyToDBMs(rows: DBM[], opt?: CommonDaoOptions): DBM[];
170
170
  /**
171
171
  * Returns *converted value* (NOT the same reference).
172
172
  * Does NOT mutate the object.
@@ -183,43 +183,6 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
183
183
  withIds(ids: ID[]): DaoWithIds<CommonDao<BM, DBM, ID>>;
184
184
  withRowsToSave(rows: Unsaved<BM>[]): DaoWithRows<CommonDao<BM, DBM, ID>>;
185
185
  withRowToSave(row: Unsaved<BM>, opt?: DaoWithRowOptions<BM>): DaoWithRow<CommonDao<BM, DBM, ID>>;
186
- /**
187
- * Helper to decompress legacy compressed data when migrating away from auto-compression.
188
- * Use as your `beforeDBMToBM` hook to decompress legacy rows on read.
189
- *
190
- * @example
191
- * const dao = new CommonDao({
192
- * hooks: {
193
- * beforeDBMToBM: CommonDao.decompressLegacyRow,
194
- * }
195
- * })
196
- *
197
- * // Or within an existing hook:
198
- * beforeDBMToBM: async (dbm) => {
199
- * await CommonDao.decompressLegacyRow(dbm)
200
- * // ... other transformations
201
- * return dbm
202
- * }
203
- */
204
- static decompressLegacyRow<T extends ObjectWithId>(this: void, row: T): Promise<T>;
205
- /**
206
- * Temporary helper to migrate from the old `data` compressed property to the new `__compressed` property.
207
- * Use as your `beforeDBMToBM` hook during the migration period.
208
- *
209
- * Migration steps:
210
- * 1. Add `beforeDBMToBM: CommonDao.migrateCompressedDataProperty` to your hooks
211
- * 2. Deploy - old data (with `data` property) will be decompressed on read and recompressed to `__compressed` on write
212
- * 3. Once all data has been naturally rewritten, remove the hook
213
- *
214
- * @example
215
- * const dao = new CommonDao({
216
- * compress: { keys: ['field1', 'field2'] },
217
- * hooks: {
218
- * beforeDBMToBM: CommonDao.migrateCompressedDataProperty,
219
- * }
220
- * })
221
- */
222
- static migrateCompressedDataProperty<T extends ObjectWithId>(row: T): Promise<T>;
223
186
  /**
224
187
  * Load rows (by their ids) from Multiple tables at once.
225
188
  * An optimized way to load data, minimizing DB round-trips.
@@ -83,21 +83,21 @@ export class CommonDao {
83
83
  if (!id)
84
84
  return null;
85
85
  const [dbm] = await this.loadByIds([id], opt);
86
- return await this.dbmToBM(dbm, opt);
86
+ return this.dbmToBM(dbm, opt);
87
87
  }
88
88
  async getByIdAsDBM(id, opt = {}) {
89
89
  if (!id)
90
90
  return null;
91
91
  const [row] = await this.loadByIds([id], opt);
92
- return await this.anyToDBM(row, opt);
92
+ return this.anyToDBM(row, opt);
93
93
  }
94
94
  async getByIds(ids, opt = {}) {
95
95
  const dbms = await this.loadByIds(ids, opt);
96
- return await this.dbmsToBM(dbms, opt);
96
+ return this.dbmsToBM(dbms, opt);
97
97
  }
98
98
  async getByIdsAsDBM(ids, opt = {}) {
99
99
  const rows = await this.loadByIds(ids, opt);
100
- return await this.anyToDBMs(rows);
100
+ return this.anyToDBMs(rows);
101
101
  }
102
102
  // DRY private method
103
103
  async loadByIds(ids, opt = {}) {
@@ -149,7 +149,7 @@ export class CommonDao {
149
149
  const { rows: rawRows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
150
150
  const isPartialQuery = !!q._selectedFieldNames;
151
151
  const rows = isPartialQuery ? rawRows : await this.storageRowsToDBMs(rawRows);
152
- const bms = isPartialQuery ? rows : await this.dbmsToBM(rows, opt);
152
+ const bms = isPartialQuery ? rows : this.dbmsToBM(rows, opt);
153
153
  return {
154
154
  rows: bms,
155
155
  ...queryResult,
@@ -165,7 +165,7 @@ export class CommonDao {
165
165
  const { rows: rawRows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
166
166
  const isPartialQuery = !!q._selectedFieldNames;
167
167
  const rows = isPartialQuery ? rawRows : await this.storageRowsToDBMs(rawRows);
168
- const dbms = isPartialQuery ? rows : await this.anyToDBMs(rows, opt);
168
+ const dbms = isPartialQuery ? rows : this.anyToDBMs(rows, opt);
169
169
  return { rows: dbms, ...queryResult };
170
170
  }
171
171
  async runQueryCount(q, opt = {}) {
@@ -185,7 +185,7 @@ export class CommonDao {
185
185
  return pipeline;
186
186
  opt.skipValidation ??= true;
187
187
  opt.errorMode ||= ErrorMode.SUPPRESS;
188
- return pipeline.map(async dbm => await this.anyToDBM(dbm, opt), { errorMode: opt.errorMode });
188
+ return pipeline.mapSync(dbm => this.anyToDBM(dbm, opt), { errorMode: opt.errorMode });
189
189
  }
190
190
  streamQuery(q, opt = {}) {
191
191
  this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
@@ -199,7 +199,7 @@ export class CommonDao {
199
199
  return pipeline;
200
200
  opt.skipValidation ??= true;
201
201
  opt.errorMode ||= ErrorMode.SUPPRESS;
202
- return pipeline.map(async dbm => await this.dbmToBM(dbm, opt), { errorMode: opt.errorMode });
202
+ return pipeline.mapSync(dbm => this.dbmToBM(dbm, opt), { errorMode: opt.errorMode });
203
203
  }
204
204
  async queryIds(q, opt = {}) {
205
205
  this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
@@ -347,7 +347,7 @@ export class CommonDao {
347
347
  }
348
348
  this.assignIdCreatedUpdated(bm, opt); // mutates
349
349
  _typeCast(bm);
350
- const dbm = await this.bmToDBM(bm, opt); // validates BM
350
+ const dbm = this.bmToDBM(bm, opt); // validates BM
351
351
  this.cfg.hooks.beforeSave?.(dbm);
352
352
  const table = opt.table || this.cfg.table;
353
353
  const saveOptions = this.prepareSaveOptions(opt);
@@ -361,7 +361,7 @@ export class CommonDao {
361
361
  async saveAsDBM(dbm, opt = {}) {
362
362
  this.requireWriteAccess();
363
363
  this.assignIdCreatedUpdated(dbm, opt); // mutates
364
- const validDbm = await this.anyToDBM(dbm, opt);
364
+ const validDbm = this.anyToDBM(dbm, opt);
365
365
  this.cfg.hooks.beforeSave?.(validDbm);
366
366
  const table = opt.table || this.cfg.table;
367
367
  const saveOptions = this.prepareSaveOptions(opt);
@@ -377,7 +377,7 @@ export class CommonDao {
377
377
  return [];
378
378
  this.requireWriteAccess();
379
379
  bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt));
380
- const dbms = await this.bmsToDBM(bms, opt);
380
+ const dbms = this.bmsToDBM(bms, opt);
381
381
  if (this.cfg.hooks.beforeSave) {
382
382
  dbms.forEach(dbm => this.cfg.hooks.beforeSave(dbm));
383
383
  }
@@ -395,7 +395,7 @@ export class CommonDao {
395
395
  return [];
396
396
  this.requireWriteAccess();
397
397
  dbms.forEach(dbm => this.assignIdCreatedUpdated(dbm, opt));
398
- const validDbms = await this.anyToDBMs(dbms, opt);
398
+ const validDbms = this.anyToDBMs(dbms, opt);
399
399
  if (this.cfg.hooks.beforeSave) {
400
400
  validDbms.forEach(dbm => this.cfg.hooks.beforeSave(dbm));
401
401
  }
@@ -449,7 +449,7 @@ export class CommonDao {
449
449
  await p
450
450
  .map(async bm => {
451
451
  this.assignIdCreatedUpdated(bm, opt);
452
- const dbm = await this.bmToDBM(bm, opt);
452
+ const dbm = this.bmToDBM(bm, opt);
453
453
  beforeSave?.(dbm);
454
454
  return await this.dbmToStorageRow(dbm);
455
455
  }, { errorMode })
@@ -560,32 +560,31 @@ export class CommonDao {
560
560
  const { table } = this.cfg;
561
561
  return await this.cfg.db.incrementBatch(table, prop, incrementMap);
562
562
  }
563
- async dbmToBM(_dbm, opt = {}) {
563
+ dbmToBM(_dbm, opt = {}) {
564
564
  if (!_dbm)
565
565
  return null;
566
566
  // optimization: no need to run full joi DBM validation, cause BM validation will be run
567
567
  // const dbm = this.anyToDBM(_dbm, opt)
568
568
  const dbm = { ..._dbm, ...this.cfg.hooks.parseNaturalId(_dbm.id) };
569
569
  // DBM > BM
570
- const bm = ((await this.cfg.hooks.beforeDBMToBM?.(dbm)) || dbm);
570
+ const bm = (this.cfg.hooks.beforeDBMToBM?.(dbm) || dbm);
571
571
  // Validate/convert BM
572
572
  return this.validateAndConvert(bm, 'load', opt);
573
573
  }
574
- async dbmsToBM(dbms, opt = {}) {
575
- return await pMap(dbms, async dbm => await this.dbmToBM(dbm, opt));
574
+ dbmsToBM(dbms, opt = {}) {
575
+ return dbms.map(dbm => this.dbmToBM(dbm, opt));
576
576
  }
577
- async bmToDBM(bm, opt) {
577
+ bmToDBM(bm, opt) {
578
578
  if (bm === undefined)
579
579
  return null;
580
580
  // bm gets assigned to the new reference
581
581
  bm = this.validateAndConvert(bm, 'save', opt);
582
582
  // BM > DBM
583
- const dbm = ((await this.cfg.hooks.beforeBMToDBM?.(bm)) || bm);
583
+ const dbm = (this.cfg.hooks.beforeBMToDBM?.(bm) || bm);
584
584
  return dbm;
585
585
  }
586
- async bmsToDBM(bms, opt = {}) {
587
- // try/catch?
588
- return await pMap(bms, async bm => await this.bmToDBM(bm, opt));
586
+ bmsToDBM(bms, opt = {}) {
587
+ return bms.map(bm => this.bmToDBM(bm, opt));
589
588
  }
590
589
  // STORAGE LAYER (compression/decompression at DB boundary)
591
590
  // These methods convert between DBM (logical model) and storage format (physical, possibly compressed).
@@ -660,15 +659,13 @@ export class CommonDao {
660
659
  _typeCast(dbm);
661
660
  if (!Buffer.isBuffer(dbm.__compressed))
662
661
  return; // No compressed data
663
- try {
664
- const bufferString = await decompressZstdOrInflateToString(dbm.__compressed);
665
- const properties = JSON.parse(bufferString);
666
- dbm.__compressed = undefined;
667
- Object.assign(dbm, properties);
668
- }
669
- catch { }
662
+ // todo: stop supporting Inflate when we are sure that we have migrated everything to zstd
663
+ const bufferString = await decompressZstdOrInflateToString(dbm.__compressed);
664
+ const properties = JSON.parse(bufferString);
665
+ dbm.__compressed = undefined;
666
+ Object.assign(dbm, properties);
670
667
  }
671
- async anyToDBM(dbm, _opt = {}) {
668
+ anyToDBM(dbm, _opt = {}) {
672
669
  if (!dbm)
673
670
  return null;
674
671
  // this shouldn't be happening on load! but should on save!
@@ -678,8 +675,8 @@ export class CommonDao {
678
675
  // return this.validateAndConvert(dbm, this.cfg.dbmSchema, DBModelType.DBM, opt)
679
676
  return dbm;
680
677
  }
681
- async anyToDBMs(rows, opt = {}) {
682
- return await pMap(rows, async entity => await this.anyToDBM(entity, opt));
678
+ anyToDBMs(rows, opt = {}) {
679
+ return rows.map(entity => this.anyToDBM(entity, opt));
683
680
  }
684
681
  /**
685
682
  * Returns *converted value* (NOT the same reference).
@@ -751,73 +748,6 @@ export class CommonDao {
751
748
  opt: opt,
752
749
  };
753
750
  }
754
- /**
755
- * Helper to decompress legacy compressed data when migrating away from auto-compression.
756
- * Use as your `beforeDBMToBM` hook to decompress legacy rows on read.
757
- *
758
- * @example
759
- * const dao = new CommonDao({
760
- * hooks: {
761
- * beforeDBMToBM: CommonDao.decompressLegacyRow,
762
- * }
763
- * })
764
- *
765
- * // Or within an existing hook:
766
- * beforeDBMToBM: async (dbm) => {
767
- * await CommonDao.decompressLegacyRow(dbm)
768
- * // ... other transformations
769
- * return dbm
770
- * }
771
- */
772
- static async decompressLegacyRow(row) {
773
- // Check both __compressed (current) and data (legacy) for backward compatibility
774
- const compressed = row.__compressed ?? row.data;
775
- if (!Buffer.isBuffer(compressed))
776
- return row;
777
- try {
778
- const bufferString = await decompressZstdOrInflateToString(compressed);
779
- const properties = JSON.parse(bufferString);
780
- row.__compressed = undefined;
781
- row.data = undefined;
782
- Object.assign(row, properties);
783
- }
784
- catch {
785
- // Decompression failed - field is not compressed, leave as-is
786
- }
787
- return row;
788
- }
789
- /**
790
- * Temporary helper to migrate from the old `data` compressed property to the new `__compressed` property.
791
- * Use as your `beforeDBMToBM` hook during the migration period.
792
- *
793
- * Migration steps:
794
- * 1. Add `beforeDBMToBM: CommonDao.migrateCompressedDataProperty` to your hooks
795
- * 2. Deploy - old data (with `data` property) will be decompressed on read and recompressed to `__compressed` on write
796
- * 3. Once all data has been naturally rewritten, remove the hook
797
- *
798
- * @example
799
- * const dao = new CommonDao({
800
- * compress: { keys: ['field1', 'field2'] },
801
- * hooks: {
802
- * beforeDBMToBM: CommonDao.migrateCompressedDataProperty,
803
- * }
804
- * })
805
- */
806
- static async migrateCompressedDataProperty(row) {
807
- const data = row.data;
808
- if (!Buffer.isBuffer(data))
809
- return row;
810
- try {
811
- const bufferString = await decompressZstdOrInflateToString(data);
812
- const properties = JSON.parse(bufferString);
813
- row.data = undefined;
814
- Object.assign(row, properties);
815
- }
816
- catch {
817
- // Decompression failed - data field is not compressed, leave as-is
818
- }
819
- return row;
820
- }
821
751
  /**
822
752
  * Load rows (by their ids) from Multiple tables at once.
823
753
  * An optimized way to load data, minimizing DB round-trips.
@@ -881,7 +811,7 @@ export class CommonDao {
881
811
  const row = dbmByTableById[table][input.id];
882
812
  // Decompress before converting to BM
883
813
  const dbm = row ? await dao.storageRowToDBM(row) : undefined;
884
- bmsByProp[prop] = (await dao.dbmToBM(dbm, opt)) || null;
814
+ bmsByProp[prop] = dao.dbmToBM(dbm, opt) || null;
885
815
  }
886
816
  else {
887
817
  // Plural
@@ -890,7 +820,7 @@ export class CommonDao {
890
820
  const rows = input.ids.map(id => dbmByTableById[table][id]).filter(_isTruthy);
891
821
  // Decompress before converting to BM
892
822
  const dbms = await dao.storageRowsToDBMs(rows);
893
- bmsByProp[prop] = await dao.dbmsToBM(dbms, opt);
823
+ bmsByProp[prop] = dao.dbmsToBM(dbms, opt);
894
824
  }
895
825
  });
896
826
  return bmsByProp;
@@ -941,7 +871,7 @@ export class CommonDao {
941
871
  }
942
872
  }
943
873
  dao.assignIdCreatedUpdated(row, opt);
944
- const dbm = await dao.bmToDBM(row, opt);
874
+ const dbm = dao.bmToDBM(row, opt);
945
875
  dao.cfg.hooks.beforeSave?.(dbm);
946
876
  const storageRow = await dao.dbmToStorageRow(dbm);
947
877
  dbmsByTable[table].push(storageRow);
@@ -949,7 +879,7 @@ export class CommonDao {
949
879
  else {
950
880
  // Plural
951
881
  input.rows.forEach(bm => dao.assignIdCreatedUpdated(bm, opt));
952
- const dbms = await dao.bmsToDBM(input.rows, opt);
882
+ const dbms = dao.bmsToDBM(input.rows, opt);
953
883
  if (dao.cfg.hooks.beforeSave) {
954
884
  dbms.forEach(dbm => dao.cfg.hooks.beforeSave(dbm));
955
885
  }
@@ -31,8 +31,8 @@ export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntit
31
31
  * - patch, patchAsDBM
32
32
  */
33
33
  beforeCreate: (bm: Partial<BM>) => Partial<BM>;
34
- beforeDBMToBM: (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>;
35
- beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>;
34
+ beforeDBMToBM: (dbm: DBM) => Partial<BM>;
35
+ beforeBMToDBM: (bm: BM) => Partial<DBM>;
36
36
  /**
37
37
  * Allows to access the DBM just after it has been loaded from the DB.
38
38
  *
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@naturalcycles/db-lib",
3
3
  "type": "module",
4
- "version": "10.45.2",
4
+ "version": "10.46.1",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@naturalcycles/nodejs-lib": "^15"
8
8
  },
9
9
  "devDependencies": {
10
- "@typescript/native-preview": "7.0.0-dev.20260201.1",
10
+ "@typescript/native-preview": "7.0.0-dev.20260301.1",
11
11
  "@naturalcycles/dev-lib": "18.4.2"
12
12
  },
13
13
  "files": [
@@ -40,8 +40,8 @@ export interface CommonDaoHooks<
40
40
  */
41
41
  beforeCreate: (bm: Partial<BM>) => Partial<BM>
42
42
 
43
- beforeDBMToBM: (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>
44
- beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>
43
+ beforeDBMToBM: (dbm: DBM) => Partial<BM>
44
+ beforeBMToDBM: (bm: BM) => Partial<DBM>
45
45
 
46
46
  /**
47
47
  * Allows to access the DBM just after it has been loaded from the DB.
@@ -131,23 +131,23 @@ export class CommonDao<
131
131
  async getById(id?: ID | null, opt: CommonDaoReadOptions = {}): Promise<BM | null> {
132
132
  if (!id) return null
133
133
  const [dbm] = await this.loadByIds([id], opt)
134
- return await this.dbmToBM(dbm, opt)
134
+ return this.dbmToBM(dbm, opt)
135
135
  }
136
136
 
137
137
  async getByIdAsDBM(id?: ID | null, opt: CommonDaoReadOptions = {}): Promise<DBM | null> {
138
138
  if (!id) return null
139
139
  const [row] = await this.loadByIds([id], opt)
140
- return await this.anyToDBM(row, opt)
140
+ return this.anyToDBM(row, opt)
141
141
  }
142
142
 
143
143
  async getByIds(ids: ID[], opt: CommonDaoReadOptions = {}): Promise<BM[]> {
144
144
  const dbms = await this.loadByIds(ids, opt)
145
- return await this.dbmsToBM(dbms, opt)
145
+ return this.dbmsToBM(dbms, opt)
146
146
  }
147
147
 
148
148
  async getByIdsAsDBM(ids: ID[], opt: CommonDaoReadOptions = {}): Promise<DBM[]> {
149
149
  const rows = await this.loadByIds(ids, opt)
150
- return await this.anyToDBMs(rows)
150
+ return this.anyToDBMs(rows)
151
151
  }
152
152
 
153
153
  // DRY private method
@@ -217,7 +217,7 @@ export class CommonDao<
217
217
  const { rows: rawRows, ...queryResult } = await this.cfg.db.runQuery<DBM>(q, opt)
218
218
  const isPartialQuery = !!q._selectedFieldNames
219
219
  const rows = isPartialQuery ? rawRows : await this.storageRowsToDBMs(rawRows)
220
- const bms = isPartialQuery ? (rows as any[]) : await this.dbmsToBM(rows, opt)
220
+ const bms = isPartialQuery ? (rows as any[]) : this.dbmsToBM(rows, opt)
221
221
  return {
222
222
  rows: bms,
223
223
  ...queryResult,
@@ -238,7 +238,7 @@ export class CommonDao<
238
238
  const { rows: rawRows, ...queryResult } = await this.cfg.db.runQuery<DBM>(q, opt)
239
239
  const isPartialQuery = !!q._selectedFieldNames
240
240
  const rows = isPartialQuery ? rawRows : await this.storageRowsToDBMs(rawRows)
241
- const dbms = isPartialQuery ? rows : await this.anyToDBMs(rows, opt)
241
+ const dbms = isPartialQuery ? rows : this.anyToDBMs(rows, opt)
242
242
  return { rows: dbms, ...queryResult }
243
243
  }
244
244
 
@@ -263,7 +263,7 @@ export class CommonDao<
263
263
  opt.skipValidation ??= true
264
264
  opt.errorMode ||= ErrorMode.SUPPRESS
265
265
 
266
- return pipeline.map(async dbm => await this.anyToDBM(dbm, opt), { errorMode: opt.errorMode })
266
+ return pipeline.mapSync(dbm => this.anyToDBM(dbm, opt), { errorMode: opt.errorMode })
267
267
  }
268
268
 
269
269
  streamQuery(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<BM> = {}): Pipeline<BM> {
@@ -281,7 +281,7 @@ export class CommonDao<
281
281
  opt.skipValidation ??= true
282
282
  opt.errorMode ||= ErrorMode.SUPPRESS
283
283
 
284
- return pipeline.map(async dbm => await this.dbmToBM(dbm, opt), { errorMode: opt.errorMode })
284
+ return pipeline.mapSync(dbm => this.dbmToBM(dbm, opt), { errorMode: opt.errorMode })
285
285
  }
286
286
 
287
287
  async queryIds(q: DBQuery<DBM>, opt: CommonDaoReadOptions = {}): Promise<ID[]> {
@@ -466,7 +466,7 @@ export class CommonDao<
466
466
 
467
467
  this.assignIdCreatedUpdated(bm, opt) // mutates
468
468
  _typeCast<BM>(bm)
469
- const dbm = await this.bmToDBM(bm, opt) // validates BM
469
+ const dbm = this.bmToDBM(bm, opt) // validates BM
470
470
  this.cfg.hooks!.beforeSave?.(dbm)
471
471
  const table = opt.table || this.cfg.table
472
472
  const saveOptions = this.prepareSaveOptions(opt)
@@ -484,7 +484,7 @@ export class CommonDao<
484
484
  async saveAsDBM(dbm: Unsaved<DBM>, opt: CommonDaoSaveOptions<BM, DBM> = {}): Promise<DBM> {
485
485
  this.requireWriteAccess()
486
486
  this.assignIdCreatedUpdated(dbm, opt) // mutates
487
- const validDbm = await this.anyToDBM(dbm, opt)
487
+ const validDbm = this.anyToDBM(dbm, opt)
488
488
  this.cfg.hooks!.beforeSave?.(validDbm)
489
489
  const table = opt.table || this.cfg.table
490
490
  const saveOptions = this.prepareSaveOptions(opt)
@@ -503,7 +503,7 @@ export class CommonDao<
503
503
  if (!bms.length) return []
504
504
  this.requireWriteAccess()
505
505
  bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt))
506
- const dbms = await this.bmsToDBM(bms as BM[], opt)
506
+ const dbms = this.bmsToDBM(bms as BM[], opt)
507
507
  if (this.cfg.hooks!.beforeSave) {
508
508
  dbms.forEach(dbm => this.cfg.hooks!.beforeSave!(dbm))
509
509
  }
@@ -527,7 +527,7 @@ export class CommonDao<
527
527
  if (!dbms.length) return []
528
528
  this.requireWriteAccess()
529
529
  dbms.forEach(dbm => this.assignIdCreatedUpdated(dbm, opt))
530
- const validDbms = await this.anyToDBMs(dbms as DBM[], opt)
530
+ const validDbms = this.anyToDBMs(dbms as DBM[], opt)
531
531
  if (this.cfg.hooks!.beforeSave) {
532
532
  validDbms.forEach(dbm => this.cfg.hooks!.beforeSave!(dbm))
533
533
  }
@@ -600,7 +600,7 @@ export class CommonDao<
600
600
  .map(
601
601
  async bm => {
602
602
  this.assignIdCreatedUpdated(bm, opt)
603
- const dbm = await this.bmToDBM(bm, opt)
603
+ const dbm = this.bmToDBM(bm, opt)
604
604
  beforeSave?.(dbm)
605
605
  return await this.dbmToStorageRow(dbm)
606
606
  },
@@ -739,9 +739,9 @@ export class CommonDao<
739
739
 
740
740
  // CONVERSIONS
741
741
 
742
- async dbmToBM(_dbm: undefined, opt?: CommonDaoOptions): Promise<null>
743
- async dbmToBM(_dbm?: DBM, opt?: CommonDaoOptions): Promise<BM>
744
- async dbmToBM(_dbm?: DBM, opt: CommonDaoOptions = {}): Promise<BM | null> {
742
+ dbmToBM(_dbm: undefined, opt?: CommonDaoOptions): null
743
+ dbmToBM(_dbm?: DBM, opt?: CommonDaoOptions): BM
744
+ dbmToBM(_dbm?: DBM, opt: CommonDaoOptions = {}): BM | null {
745
745
  if (!_dbm) return null
746
746
 
747
747
  // optimization: no need to run full joi DBM validation, cause BM validation will be run
@@ -749,37 +749,36 @@ export class CommonDao<
749
749
  const dbm: DBM = { ..._dbm, ...this.cfg.hooks!.parseNaturalId!(_dbm.id as ID) }
750
750
 
751
751
  // DBM > BM
752
- const bm = ((await this.cfg.hooks!.beforeDBMToBM?.(dbm)) || dbm) as Partial<BM>
752
+ const bm = (this.cfg.hooks!.beforeDBMToBM?.(dbm) || dbm) as Partial<BM>
753
753
 
754
754
  // Validate/convert BM
755
755
  return this.validateAndConvert(bm, 'load', opt)
756
756
  }
757
757
 
758
- async dbmsToBM(dbms: DBM[], opt: CommonDaoOptions = {}): Promise<BM[]> {
759
- return await pMap(dbms, async dbm => await this.dbmToBM(dbm, opt))
758
+ dbmsToBM(dbms: DBM[], opt: CommonDaoOptions = {}): BM[] {
759
+ return dbms.map(dbm => this.dbmToBM(dbm, opt))
760
760
  }
761
761
 
762
762
  /**
763
763
  * Mutates object with properties: id, created, updated.
764
764
  * Returns DBM (new reference).
765
765
  */
766
- async bmToDBM(bm: undefined, opt?: CommonDaoOptions): Promise<null>
767
- async bmToDBM(bm?: BM, opt?: CommonDaoOptions): Promise<DBM>
768
- async bmToDBM(bm?: BM, opt?: CommonDaoOptions): Promise<DBM | null> {
766
+ bmToDBM(bm: undefined, opt?: CommonDaoOptions): null
767
+ bmToDBM(bm?: BM, opt?: CommonDaoOptions): DBM
768
+ bmToDBM(bm?: BM, opt?: CommonDaoOptions): DBM | null {
769
769
  if (bm === undefined) return null
770
770
 
771
771
  // bm gets assigned to the new reference
772
772
  bm = this.validateAndConvert(bm, 'save', opt)
773
773
 
774
774
  // BM > DBM
775
- const dbm = ((await this.cfg.hooks!.beforeBMToDBM?.(bm)) || bm) as DBM
775
+ const dbm = (this.cfg.hooks!.beforeBMToDBM?.(bm) || bm) as DBM
776
776
 
777
777
  return dbm
778
778
  }
779
779
 
780
- async bmsToDBM(bms: BM[], opt: CommonDaoOptions = {}): Promise<DBM[]> {
781
- // try/catch?
782
- return await pMap(bms, async bm => await this.bmToDBM(bm, opt))
780
+ bmsToDBM(bms: BM[], opt: CommonDaoOptions = {}): DBM[] {
781
+ return bms.map(bm => this.bmToDBM(bm, opt))
783
782
  }
784
783
 
785
784
  // STORAGE LAYER (compression/decompression at DB boundary)
@@ -857,17 +856,16 @@ export class CommonDao<
857
856
  _typeCast<Compressed<DBM>>(dbm)
858
857
  if (!Buffer.isBuffer(dbm.__compressed)) return // No compressed data
859
858
 
860
- try {
861
- const bufferString = await decompressZstdOrInflateToString(dbm.__compressed)
862
- const properties = JSON.parse(bufferString)
863
- dbm.__compressed = undefined
864
- Object.assign(dbm, properties)
865
- } catch {}
859
+ // todo: stop supporting Inflate when we are sure that we have migrated everything to zstd
860
+ const bufferString = await decompressZstdOrInflateToString(dbm.__compressed)
861
+ const properties = JSON.parse(bufferString)
862
+ dbm.__compressed = undefined
863
+ Object.assign(dbm, properties)
866
864
  }
867
865
 
868
- async anyToDBM(dbm: undefined, opt?: CommonDaoOptions): Promise<null>
869
- async anyToDBM(dbm?: any, opt?: CommonDaoOptions): Promise<DBM>
870
- async anyToDBM(dbm?: DBM, _opt: CommonDaoOptions = {}): Promise<DBM | null> {
866
+ anyToDBM(dbm: undefined, opt?: CommonDaoOptions): null
867
+ anyToDBM(dbm?: any, opt?: CommonDaoOptions): DBM
868
+ anyToDBM(dbm?: DBM, _opt: CommonDaoOptions = {}): DBM | null {
871
869
  if (!dbm) return null
872
870
 
873
871
  // this shouldn't be happening on load! but should on save!
@@ -880,8 +878,8 @@ export class CommonDao<
880
878
  return dbm
881
879
  }
882
880
 
883
- async anyToDBMs(rows: DBM[], opt: CommonDaoOptions = {}): Promise<DBM[]> {
884
- return await pMap(rows, async entity => await this.anyToDBM(entity, opt))
881
+ anyToDBMs(rows: DBM[], opt: CommonDaoOptions = {}): DBM[] {
882
+ return rows.map(entity => this.anyToDBM(entity, opt))
885
883
  }
886
884
 
887
885
  /**
@@ -971,75 +969,6 @@ export class CommonDao<
971
969
  }
972
970
  }
973
971
 
974
- /**
975
- * Helper to decompress legacy compressed data when migrating away from auto-compression.
976
- * Use as your `beforeDBMToBM` hook to decompress legacy rows on read.
977
- *
978
- * @example
979
- * const dao = new CommonDao({
980
- * hooks: {
981
- * beforeDBMToBM: CommonDao.decompressLegacyRow,
982
- * }
983
- * })
984
- *
985
- * // Or within an existing hook:
986
- * beforeDBMToBM: async (dbm) => {
987
- * await CommonDao.decompressLegacyRow(dbm)
988
- * // ... other transformations
989
- * return dbm
990
- * }
991
- */
992
- static async decompressLegacyRow<T extends ObjectWithId>(this: void, row: T): Promise<T> {
993
- // Check both __compressed (current) and data (legacy) for backward compatibility
994
- const compressed = (row as any).__compressed ?? (row as any).data
995
- if (!Buffer.isBuffer(compressed)) return row
996
-
997
- try {
998
- const bufferString = await decompressZstdOrInflateToString(compressed)
999
- const properties = JSON.parse(bufferString)
1000
- ;(row as any).__compressed = undefined
1001
- ;(row as any).data = undefined
1002
- Object.assign(row, properties)
1003
- } catch {
1004
- // Decompression failed - field is not compressed, leave as-is
1005
- }
1006
-
1007
- return row
1008
- }
1009
-
1010
- /**
1011
- * Temporary helper to migrate from the old `data` compressed property to the new `__compressed` property.
1012
- * Use as your `beforeDBMToBM` hook during the migration period.
1013
- *
1014
- * Migration steps:
1015
- * 1. Add `beforeDBMToBM: CommonDao.migrateCompressedDataProperty` to your hooks
1016
- * 2. Deploy - old data (with `data` property) will be decompressed on read and recompressed to `__compressed` on write
1017
- * 3. Once all data has been naturally rewritten, remove the hook
1018
- *
1019
- * @example
1020
- * const dao = new CommonDao({
1021
- * compress: { keys: ['field1', 'field2'] },
1022
- * hooks: {
1023
- * beforeDBMToBM: CommonDao.migrateCompressedDataProperty,
1024
- * }
1025
- * })
1026
- */
1027
- static async migrateCompressedDataProperty<T extends ObjectWithId>(row: T): Promise<T> {
1028
- const data = (row as any).data
1029
- if (!Buffer.isBuffer(data)) return row
1030
-
1031
- try {
1032
- const bufferString = await decompressZstdOrInflateToString(data)
1033
- const properties = JSON.parse(bufferString)
1034
- ;(row as any).data = undefined
1035
- Object.assign(row, properties)
1036
- } catch {
1037
- // Decompression failed - data field is not compressed, leave as-is
1038
- }
1039
-
1040
- return row
1041
- }
1042
-
1043
972
  /**
1044
973
  * Load rows (by their ids) from Multiple tables at once.
1045
974
  * An optimized way to load data, minimizing DB round-trips.
@@ -1128,7 +1057,7 @@ export class CommonDao<
1128
1057
  const row = dbmByTableById[table]![input.id]
1129
1058
  // Decompress before converting to BM
1130
1059
  const dbm = row ? await dao.storageRowToDBM(row) : undefined
1131
- bmsByProp[prop] = (await dao.dbmToBM(dbm, opt)) || null
1060
+ bmsByProp[prop] = dao.dbmToBM(dbm, opt) || null
1132
1061
  } else {
1133
1062
  // Plural
1134
1063
  // We apply filtering, to be able to support multiple input props fetching from the same table.
@@ -1136,7 +1065,7 @@ export class CommonDao<
1136
1065
  const rows = input.ids.map(id => dbmByTableById[table]![id]).filter(_isTruthy)
1137
1066
  // Decompress before converting to BM
1138
1067
  const dbms = await dao.storageRowsToDBMs(rows)
1139
- bmsByProp[prop] = await dao.dbmsToBM(dbms, opt)
1068
+ bmsByProp[prop] = dao.dbmsToBM(dbms, opt)
1140
1069
  }
1141
1070
  })
1142
1071
 
@@ -1198,14 +1127,14 @@ export class CommonDao<
1198
1127
  }
1199
1128
 
1200
1129
  dao.assignIdCreatedUpdated(row, opt)
1201
- const dbm = await dao.bmToDBM(row, opt)
1130
+ const dbm = dao.bmToDBM(row, opt)
1202
1131
  dao.cfg.hooks!.beforeSave?.(dbm)
1203
1132
  const storageRow = await dao.dbmToStorageRow(dbm)
1204
1133
  dbmsByTable[table].push(storageRow)
1205
1134
  } else {
1206
1135
  // Plural
1207
1136
  input.rows.forEach(bm => dao.assignIdCreatedUpdated(bm, opt))
1208
- const dbms = await dao.bmsToDBM(input.rows, opt)
1137
+ const dbms = dao.bmsToDBM(input.rows, opt)
1209
1138
  if (dao.cfg.hooks!.beforeSave) {
1210
1139
  dbms.forEach(dbm => dao.cfg.hooks!.beforeSave!(dbm))
1211
1140
  }