@opra/common 1.17.4 → 1.17.6

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.
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ComplexTypeBase = exports.FIELD_PATH_PATTERN = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const object_hash_1 = tslib_1.__importDefault(require("object-hash"));
4
6
  const ts_gems_1 = require("ts-gems");
5
7
  const valgen_1 = require("valgen");
6
8
  const index_js_1 = require("../../helpers/index.js");
@@ -199,14 +201,18 @@ class ComplexTypeBaseClass extends data_type_js_1.DataType {
199
201
  *
200
202
  */
201
203
  generateCodec(codec, options) {
202
- const projection = Array.isArray(options?.projection)
203
- ? (0, index_js_1.parseFieldsProjection)(options.projection)
204
- : options?.projection;
205
- const schema = this._generateSchema(codec, {
206
- ...options,
207
- projection,
208
- currentPath: '',
209
- });
204
+ const context = options?.cache
205
+ ? options
206
+ : {
207
+ ...options,
208
+ projection: Array.isArray(options?.projection)
209
+ ? (0, index_js_1.parseFieldsProjection)(options.projection)
210
+ : options?.projection,
211
+ currentPath: '',
212
+ };
213
+ let schema = this._generateSchema(codec, context);
214
+ if (context.generateSchemaHook)
215
+ schema = context.generateSchemaHook(schema, this, context.currentPath);
210
216
  let additionalFields;
211
217
  if (this.additionalFields instanceof data_type_js_1.DataType) {
212
218
  additionalFields = this.additionalFields.generateCodec(codec, options);
@@ -218,10 +224,10 @@ class ComplexTypeBaseClass extends data_type_js_1.DataType {
218
224
  additionalFields = 'error';
219
225
  else {
220
226
  const message = additionalFields[1];
221
- additionalFields = (0, valgen_1.validator)((input, context, _this) => context.fail(_this, message, input));
227
+ additionalFields = (0, valgen_1.validator)((input, ctx, _this) => ctx.fail(_this, message, input));
222
228
  }
223
229
  }
224
- return valgen_1.vg.isObject(schema, {
230
+ const fn = valgen_1.vg.isObject(schema, {
225
231
  ctor: this.name === 'object' ? Object : this.ctor,
226
232
  additionalFields,
227
233
  name: this.name,
@@ -229,8 +235,17 @@ class ComplexTypeBaseClass extends data_type_js_1.DataType {
229
235
  caseInSensitive: options?.caseInSensitive,
230
236
  onFail: options?.onFail,
231
237
  });
238
+ if (context.level === 0 && context.forwardCallbacks?.size) {
239
+ for (const cb of context.forwardCallbacks) {
240
+ cb();
241
+ }
242
+ }
243
+ return fn;
232
244
  }
233
245
  _generateSchema(codec, context) {
246
+ context.fieldCache = context.fieldCache || new Map();
247
+ context.level = context.level || 0;
248
+ context.forwardCallbacks = context.forwardCallbacks || new Set();
234
249
  const schema = {};
235
250
  const { currentPath, projection } = context;
236
251
  const pickList = !!(projection && Object.values(projection).find(p => !p.sign));
@@ -263,14 +278,40 @@ class ComplexTypeBaseClass extends data_type_js_1.DataType {
263
278
  continue;
264
279
  }
265
280
  }
266
- const fn = this._generateFieldCodec(codec, field, {
267
- ...context,
268
- partial: context.partial === 'deep' ? context.partial : undefined,
269
- projection: typeof projection === 'object'
270
- ? projection[fieldName]?.projection || '*'
271
- : projection,
272
- currentPath: currentPath + (currentPath ? '.' : '') + fieldName,
273
- });
281
+ const subProjection = typeof projection === 'object'
282
+ ? projection[fieldName]?.projection || '*'
283
+ : projection;
284
+ let cacheItem = context.fieldCache.get(field);
285
+ const cacheKey = typeof subProjection === 'string'
286
+ ? subProjection
287
+ : (0, object_hash_1.default)(subProjection || {});
288
+ if (!cacheItem) {
289
+ cacheItem = {};
290
+ context.fieldCache.set(field, cacheItem);
291
+ }
292
+ let fn = cacheItem[cacheKey];
293
+ /** If in progress (circular) */
294
+ if (fn === null) {
295
+ // Temporary set any
296
+ fn = valgen_1.vg.isAny();
297
+ context.forwardCallbacks.add(() => {
298
+ fn = cacheItem[cacheKey];
299
+ schema[fieldName] =
300
+ context.partial || !field.required
301
+ ? valgen_1.vg.optional(fn)
302
+ : valgen_1.vg.required(fn);
303
+ });
304
+ }
305
+ else if (!fn) {
306
+ cacheItem[cacheKey] = null;
307
+ fn = this._generateFieldCodec(codec, field, {
308
+ ...context,
309
+ partial: context.partial === 'deep' ? context.partial : undefined,
310
+ projection: subProjection,
311
+ currentPath: currentPath + (currentPath ? '.' : '') + fieldName,
312
+ });
313
+ cacheItem[cacheKey] = fn;
314
+ }
274
315
  schema[fieldName] =
275
316
  context.partial || !field.required ? valgen_1.vg.optional(fn) : valgen_1.vg.required(fn);
276
317
  }
@@ -281,7 +322,10 @@ class ComplexTypeBaseClass extends data_type_js_1.DataType {
281
322
  return schema;
282
323
  }
283
324
  _generateFieldCodec(codec, field, context) {
284
- let fn = field.type.generateCodec(codec, context);
325
+ let fn = field.type.generateCodec(codec, {
326
+ ...context,
327
+ level: context.level + 1,
328
+ });
285
329
  if (field.fixed)
286
330
  fn = valgen_1.vg.isEqual(field.fixed);
287
331
  if (field.isArray)
@@ -1,3 +1,4 @@
1
+ import hashObject from 'object-hash';
1
2
  import { asMutable } from 'ts-gems';
2
3
  import { validator, vg } from 'valgen';
3
4
  import { parseFieldsProjection, ResponsiveMap, } from '../../helpers/index.js';
@@ -196,14 +197,18 @@ class ComplexTypeBaseClass extends DataType {
196
197
  *
197
198
  */
198
199
  generateCodec(codec, options) {
199
- const projection = Array.isArray(options?.projection)
200
- ? parseFieldsProjection(options.projection)
201
- : options?.projection;
202
- const schema = this._generateSchema(codec, {
203
- ...options,
204
- projection,
205
- currentPath: '',
206
- });
200
+ const context = options?.cache
201
+ ? options
202
+ : {
203
+ ...options,
204
+ projection: Array.isArray(options?.projection)
205
+ ? parseFieldsProjection(options.projection)
206
+ : options?.projection,
207
+ currentPath: '',
208
+ };
209
+ let schema = this._generateSchema(codec, context);
210
+ if (context.generateSchemaHook)
211
+ schema = context.generateSchemaHook(schema, this, context.currentPath);
207
212
  let additionalFields;
208
213
  if (this.additionalFields instanceof DataType) {
209
214
  additionalFields = this.additionalFields.generateCodec(codec, options);
@@ -215,10 +220,10 @@ class ComplexTypeBaseClass extends DataType {
215
220
  additionalFields = 'error';
216
221
  else {
217
222
  const message = additionalFields[1];
218
- additionalFields = validator((input, context, _this) => context.fail(_this, message, input));
223
+ additionalFields = validator((input, ctx, _this) => ctx.fail(_this, message, input));
219
224
  }
220
225
  }
221
- return vg.isObject(schema, {
226
+ const fn = vg.isObject(schema, {
222
227
  ctor: this.name === 'object' ? Object : this.ctor,
223
228
  additionalFields,
224
229
  name: this.name,
@@ -226,8 +231,17 @@ class ComplexTypeBaseClass extends DataType {
226
231
  caseInSensitive: options?.caseInSensitive,
227
232
  onFail: options?.onFail,
228
233
  });
234
+ if (context.level === 0 && context.forwardCallbacks?.size) {
235
+ for (const cb of context.forwardCallbacks) {
236
+ cb();
237
+ }
238
+ }
239
+ return fn;
229
240
  }
230
241
  _generateSchema(codec, context) {
242
+ context.fieldCache = context.fieldCache || new Map();
243
+ context.level = context.level || 0;
244
+ context.forwardCallbacks = context.forwardCallbacks || new Set();
231
245
  const schema = {};
232
246
  const { currentPath, projection } = context;
233
247
  const pickList = !!(projection && Object.values(projection).find(p => !p.sign));
@@ -260,14 +274,40 @@ class ComplexTypeBaseClass extends DataType {
260
274
  continue;
261
275
  }
262
276
  }
263
- const fn = this._generateFieldCodec(codec, field, {
264
- ...context,
265
- partial: context.partial === 'deep' ? context.partial : undefined,
266
- projection: typeof projection === 'object'
267
- ? projection[fieldName]?.projection || '*'
268
- : projection,
269
- currentPath: currentPath + (currentPath ? '.' : '') + fieldName,
270
- });
277
+ const subProjection = typeof projection === 'object'
278
+ ? projection[fieldName]?.projection || '*'
279
+ : projection;
280
+ let cacheItem = context.fieldCache.get(field);
281
+ const cacheKey = typeof subProjection === 'string'
282
+ ? subProjection
283
+ : hashObject(subProjection || {});
284
+ if (!cacheItem) {
285
+ cacheItem = {};
286
+ context.fieldCache.set(field, cacheItem);
287
+ }
288
+ let fn = cacheItem[cacheKey];
289
+ /** If in progress (circular) */
290
+ if (fn === null) {
291
+ // Temporary set any
292
+ fn = vg.isAny();
293
+ context.forwardCallbacks.add(() => {
294
+ fn = cacheItem[cacheKey];
295
+ schema[fieldName] =
296
+ context.partial || !field.required
297
+ ? vg.optional(fn)
298
+ : vg.required(fn);
299
+ });
300
+ }
301
+ else if (!fn) {
302
+ cacheItem[cacheKey] = null;
303
+ fn = this._generateFieldCodec(codec, field, {
304
+ ...context,
305
+ partial: context.partial === 'deep' ? context.partial : undefined,
306
+ projection: subProjection,
307
+ currentPath: currentPath + (currentPath ? '.' : '') + fieldName,
308
+ });
309
+ cacheItem[cacheKey] = fn;
310
+ }
271
311
  schema[fieldName] =
272
312
  context.partial || !field.required ? vg.optional(fn) : vg.required(fn);
273
313
  }
@@ -278,7 +318,10 @@ class ComplexTypeBaseClass extends DataType {
278
318
  return schema;
279
319
  }
280
320
  _generateFieldCodec(codec, field, context) {
281
- let fn = field.type.generateCodec(codec, context);
321
+ let fn = field.type.generateCodec(codec, {
322
+ ...context,
323
+ level: context.level + 1,
324
+ });
282
325
  if (field.fixed)
283
326
  fn = vg.isEqual(field.fixed);
284
327
  if (field.isArray)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/common",
3
- "version": "1.17.4",
3
+ "version": "1.17.6",
4
4
  "description": "Opra common package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -12,6 +12,7 @@
12
12
  "@browsery/type-is": "^1.6.18-r8",
13
13
  "@jsopen/objects": "^1.6.2",
14
14
  "fast-tokenizer": "^1.7.0",
15
+ "object-hash": "^3.0.0",
15
16
  "putil-promisify": "^1.10.1",
16
17
  "putil-varhelpers": "^1.6.5",
17
18
  "reflect-metadata": "^0.2.2",
@@ -73,13 +73,14 @@ declare abstract class ComplexTypeBaseClass extends DataType {
73
73
  *
74
74
  */
75
75
  generateCodec(codec: 'encode' | 'decode', options?: DataType.GenerateCodecOptions): Validator;
76
- protected _generateSchema(codec: 'encode' | 'decode', context: StrictOmit<DataType.GenerateCodecOptions, 'projection'> & {
77
- currentPath: string;
78
- projection?: FieldsProjection | '*';
79
- }): IsObject.Schema;
80
- protected _generateFieldCodec(codec: 'encode' | 'decode', field: ApiField, context: StrictOmit<DataType.GenerateCodecOptions, 'projection'> & {
81
- currentPath: string;
82
- projection?: FieldsProjection | '*';
83
- }): Validator;
76
+ protected _generateSchema(codec: 'encode' | 'decode', context: GenerateCodecContext): IsObject.Schema;
77
+ protected _generateFieldCodec(codec: 'encode' | 'decode', field: ApiField, context: GenerateCodecContext): Validator;
84
78
  }
79
+ type GenerateCodecContext = StrictOmit<DataType.GenerateCodecOptions, 'projection'> & {
80
+ currentPath: string;
81
+ projection?: FieldsProjection | '*';
82
+ level?: number;
83
+ fieldCache?: Map<ApiField, Record<string, Validator | null>>;
84
+ forwardCallbacks?: Set<Function>;
85
+ };
85
86
  export {};
@@ -1,5 +1,5 @@
1
1
  import { type StrictOmit, type Type } from 'ts-gems';
2
- import type { ValidationOptions, Validator } from 'valgen';
2
+ import type { IsObject, ValidationOptions, Validator } from 'valgen';
3
3
  import { FieldsProjection } from '../../helpers/index.js';
4
4
  import type { DataTypeBase } from '../../schema/data-type/data-type.interface.js';
5
5
  import { OpraSchema } from '../../schema/index.js';
@@ -7,6 +7,7 @@ import type { ApiDocument } from '../api-document.js';
7
7
  import { DocumentElement } from '../common/document-element.js';
8
8
  import { DocumentInitContext } from '../common/document-init-context.js';
9
9
  import { nodeInspectCustom } from '../utils/inspect.util.js';
10
+ import type { ComplexType } from './complex-type.js';
10
11
  /**
11
12
  * @namespace DataType
12
13
  */
@@ -30,6 +31,7 @@ export declare namespace DataType {
30
31
  ignoreReadonlyFields?: boolean;
31
32
  ignoreWriteonlyFields?: boolean;
32
33
  allowPatchOperators?: boolean;
34
+ generateSchemaHook?: (schema: IsObject.Schema, dataType: ComplexType, path: string) => IsObject.Schema;
33
35
  }
34
36
  }
35
37
  interface DataTypeStatic {