@naturalcycles/db-lib 8.19.0 → 8.21.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ## [8.21.1](https://github.com/NaturalCycles/db-lib/compare/v8.21.0...v8.21.1) (2021-10-18)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * test for saving/loading undefined values ([e7a8dcf](https://github.com/NaturalCycles/db-lib/commit/e7a8dcff296ba7c8c23c4157510dd79fe7ef1727))
7
+
8
+ # [8.21.0](https://github.com/NaturalCycles/db-lib/compare/v8.20.1...v8.21.0) (2021-10-18)
9
+
10
+
11
+ ### Features
12
+
13
+ * preserve `null` values in CommonDao load and save ([6f9b0d6](https://github.com/NaturalCycles/db-lib/commit/6f9b0d6e8419d2d1a0c025b7375c2504c3b267d5))
14
+
15
+ ## [8.20.1](https://github.com/NaturalCycles/db-lib/compare/v8.20.0...v8.20.1) (2021-10-17)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * types, test ([fb2f741](https://github.com/NaturalCycles/db-lib/commit/fb2f741d8c5c63a04fb70f48fcafbb529669caf2))
21
+
22
+ # [8.20.0](https://github.com/NaturalCycles/db-lib/compare/v8.19.0...v8.20.0) (2021-10-17)
23
+
24
+
25
+ ### Features
26
+
27
+ * CommonDao.runUnionQueries ([ed6c986](https://github.com/NaturalCycles/db-lib/commit/ed6c986e8ba25b660903e235c062237b5100e63c))
28
+
1
29
  # [8.19.0](https://github.com/NaturalCycles/db-lib/compare/v8.18.0...v8.19.0) (2021-10-17)
2
30
 
3
31
 
@@ -41,7 +41,7 @@ class FileDB extends __1.BaseCommonDB {
41
41
  async saveBatch(table, rows, _opt) {
42
42
  if (!rows.length)
43
43
  return; // save some api calls
44
- // 1. Load the whole file from gh
44
+ // 1. Load the whole file
45
45
  const byId = (0, js_lib_1._by)(await this.loadFile(table), r => r.id);
46
46
  // 2. Merge with new data (using ids)
47
47
  let saved = 0;
@@ -173,6 +173,7 @@ class FileDB extends __1.BaseCommonDB {
173
173
  * Mutates
174
174
  */
175
175
  sortRows(rows) {
176
+ rows.forEach(r => (0, js_lib_1._filterUndefinedValues)(r, true));
176
177
  if (this.cfg.sortOnSave) {
177
178
  (0, js_lib_1._sortBy)(rows, r => r[this.cfg.sortOnSave.name], true);
178
179
  if (this.cfg.sortOnSave.descending)
@@ -1,7 +1,7 @@
1
1
  import { JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib';
2
2
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
3
  import { CommonDB } from './common.db';
4
- import { CommonDBOptions, ObjectWithId, RunQueryResult } from './db.model';
4
+ import { CommonDBOptions, CommonDBSaveOptions, ObjectWithId, RunQueryResult } from './db.model';
5
5
  import { DBQuery } from './query/dbQuery';
6
6
  import { DBTransaction } from './transaction/dbTransaction';
7
7
  /**
@@ -18,7 +18,7 @@ export declare class BaseCommonDB implements CommonDB {
18
18
  getByIds<ROW extends ObjectWithId>(_table: string, _ids: string[]): Promise<ROW[]>;
19
19
  runQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<RunQueryResult<ROW>>;
20
20
  runQueryCount<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number>;
21
- saveBatch<ROW extends ObjectWithId>(_table: string, _rows: ROW[]): Promise<void>;
21
+ saveBatch<ROW extends ObjectWithId>(_table: string, _rows: ROW[], _opt?: CommonDBSaveOptions<ROW>): Promise<void>;
22
22
  streamQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): ReadableTyped<ROW>;
23
23
  /**
24
24
  * Naive implementation.
@@ -37,7 +37,7 @@ class BaseCommonDB {
37
37
  async runQueryCount(_q) {
38
38
  return 0;
39
39
  }
40
- async saveBatch(_table, _rows) { }
40
+ async saveBatch(_table, _rows, _opt) { }
41
41
  streamQuery(_q) {
42
42
  return stream_1.Readable.from([]);
43
43
  }
@@ -40,6 +40,12 @@ export declare class CommonDao<BM extends Partial<ObjectWithId>, DBM extends Obj
40
40
  */
41
41
  query(table?: string): RunnableDBQuery<BM, DBM, TM>;
42
42
  runQuery(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<Saved<BM>[]>;
43
+ /**
44
+ * Convenience method that runs multiple queries in parallel and then merges their results together.
45
+ * Does deduplication by id.
46
+ * Order is not guaranteed, as queries run in parallel.
47
+ */
48
+ runUnionQueries(queries: DBQuery<DBM>[], opt?: CommonDaoOptions): Promise<Saved<BM>[]>;
43
49
  runQueryExtended(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<RunQueryResult<Saved<BM>>>;
44
50
  runQueryAsDBM(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<DBM[]>;
45
51
  runQueryExtendedAsDBM(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<RunQueryResult<DBM>>;
@@ -175,6 +175,15 @@ class CommonDao {
175
175
  const { rows } = await this.runQueryExtended(q, opt);
176
176
  return rows;
177
177
  }
178
+ /**
179
+ * Convenience method that runs multiple queries in parallel and then merges their results together.
180
+ * Does deduplication by id.
181
+ * Order is not guaranteed, as queries run in parallel.
182
+ */
183
+ async runUnionQueries(queries, opt) {
184
+ const results = (await (0, js_lib_1.pMap)(queries, async (q) => (await this.runQueryExtended(q, opt)).rows)).flat();
185
+ return (0, js_lib_1._uniqBy)(results, r => r.id);
186
+ }
178
187
  async runQueryExtended(q, opt = {}) {
179
188
  q.table = opt.table || q.table;
180
189
  const op = `runQuery(${q.pretty()})`;
@@ -630,8 +639,14 @@ class CommonDao {
630
639
  // `raw` option completely bypasses any processing
631
640
  if (opt.raw)
632
641
  return obj;
642
+ // Kirill 2021-10-18: I realized that there's little reason to keep removing null values
643
+ // So, from now on we'll preserve them
644
+ // "undefined" values, I believe, are/were not saved to/from DB anyway (due to e.g JSON.stringify removing them)
645
+ // But let's keep watching it!
646
+ //
633
647
  // Filter null and undefined values
634
- obj = (0, js_lib_1._filterNullishValues)(obj);
648
+ // obj = _filterNullishValues(obj as any)
649
+ obj = { ...obj }; // prevent mutation
635
650
  // Pre-validation hooks
636
651
  if (modelType === db_model_1.DBModelType.DBM) {
637
652
  obj = this.cfg.hooks.beforeDBMValidate(obj);
@@ -22,7 +22,7 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
22
22
  // tableSchemas = true,
23
23
  createTable = true, dbQueryFilter = true,
24
24
  // dbQueryFilterIn = true,
25
- dbQueryOrder = true, dbQuerySelectFields = true, streaming = true, strongConsistency = true, } = features;
25
+ dbQueryOrder = true, dbQuerySelectFields = true, streaming = true, strongConsistency = true, nullValues = true, } = features;
26
26
  // const {
27
27
  // allowExtraPropertiesInResponse,
28
28
  // allowBooleansAsUndefined,
@@ -72,6 +72,29 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
72
72
  expect(await dao.getByIds(['abc', 'abcd'])).toEqual([]);
73
73
  });
74
74
  // SAVE
75
+ if (nullValues) {
76
+ test('should allow to save and load null values', async () => {
77
+ const item3 = {
78
+ ...(0, test_model_1.createTestItemBM)(3),
79
+ k2: null,
80
+ };
81
+ await dao.save(item3);
82
+ const item3Loaded = await dao.requireById(item3.id);
83
+ (0, dbTest_1.expectMatch)([item3], [item3Loaded], quirks);
84
+ expect(item3Loaded.k2).toBe(null);
85
+ });
86
+ }
87
+ test('undefined values should not be saved/loaded', async () => {
88
+ const item3 = {
89
+ ...(0, test_model_1.createTestItemBM)(3),
90
+ k2: undefined,
91
+ };
92
+ await dao.save(item3);
93
+ const item3Loaded = await dao.requireById(item3.id);
94
+ (0, dbTest_1.expectMatch)([item3], [item3Loaded], quirks);
95
+ expect(item3Loaded.k2).toBe(undefined);
96
+ expect(Object.keys(item3Loaded)).not.toContain('k2');
97
+ });
75
98
  test('saveBatch test items', async () => {
76
99
  const itemsSaved = await dao.saveBatch(items);
77
100
  expect(itemsSaved[0]).toBe(items[0]); // expect "same object" returned
@@ -17,6 +17,7 @@ export interface CommonDBImplementationFeatures {
17
17
  strongConsistency?: boolean;
18
18
  streaming?: boolean;
19
19
  bufferSupport?: boolean;
20
+ nullValues?: boolean;
20
21
  }
21
22
  /**
22
23
  * All options default to `false`.
@@ -12,7 +12,7 @@ const test_util_1 = require("./test.util");
12
12
  function runCommonDBTest(db, features = {}, quirks = {}) {
13
13
  const { querying = true, tableSchemas = true, createTable = true, dbQueryFilter = true,
14
14
  // dbQueryFilterIn = true,
15
- dbQueryOrder = true, dbQuerySelectFields = true, streaming = true, strongConsistency = true, bufferSupport = true, } = features;
15
+ dbQueryOrder = true, dbQuerySelectFields = true, streaming = true, strongConsistency = true, bufferSupport = true, nullValues = true, } = features;
16
16
  // const {
17
17
  // allowExtraPropertiesInResponse,
18
18
  // allowBooleansAsUndefined,
@@ -58,6 +58,29 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
58
58
  expect(await db.getByIds(test_model_1.TEST_TABLE, ['abc', 'abcd'])).toEqual([]);
59
59
  });
60
60
  // SAVE
61
+ if (nullValues) {
62
+ test('should allow to save and load null values', async () => {
63
+ const item3 = {
64
+ ...(0, test_model_1.createTestItemDBM)(3),
65
+ k2: null,
66
+ };
67
+ await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
68
+ const item3Loaded = (await db.getByIds(test_model_1.TEST_TABLE, [item3.id]))[0];
69
+ expectMatch([item3], [item3Loaded], quirks);
70
+ expect(item3Loaded.k2).toBe(null);
71
+ });
72
+ }
73
+ test('undefined values should not be saved/loaded', async () => {
74
+ const item3 = {
75
+ ...(0, test_model_1.createTestItemDBM)(3),
76
+ k2: undefined,
77
+ };
78
+ await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
79
+ const item3Loaded = (await db.getByIds(test_model_1.TEST_TABLE, [item3.id]))[0];
80
+ expectMatch([item3], [item3Loaded], quirks);
81
+ expect(item3Loaded.k2).toBe(undefined);
82
+ expect(Object.keys(item3Loaded)).not.toContain('k2');
83
+ });
61
84
  test('saveBatch test items', async () => {
62
85
  await db.saveBatch(test_model_1.TEST_TABLE, items);
63
86
  });
@@ -3,7 +3,7 @@ import { JsonSchemaObject, BaseDBEntity, Saved } from '@naturalcycles/js-lib';
3
3
  export declare const TEST_TABLE = "TEST_TABLE";
4
4
  export interface TestItemBM extends BaseDBEntity {
5
5
  k1: string;
6
- k2?: string;
6
+ k2?: string | null;
7
7
  k3?: number;
8
8
  even?: boolean;
9
9
  b1?: Buffer;
@@ -7,14 +7,14 @@ const MOCK_TS_2018_06_21 = 1529539200;
7
7
  exports.TEST_TABLE = 'TEST_TABLE';
8
8
  exports.testItemBMSchema = (0, nodejs_lib_1.objectSchema)({
9
9
  k1: nodejs_lib_1.stringSchema,
10
- k2: nodejs_lib_1.stringSchema.optional(),
10
+ k2: nodejs_lib_1.stringSchema.allow(null).optional(),
11
11
  k3: nodejs_lib_1.numberSchema.optional(),
12
12
  even: nodejs_lib_1.booleanSchema.optional(),
13
13
  b1: nodejs_lib_1.binarySchema.optional(),
14
14
  }).concat(nodejs_lib_1.baseDBEntitySchema);
15
15
  exports.testItemDBMSchema = (0, nodejs_lib_1.objectSchema)({
16
16
  k1: nodejs_lib_1.stringSchema,
17
- k2: nodejs_lib_1.stringSchema.optional(),
17
+ k2: nodejs_lib_1.stringSchema.allow(null).optional(),
18
18
  k3: nodejs_lib_1.numberSchema.optional(),
19
19
  even: nodejs_lib_1.booleanSchema.optional(),
20
20
  b1: nodejs_lib_1.binarySchema.optional(),
@@ -26,7 +26,7 @@ exports.testItemTMSchema = (0, nodejs_lib_1.objectSchema)({
26
26
  exports.testItemBMJsonSchema = js_lib_1.jsonSchema
27
27
  .rootObject({
28
28
  k1: js_lib_1.jsonSchema.string(),
29
- k2: js_lib_1.jsonSchema.string().optional(),
29
+ k2: js_lib_1.jsonSchema.oneOf([js_lib_1.jsonSchema.string(), js_lib_1.jsonSchema.null()]).optional(),
30
30
  k3: js_lib_1.jsonSchema.number().optional(),
31
31
  even: js_lib_1.jsonSchema.boolean().optional(),
32
32
  b1: js_lib_1.jsonSchema.buffer().optional(),
package/package.json CHANGED
@@ -42,7 +42,7 @@
42
42
  "engines": {
43
43
  "node": ">=12.13"
44
44
  },
45
- "version": "8.19.0",
45
+ "version": "8.21.1",
46
46
  "description": "Lowest Common Denominator API to supported Databases",
47
47
  "keywords": [
48
48
  "db",
@@ -10,6 +10,7 @@ import {
10
10
  _stringMapValues,
11
11
  _uniq,
12
12
  JsonSchemaRootObject,
13
+ _filterUndefinedValues,
13
14
  } from '@naturalcycles/js-lib'
14
15
  import { Debug, readableCreate, ReadableTyped } from '@naturalcycles/nodejs-lib'
15
16
  import { dimGrey } from '@naturalcycles/nodejs-lib/dist/colors'
@@ -82,7 +83,7 @@ export class FileDB extends BaseCommonDB implements CommonDB {
82
83
  ): Promise<void> {
83
84
  if (!rows.length) return // save some api calls
84
85
 
85
- // 1. Load the whole file from gh
86
+ // 1. Load the whole file
86
87
  const byId = _by(await this.loadFile<ROW>(table), r => r.id)
87
88
 
88
89
  // 2. Merge with new data (using ids)
@@ -261,6 +262,8 @@ export class FileDB extends BaseCommonDB implements CommonDB {
261
262
  * Mutates
262
263
  */
263
264
  sortRows<ROW>(rows: ROW[]): ROW[] {
265
+ rows.forEach(r => _filterUndefinedValues(r, true))
266
+
264
267
  if (this.cfg.sortOnSave) {
265
268
  _sortBy(rows, r => r[this.cfg.sortOnSave!.name], true)
266
269
  if (this.cfg.sortOnSave.descending) rows.reverse() // mutates
@@ -2,7 +2,7 @@ import { Readable } from 'stream'
2
2
  import { JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib'
3
3
  import { ReadableTyped } from '@naturalcycles/nodejs-lib'
4
4
  import { CommonDB } from './common.db'
5
- import { CommonDBOptions, ObjectWithId, RunQueryResult } from './db.model'
5
+ import { CommonDBOptions, CommonDBSaveOptions, ObjectWithId, RunQueryResult } from './db.model'
6
6
  import { DBQuery } from './query/dbQuery'
7
7
  import { DBTransaction } from './transaction/dbTransaction'
8
8
  import { commitDBTransactionSimple } from './transaction/dbTransaction.util'
@@ -55,7 +55,11 @@ export class BaseCommonDB implements CommonDB {
55
55
  return 0
56
56
  }
57
57
 
58
- async saveBatch<ROW extends ObjectWithId>(_table: string, _rows: ROW[]): Promise<void> {}
58
+ async saveBatch<ROW extends ObjectWithId>(
59
+ _table: string,
60
+ _rows: ROW[],
61
+ _opt?: CommonDBSaveOptions<ROW>,
62
+ ): Promise<void> {}
59
63
 
60
64
  streamQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): ReadableTyped<ROW> {
61
65
  return Readable.from([])
@@ -3,13 +3,13 @@ import {
3
3
  AsyncMapper,
4
4
  ErrorMode,
5
5
  JsonSchemaObject,
6
- _filterNullishValues,
7
6
  _passthroughPredicate,
8
7
  _since,
9
8
  _truncate,
10
9
  pMap,
11
10
  JsonSchemaRootObject,
12
11
  Saved,
12
+ _uniqBy,
13
13
  } from '@naturalcycles/js-lib'
14
14
  import {
15
15
  AjvSchema,
@@ -242,6 +242,18 @@ export class CommonDao<
242
242
  return rows
243
243
  }
244
244
 
245
+ /**
246
+ * Convenience method that runs multiple queries in parallel and then merges their results together.
247
+ * Does deduplication by id.
248
+ * Order is not guaranteed, as queries run in parallel.
249
+ */
250
+ async runUnionQueries(queries: DBQuery<DBM>[], opt?: CommonDaoOptions): Promise<Saved<BM>[]> {
251
+ const results = (
252
+ await pMap(queries, async q => (await this.runQueryExtended(q, opt)).rows)
253
+ ).flat()
254
+ return _uniqBy(results, r => r.id)
255
+ }
256
+
245
257
  async runQueryExtended(
246
258
  q: DBQuery<DBM>,
247
259
  opt: CommonDaoOptions = {},
@@ -852,8 +864,14 @@ export class CommonDao<
852
864
  // `raw` option completely bypasses any processing
853
865
  if (opt.raw) return obj as any as OUT
854
866
 
867
+ // Kirill 2021-10-18: I realized that there's little reason to keep removing null values
868
+ // So, from now on we'll preserve them
869
+ // "undefined" values, I believe, are/were not saved to/from DB anyway (due to e.g JSON.stringify removing them)
870
+ // But let's keep watching it!
871
+ //
855
872
  // Filter null and undefined values
856
- obj = _filterNullishValues(obj as any)
873
+ // obj = _filterNullishValues(obj as any)
874
+ obj = { ...obj } // prevent mutation
857
875
 
858
876
  // Pre-validation hooks
859
877
  if (modelType === DBModelType.DBM) {
@@ -10,6 +10,7 @@ import {
10
10
  testItemDBMSchema,
11
11
  testItemTMSchema,
12
12
  TEST_TABLE,
13
+ createTestItemBM,
13
14
  } from './test.model'
14
15
  import { getTestItemSchema, TestItemBM } from '.'
15
16
 
@@ -38,6 +39,7 @@ export function runCommonDaoTest(
38
39
  dbQuerySelectFields = true,
39
40
  streaming = true,
40
41
  strongConsistency = true,
42
+ nullValues = true,
41
43
  } = features
42
44
 
43
45
  // const {
@@ -101,6 +103,31 @@ export function runCommonDaoTest(
101
103
  })
102
104
 
103
105
  // SAVE
106
+ if (nullValues) {
107
+ test('should allow to save and load null values', async () => {
108
+ const item3 = {
109
+ ...createTestItemBM(3),
110
+ k2: null,
111
+ }
112
+ await dao.save(item3)
113
+ const item3Loaded = await dao.requireById(item3.id)
114
+ expectMatch([item3], [item3Loaded], quirks)
115
+ expect(item3Loaded.k2).toBe(null)
116
+ })
117
+ }
118
+
119
+ test('undefined values should not be saved/loaded', async () => {
120
+ const item3 = {
121
+ ...createTestItemBM(3),
122
+ k2: undefined,
123
+ }
124
+ await dao.save(item3)
125
+ const item3Loaded = await dao.requireById(item3.id)
126
+ expectMatch([item3], [item3Loaded], quirks)
127
+ expect(item3Loaded.k2).toBe(undefined)
128
+ expect(Object.keys(item3Loaded)).not.toContain('k2')
129
+ })
130
+
104
131
  test('saveBatch test items', async () => {
105
132
  const itemsSaved = await dao.saveBatch(items)
106
133
  expect(itemsSaved[0]).toBe(items[0]) // expect "same object" returned
@@ -34,6 +34,7 @@ export interface CommonDBImplementationFeatures {
34
34
  streaming?: boolean
35
35
 
36
36
  bufferSupport?: boolean
37
+ nullValues?: boolean
37
38
  }
38
39
 
39
40
  /**
@@ -76,6 +77,7 @@ export function runCommonDBTest(
76
77
  streaming = true,
77
78
  strongConsistency = true,
78
79
  bufferSupport = true,
80
+ nullValues = true,
79
81
  } = features
80
82
 
81
83
  // const {
@@ -135,6 +137,31 @@ export function runCommonDBTest(
135
137
  })
136
138
 
137
139
  // SAVE
140
+ if (nullValues) {
141
+ test('should allow to save and load null values', async () => {
142
+ const item3 = {
143
+ ...createTestItemDBM(3),
144
+ k2: null,
145
+ }
146
+ await db.saveBatch(TEST_TABLE, [item3])
147
+ const item3Loaded = (await db.getByIds<TestItemDBM>(TEST_TABLE, [item3.id]))[0]!
148
+ expectMatch([item3], [item3Loaded], quirks)
149
+ expect(item3Loaded.k2).toBe(null)
150
+ })
151
+ }
152
+
153
+ test('undefined values should not be saved/loaded', async () => {
154
+ const item3 = {
155
+ ...createTestItemDBM(3),
156
+ k2: undefined,
157
+ }
158
+ await db.saveBatch(TEST_TABLE, [item3])
159
+ const item3Loaded = (await db.getByIds<TestItemDBM>(TEST_TABLE, [item3.id]))[0]!
160
+ expectMatch([item3], [item3Loaded], quirks)
161
+ expect(item3Loaded.k2).toBe(undefined)
162
+ expect(Object.keys(item3Loaded)).not.toContain('k2')
163
+ })
164
+
138
165
  test('saveBatch test items', async () => {
139
166
  await db.saveBatch(TEST_TABLE, items)
140
167
  })
@@ -15,7 +15,7 @@ export const TEST_TABLE = 'TEST_TABLE'
15
15
 
16
16
  export interface TestItemBM extends BaseDBEntity {
17
17
  k1: string
18
- k2?: string
18
+ k2?: string | null
19
19
  k3?: number
20
20
  even?: boolean
21
21
  b1?: Buffer
@@ -30,7 +30,7 @@ export interface TestItemTM {
30
30
 
31
31
  export const testItemBMSchema = objectSchema<TestItemBM>({
32
32
  k1: stringSchema,
33
- k2: stringSchema.optional(),
33
+ k2: stringSchema.allow(null).optional(),
34
34
  k3: numberSchema.optional(),
35
35
  even: booleanSchema.optional(),
36
36
  b1: binarySchema.optional(),
@@ -38,7 +38,7 @@ export const testItemBMSchema = objectSchema<TestItemBM>({
38
38
 
39
39
  export const testItemDBMSchema = objectSchema<TestItemDBM>({
40
40
  k1: stringSchema,
41
- k2: stringSchema.optional(),
41
+ k2: stringSchema.allow(null).optional(),
42
42
  k3: numberSchema.optional(),
43
43
  even: booleanSchema.optional(),
44
44
  b1: binarySchema.optional(),
@@ -52,7 +52,7 @@ export const testItemTMSchema = objectSchema<TestItemTM>({
52
52
  export const testItemBMJsonSchema = jsonSchema
53
53
  .rootObject<TestItemBM>({
54
54
  k1: jsonSchema.string(),
55
- k2: jsonSchema.string().optional(),
55
+ k2: jsonSchema.oneOf<string | null>([jsonSchema.string(), jsonSchema.null()]).optional(),
56
56
  k3: jsonSchema.number().optional(),
57
57
  even: jsonSchema.boolean().optional(),
58
58
  b1: jsonSchema.buffer().optional(),