import { StatementResponseWithAttachments , StatementsResponseWithAttachments , Statement , Attachment , } from "../XAPI" ; export interface MultiPart { header : { "Content-Type" : string ; } ; blob : Blob ; } const crlf = "\r\n" ; export function parseMultiPart ( data ) { const boundary = data . trim ( ) . split ( crlf ) [ 0 ] . trim ( ) ; const parts = data . split ( boundary ) . map ( ( part ) => part . trim ( ) ) . filter ( ( part ) => part !== "" && part !== "--" ) ; const parsedParts = [ ] ; for ( let partIndex = 0 ; partIndex < parts . length ; partIndex ++ ) { const headers = { } ; const items = parts [ partIndex ] . split ( crlf ) ; for ( let headerIndex = 0 ; headerIndex < items . length - 2 ; headerIndex ++ ) { const header = items [ headerIndex ] . split ( ":" ) ; headers [ header [ 0 ] ] = header [ 1 ] ; } let data = items [ items . length - 1 ] ; if ( headers [ "Content-Type" ] . indexOf ( "application/json" ) > - 1 ) { data = JSON . parse ( data as string ) ; } parsedParts . push ( data ) ; } return parsedParts as StatementResponseWithAttachments | StatementsResponseWithAttachments ; } export function createMultiPart ( statementish , attachments ) { const blobParts = [ ] ; const boundary = createBoundaryIdentifier ( ) ; const header = { "Content-Type" : `multipart/mixed; boundary=${ boundary }` , } ; blobParts . push ( createStatementHeader ( statementish , boundary ) ) ; const flatAttachmentMetaArray = Array . isArray ( statementish ) ? statementish . reduce ( ( acc , val ) => acc . concat ( val . attachments ) , [ ] ) : statementish . attachments ; attachments . forEach ( ( attachmentArrayBuffer , index ) => { /**
         * TODO: SHOULD only include one copy of an Attachment's data when the same Attachment is used in multiple Statements that are sent together.
         * REF: https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#requirements-for-attachment-statement-batches
         * */ const attachmentMeta = flatAttachmentMetaArray [ index ] ; blobParts . push ( createAttachmentHeader ( attachmentMeta , boundary ) ) ; blobParts . push ( attachmentArrayBuffer ) ; } ) ; blobParts . push ( `${ crlf }--${ boundary }--${ crlf }` ) ; return { header : header , blob : new Blob ( blobParts ) , } ; } function createBoundaryIdentifier ( ) { return ( ( Math . random ( ) + " " ) . substring ( 2 , 10 ) + ( Math . random ( ) + " " ) . substring ( 2 , 10 ) ) ; } function createStatementHeader ( statement , boundary ) { return ( [ `--${ boundary }` , "Content-Type: application/json" , 'Content-Disposition: form-data; name="statement"' , "" , JSON . stringify ( statement ) , ] . join ( crlf ) + crlf ) ; } function createAttachmentHeader ( attachmentMeta , boundary ) { return ( [ `--${ boundary }` , `Content-Type: ${ attachmentMeta . contentType }` , "Content-Transfer-Encoding: binary" , `X-Experience-API-Hash: ${ attachmentMeta . sha2 }` , ] . join ( crlf ) + crlf + crlf ) ; }