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

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.39",
5
5
  "license": "Apache-2.0",
6
6
  "main": "index.js",
7
7
  "module": "index.js",
@@ -391,6 +391,7 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
391
391
  this._showExamples = !this.noMainExample && (
392
392
  this.renderMediaSelector ||
393
393
  this.isObject ||
394
+ this.isArray ||
394
395
  this._renderMainExample
395
396
  );
396
397
  }
@@ -448,9 +449,16 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
448
449
  } else if (
449
450
  this._hasType(type, shapesKey.UnionShape)
450
451
  ) {
451
- isUnion = true;
452
- key = this._getAmfKey(shapesKey.anyOf);
453
- this.unionTypes = this._computeTypes(type, key);
452
+ // Check if this is a nullable union (type | null) which should be rendered as scalar
453
+ const nullableCheck = this._checkNullableUnion(type);
454
+ if (nullableCheck && nullableCheck.isNullable) {
455
+ // Treat nullable types as scalar for cleaner rendering
456
+ isScalar = true;
457
+ } else {
458
+ isUnion = true;
459
+ key = this._getAmfKey(shapesKey.anyOf);
460
+ this.unionTypes = this._computeTypes(type, key);
461
+ }
454
462
  } else if (this._hasProperty(type, this.ns.w3.shacl.xone)) {
455
463
  isOneOf = true;
456
464
  key = this._getAmfKey(this.ns.w3.shacl.xone);
@@ -523,15 +531,16 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
523
531
  }
524
532
 
525
533
  // Determine if we should show the examples section
526
- // Priority: noMainExample (hide) > renderMediaSelector (show) > isObject (show) > _renderMainExample
534
+ // Priority: noMainExample (hide) > renderMediaSelector (show) > isObject (show) > isArray (show) > _renderMainExample
527
535
  this._showExamples = !this.noMainExample && (
528
536
  this.renderMediaSelector || // Need to show the section for the media type selector
529
537
  isObject || // Objects can generate examples automatically
538
+ isArray || // Arrays can generate examples automatically
530
539
  this._renderMainExample // Has explicit examples
531
540
  );
532
541
 
533
- // Effective media type - use 'application/json' as default for objects without mediaType
534
- this._exampleMediaType = this.mediaType || (isObject ? 'application/json' : undefined);
542
+ // Effective media type - use 'application/json' as default for objects and arrays without mediaType
543
+ this._exampleMediaType = this.mediaType || (isObject || isArray ? 'application/json' : undefined);
535
544
  }
536
545
 
537
546
  /**
@@ -1134,7 +1143,7 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
1134
1143
  : this._renderMainExample;
1135
1144
  const exampleMediaType = this._exampleMediaType !== undefined
1136
1145
  ? this._exampleMediaType
1137
- : (this.mediaType || (this.isObject ? 'application/json' : undefined));
1146
+ : (this.mediaType || (this.isObject || this.isArray ? 'application/json' : undefined));
1138
1147
 
1139
1148
  return html`<style>${this.styles}</style>
1140
1149
  ${shouldRenderExamples ? html`<section class="examples">
@@ -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"