@opra/common 1.5.0 → 1.5.2

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.
Files changed (57) hide show
  1. package/browser/index.cjs +5 -5
  2. package/browser/index.mjs +5 -5
  3. package/cjs/document/api-document.js +2 -3
  4. package/cjs/document/common/document-init-context.js +0 -5
  5. package/cjs/document/common/document-node.js +2 -3
  6. package/cjs/document/data-type/api-field.js +9 -18
  7. package/cjs/document/data-type/complex-type-base.js +16 -14
  8. package/cjs/document/data-type/complex-type.js +14 -7
  9. package/cjs/document/data-type/data-type.js +12 -14
  10. package/cjs/document/data-type/enum-type.js +12 -9
  11. package/cjs/document/data-type/extended-types/field-path.type.js +7 -7
  12. package/cjs/document/data-type/extended-types/filter.type.js +5 -5
  13. package/cjs/document/data-type/mapped-type.js +10 -4
  14. package/cjs/document/data-type/mixin-type.js +13 -4
  15. package/cjs/document/data-type/simple-type.js +11 -16
  16. package/cjs/document/decorators/api-field-decorator.js +37 -3
  17. package/cjs/document/decorators/simple-type.decorator.js +2 -0
  18. package/cjs/document/factory/api-document.factory.js +0 -1
  19. package/cjs/document/utils/test-scope-match.js +13 -0
  20. package/cjs/filter/filter-rules.js +6 -7
  21. package/esm/document/api-document.js +2 -3
  22. package/esm/document/common/document-init-context.js +0 -5
  23. package/esm/document/common/document-node.js +2 -3
  24. package/esm/document/data-type/api-field.js +10 -19
  25. package/esm/document/data-type/complex-type-base.js +16 -14
  26. package/esm/document/data-type/complex-type.js +14 -7
  27. package/esm/document/data-type/data-type.js +12 -14
  28. package/esm/document/data-type/enum-type.js +12 -9
  29. package/esm/document/data-type/extended-types/field-path.type.js +7 -7
  30. package/esm/document/data-type/extended-types/filter.type.js +5 -5
  31. package/esm/document/data-type/mapped-type.js +10 -4
  32. package/esm/document/data-type/mixin-type.js +13 -4
  33. package/esm/document/data-type/simple-type.js +11 -16
  34. package/esm/document/decorators/api-field-decorator.js +36 -2
  35. package/esm/document/decorators/simple-type.decorator.js +2 -0
  36. package/esm/document/factory/api-document.factory.js +0 -1
  37. package/esm/document/utils/test-scope-match.js +10 -0
  38. package/esm/filter/filter-rules.js +6 -7
  39. package/package.json +1 -1
  40. package/types/document/api-document.d.ts +2 -3
  41. package/types/document/common/document-init-context.d.ts +0 -1
  42. package/types/document/common/document-node.d.ts +8 -8
  43. package/types/document/data-type/api-field.d.ts +40 -27
  44. package/types/document/data-type/complex-type-base.d.ts +4 -2
  45. package/types/document/data-type/complex-type.d.ts +1 -0
  46. package/types/document/data-type/data-type.d.ts +6 -5
  47. package/types/document/data-type/enum-type.d.ts +1 -0
  48. package/types/document/data-type/extended-types/field-path.type.d.ts +2 -2
  49. package/types/document/data-type/mapped-type.d.ts +1 -0
  50. package/types/document/data-type/mixin-type.d.ts +1 -0
  51. package/types/document/data-type/simple-type.d.ts +1 -1
  52. package/types/document/decorators/api-field-decorator.d.ts +5 -3
  53. package/types/document/decorators/simple-type.decorator.d.ts +34 -0
  54. package/types/document/factory/api-document.factory.d.ts +1 -2
  55. package/types/document/http/http-api.d.ts +1 -1
  56. package/types/document/utils/test-scope-match.d.ts +1 -0
  57. package/types/filter/filter-rules.d.ts +3 -3
@@ -71,7 +71,6 @@ export class ApiDocument extends DocumentElement {
71
71
  url: this.url,
72
72
  info: cloneObject(this.info, true),
73
73
  });
74
- options = options || { scopes: this.scopes };
75
74
  if (this.references.size) {
76
75
  let i = 0;
77
76
  const references = {};
@@ -91,7 +90,7 @@ export class ApiDocument extends DocumentElement {
91
90
  if (this.types.size) {
92
91
  out.types = {};
93
92
  for (const v of this.types.values()) {
94
- if (!v.inScope(options.scopes))
93
+ if (options?.scope && !v.inScope(options?.scope))
95
94
  continue;
96
95
  out.types[v.name] = v.toJSON(options);
97
96
  }
@@ -110,7 +109,7 @@ export class ApiDocument extends DocumentElement {
110
109
  }
111
110
  _findDataType(nameOrCtor, scope, visitedRefs) {
112
111
  let result = this.types.get(nameOrCtor);
113
- if (result && result.inScope(scope))
112
+ if (result && (!scope || result.inScope(scope)))
114
113
  return result;
115
114
  if (!this.references.size)
116
115
  return;
@@ -6,11 +6,6 @@ export class DocumentInitContext {
6
6
  this.showErrorDetails = true;
7
7
  this.maxErrors = options?.maxErrors || 0;
8
8
  this.error.message = '';
9
- this.scopes = options?.scopes
10
- ? Array.isArray(options.scopes)
11
- ? options?.scopes
12
- : [options?.scopes]
13
- : undefined;
14
9
  }
15
10
  addError(error) {
16
11
  if (!this.error.details.length) {
@@ -22,9 +22,8 @@ export class DocumentNode {
22
22
  return !!this.findDataType(nameOrCtor, scope);
23
23
  }
24
24
  findDataType(nameOrCtor, scope) {
25
- scope = scope || this.getDocument().scopes;
26
25
  const dt = this[kDataTypeMap]?.get(nameOrCtor);
27
- if (dt && dt.inScope(scope))
26
+ if (dt && (!scope || dt.inScope(scope)))
28
27
  return dt;
29
28
  return this.parent
30
29
  ? this.parent.findDataType(nameOrCtor, scope)
@@ -35,7 +34,7 @@ export class DocumentNode {
35
34
  */
36
35
  getDataType(nameOrCtor, scope) {
37
36
  const dt = this.findDataType(nameOrCtor);
38
- if (dt && dt.inScope(scope))
37
+ if (dt && (!scope || dt.inScope(scope)))
39
38
  return dt;
40
39
  let name = '';
41
40
  if (typeof nameOrCtor === 'function') {
@@ -2,10 +2,10 @@ import { omitUndefined } from '@jsopen/objects';
2
2
  import { asMutable } from 'ts-gems';
3
3
  import { DocumentElement } from '../common/document-element.js';
4
4
  import { DECORATOR } from '../constants.js';
5
- import { ApiFieldDecorator } from '../decorators/api-field-decorator.js';
5
+ import { ApiFieldDecoratorFactory } from '../decorators/api-field-decorator.js';
6
+ import { testScopeMatch } from '../utils/test-scope-match.js';
6
7
  import { ComplexTypeBase } from './complex-type-base.js';
7
8
  /**
8
- * @constructor ApiField
9
9
  * @decorator ApiField
10
10
  */
11
11
  export const ApiField = function (...args) {
@@ -25,7 +25,7 @@ export const ApiField = function (...args) {
25
25
  throw new Error('Field origin should be one of ComplexType, MappedType or MixinType');
26
26
  }
27
27
  _this.origin = origin;
28
- _this.scopes = initArgs.scopes;
28
+ _this.scopePattern = initArgs.scopePattern;
29
29
  _this.type = initArgs.type || owner.node.getDataType('any');
30
30
  _this.description = initArgs.description;
31
31
  _this.isArray = initArgs.isArray;
@@ -41,22 +41,13 @@ export const ApiField = function (...args) {
41
41
  _this.examples = initArgs.examples;
42
42
  };
43
43
  /**
44
- *
45
- * @class ApiField
44
+ * The ApiFieldClass represents a descriptive metadata structure for API fields,
45
+ * supporting features like data type definition, scoping, localization, and constraints.
46
+ * This class extends DocumentElement, inheriting base document structure capabilities.
46
47
  */
47
48
  class ApiFieldClass extends DocumentElement {
48
- inScope(scopes) {
49
- if (!this.scopes?.length || !scopes)
50
- return true;
51
- /** this.scope should match all required scopes */
52
- scopes = Array.isArray(scopes) ? scopes : [scopes];
53
- for (const scope of scopes) {
54
- if (!this.scopes.some(s => {
55
- return typeof s === 'string' ? scope === s : s.test(scope);
56
- }))
57
- return false;
58
- }
59
- return true;
49
+ inScope(scope) {
50
+ return testScopeMatch(scope, this.scopePattern);
60
51
  }
61
52
  toJSON(options) {
62
53
  const typeName = this.type
@@ -80,5 +71,5 @@ class ApiFieldClass extends DocumentElement {
80
71
  }
81
72
  }
82
73
  ApiField.prototype = ApiFieldClass.prototype;
83
- Object.assign(ApiField, ApiFieldDecorator);
84
- ApiField[DECORATOR] = ApiFieldDecorator;
74
+ Object.assign(ApiField, ApiFieldDecoratorFactory);
75
+ ApiField[DECORATOR] = ApiFieldDecoratorFactory;
@@ -1,7 +1,6 @@
1
1
  import { asMutable } from 'ts-gems';
2
2
  import { validator, vg } from 'valgen';
3
3
  import { parseFieldsProjection, ResponsiveMap, } from '../../helpers/index.js';
4
- import { translate } from '../../i18n/index.js';
5
4
  import { DataType } from './data-type.js';
6
5
  export const FIELD_PATH_PATTERN = /^([+-])?([a-z$_][\w.]*)$/i;
7
6
  /**
@@ -24,21 +23,29 @@ class ComplexTypeBaseClass extends DataType {
24
23
  /**
25
24
  *
26
25
  */
27
- findField(nameOrPath) {
26
+ findField(nameOrPath, scope) {
28
27
  if (nameOrPath.includes('.')) {
29
- const fieldPath = this.parseFieldPath(nameOrPath);
28
+ const fieldPath = this.parseFieldPath(nameOrPath, { scope });
29
+ if (fieldPath.length === 0)
30
+ throw new Error(`Field "${nameOrPath}" does not exist in scope "${scope}"`);
30
31
  const lastItem = fieldPath.pop();
31
32
  return lastItem?.field;
32
33
  }
33
- return this.fields.get(nameOrPath);
34
+ const field = this.fields.get(nameOrPath);
35
+ if (field && scope && !field.inScope(scope))
36
+ return;
37
+ return field;
34
38
  }
35
39
  /**
36
40
  *
37
41
  */
38
- getField(nameOrPath) {
42
+ getField(nameOrPath, scope) {
39
43
  const field = this.findField(nameOrPath);
40
- if (!field)
41
- throw new Error(translate('error:UNKNOWN_FIELD', { field: nameOrPath }));
44
+ if (field && scope && !field.inScope(scope))
45
+ throw new Error(`Field "${nameOrPath}" does not exist in scope "${scope}"`);
46
+ if (!field) {
47
+ throw new Error(`Field (${nameOrPath}) does not exist`);
48
+ }
42
49
  return field;
43
50
  }
44
51
  /**
@@ -69,7 +76,7 @@ class ComplexTypeBaseClass extends DataType {
69
76
  }
70
77
  if (dataType) {
71
78
  if (dataType instanceof ComplexTypeBase) {
72
- field = dataType.fields.get(item.fieldName);
79
+ field = dataType.getField(item.fieldName, options?.scope);
73
80
  if (field) {
74
81
  item.fieldName = field.name;
75
82
  item.field = field;
@@ -146,17 +153,12 @@ class ComplexTypeBaseClass extends DataType {
146
153
  const schema = {};
147
154
  const { currentPath, projection } = context;
148
155
  const pickList = !!(projection && Object.values(projection).find(p => !p.sign));
149
- const scope = context.scope
150
- ? Array.isArray(context.scope)
151
- ? context.scope
152
- : [context.scope]
153
- : undefined;
154
156
  // Process fields
155
157
  let fieldName;
156
158
  for (const field of this.fields.values()) {
157
159
  if (
158
160
  /** Ignore field if required scope(s) do not match field scopes */
159
- (scope && !field.inScope(scope)) ||
161
+ (context.scope && !field.inScope(context.scope)) ||
160
162
  /** Ignore field if readonly and ignoreReadonlyFields option true */
161
163
  (context.ignoreReadonlyFields && field.readonly) ||
162
164
  /** Ignore field if writeonly and ignoreWriteonlyFields option true */
@@ -69,21 +69,19 @@ class ComplexTypeClass extends ComplexTypeBase {
69
69
  return !!this.base?.extendsFrom(baseType);
70
70
  }
71
71
  toJSON(options) {
72
+ const superJson = super.toJSON(options);
72
73
  const baseName = this.base
73
74
  ? this.node.getDataTypeNameWithNs(this.base)
74
75
  : undefined;
75
- if (options?.scopes && !this.base?.inScope(options?.scopes))
76
- throw new TypeError(`Base type ${baseName ? '(' + baseName + ')' : ''} of ${this.name ? +this.name : 'embedded'} type ` +
77
- `is not in required scope(s) [${options.scopes}`);
78
- const out = omitUndefined({
79
- ...ComplexTypeBase.prototype.toJSON.call(this),
76
+ const out = {
77
+ ...superJson,
80
78
  kind: this.kind,
81
79
  base: this.base
82
80
  ? baseName
83
81
  ? baseName
84
82
  : this.base.toJSON(options)
85
83
  : undefined,
86
- });
84
+ };
87
85
  if (this.additionalFields) {
88
86
  if (this.additionalFields instanceof DataType) {
89
87
  const typeName = this.node.getDataTypeNameWithNs(this.additionalFields);
@@ -98,7 +96,8 @@ class ComplexTypeClass extends ComplexTypeBase {
98
96
  const fields = {};
99
97
  let i = 0;
100
98
  for (const field of this.fields.values()) {
101
- if (field.origin === this && field.inScope(options?.scopes)) {
99
+ if (field.origin === this &&
100
+ (!options?.scope || field.inScope(options?.scope))) {
102
101
  fields[field.name] = field.toJSON(options);
103
102
  i++;
104
103
  }
@@ -108,6 +107,14 @@ class ComplexTypeClass extends ComplexTypeBase {
108
107
  }
109
108
  return omitUndefined(out);
110
109
  }
110
+ _locateBase(callback) {
111
+ if (!this.base)
112
+ return;
113
+ if (callback(this.base))
114
+ return this.base;
115
+ if (this.base._locateBase)
116
+ return this.base._locateBase(callback);
117
+ }
111
118
  }
112
119
  ComplexType.prototype = ComplexTypeClass.prototype;
113
120
  Object.assign(ComplexType, ComplexTypeDecorator);
@@ -3,6 +3,7 @@ import { asMutable } from 'ts-gems';
3
3
  import { DocumentElement } from '../common/document-element.js';
4
4
  import { CLASS_NAME_PATTERN } from '../constants.js';
5
5
  import { colorFgMagenta, colorFgYellow, colorReset, nodeInspectCustom, } from '../utils/inspect.util.js';
6
+ import { testScopeMatch } from '../utils/test-scope-match.js';
6
7
  /**
7
8
  * DataType constructor
8
9
  */
@@ -17,7 +18,7 @@ context) {
17
18
  DocumentElement.call(this, owner);
18
19
  const _this = asMutable(this);
19
20
  _this.kind = initArgs.kind;
20
- _this.scopes = initArgs.scopes;
21
+ _this.scopePattern = initArgs.scopePattern;
21
22
  _this.name = initArgs.name;
22
23
  _this.description = initArgs.description;
23
24
  _this.abstract = initArgs.abstract;
@@ -31,21 +32,18 @@ class DataTypeClass extends DocumentElement {
31
32
  get embedded() {
32
33
  return !this.name;
33
34
  }
34
- inScope(scopes) {
35
- if (!this.scopes?.length || !scopes)
36
- return true;
37
- /** this.scope should match all required scopes */
38
- scopes = Array.isArray(scopes) ? scopes : [scopes];
39
- for (const scope of scopes) {
40
- if (!this.scopes.some(s => {
41
- return typeof s === 'string' ? scope === s : s.test(scope);
42
- }))
43
- return false;
44
- }
45
- return true;
35
+ inScope(scope) {
36
+ return testScopeMatch(scope, this.scopePattern);
46
37
  }
47
- // eslint-disable-next-line
48
38
  toJSON(options) {
39
+ if (options?.scope) {
40
+ /** Locate base model which is not available for given scope */
41
+ const outOfScope = this._locateBase(dt => !dt.inScope(options.scope));
42
+ if (outOfScope) {
43
+ const baseName = this.node.getDataTypeNameWithNs(outOfScope);
44
+ throw new TypeError(`"${baseName}" model is not available for "${options.scope}" scope`);
45
+ }
46
+ }
49
47
  return omitUndefined({
50
48
  kind: this.kind,
51
49
  description: this.description,
@@ -49,22 +49,25 @@ class EnumTypeClass extends DataType {
49
49
  return vg.isEnum(Object.keys(this.attributes));
50
50
  }
51
51
  toJSON(options) {
52
+ const superJson = super.toJSON(options);
52
53
  const baseName = this.base
53
54
  ? this.node.getDataTypeNameWithNs(this.base)
54
55
  : undefined;
55
- if (options?.scopes && !this.base?.inScope(options?.scopes))
56
- throw new TypeError(`Base type ${baseName ? '(' + baseName + ')' : ''} of ${this.name ? +this.name : 'embedded'} type ` +
57
- `is not in required scope(s) [${options.scopes}`);
58
56
  return omitUndefined({
59
- ...DataType.prototype.toJSON.call(this),
60
- base: this.base
61
- ? baseName
62
- ? baseName
63
- : this.base.toJSON(options)
64
- : undefined,
57
+ ...superJson,
58
+ kind: this.kind,
59
+ base: baseName,
65
60
  attributes: cloneObject(this.ownAttributes),
66
61
  });
67
62
  }
63
+ _locateBase(callback) {
64
+ if (!this.base)
65
+ return;
66
+ if (callback(this.base))
67
+ return this.base;
68
+ if (this.base._locateBase)
69
+ return this.base._locateBase(callback);
70
+ }
68
71
  }
69
72
  EnumType.prototype = EnumTypeClass.prototype;
70
73
  Object.assign(EnumType, EnumTypeClass);
@@ -1,33 +1,33 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
2
  import { toString, validator, vg } from 'valgen';
3
3
  import { DECODER, ENCODER } from '../../constants.js';
4
+ import { DataType } from '../data-type.js';
4
5
  import { SimpleType } from '../simple-type.js';
5
6
  let FieldPathType = class FieldPathType {
6
7
  constructor(attributes) {
7
8
  if (attributes)
8
9
  Object.assign(this, attributes);
9
10
  }
10
- [DECODER](properties, element) {
11
+ [DECODER](properties, element, scope) {
11
12
  const dataType = properties.dataType
12
13
  ? element.node.getComplexType(properties.dataType)
13
14
  : element.node.getComplexType('object');
14
15
  const allowSigns = properties.allowSigns;
15
- const decodeFieldPath = validator('decodeFieldPath', (input) => dataType.normalizeFieldPath(input, { allowSigns }));
16
+ const decodeFieldPath = validator('decodeFieldPath', (input) => dataType.normalizeFieldPath(input, { allowSigns, scope }));
16
17
  return vg.pipe([toString, decodeFieldPath]);
17
18
  }
18
- [ENCODER](properties, element) {
19
- return this[DECODER](properties, element);
19
+ [ENCODER](properties, element, scope) {
20
+ return this[DECODER](properties, element, scope);
20
21
  }
21
22
  toJSON(properties, element, options) {
22
23
  const dataType = properties.dataType
23
24
  ? element.node.getComplexType(properties.dataType)
24
25
  : element.node.getComplexType('object');
26
+ /** Test scope */
27
+ DataType.prototype.toJSON.call(dataType, options);
25
28
  const typeName = dataType
26
29
  ? element.node.getDataTypeNameWithNs(dataType)
27
30
  : undefined;
28
- if (options?.scopes && !dataType.inScope(options?.scopes))
29
- throw new TypeError(`Data type ${typeName ? '(' + typeName + ')' : ''} ` +
30
- `is not in required scope(s) [${options.scopes}`);
31
31
  return {
32
32
  dataType: typeName ? typeName : dataType.toJSON(options),
33
33
  allowSigns: properties.allowSigns,
@@ -3,6 +3,7 @@ import { validator } from 'valgen';
3
3
  import { FilterRules } from '../../../filter/filter-rules.js';
4
4
  import { OpraFilter } from '../../../filter/index.js';
5
5
  import { DECODER, ENCODER } from '../../constants.js';
6
+ import { DataType } from '../data-type.js';
6
7
  import { SimpleType } from '../simple-type.js';
7
8
  let FilterType = class FilterType {
8
9
  constructor(attributes) {
@@ -25,12 +26,11 @@ let FilterType = class FilterType {
25
26
  const dataType = properties.dataType
26
27
  ? element.node.getComplexType(properties.dataType)
27
28
  : element.node.getComplexType('object');
29
+ /** Test scope */
30
+ DataType.prototype.toJSON.call(dataType, options);
28
31
  const typeName = dataType
29
32
  ? element.node.getDataTypeNameWithNs(dataType)
30
33
  : undefined;
31
- if (options?.scopes && !dataType.inScope(options?.scopes))
32
- throw new TypeError(`Data type ${typeName ? '(' + typeName + ')' : ''} ` +
33
- `is not in required scope(s) [${options.scopes}`);
34
34
  return {
35
35
  dataType: typeName ? typeName : dataType.toJSON(options),
36
36
  rules: properties.rules,
@@ -62,12 +62,12 @@ FilterType = __decorate([
62
62
  __metadata("design:paramtypes", [Object])
63
63
  ], FilterType);
64
64
  export { FilterType };
65
- const decodeFilter = (dataType, rules, element) => validator('decodeFilter', (input, context, _this) => {
65
+ const decodeFilter = (dataType, rules, scope) => validator('decodeFilter', (input, context, _this) => {
66
66
  if (typeof input === 'string') {
67
67
  try {
68
68
  const filter = OpraFilter.parse(input);
69
69
  if (rules)
70
- return rules.normalizeFilter(filter, dataType, element);
70
+ return rules.normalizeFilter(filter, dataType, scope);
71
71
  return filter;
72
72
  }
73
73
  catch (e) {
@@ -86,14 +86,12 @@ class MappedTypeClass extends ComplexTypeBase {
86
86
  return !!this.base?.extendsFrom(baseType);
87
87
  }
88
88
  toJSON(options) {
89
+ const superJson = super.toJSON(options);
89
90
  const baseName = this.base
90
91
  ? this.node.getDataTypeNameWithNs(this.base)
91
92
  : undefined;
92
- if (options?.scopes && !this.base?.inScope(options?.scopes))
93
- throw new TypeError(`Base type ${baseName ? '(' + baseName + ')' : ''} of ${this.name ? +this.name : 'embedded'} type ` +
94
- `is not in required scope(s) [${options.scopes}`);
95
93
  return omitUndefined({
96
- ...ComplexTypeBase.prototype.toJSON.call(this, options),
94
+ ...superJson,
97
95
  base: baseName ? baseName : this.base.toJSON(options),
98
96
  kind: this.kind,
99
97
  pick: this.pick,
@@ -102,6 +100,14 @@ class MappedTypeClass extends ComplexTypeBase {
102
100
  required: this.required,
103
101
  });
104
102
  }
103
+ _locateBase(callback) {
104
+ if (!this.base)
105
+ return;
106
+ if (callback(this.base))
107
+ return this.base;
108
+ if (this.base._locateBase)
109
+ return this.base._locateBase(callback);
110
+ }
105
111
  }
106
112
  MappedType.prototype = MappedTypeClass.prototype;
107
113
  MappedType._applyMixin = () => undefined;
@@ -55,18 +55,27 @@ class MixinTypeClass extends ComplexTypeBase {
55
55
  return false;
56
56
  }
57
57
  toJSON(options) {
58
+ const superJson = super.toJSON(options);
58
59
  return omitUndefined({
59
- ...ComplexTypeBase.prototype.toJSON.call(this),
60
+ ...superJson,
60
61
  kind: this.kind,
61
62
  types: this.types.map(base => {
62
63
  const baseName = this.node.getDataTypeNameWithNs(base);
63
- if (options?.scopes && !base?.inScope(options?.scopes))
64
- throw new TypeError(`Base type ${baseName ? '(' + baseName + ')' : ''} of ${this.name ? +this.name : 'embedded'} type ` +
65
- `is not in required scope(s) [${options.scopes}`);
66
64
  return baseName ? baseName : base.toJSON(options);
67
65
  }),
68
66
  });
69
67
  }
68
+ _locateBase(callback) {
69
+ for (const t of this.types) {
70
+ if (callback(t))
71
+ return t;
72
+ if (t._locateBase) {
73
+ const x = t._locateBase(callback);
74
+ if (x)
75
+ return x;
76
+ }
77
+ }
78
+ }
70
79
  }
71
80
  MixinType.prototype = MixinTypeClass.prototype;
72
81
  MixinType[DECORATOR] = MixinTypeFactory;
@@ -67,7 +67,7 @@ class SimpleTypeClass extends DataType {
67
67
  let t = this;
68
68
  while (t) {
69
69
  if (t._generateDecoder)
70
- return t._generateDecoder(prop, options?.documentElement || this.owner);
70
+ return t._generateDecoder(prop, options?.documentElement || this.owner, options?.scope);
71
71
  t = this.base;
72
72
  }
73
73
  return isAny;
@@ -75,12 +75,16 @@ class SimpleTypeClass extends DataType {
75
75
  let t = this;
76
76
  while (t) {
77
77
  if (t._generateEncoder)
78
- return t._generateEncoder(prop, options?.documentElement || this.owner);
78
+ return t._generateEncoder(prop, options?.documentElement || this.owner, options?.scope);
79
79
  t = this.base;
80
80
  }
81
81
  return isAny;
82
82
  }
83
83
  toJSON(options) {
84
+ const superJson = super.toJSON(options);
85
+ const baseName = this.base
86
+ ? this.node.getDataTypeNameWithNs(this.base)
87
+ : undefined;
84
88
  const attributes = omitUndefined(this.ownAttributes);
85
89
  let properties;
86
90
  if (this.properties && typeof this.properties.toJSON === 'function') {
@@ -88,22 +92,13 @@ class SimpleTypeClass extends DataType {
88
92
  }
89
93
  else
90
94
  properties = this.properties ? cloneObject(this.properties) : {};
91
- const baseName = this.base
92
- ? this.node.getDataTypeNameWithNs(this.base)
93
- : undefined;
94
- if (options?.scopes && !this.base?.inScope(options?.scopes))
95
- throw new TypeError(`Base type ${baseName ? '(' + baseName + ')' : ''} of ${this.name ? +this.name : 'embedded'} type ` +
96
- `is not in required scope(s) [${options.scopes}`);
97
- const out = omitUndefined({
98
- ...DataType.prototype.toJSON.apply(this),
99
- base: this.base
100
- ? baseName
101
- ? baseName
102
- : this.base.toJSON(options)
103
- : undefined,
95
+ const out = {
96
+ ...superJson,
97
+ kind: this.kind,
98
+ base: baseName,
104
99
  attributes: attributes && Object.keys(attributes).length ? attributes : undefined,
105
100
  properties: Object.keys(properties).length ? properties : undefined,
106
- });
101
+ };
107
102
  if (Object.keys(this.ownNameMappings).length)
108
103
  out.nameMappings = { ...this.ownNameMappings };
109
104
  return omitUndefined(out, true);
@@ -1,7 +1,13 @@
1
+ // import { omitUndefined } from '@jsopen/objects';
2
+ // import { StrictOmit } from 'ts-gems';
1
3
  import { OpraSchema } from '../../schema/index.js';
2
4
  import { DATATYPE_METADATA } from '../constants.js';
3
- export function ApiFieldDecorator(options) {
4
- return function (target, propertyKey) {
5
+ export function ApiFieldDecoratorFactory(options) {
6
+ const decoratorChain = [];
7
+ /**
8
+ *
9
+ */
10
+ const decorator = function (target, propertyKey) {
5
11
  if (typeof propertyKey !== 'string')
6
12
  throw new TypeError(`Symbol properties can't be used as a field`);
7
13
  const metadata = Reflect.getOwnMetadata(DATATYPE_METADATA, target.constructor) ||
@@ -11,6 +17,11 @@ export function ApiFieldDecorator(options) {
11
17
  const designType = Reflect.getMetadata('design:type', target, propertyKey);
12
18
  const elemMeta = (metadata.fields[propertyKey] = {
13
19
  ...options,
20
+ scopePattern: options?.scopePattern
21
+ ? Array.isArray(options.scopePattern)
22
+ ? options.scopePattern
23
+ : [options.scopePattern]
24
+ : undefined,
14
25
  });
15
26
  if (designType === Array) {
16
27
  elemMeta.isArray = true;
@@ -18,5 +29,28 @@ export function ApiFieldDecorator(options) {
18
29
  else
19
30
  elemMeta.type = elemMeta.type || designType;
20
31
  Reflect.defineMetadata(DATATYPE_METADATA, metadata, target.constructor);
32
+ for (const fn of decoratorChain)
33
+ fn(metadata, target);
21
34
  };
35
+ // /**
36
+ // *
37
+ // */
38
+ // decorator.Override = (
39
+ // scopes: (string | RegExp) | (string | RegExp)[],
40
+ // opts: StrictOmit<ApiField.Options, 'isArray' | 'type' | 'scopePattern'>,
41
+ // ): any => {
42
+ // decoratorChain.push((meta: ApiField.Metadata): void => {
43
+ // meta.overrides = meta.overrides || [];
44
+ // meta.overrides.push(
45
+ // omitUndefined({
46
+ // ...opts,
47
+ // scopes: Array.isArray(scopes) ? scopes : [scopes],
48
+ // type: undefined,
49
+ // isArray: undefined,
50
+ // }),
51
+ // );
52
+ // });
53
+ // return decorator;
54
+ // };
55
+ return decorator;
22
56
  }
@@ -24,6 +24,8 @@ export function SimpleTypeDecoratorFactory(options) {
24
24
  metadata.kind = OpraSchema.SimpleType.Kind;
25
25
  metadata.name = name;
26
26
  Reflect.defineMetadata(DATATYPE_METADATA, metadata, target);
27
+ for (const fn of decoratorChain)
28
+ fn(metadata, target);
27
29
  };
28
30
  decorator.Example = (value, description) => {
29
31
  decoratorChain.push((meta) => {
@@ -27,7 +27,6 @@ export class ApiDocumentFactory {
27
27
  : new DocumentInitContext(options);
28
28
  try {
29
29
  const document = new ApiDocument();
30
- document.scopes = context.scopes;
31
30
  await factory.initDocument(document, context, schemaOrUrl);
32
31
  if (context.error.details.length)
33
32
  throw context.error;
@@ -0,0 +1,10 @@
1
+ export function testScopeMatch(scope, pattern) {
2
+ if (!(scope && pattern))
3
+ return true;
4
+ if (Array.isArray(pattern)) {
5
+ return pattern.some(x => {
6
+ return typeof x === 'string' ? scope === x : x.test(scope);
7
+ });
8
+ }
9
+ return typeof pattern === 'string' ? scope === pattern : pattern.test(scope);
10
+ }
@@ -29,12 +29,11 @@ export class FilterRules {
29
29
  operators,
30
30
  }));
31
31
  }
32
- normalizeFilter(filter, currentType, element) {
32
+ normalizeFilter(filter, currentType, scope) {
33
33
  const ast = typeof filter === 'string' ? parse(filter) : filter;
34
- const doc = element?.node.getDocument();
35
- return this.normalizeFilterAst(ast, [], currentType, doc?.scopes);
34
+ return this.normalizeFilterAst(ast, [], currentType, scope);
36
35
  }
37
- normalizeFilterAst(ast, stack, currentType, scopes) {
36
+ normalizeFilterAst(ast, stack, currentType, scope) {
38
37
  if (ast instanceof ComparisonExpression) {
39
38
  stack.push(ast);
40
39
  this.normalizeFilterAst(ast.left, stack, currentType);
@@ -97,8 +96,8 @@ export class FilterRules {
97
96
  return ast;
98
97
  }
99
98
  if (ast instanceof QualifiedIdentifier && currentType) {
100
- ast.value = currentType.normalizeFieldPath(ast.value);
101
- ast.field = currentType.getField(ast.value);
99
+ ast.value = currentType.normalizeFieldPath(ast.value, { scope });
100
+ ast.field = currentType.getField(ast.value, scope);
102
101
  ast.dataType = ast.field.type;
103
102
  return ast;
104
103
  }
@@ -126,7 +125,7 @@ export class FilterRules {
126
125
  decoder = this._decoderCache.get(comp.left.field);
127
126
  if (!decoder) {
128
127
  decoder = comp.left.field.type.generateCodec('decode', {
129
- scope: scopes,
128
+ scope,
130
129
  projection: '*',
131
130
  ignoreWriteonlyFields: true,
132
131
  coerce: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/common",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Opra common package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",