@player-tools/xlr-sdk 0.2.1-next.3 → 0.2.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/src/sdk.ts CHANGED
@@ -2,10 +2,15 @@ import type {
2
2
  Manifest,
3
3
  NamedType,
4
4
  NodeType,
5
+ ObjectType,
5
6
  TransformFunction,
6
7
  TSManifest,
7
8
  } from '@player-tools/xlr';
8
9
  import type { TopLevelDeclaration } from '@player-tools/xlr-utils';
10
+ import {
11
+ computeEffectiveObject,
12
+ resolveReferenceNode,
13
+ } from '@player-tools/xlr-utils';
9
14
  import { fillInGenerics } from '@player-tools/xlr-utils';
10
15
  import type { Node } from 'jsonc-parser';
11
16
  import { TSWriter } from '@player-tools/xlr-converters';
@@ -17,6 +22,12 @@ import type { XLRRegistry, Filters } from './registry';
17
22
  import { BasicXLRRegistry } from './registry';
18
23
  import type { ExportTypes } from './types';
19
24
  import { XLRValidator } from './validator';
25
+ import { simpleTransformGenerator } from './utils';
26
+
27
+ export interface GetTypeOptions {
28
+ /** Resolves `extends` fields in objects */
29
+ getRawType?: boolean;
30
+ }
20
31
 
21
32
  /**
22
33
  * Abstraction for interfacing with XLRs making it more approachable to use without understanding the inner workings of the types and how they are packaged
@@ -28,10 +39,17 @@ export class XLRSDK {
28
39
 
29
40
  constructor(customRegistry?: XLRRegistry) {
30
41
  this.registry = customRegistry ?? new BasicXLRRegistry();
31
- this.validator = new XLRValidator(this.registry);
42
+ this.validator = new XLRValidator(this.getType.bind(this));
32
43
  this.tsWriter = new TSWriter();
33
44
  }
34
45
 
46
+ /**
47
+ * Loads definitions from a path on the filesystem
48
+ *
49
+ * @param inputPath - path to the directory to load (above the xlr folder)
50
+ * @param filters - Any filters to apply when loading the types (a positive match will omit)
51
+ * @param transforms - any transforms to apply to the types being loaded
52
+ */
35
53
  public loadDefinitionsFromDisk(
36
54
  inputPath: string,
37
55
  filters?: Omit<Filters, 'pluginFilter'>,
@@ -66,24 +84,34 @@ export class XLRSDK {
66
84
  )
67
85
  .toString()
68
86
  );
69
- transforms?.forEach((transform) => transform(cType, capabilityName));
70
- const resolvedType = fillInGenerics(cType) as NamedType<NodeType>;
71
- this.registry.add(resolvedType, manifest.pluginName, capabilityName);
87
+ const effectiveType =
88
+ transforms?.reduce(
89
+ (typeAccumulator: NamedType<NodeType>, transformFn) =>
90
+ transformFn(
91
+ typeAccumulator,
92
+ capabilityName
93
+ ) as NamedType<NodeType>,
94
+ cType
95
+ ) ?? cType;
96
+
97
+ this.registry.add(effectiveType, manifest.pluginName, capabilityName);
72
98
  }
73
99
  });
74
100
  });
75
101
  }
76
102
 
103
+ /**
104
+ * Load definitions from a js/ts file in memory
105
+ *
106
+ * @param manifest - The imported XLR manifest module
107
+ * @param filters - Any filters to apply when loading the types (a positive match will omit)
108
+ * @param transforms - any transforms to apply to the types being loaded
109
+ */
77
110
  public async loadDefinitionsFromModule(
78
- inputPath: string,
111
+ manifest: TSManifest,
79
112
  filters?: Omit<Filters, 'pluginFilter'>,
80
113
  transforms?: Array<TransformFunction>
81
114
  ) {
82
- const importManifest = await import(
83
- path.join(inputPath, 'xlr', 'manifest.js')
84
- );
85
- const manifest = importManifest.default as TSManifest;
86
-
87
115
  Object.keys(manifest.capabilities)?.forEach((capabilityName) => {
88
116
  if (
89
117
  filters?.capabilityFilter &&
@@ -96,30 +124,82 @@ export class XLRSDK {
96
124
  !filters?.typeFilter ||
97
125
  !extension.name.match(filters?.typeFilter)
98
126
  ) {
99
- transforms?.forEach((transform) =>
100
- transform(extension, extension.name)
101
- );
102
- const resolvedType = fillInGenerics(extension) as NamedType<NodeType>;
103
- this.registry.add(resolvedType, manifest.pluginName, extension.name);
127
+ const effectiveType =
128
+ transforms?.reduce(
129
+ (typeAccumulator: NamedType<NodeType>, transformFn) =>
130
+ transformFn(
131
+ typeAccumulator,
132
+ capabilityName
133
+ ) as NamedType<NodeType>,
134
+ extension
135
+ ) ?? extension;
136
+
137
+ this.registry.add(effectiveType, manifest.pluginName, capabilityName);
104
138
  }
105
139
  });
106
140
  });
107
141
  }
108
142
 
109
- public getType(id: string) {
110
- return this.registry.get(id);
143
+ /**
144
+ * Returns a Type that has been previously loaded
145
+ *
146
+ * @param id - Type to retrieve
147
+ * @param options - `GetTypeOptions`
148
+ * @returns `NamedType<NodeType>` | `undefined`
149
+ */
150
+ public getType(
151
+ id: string,
152
+ options?: GetTypeOptions
153
+ ): NamedType<NodeType> | undefined {
154
+ let type = this.registry.get(id);
155
+ if (options?.getRawType === true || !type) {
156
+ return type;
157
+ }
158
+
159
+ type = this.resolveType(type);
160
+
161
+ return fillInGenerics(type) as NamedType;
111
162
  }
112
163
 
164
+ /**
165
+ * Returns if a Type with `id` has been loaded into the DSK
166
+ *
167
+ * @param id - Type to retrieve
168
+ * @returns `boolean`
169
+ */
113
170
  public hasType(id: string) {
114
171
  return this.registry.has(id);
115
172
  }
116
173
 
174
+ /**
175
+ * Lists types that have been loaded into the SDK
176
+ *
177
+ * @param filters - Any filters to apply to the types returned (a positive match will omit)
178
+ * @returns `Array<NamedTypes>`
179
+ */
117
180
  public listTypes(filters?: Filters) {
118
181
  return this.registry.list(filters);
119
182
  }
120
183
 
121
- public validate(typeName: string, rootNode: Node) {
122
- const xlr = this.registry.get(typeName);
184
+ /**
185
+ * Returns meta information around a registered type
186
+ *
187
+ * @param id - Name of Type to retrieve
188
+ * @returns `TypeMetaData` | `undefined`
189
+ */
190
+ public getTypeInfo(id: string) {
191
+ return this.registry.info(id);
192
+ }
193
+
194
+ /**
195
+ * Validates if a JSONC Node follows the XLR Type registered under the `typeName` specified
196
+ *
197
+ * @param typeName - Registered XLR Type to use for validation
198
+ * @param rootNode - Node to validate
199
+ * @returns `Array<ValidationErrors>`
200
+ */
201
+ public validateByName(typeName: string, rootNode: Node) {
202
+ const xlr = this.getType(typeName, { getRawType: true });
123
203
  if (!xlr) {
124
204
  throw new Error(
125
205
  `Type ${typeName} does not exist in registry, can't validate`
@@ -129,6 +209,17 @@ export class XLRSDK {
129
209
  return this.validator.validateType(rootNode, xlr);
130
210
  }
131
211
 
212
+ /**
213
+ * Validates if a JSONC Node follows the supplied XLR Type
214
+ *
215
+ * @param type - Type to validate against
216
+ * @param rootNode - Node to validate
217
+ * @returns `Array<ValidationErrors>`
218
+ */
219
+ public validateByType(type: NodeType, rootNode: Node) {
220
+ return this.validator.validateType(rootNode, type);
221
+ }
222
+
132
223
  /**
133
224
  * Exports the types loaded into the registry to the specified format
134
225
  *
@@ -145,13 +236,18 @@ export class XLRSDK {
145
236
  transforms?: Array<TransformFunction>
146
237
  ): [string, string][] {
147
238
  const typesToExport = this.registry.list(filters).map((type) => {
148
- transforms?.forEach((transformFunction) =>
149
- transformFunction(
150
- type,
151
- this.registry.info(type.name)?.capability as string
152
- )
153
- );
154
- return type;
239
+ const resolvedType = this.resolveType(type);
240
+ const effectiveType =
241
+ transforms?.reduce(
242
+ (typeAccumulator: NamedType<NodeType>, transformFn) =>
243
+ transformFn(
244
+ typeAccumulator,
245
+ this.registry.info(type.name)?.capability as string
246
+ ) as NamedType<NodeType>,
247
+ resolvedType
248
+ ) ?? resolvedType;
249
+
250
+ return effectiveType;
155
251
  });
156
252
 
157
253
  if (exportType === 'TypeScript') {
@@ -162,6 +258,36 @@ export class XLRSDK {
162
258
  throw new Error(`Unknown export format ${exportType}`);
163
259
  }
164
260
 
261
+ private resolveType(type: NodeType) {
262
+ return simpleTransformGenerator('object', 'any', (objectNode) => {
263
+ if (objectNode.extends) {
264
+ const refName = objectNode.extends.ref.split('<')[0];
265
+ let extendedType = this.getType(refName, { getRawType: true });
266
+ if (!extendedType) {
267
+ throw new Error(
268
+ `Error resolving ${objectNode.name}: can't find extended type ${refName}`
269
+ );
270
+ }
271
+
272
+ extendedType = resolveReferenceNode(
273
+ objectNode.extends,
274
+ extendedType as NamedType<ObjectType>
275
+ ) as NamedType;
276
+ return {
277
+ ...computeEffectiveObject(
278
+ extendedType as ObjectType,
279
+ objectNode as ObjectType,
280
+ false
281
+ ),
282
+ name: objectNode.name,
283
+ description: objectNode.description,
284
+ };
285
+ }
286
+
287
+ return objectNode;
288
+ })(type, 'any') as NamedType;
289
+ }
290
+
165
291
  private exportToTypeScript(
166
292
  typesToExport: NamedType[],
167
293
  importMap: Map<string, string[]>
@@ -202,7 +328,6 @@ export class XLRSDK {
202
328
  const applicableImports = imports.filter((i) => referencedImports.has(i));
203
329
  resultFile = ts.factory.updateSourceFile(resultFile, [
204
330
  ts.factory.createImportDeclaration(
205
- /* decorators */ undefined,
206
331
  /* modifiers */ undefined,
207
332
  ts.factory.createImportClause(
208
333
  false,
package/src/utils.ts CHANGED
@@ -6,6 +6,8 @@ import type {
6
6
  NodeTypeMap,
7
7
  TransformFunction,
8
8
  NodeType,
9
+ ObjectProperty,
10
+ RefNode,
9
11
  } from '@player-tools/xlr';
10
12
 
11
13
  /**
@@ -17,68 +19,127 @@ export function simpleTransformGenerator<
17
19
  >(
18
20
  typeToTransform: T,
19
21
  capabilityToTransform: string,
20
- functionToRun: (input: NodeTypeMap[T]) => void
22
+ functionToRun: (input: NodeTypeMap[T]) => NodeTypeMap[T]
21
23
  ): TransformFunction {
22
24
  /** walker for an XLR tree to touch every node */
23
25
  const walker: TransformFunction = (
24
- node: NamedType | NodeType,
26
+ n: NamedType | NodeType,
25
27
  capability: string
26
28
  ) => {
27
29
  // Run transform on base node before running on children
28
30
  if (capability === capabilityToTransform) {
31
+ let node = { ...n };
29
32
  if (node.type === typeToTransform) {
30
- functionToRun(node as unknown as NodeTypeMap[T]);
33
+ node = functionToRun(node as unknown as NodeTypeMap[T]);
31
34
  }
32
35
 
33
36
  if (node.type === 'object') {
34
- if (node.extends) {
35
- walker(node, capability);
36
- }
37
+ const newObjectProperties: Record<string, ObjectProperty> = {};
37
38
 
38
39
  for (const key in node.properties) {
39
40
  const value = node.properties[key];
40
- walker(value.node, capability);
41
+ newObjectProperties[key] = {
42
+ required: value.required,
43
+ node: walker(value.node, capability),
44
+ };
41
45
  }
42
46
 
43
- if (node.additionalProperties) {
44
- walker(node.additionalProperties, capability);
45
- }
46
- } else if (node.type === 'array') {
47
- walker(node.elementType, capability);
48
- } else if (node.type === 'and') {
49
- node.and.forEach((element) => walker(element, capability));
50
- } else if (node.type === 'or') {
51
- node.or.forEach((element) => walker(element, capability));
52
- } else if (node.type === 'ref') {
53
- node.genericArguments?.forEach((element) =>
54
- walker(element, capability)
55
- );
56
- } else if (node.type === 'tuple') {
57
- if (node.additionalItems) {
58
- walker(node.additionalItems, capability);
59
- }
47
+ return {
48
+ ...node,
49
+ properties: { ...newObjectProperties },
50
+ extends: node.extends
51
+ ? (walker(node.extends, capability) as RefNode)
52
+ : undefined,
53
+ additionalProperties: node.additionalProperties
54
+ ? walker(node.additionalProperties, capability)
55
+ : false,
56
+ };
57
+ }
60
58
 
61
- node.elementTypes.forEach((element) => walker(element, capability));
62
- } else if (node.type === 'function') {
63
- node.parameters.forEach((param) => {
64
- walker(param.type, capability);
65
- if (param.default) {
66
- walker(param.default, capability);
67
- }
68
- });
69
- if (node.returnType) {
70
- walker(node.returnType, capability);
71
- }
72
- } else if (node.type === 'record') {
73
- walker(node.keyType, capability);
74
- walker(node.valueType, capability);
75
- } else if (node.type === 'conditional') {
76
- walker(node.check.left, capability);
77
- walker(node.check.right, capability);
78
- walker(node.value.true, capability);
79
- walker(node.value.false, capability);
59
+ if (node.type === 'array') {
60
+ return {
61
+ ...node,
62
+ elementType: walker(node.elementType, capability),
63
+ };
64
+ }
65
+
66
+ if (node.type === 'and') {
67
+ return {
68
+ ...node,
69
+ and: node.and.map((element) => walker(element, capability)),
70
+ };
71
+ }
72
+
73
+ if (node.type === 'or') {
74
+ return {
75
+ ...node,
76
+ or: node.or.map((element) => walker(element, capability)),
77
+ };
78
+ }
79
+
80
+ if (node.type === 'ref') {
81
+ return {
82
+ ...node,
83
+ genericArguments: node.genericArguments?.map((arg) =>
84
+ walker(arg, capability)
85
+ ),
86
+ };
87
+ }
88
+
89
+ if (node.type === 'tuple') {
90
+ return {
91
+ ...node,
92
+ elementTypes: node.elementTypes.map((type) =>
93
+ walker(type, capability)
94
+ ),
95
+ additionalItems: node.additionalItems
96
+ ? walker(node.additionalItems, capability)
97
+ : false,
98
+ };
99
+ }
100
+
101
+ if (node.type === 'function') {
102
+ return {
103
+ ...node,
104
+ parameters: node.parameters.map((param) => {
105
+ return {
106
+ ...param,
107
+ type: walker(param.type, capability),
108
+ default: param.default
109
+ ? walker(param.default, capability)
110
+ : undefined,
111
+ };
112
+ }),
113
+ returnType: node.returnType
114
+ ? walker(node.returnType, capability)
115
+ : undefined,
116
+ };
117
+ }
118
+
119
+ if (node.type === 'record') {
120
+ return {
121
+ ...node,
122
+ keyType: walker(node.keyType, capability),
123
+ valueType: walker(node.valueType, capability),
124
+ };
125
+ }
126
+
127
+ if (node.type === 'conditional') {
128
+ return {
129
+ ...node,
130
+ check: {
131
+ left: walker(node.check.left, capability),
132
+ right: walker(node.check.left, capability),
133
+ },
134
+ value: {
135
+ true: walker(node.value.true, capability),
136
+ false: walker(node.value.false, capability),
137
+ },
138
+ };
80
139
  }
81
140
  }
141
+
142
+ return n;
82
143
  };
83
144
 
84
145
  return walker;
package/src/validator.ts CHANGED
@@ -13,21 +13,20 @@ import {
13
13
  makePropertyMap,
14
14
  resolveConditional,
15
15
  isPrimitiveTypeNode,
16
- fillInGenerics,
17
- isGenericNodeType,
16
+ resolveReferenceNode,
17
+ computeEffectiveObject,
18
18
  } from '@player-tools/xlr-utils';
19
19
  import type { ValidationError } from './types';
20
- import type { XLRRegistry } from './registry';
21
20
 
22
21
  /**
23
22
  * Validator for XLRs on JSON Nodes
24
23
  */
25
24
  export class XLRValidator {
26
- private typeMap: XLRRegistry;
25
+ private resolveType: (id: string) => NamedType<NodeType> | undefined;
27
26
  private regexCache: Map<string, RegExp>;
28
27
 
29
- constructor(typeMap: XLRRegistry) {
30
- this.typeMap = typeMap;
28
+ constructor(resolveType: (id: string) => NamedType<NodeType> | undefined) {
29
+ this.resolveType = resolveType;
31
30
  this.regexCache = new Map();
32
31
  }
33
32
 
@@ -74,7 +73,10 @@ export class XLRValidator {
74
73
  message: `Does not match any of the expected types for type: '${xlrNode.name}'`,
75
74
  });
76
75
  } else if (xlrNode.type === 'and') {
77
- const effectiveType = this.computeIntersectionType(xlrNode.and);
76
+ const effectiveType = {
77
+ ...this.computeIntersectionType(xlrNode.and),
78
+ ...(xlrNode.name ? { name: xlrNode.name } : {}),
79
+ };
78
80
  validationIssues.push(...this.validateType(rootNode, effectiveType));
79
81
  } else if (xlrNode.type === 'record') {
80
82
  rootNode.children?.forEach((child) => {
@@ -120,14 +122,35 @@ export class XLRValidator {
120
122
  }
121
123
  }
122
124
  } else if (xlrNode.type === 'conditional') {
123
- const resolvedType = resolveConditional(xlrNode);
124
- if (resolvedType === xlrNode) {
125
+ // Resolve RefNodes in check conditions if needed
126
+ let { right, left } = xlrNode.check;
127
+
128
+ if (right.type === 'ref') {
129
+ right = this.getRefType(right);
130
+ }
131
+
132
+ if (left.type === 'ref') {
133
+ left = this.getRefType(left);
134
+ }
135
+
136
+ const resolvedXLRNode = {
137
+ ...xlrNode,
138
+ check: {
139
+ left,
140
+ right,
141
+ },
142
+ };
143
+
144
+ const resolvedConditional = resolveConditional(resolvedXLRNode);
145
+ if (resolvedConditional === resolvedXLRNode) {
125
146
  throw Error(
126
147
  `Unable to resolve conditional type at runtime: ${xlrNode.name}`
127
148
  );
128
149
  }
129
150
 
130
- validationIssues.push(...this.validateType(rootNode, resolvedType));
151
+ validationIssues.push(
152
+ ...this.validateType(rootNode, resolvedConditional)
153
+ );
131
154
  } else {
132
155
  throw Error(`Unknown type ${xlrNode.type}`);
133
156
  }
@@ -174,7 +197,7 @@ export class XLRValidator {
174
197
 
175
198
  if (xlrNode.extends) {
176
199
  const extendedNode = this.getRefType(xlrNode.extends) as ObjectType;
177
- effectiveXLRNode = this.computeEffectiveObject(extendedNode, xlrNode);
200
+ effectiveXLRNode = computeEffectiveObject(extendedNode, xlrNode);
178
201
  }
179
202
 
180
203
  // eslint-disable-next-line guard-for-in, no-restricted-syntax
@@ -233,27 +256,22 @@ export class XLRValidator {
233
256
  }
234
257
 
235
258
  return typeof literalType.value === 'boolean';
236
- break;
237
259
  case 'number':
238
260
  if (expectedType.const) {
239
261
  return expectedType.const === literalType.value;
240
262
  }
241
263
 
242
264
  return typeof literalType.value === 'number';
243
- break;
244
265
  case 'string':
245
266
  if (expectedType.const) {
246
267
  return expectedType.const === literalType.value;
247
268
  }
248
269
 
249
270
  return typeof literalType.value === 'string';
250
- break;
251
271
  case 'null':
252
272
  return literalType.value === 'null';
253
- break;
254
273
  case 'never':
255
274
  return literalType === undefined;
256
- break;
257
275
  case 'any':
258
276
  return literalType !== undefined;
259
277
  case 'unknown':
@@ -267,27 +285,16 @@ export class XLRValidator {
267
285
 
268
286
  private getRefType(ref: RefType): NodeType {
269
287
  let refName = ref.ref;
270
- const { genericArguments } = ref;
271
-
272
288
  if (refName.indexOf('<') > 0) {
273
289
  [refName] = refName.split('<');
274
290
  }
275
291
 
276
- const actualType = this.typeMap.get(refName) as NodeType;
277
- const genericMap: Map<string, NodeType> = new Map();
278
-
279
- // Compose first level generics here since `fillInGenerics` won't process them if a map is passed in
280
- if (genericArguments && isGenericNodeType(actualType)) {
281
- actualType.genericTokens.forEach((token, index) => {
282
- genericMap.set(
283
- token.symbol,
284
- genericArguments[index] ?? token.default ?? token.constraints
285
- );
286
- });
292
+ const actualType = this.resolveType(refName);
293
+ if (!actualType) {
294
+ throw new Error(`Error: can't resolve type reference ${refName}`);
287
295
  }
288
296
 
289
- // Fill in generics
290
- return fillInGenerics(actualType, genericMap);
297
+ return resolveReferenceNode(ref, actualType);
291
298
  }
292
299
 
293
300
  private getRegex(expString: string): RegExp {
@@ -310,6 +317,12 @@ export class XLRValidator {
310
317
 
311
318
  if (firstElement.type === 'and') {
312
319
  effectiveType = this.computeIntersectionType(firstElement.and);
320
+ } else if (firstElement.type === 'record') {
321
+ effectiveType = {
322
+ type: 'object',
323
+ properties: {},
324
+ additionalProperties: firstElement.valueType,
325
+ };
313
326
  } else if (firstElement.type !== 'or' && firstElement.type !== 'object') {
314
327
  throw new Error(
315
328
  `Can't compute a union with a non-object type ${firstElement.type} (${firstElement.name})`
@@ -321,6 +334,14 @@ export class XLRValidator {
321
334
  types.slice(1).forEach((type) => {
322
335
  let typeToApply = type;
323
336
 
337
+ if (typeToApply.type === 'record') {
338
+ typeToApply = {
339
+ type: 'object',
340
+ properties: {},
341
+ additionalProperties: typeToApply.valueType,
342
+ };
343
+ }
344
+
324
345
  if (type.type === 'ref') {
325
346
  typeToApply = this.getRefType(type);
326
347
  }
@@ -331,10 +352,7 @@ export class XLRValidator {
331
352
 
332
353
  if (typeToApply.type === 'object') {
333
354
  if (effectiveType.type === 'object') {
334
- effectiveType = this.computeEffectiveObject(
335
- effectiveType,
336
- typeToApply
337
- );
355
+ effectiveType = computeEffectiveObject(effectiveType, typeToApply);
338
356
  } else {
339
357
  effectiveType = {
340
358
  ...effectiveType,
@@ -363,45 +381,4 @@ export class XLRValidator {
363
381
 
364
382
  return effectiveType;
365
383
  }
366
-
367
- private computeEffectiveObject(
368
- base: ObjectType,
369
- operand: ObjectType,
370
- errorOnOverlap = true
371
- ): ObjectType {
372
- const baseObjectName = base.name ?? 'object literal';
373
- const operandObjectName = operand.name ?? 'object literal';
374
- const newObject = {
375
- ...base,
376
- name: `${baseObjectName} & ${operandObjectName}`,
377
- description: `Effective type combining ${baseObjectName} and ${operandObjectName}`,
378
- };
379
-
380
- // eslint-disable-next-line no-restricted-syntax, guard-for-in
381
- for (const property in operand.properties) {
382
- if (
383
- newObject.properties[property] !== undefined &&
384
- newObject.properties[property].node.type !==
385
- operand.properties[property].node.type &&
386
- errorOnOverlap
387
- ) {
388
- throw new Error(
389
- `Can't compute effective type for ${baseObjectName} and ${operandObjectName} because of conflicting properties ${property}`
390
- );
391
- }
392
-
393
- newObject.properties[property] = operand.properties[property];
394
- }
395
-
396
- if (newObject.additionalProperties && operand.additionalProperties) {
397
- newObject.additionalProperties = {
398
- type: 'and',
399
- and: [newObject.additionalProperties, operand.additionalProperties],
400
- };
401
- } else if (operand.additionalProperties) {
402
- newObject.additionalProperties = operand.additionalProperties;
403
- }
404
-
405
- return newObject;
406
- }
407
384
  }