@@ -8,18 +8,32 @@ import {
88// Simplify a mime type as much as we can, without throwing any errors
99export const getBaseContentType = ( mimeType : string | undefined ) => {
1010 const typeWithoutParams = ( mimeType || '' ) . split ( ';' ) [ 0 ] ;
11- const [ type , combinedSubTypes ] = typeWithoutParams . split ( / \/ ( .+ ) / ) ;
1211
12+ let [ type , combinedSubTypes ] = typeWithoutParams . split ( / \/ ( .+ ) / ) ;
1313 if ( ! combinedSubTypes ) return type ;
1414
15- // A list of types from most specific to most generic: [svg, xml] for image/svg+xml
16- const subTypes = combinedSubTypes . split ( '+' ) ;
15+ if ( DEFAULT_SUBTYPE [ combinedSubTypes ] ) {
16+ combinedSubTypes = `${ combinedSubTypes } +${ DEFAULT_SUBTYPE [ combinedSubTypes ] } ` ;
17+ }
18+
19+ // If this is a known type with an exact match, return that directly:
20+ if ( mimeTypeToContentTypeMap [ type + '/' + combinedSubTypes ] ) {
21+ return type + '/' + combinedSubTypes ;
22+ }
1723
24+ // Otherwise, wr collect a list of types from most specific to most generic: [svg, xml] for image/svg+xml
25+ // and then look through in order to see if there are any matches here:
26+ const subTypes = combinedSubTypes . split ( '+' ) ;
1827 const possibleTypes = subTypes . map ( st => type + '/' + st ) ;
19- return _ . find ( possibleTypes , t => ! ! mimeTypeToContentTypeMap [ t ] ) ||
28+
29+ return _ . find ( possibleTypes , t => ! ! mimeTypeToContentTypeMap [ t ] ) || // Subtype match
2030 _ . last ( possibleTypes ) ! ; // If we recognize none - return the most generic
2131}
2232
33+ const DEFAULT_SUBTYPE : { [ type : string ] : string } = {
34+ 'grpc' : 'proto' // Protobuf is the default gRPC content type (but not the only one!)
35+ } ;
36+
2337export type ViewableContentType =
2438 | 'raw'
2539 | 'text'
@@ -33,7 +47,8 @@ export type ViewableContentType =
3347 | 'markdown'
3448 | 'yaml'
3549 | 'image'
36- | 'protobuf' ;
50+ | 'protobuf'
51+ | 'grpc-proto' ;
3752
3853export const EditableContentTypes = [
3954 'text' ,
@@ -94,6 +109,9 @@ const mimeTypeToContentTypeMap: { [mimeType: string]: ViewableContentType } = {
94109 'application/vnd.google.protobuf' : 'protobuf' ,
95110 'application/x-google-protobuf' : 'protobuf' ,
96111 'application/proto' : 'protobuf' , // N.b. this covers all application/XXX+proto values
112+ 'application/x-protobuffer' : 'protobuf' , // Commonly seen in Google apps
113+
114+ 'application/grpc+proto' : 'grpc-proto' , // Used in GRPC requests (protobuf but with special headers)
97115
98116 'application/octet-stream' : 'raw'
99117} as const ;
@@ -120,6 +138,7 @@ export function getContentEditorName(contentType: ViewableContentType): string {
120138 : contentType === 'json' ? 'JSON'
121139 : contentType === 'css' ? 'CSS'
122140 : contentType === 'url-encoded' ? 'URL-Encoded'
141+ : contentType === 'grpc-proto' ? 'gRPC'
123142 : _ . capitalize ( contentType ) ;
124143}
125144
@@ -161,10 +180,15 @@ export function getCompatibleTypes(
161180 types . add ( 'xml' ) ;
162181 }
163182
183+ if ( ! types . has ( 'grpc-proto' ) && rawContentType === 'application/grpc' ) {
184+ types . add ( 'grpc-proto' )
185+ }
186+
164187 if (
165188 body &&
166189 isProbablyProtobuf ( body ) &&
167190 ! types . has ( 'protobuf' ) &&
191+ ! types . has ( 'grpc-proto' ) &&
168192 // If it's probably unmarked protobuf, and it's a manageable size, try
169193 // parsing it just to check:
170194 ( body . length < 100_000 && isValidProtobuf ( body ) )
@@ -192,4 +216,4 @@ export function getCompatibleTypes(
192216 types . add ( 'raw' ) ;
193217
194218 return Array . from ( types ) ;
195- }
219+ }
0 commit comments