@opra/common 1.17.3 → 1.17.5

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 hash_object_1 = tslib_1.__importDefault(require("hash-object"));
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,16 @@ 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
+ const schema = this._generateSchema(codec, context);
210
214
  let additionalFields;
211
215
  if (this.additionalFields instanceof data_type_js_1.DataType) {
212
216
  additionalFields = this.additionalFields.generateCodec(codec, options);
@@ -218,10 +222,10 @@ class ComplexTypeBaseClass extends data_type_js_1.DataType {
218
222
  additionalFields = 'error';
219
223
  else {
220
224
  const message = additionalFields[1];
221
- additionalFields = (0, valgen_1.validator)((input, context, _this) => context.fail(_this, message, input));
225
+ additionalFields = (0, valgen_1.validator)((input, ctx, _this) => ctx.fail(_this, message, input));
222
226
  }
223
227
  }
224
- return valgen_1.vg.isObject(schema, {
228
+ const fn = valgen_1.vg.isObject(schema, {
225
229
  ctor: this.name === 'object' ? Object : this.ctor,
226
230
  additionalFields,
227
231
  name: this.name,
@@ -229,8 +233,17 @@ class ComplexTypeBaseClass extends data_type_js_1.DataType {
229
233
  caseInSensitive: options?.caseInSensitive,
230
234
  onFail: options?.onFail,
231
235
  });
236
+ if (context.level === 0 && context.forwardCallbacks?.size) {
237
+ for (const cb of context.forwardCallbacks) {
238
+ cb();
239
+ }
240
+ }
241
+ return fn;
232
242
  }
233
243
  _generateSchema(codec, context) {
244
+ context.cache = context.cache || new Map();
245
+ context.level = context.level || 0;
246
+ context.forwardCallbacks = context.forwardCallbacks || new Set();
234
247
  const schema = {};
235
248
  const { currentPath, projection } = context;
236
249
  const pickList = !!(projection && Object.values(projection).find(p => !p.sign));
@@ -263,14 +276,40 @@ class ComplexTypeBaseClass extends data_type_js_1.DataType {
263
276
  continue;
264
277
  }
265
278
  }
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
- });
279
+ const subProjection = typeof projection === 'object'
280
+ ? projection[fieldName]?.projection || '*'
281
+ : projection;
282
+ let cacheItem = context.cache.get(field.type);
283
+ const cacheKey = typeof subProjection === 'string'
284
+ ? subProjection
285
+ : (0, hash_object_1.default)(subProjection || {});
286
+ if (!cacheItem) {
287
+ cacheItem = {};
288
+ context.cache.set(field.type, cacheItem);
289
+ }
290
+ let fn = cacheItem[cacheKey];
291
+ /** If in progress (circular) */
292
+ if (fn === null) {
293
+ // Temporary set any
294
+ fn = valgen_1.vg.isAny();
295
+ context.forwardCallbacks.add(() => {
296
+ fn = cacheItem[cacheKey];
297
+ schema[fieldName] =
298
+ context.partial || !field.required
299
+ ? valgen_1.vg.optional(fn)
300
+ : valgen_1.vg.required(fn);
301
+ });
302
+ }
303
+ else if (!fn) {
304
+ cacheItem[cacheKey] = null;
305
+ fn = this._generateFieldCodec(codec, field, {
306
+ ...context,
307
+ partial: context.partial === 'deep' ? context.partial : undefined,
308
+ projection: subProjection,
309
+ currentPath: currentPath + (currentPath ? '.' : '') + fieldName,
310
+ });
311
+ cacheItem[cacheKey] = fn;
312
+ }
274
313
  schema[fieldName] =
275
314
  context.partial || !field.required ? valgen_1.vg.optional(fn) : valgen_1.vg.required(fn);
276
315
  }
@@ -281,7 +320,10 @@ class ComplexTypeBaseClass extends data_type_js_1.DataType {
281
320
  return schema;
282
321
  }
283
322
  _generateFieldCodec(codec, field, context) {
284
- let fn = field.type.generateCodec(codec, context);
323
+ let fn = field.type.generateCodec(codec, {
324
+ ...context,
325
+ level: context.level + 1,
326
+ });
285
327
  if (field.fixed)
286
328
  fn = valgen_1.vg.isEqual(field.fixed);
287
329
  if (field.isArray)
@@ -152,7 +152,7 @@ class DataTypeFactory {
152
152
  * @param thunk
153
153
  * @protected
154
154
  */
155
- static async _importDataTypeArgs(context, owner, thunk) {
155
+ static async _importDataTypeArgs(context, owner, thunk, checkCircularDeps) {
156
156
  thunk = await (0, index_js_1.resolveThunk)(thunk);
157
157
  const { importQueue, initArgsMap } = context;
158
158
  // Check if data type already exist (maybe a builtin type or already imported)
@@ -226,7 +226,7 @@ class DataTypeFactory {
226
226
  if (metadata.name) {
227
227
  const curr = initArgsMap?.get(metadata.name);
228
228
  if (curr) {
229
- if (curr[initializingSymbol])
229
+ if (checkCircularDeps && curr[initializingSymbol])
230
230
  return context.addError('Circular reference detected');
231
231
  return metadata.name;
232
232
  }
@@ -297,7 +297,7 @@ class DataTypeFactory {
297
297
  await context.enterAsync('.base', async () => {
298
298
  let baseArgs;
299
299
  if (metadata.base) {
300
- baseArgs = await this._importDataTypeArgs(context, owner, metadata.base);
300
+ baseArgs = await this._importDataTypeArgs(context, owner, metadata.base, true);
301
301
  }
302
302
  else if (initArgs.ctor) {
303
303
  const baseClass = Object.getPrototypeOf(initArgs.ctor.prototype).constructor;
@@ -362,7 +362,7 @@ class DataTypeFactory {
362
362
  await context.enterAsync('.base', async () => {
363
363
  let baseArgs;
364
364
  if (metadata.base) {
365
- baseArgs = await this._importDataTypeArgs(context, owner, metadata.base);
365
+ baseArgs = await this._importDataTypeArgs(context, owner, metadata.base, true);
366
366
  }
367
367
  else if (initArgs.ctor) {
368
368
  const baseClass = Object.getPrototypeOf(initArgs.ctor.prototype).constructor;
@@ -395,7 +395,7 @@ class DataTypeFactory {
395
395
  await context.enterAsync('.base', async () => {
396
396
  let baseArgs;
397
397
  if (metadata.base) {
398
- baseArgs = await this._importDataTypeArgs(context, owner, metadata.base);
398
+ baseArgs = await this._importDataTypeArgs(context, owner, metadata.base, true);
399
399
  }
400
400
  else if (initArgs.ctor) {
401
401
  const baseClass = Object.getPrototypeOf(initArgs.ctor.prototype).constructor;
@@ -1,3 +1,4 @@
1
+ import hashObject from 'hash-object';
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,16 @@ 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
+ const schema = this._generateSchema(codec, context);
207
210
  let additionalFields;
208
211
  if (this.additionalFields instanceof DataType) {
209
212
  additionalFields = this.additionalFields.generateCodec(codec, options);
@@ -215,10 +218,10 @@ class ComplexTypeBaseClass extends DataType {
215
218
  additionalFields = 'error';
216
219
  else {
217
220
  const message = additionalFields[1];
218
- additionalFields = validator((input, context, _this) => context.fail(_this, message, input));
221
+ additionalFields = validator((input, ctx, _this) => ctx.fail(_this, message, input));
219
222
  }
220
223
  }
221
- return vg.isObject(schema, {
224
+ const fn = vg.isObject(schema, {
222
225
  ctor: this.name === 'object' ? Object : this.ctor,
223
226
  additionalFields,
224
227
  name: this.name,
@@ -226,8 +229,17 @@ class ComplexTypeBaseClass extends DataType {
226
229
  caseInSensitive: options?.caseInSensitive,
227
230
  onFail: options?.onFail,
228
231
  });
232
+ if (context.level === 0 && context.forwardCallbacks?.size) {
233
+ for (const cb of context.forwardCallbacks) {
234
+ cb();
235
+ }
236
+ }
237
+ return fn;
229
238
  }
230
239
  _generateSchema(codec, context) {
240
+ context.cache = context.cache || new Map();
241
+ context.level = context.level || 0;
242
+ context.forwardCallbacks = context.forwardCallbacks || new Set();
231
243
  const schema = {};
232
244
  const { currentPath, projection } = context;
233
245
  const pickList = !!(projection && Object.values(projection).find(p => !p.sign));
@@ -260,14 +272,40 @@ class ComplexTypeBaseClass extends DataType {
260
272
  continue;
261
273
  }
262
274
  }
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
- });
275
+ const subProjection = typeof projection === 'object'
276
+ ? projection[fieldName]?.projection || '*'
277
+ : projection;
278
+ let cacheItem = context.cache.get(field.type);
279
+ const cacheKey = typeof subProjection === 'string'
280
+ ? subProjection
281
+ : hashObject(subProjection || {});
282
+ if (!cacheItem) {
283
+ cacheItem = {};
284
+ context.cache.set(field.type, cacheItem);
285
+ }
286
+ let fn = cacheItem[cacheKey];
287
+ /** If in progress (circular) */
288
+ if (fn === null) {
289
+ // Temporary set any
290
+ fn = vg.isAny();
291
+ context.forwardCallbacks.add(() => {
292
+ fn = cacheItem[cacheKey];
293
+ schema[fieldName] =
294
+ context.partial || !field.required
295
+ ? vg.optional(fn)
296
+ : vg.required(fn);
297
+ });
298
+ }
299
+ else if (!fn) {
300
+ cacheItem[cacheKey] = null;
301
+ fn = this._generateFieldCodec(codec, field, {
302
+ ...context,
303
+ partial: context.partial === 'deep' ? context.partial : undefined,
304
+ projection: subProjection,
305
+ currentPath: currentPath + (currentPath ? '.' : '') + fieldName,
306
+ });
307
+ cacheItem[cacheKey] = fn;
308
+ }
271
309
  schema[fieldName] =
272
310
  context.partial || !field.required ? vg.optional(fn) : vg.required(fn);
273
311
  }
@@ -278,7 +316,10 @@ class ComplexTypeBaseClass extends DataType {
278
316
  return schema;
279
317
  }
280
318
  _generateFieldCodec(codec, field, context) {
281
- let fn = field.type.generateCodec(codec, context);
319
+ let fn = field.type.generateCodec(codec, {
320
+ ...context,
321
+ level: context.level + 1,
322
+ });
282
323
  if (field.fixed)
283
324
  fn = vg.isEqual(field.fixed);
284
325
  if (field.isArray)
@@ -149,7 +149,7 @@ export class DataTypeFactory {
149
149
  * @param thunk
150
150
  * @protected
151
151
  */
152
- static async _importDataTypeArgs(context, owner, thunk) {
152
+ static async _importDataTypeArgs(context, owner, thunk, checkCircularDeps) {
153
153
  thunk = await resolveThunk(thunk);
154
154
  const { importQueue, initArgsMap } = context;
155
155
  // Check if data type already exist (maybe a builtin type or already imported)
@@ -223,7 +223,7 @@ export class DataTypeFactory {
223
223
  if (metadata.name) {
224
224
  const curr = initArgsMap?.get(metadata.name);
225
225
  if (curr) {
226
- if (curr[initializingSymbol])
226
+ if (checkCircularDeps && curr[initializingSymbol])
227
227
  return context.addError('Circular reference detected');
228
228
  return metadata.name;
229
229
  }
@@ -294,7 +294,7 @@ export class DataTypeFactory {
294
294
  await context.enterAsync('.base', async () => {
295
295
  let baseArgs;
296
296
  if (metadata.base) {
297
- baseArgs = await this._importDataTypeArgs(context, owner, metadata.base);
297
+ baseArgs = await this._importDataTypeArgs(context, owner, metadata.base, true);
298
298
  }
299
299
  else if (initArgs.ctor) {
300
300
  const baseClass = Object.getPrototypeOf(initArgs.ctor.prototype).constructor;
@@ -359,7 +359,7 @@ export class DataTypeFactory {
359
359
  await context.enterAsync('.base', async () => {
360
360
  let baseArgs;
361
361
  if (metadata.base) {
362
- baseArgs = await this._importDataTypeArgs(context, owner, metadata.base);
362
+ baseArgs = await this._importDataTypeArgs(context, owner, metadata.base, true);
363
363
  }
364
364
  else if (initArgs.ctor) {
365
365
  const baseClass = Object.getPrototypeOf(initArgs.ctor.prototype).constructor;
@@ -392,7 +392,7 @@ export class DataTypeFactory {
392
392
  await context.enterAsync('.base', async () => {
393
393
  let baseArgs;
394
394
  if (metadata.base) {
395
- baseArgs = await this._importDataTypeArgs(context, owner, metadata.base);
395
+ baseArgs = await this._importDataTypeArgs(context, owner, metadata.base, true);
396
396
  }
397
397
  else if (initArgs.ctor) {
398
398
  const baseClass = Object.getPrototypeOf(initArgs.ctor.prototype).constructor;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/common",
3
- "version": "1.17.3",
3
+ "version": "1.17.5",
4
4
  "description": "Opra common package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -10,15 +10,16 @@
10
10
  "@browsery/http-parser": "^0.5.9-r4",
11
11
  "@browsery/i18next": "^24.2.2",
12
12
  "@browsery/type-is": "^1.6.18-r8",
13
- "@jsopen/objects": "^1.6.0",
13
+ "@jsopen/objects": "^1.6.2",
14
14
  "fast-tokenizer": "^1.7.0",
15
+ "hash-object": "^5.0.1",
15
16
  "putil-promisify": "^1.10.1",
16
17
  "putil-varhelpers": "^1.6.5",
17
18
  "reflect-metadata": "^0.2.2",
18
19
  "super-fast-md5": "^1.0.3",
19
20
  "tslib": "^2.8.1",
20
21
  "uid": "^2.0.2",
21
- "valgen": "^5.15.0"
22
+ "valgen": "^5.15.2"
22
23
  },
23
24
  "type": "module",
24
25
  "exports": {
@@ -10,7 +10,7 @@ export declare class DataTypeMap {
10
10
  protected [kMap]: ResponsiveMap<DataType>;
11
11
  protected [kCtorMap]: WeakMap<object | Function | Type<any>, string>;
12
12
  get size(): number;
13
- forEach(callbackFn: (value: DataType, key: string, map: ReadonlyMap<string, DataType>) => void, thisArg?: any): void;
13
+ forEach(callbackFn: (value: DataType, key: string, map: ResponsiveMap<DataType>) => void, thisArg?: any): void;
14
14
  get(nameOrCtor: string | Type | Function | object): DataType | undefined;
15
15
  set(name: string, dataType: DataType): void;
16
16
  has(nameOrCtor: string | Type | Function | object | DataType): boolean;
@@ -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
+ cache?: Map<DataType, Record<string, Validator | null>>;
84
+ forwardCallbacks?: Set<Function>;
85
+ };
85
86
  export {};
@@ -95,7 +95,7 @@ export declare class DataTypeFactory {
95
95
  * @param thunk
96
96
  * @protected
97
97
  */
98
- protected static _importDataTypeArgs(context: DataTypeFactory.Context, owner: DocumentElement, thunk: string | Type | OpraSchema.DataType | object): Promise<DataTypeFactory.DataTypeInitArguments | string | void>;
98
+ protected static _importDataTypeArgs(context: DataTypeFactory.Context, owner: DocumentElement, thunk: string | Type | OpraSchema.DataType | object, checkCircularDeps?: boolean): Promise<DataTypeFactory.DataTypeInitArguments | string | void>;
99
99
  protected static _prepareDataTypeArgs(context: DataTypeFactory.Context, initArgs: DataTypeFactory.DataTypeInitArguments, metadata: OpraSchema.DataTypeBase | DataType.Metadata): Promise<void>;
100
100
  protected static _prepareComplexTypeArgs(context: DataTypeFactory.Context, owner: DocumentElement, initArgs: DataTypeFactory.ComplexTypeInit, metadata: ComplexType.Metadata | OpraSchema.ComplexType): Promise<void>;
101
101
  protected static _prepareEnumTypeArgs(context: DataTypeFactory.Context, owner: DocumentElement, initArgs: DataTypeFactory.EnumTypeInit, metadata: EnumType.Metadata | OpraSchema.EnumType): Promise<void>;
@@ -11,7 +11,7 @@ declare const kSize: unique symbol;
11
11
  /**
12
12
  * A Map implementation that supports case-insensitivity and ordered keys
13
13
  */
14
- export declare class ResponsiveMap<V> implements Map<string, V> {
14
+ export declare class ResponsiveMap<V> {
15
15
  private [kSize];
16
16
  private [kEntries];
17
17
  private [kKeyMap];
@@ -20,7 +20,7 @@ export declare class ResponsiveMap<V> implements Map<string, V> {
20
20
  constructor(init?: ResponsiveMapInit<V> | null, options?: ResponsiveMapOptions);
21
21
  get size(): number;
22
22
  clear(): void;
23
- forEach(callbackfn: (value: V, key: string, map: Map<string, V>) => void, thisArg?: any): void;
23
+ forEach(callbackfn: (value: V, key: string, map: ResponsiveMap<V>) => void, thisArg?: any): void;
24
24
  get(key: string): V | undefined;
25
25
  has(key: string): boolean;
26
26
  set(key: string, value: V): this;