@naturalcycles/db-lib 10.51.0 → 10.53.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.
@@ -51,7 +51,7 @@ export class CommonDao {
51
51
  // then we need to ensure that the '__compressed' property is part of the index exclusion list.
52
52
  if (this.cfg.compress?.keys) {
53
53
  const current = this.cfg.excludeFromIndexes;
54
- this.cfg.excludeFromIndexes = current ? [...current] : [];
54
+ this.cfg.excludeFromIndexes = current ? current.slice() : [];
55
55
  if (!this.cfg.excludeFromIndexes.includes('__compressed')) {
56
56
  this.cfg.excludeFromIndexes.push('__compressed');
57
57
  }
@@ -653,12 +653,17 @@ export class CommonDao {
653
653
  async compress(dbm) {
654
654
  if (!this.cfg.compress?.keys.length)
655
655
  return; // No compression requested
656
- const { keys, level = 1 } = this.cfg.compress;
656
+ const { keys, level = 1, warnSizeBytes, onOversizeWarning } = this.cfg.compress;
657
657
  const properties = _pick(dbm, keys);
658
658
  const bufferString = JSON.stringify(properties);
659
659
  // Unlike `decompress`, we're testing to use async zstd compression.
660
660
  // Async Decompression leaks memory severely. But Compression seems fine.
661
661
  const __compressed = await zip2.zstdCompress(bufferString, level);
662
+ if (warnSizeBytes && onOversizeWarning && __compressed.byteLength > warnSizeBytes) {
663
+ // Shallow-clone: `dbm` is mutated below (omit source keys + assign __compressed),
664
+ // so we hand the hook a snapshot of the pre-mutation state.
665
+ onOversizeWarning({ ...dbm }, __compressed.byteLength);
666
+ }
662
667
  _omitWithUndefined(dbm, _objectKeys(properties), { mutate: true });
663
668
  Object.assign(dbm, { __compressed });
664
669
  }
@@ -792,7 +797,7 @@ export class CommonDao {
792
797
  }
793
798
  const idsByTable = {};
794
799
  for (const [table, idSet] of _stringMapEntries(idSetByTable)) {
795
- idsByTable[table] = [...idSet];
800
+ idsByTable[table] = Array.from(idSet);
796
801
  }
797
802
  return idsByTable;
798
803
  }
@@ -185,6 +185,22 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
185
185
  * Undefined will default to level 1 (not the 3, which is the zstd default)
186
186
  */
187
187
  level?: Integer;
188
+ /**
189
+ * If set, `onOversizeWarning` is invoked whenever the resulting `__compressed`
190
+ * Buffer exceeds this size in bytes. Useful for monitoring rows approaching
191
+ * DB row-size limits (e.g. Datastore's 1MB per entity).
192
+ *
193
+ * No-op unless `onOversizeWarning` is also provided.
194
+ */
195
+ warnSizeBytes?: number;
196
+ /**
197
+ * Invoked when the `__compressed` Buffer exceeds `warnSizeBytes`.
198
+ *
199
+ * Called with a shallow clone of the DBM (since the original is mutated
200
+ * later in the compression step) and the resulting compressed size in bytes.
201
+ * The consumer decides how to surface the warning (Sentry, logger, metrics, etc.).
202
+ */
203
+ onOversizeWarning?: (dbm: DBM, size: number) => void;
188
204
  };
189
205
  }
190
206
  /**
@@ -1,3 +1,4 @@
1
+ import { comparators } from '@naturalcycles/js-lib/array';
1
2
  import { _get, _pick } from '@naturalcycles/js-lib/object/object.util.js';
2
3
  const FILTER_FNS = {
3
4
  '==': (v, val) => v === val,
@@ -30,15 +31,7 @@ export function queryInMemory(q, rows = []) {
30
31
  const [order] = q._orders;
31
32
  if (order) {
32
33
  const { name, descending } = order;
33
- rows = rows.sort((a, b) => {
34
- // oxlint-disable-next-line eqeqeq
35
- if (a[name] == b[name])
36
- return 0;
37
- if (descending) {
38
- return a[name] < b[name] ? 1 : -1;
39
- }
40
- return a[name] > b[name] ? 1 : -1;
41
- });
34
+ rows = rows.sort(comparators.by(r => r[name], { dir: descending ? 'desc' : 'asc' }));
42
35
  }
43
36
  // .offset()
44
37
  if (q._offsetValue) {
@@ -101,12 +101,12 @@ export class DBQuery {
101
101
  }
102
102
  clone() {
103
103
  return _objectAssign(new DBQuery(this.table), {
104
- _filters: [...this._filters],
104
+ _filters: this._filters.slice(),
105
105
  _limitValue: this._limitValue,
106
106
  _offsetValue: this._offsetValue,
107
- _orders: [...this._orders],
108
- _selectedFieldNames: this._selectedFieldNames && [...this._selectedFieldNames],
109
- _groupByFieldNames: this._groupByFieldNames && [...this._groupByFieldNames],
107
+ _orders: this._orders.slice(),
108
+ _selectedFieldNames: this._selectedFieldNames?.slice(),
109
+ _groupByFieldNames: this._groupByFieldNames?.slice(),
110
110
  _distinct: this._distinct,
111
111
  _startCursor: this._startCursor,
112
112
  _endCursor: this._endCursor,
@@ -130,7 +130,7 @@ export async function runCommonDBTest(db, quirks = {}) {
130
130
  test('query order by k1 desc', async () => {
131
131
  const q = new DBQuery(TEST_TABLE).order('k1', true);
132
132
  const { rows } = await db.runQuery(q);
133
- expectMatch([...items].reverse(), rows, quirks);
133
+ expectMatch(items.toReversed(), rows, quirks);
134
134
  });
135
135
  }
136
136
  if (support.dbQuerySelectFields) {
@@ -160,7 +160,7 @@ export async function runCommonDaoTest(db, quirks = {}) {
160
160
  if (support.dbQueryOrder) {
161
161
  test('query order by k1 desc', async () => {
162
162
  const rows = await dao.query().order('k1', true).runQuery();
163
- expectMatch([...expectedItems].reverse(), rows, quirks);
163
+ expectMatch(expectedItems.toReversed(), rows, quirks);
164
164
  });
165
165
  }
166
166
  if (support.dbQuerySelectFields) {
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@naturalcycles/db-lib",
3
3
  "type": "module",
4
- "version": "10.51.0",
4
+ "version": "10.53.0",
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.20260401.1",
11
- "@naturalcycles/dev-lib": "20.42.0"
10
+ "@typescript/native-preview": "beta",
11
+ "@naturalcycles/dev-lib": "18.4.2"
12
12
  },
13
13
  "files": [
14
14
  "dist",
@@ -224,6 +224,22 @@ export interface CommonDaoCfg<
224
224
  * Undefined will default to level 1 (not the 3, which is the zstd default)
225
225
  */
226
226
  level?: Integer
227
+ /**
228
+ * If set, `onOversizeWarning` is invoked whenever the resulting `__compressed`
229
+ * Buffer exceeds this size in bytes. Useful for monitoring rows approaching
230
+ * DB row-size limits (e.g. Datastore's 1MB per entity).
231
+ *
232
+ * No-op unless `onOversizeWarning` is also provided.
233
+ */
234
+ warnSizeBytes?: number
235
+ /**
236
+ * Invoked when the `__compressed` Buffer exceeds `warnSizeBytes`.
237
+ *
238
+ * Called with a shallow clone of the DBM (since the original is mutated
239
+ * later in the compression step) and the resulting compressed size in bytes.
240
+ * The consumer decides how to surface the warning (Sentry, logger, metrics, etc.).
241
+ */
242
+ onOversizeWarning?: (dbm: DBM, size: number) => void
227
243
  }
228
244
  }
229
245
 
@@ -95,7 +95,7 @@ export class CommonDao<
95
95
  // then we need to ensure that the '__compressed' property is part of the index exclusion list.
96
96
  if (this.cfg.compress?.keys) {
97
97
  const current = this.cfg.excludeFromIndexes
98
- this.cfg.excludeFromIndexes = current ? [...current] : []
98
+ this.cfg.excludeFromIndexes = current ? current.slice() : []
99
99
  if (!this.cfg.excludeFromIndexes.includes('__compressed' as any)) {
100
100
  this.cfg.excludeFromIndexes.push('__compressed' as any)
101
101
  }
@@ -849,12 +849,17 @@ export class CommonDao<
849
849
  private async compress(dbm: DBM): Promise<void> {
850
850
  if (!this.cfg.compress?.keys.length) return // No compression requested
851
851
 
852
- const { keys, level = 1 } = this.cfg.compress
852
+ const { keys, level = 1, warnSizeBytes, onOversizeWarning } = this.cfg.compress
853
853
  const properties = _pick(dbm, keys)
854
854
  const bufferString = JSON.stringify(properties)
855
855
  // Unlike `decompress`, we're testing to use async zstd compression.
856
856
  // Async Decompression leaks memory severely. But Compression seems fine.
857
857
  const __compressed = await zip2.zstdCompress(bufferString, level)
858
+ if (warnSizeBytes && onOversizeWarning && __compressed.byteLength > warnSizeBytes) {
859
+ // Shallow-clone: `dbm` is mutated below (omit source keys + assign __compressed),
860
+ // so we hand the hook a snapshot of the pre-mutation state.
861
+ onOversizeWarning({ ...dbm }, __compressed.byteLength)
862
+ }
858
863
  _omitWithUndefined(dbm as any, _objectKeys(properties), { mutate: true })
859
864
  Object.assign(dbm, { __compressed })
860
865
  }
@@ -1028,7 +1033,7 @@ export class CommonDao<
1028
1033
 
1029
1034
  const idsByTable: StringMap<string[]> = {}
1030
1035
  for (const [table, idSet] of _stringMapEntries(idSetByTable)) {
1031
- idsByTable[table] = [...idSet]
1036
+ idsByTable[table] = Array.from(idSet)
1032
1037
  }
1033
1038
  return idsByTable
1034
1039
  }
@@ -1,3 +1,4 @@
1
+ import { comparators } from '@naturalcycles/js-lib/array'
1
2
  import { _get, _pick } from '@naturalcycles/js-lib/object/object.util.js'
2
3
  import type { ObjectWithId } from '@naturalcycles/js-lib/types'
3
4
  import type { DBQuery, DBQueryFilterOperator } from '../query/dbQuery.js'
@@ -38,15 +39,9 @@ export function queryInMemory<ROW extends ObjectWithId>(q: DBQuery<ROW>, rows: R
38
39
  const [order] = q._orders
39
40
  if (order) {
40
41
  const { name, descending } = order
41
- rows = rows.sort((a, b) => {
42
- // oxlint-disable-next-line eqeqeq
43
- if (a[name] == b[name]) return 0
44
-
45
- if (descending) {
46
- return a[name] < b[name] ? 1 : -1
47
- }
48
- return a[name] > b[name] ? 1 : -1
49
- })
42
+ rows = rows.sort(
43
+ comparators.by(r => r[name] as string | number, { dir: descending ? 'desc' : 'asc' }),
44
+ )
50
45
  }
51
46
 
52
47
  // .offset()
@@ -171,12 +171,12 @@ export class DBQuery<ROW extends ObjectWithId> {
171
171
 
172
172
  clone(): DBQuery<ROW> {
173
173
  return _objectAssign(new DBQuery<ROW>(this.table), {
174
- _filters: [...this._filters],
174
+ _filters: this._filters.slice(),
175
175
  _limitValue: this._limitValue,
176
176
  _offsetValue: this._offsetValue,
177
- _orders: [...this._orders],
178
- _selectedFieldNames: this._selectedFieldNames && [...this._selectedFieldNames],
179
- _groupByFieldNames: this._groupByFieldNames && [...this._groupByFieldNames],
177
+ _orders: this._orders.slice(),
178
+ _selectedFieldNames: this._selectedFieldNames?.slice(),
179
+ _groupByFieldNames: this._groupByFieldNames?.slice(),
180
180
  _distinct: this._distinct,
181
181
  _startCursor: this._startCursor,
182
182
  _endCursor: this._endCursor,
@@ -192,7 +192,7 @@ export async function runCommonDBTest(
192
192
  test('query order by k1 desc', async () => {
193
193
  const q = new DBQuery<TestItemDBM>(TEST_TABLE).order('k1', true)
194
194
  const { rows } = await db.runQuery(q)
195
- expectMatch([...items].reverse(), rows, quirks)
195
+ expectMatch(items.toReversed(), rows, quirks)
196
196
  })
197
197
  }
198
198
 
@@ -213,7 +213,7 @@ export async function runCommonDaoTest(
213
213
  if (support.dbQueryOrder) {
214
214
  test('query order by k1 desc', async () => {
215
215
  const rows = await dao.query().order('k1', true).runQuery()
216
- expectMatch([...expectedItems].reverse(), rows, quirks)
216
+ expectMatch(expectedItems.toReversed(), rows, quirks)
217
217
  })
218
218
  }
219
219