@api-components/api-type-document 4.2.37 → 4.2.38

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@api-components/api-type-document",
3
3
  "description": "A documentation table for type (resource) properties. Works with AMF data model",
4
- "version": "4.2.37",
4
+ "version": "4.2.38",
5
5
  "license": "Apache-2.0",
6
6
  "main": "index.js",
7
7
  "module": "index.js",
@@ -448,9 +448,16 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
448
448
  } else if (
449
449
  this._hasType(type, shapesKey.UnionShape)
450
450
  ) {
451
- isUnion = true;
452
- key = this._getAmfKey(shapesKey.anyOf);
453
- this.unionTypes = this._computeTypes(type, key);
451
+ // Check if this is a nullable union (type | null) which should be rendered as scalar
452
+ const nullableCheck = this._checkNullableUnion(type);
453
+ if (nullableCheck && nullableCheck.isNullable) {
454
+ // Treat nullable types as scalar for cleaner rendering
455
+ isScalar = true;
456
+ } else {
457
+ isUnion = true;
458
+ key = this._getAmfKey(shapesKey.anyOf);
459
+ this.unionTypes = this._computeTypes(type, key);
460
+ }
454
461
  } else if (this._hasProperty(type, this.ns.w3.shacl.xone)) {
455
462
  isOneOf = true;
456
463
  key = this._getAmfKey(this.ns.w3.shacl.xone);
@@ -49,6 +49,15 @@ interface PropertyDocumentMixin extends AmfHelperMixin {
49
49
  */
50
50
  graph: boolean;
51
51
 
52
+ /**
53
+ * Checks if a union shape represents a nullable type (union with null).
54
+ * A nullable type is a union of exactly 2 members where one is NilShape.
55
+ *
56
+ * @param range AMF range object (should be UnionShape)
57
+ * @returns Returns {baseType, isNullable: true} if nullable, undefined otherwise
58
+ */
59
+ _checkNullableUnion(range: any): { baseType: any; isNullable: boolean } | undefined;
60
+
52
61
  /**
53
62
  * Computes type from a `http://raml.org/vocabularies/shapes#range` object
54
63
  *
@@ -107,6 +107,65 @@ const mxFunction = (base) => {
107
107
  this._hasMediaType = false;
108
108
  }
109
109
 
110
+ /**
111
+ * Checks if a union shape represents a nullable type (union with null).
112
+ * A nullable type is a union of exactly 2 members where one is NilShape
113
+ * and the other is any type (scalar, array, object, etc.).
114
+ *
115
+ * This is specifically for OpenAPI 3.0 nullable: true which AMF converts
116
+ * to union of type + null. This simplifies the rendering to "Type or null"
117
+ * instead of showing a full union selector.
118
+ *
119
+ * @param {any} range AMF range object (should be UnionShape)
120
+ * @return {Object|undefined} Returns {baseType, isNullable: true} if nullable, undefined otherwise
121
+ */
122
+ _checkNullableUnion(range) {
123
+ if (!range || !this._hasType(range, this.ns.aml.vocabularies.shapes.UnionShape)) {
124
+ return undefined;
125
+ }
126
+
127
+ const key = this._getAmfKey(this.ns.aml.vocabularies.shapes.anyOf);
128
+ const unionMembers = this._ensureArray(range[key]);
129
+
130
+ if (!unionMembers || unionMembers.length !== 2) {
131
+ return undefined;
132
+ }
133
+
134
+ // Check if one member is NilShape
135
+ let nilIndex = -1;
136
+ let baseTypeIndex = -1;
137
+
138
+ for (let i = 0; i < unionMembers.length; i++) {
139
+ let member = unionMembers[i];
140
+ if (Array.isArray(member)) {
141
+ [member] = member;
142
+ }
143
+ member = this._resolve(member);
144
+
145
+ if (this._hasType(member, this.ns.aml.vocabularies.shapes.NilShape)) {
146
+ nilIndex = i;
147
+ } else {
148
+ baseTypeIndex = i;
149
+ }
150
+ }
151
+
152
+ // If we found exactly one nil and one non-nil type, it's a nullable
153
+ if (nilIndex !== -1 && baseTypeIndex !== -1) {
154
+ let baseType = unionMembers[baseTypeIndex];
155
+ if (Array.isArray(baseType)) {
156
+ [baseType] = baseType;
157
+ }
158
+ baseType = this._resolve(baseType);
159
+
160
+ return {
161
+ baseType,
162
+ isNullable: true
163
+ };
164
+ }
165
+
166
+ return undefined;
167
+ }
168
+
110
169
  /**
111
170
  * Computes type from a `http://raml.org/vocabularies/shapes#range` object
112
171
  *
@@ -122,6 +181,12 @@ const mxFunction = (base) => {
122
181
  return this._computeScalarDataType(range);
123
182
  }
124
183
  if (this._hasType(range, rs.UnionShape)) {
184
+ // Check if this is a nullable union (type | null)
185
+ const nullableCheck = this._checkNullableUnion(range);
186
+ if (nullableCheck && nullableCheck.isNullable) {
187
+ const baseTypeName = this._computeRangeDataType(nullableCheck.baseType);
188
+ return `${baseTypeName} or null`;
189
+ }
125
190
  return 'Union';
126
191
  }
127
192
  if (this._hasType(range, rs.ArrayShape)) {
@@ -145,9 +210,6 @@ const mxFunction = (base) => {
145
210
  if (this._hasType(range, rs.TupleShape)) {
146
211
  return 'Tuple';
147
212
  }
148
- if (this._hasType(range, rs.UnionShape)) {
149
- return 'Union';
150
- }
151
213
  if (this._hasType(range, rs.RecursiveShape)) {
152
214
  return 'Recursive';
153
215
  }
@@ -313,7 +375,12 @@ const mxFunction = (base) => {
313
375
  * @return {Boolean}
314
376
  */
315
377
  _computeIsUnion(range) {
316
- return this._hasType(range, this.ns.aml.vocabularies.shapes.UnionShape);
378
+ if (!this._hasType(range, this.ns.aml.vocabularies.shapes.UnionShape)) {
379
+ return false;
380
+ }
381
+ // Check if it's a nullable union (which we don't treat as union for UI)
382
+ const nullableCheck = this._checkNullableUnion(range);
383
+ return !nullableCheck; // Only true if NOT nullable
317
384
  }
318
385
 
319
386
  /**
@@ -325,7 +392,15 @@ const mxFunction = (base) => {
325
392
  * @return {Boolean}
326
393
  */
327
394
  _computeIsObject(range) {
328
- return this._hasType(range, this.ns.w3.shacl.NodeShape);
395
+ if (this._hasType(range, this.ns.w3.shacl.NodeShape)) {
396
+ return true;
397
+ }
398
+ // Check if it's a nullable object
399
+ const nullableCheck = this._checkNullableUnion(range);
400
+ if (nullableCheck) {
401
+ return this._hasType(nullableCheck.baseType, this.ns.w3.shacl.NodeShape);
402
+ }
403
+ return false;
329
404
  }
330
405
 
331
406
  /**
@@ -337,7 +412,15 @@ const mxFunction = (base) => {
337
412
  * @return {Boolean}
338
413
  */
339
414
  _computeIsArray(range) {
340
- return this._hasType(range, this.ns.aml.vocabularies.shapes.ArrayShape);
415
+ if (this._hasType(range, this.ns.aml.vocabularies.shapes.ArrayShape)) {
416
+ return true;
417
+ }
418
+ // Check if it's a nullable array
419
+ const nullableCheck = this._checkNullableUnion(range);
420
+ if (nullableCheck) {
421
+ return this._hasType(nullableCheck.baseType, this.ns.aml.vocabularies.shapes.ArrayShape);
422
+ }
423
+ return false;
341
424
  }
342
425
 
343
426
  /**
@@ -735,7 +735,14 @@ export class PropertyShapeDocument extends PropertyDocumentMixin(LitElement) {
735
735
  if (!this.isComplex || !this.opened || this.isScalarArray) {
736
736
  return '';
737
737
  }
738
- const range = this._resolve(this.range);
738
+ let range = this._resolve(this.range);
739
+
740
+ // If this is a nullable complex type, extract the base type
741
+ const nullableCheck = this._checkNullableUnion(range);
742
+ if (nullableCheck) {
743
+ range = nullableCheck.baseType;
744
+ }
745
+
739
746
  const parentTypeName = this._getParentTypeName();
740
747
  return html`<api-type-document
741
748
  class="children complex"