CoolFace
Apppublic

opusdev/vector-similarity-api

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes
formDataToStream.js113 linesDownload Raw Back to helpers
1import util from 'util';2import {Readable} from 'stream';3import utils from "../utils.js";4import readBlob from "./readBlob.js";5import platform from "../platform/index.js";6 7const BOUNDARY_ALPHABET = platform.ALPHABET.ALPHA_DIGIT + '-_';8 9const textEncoder = typeof TextEncoder === 'function' ? new TextEncoder() : new util.TextEncoder();10 11const CRLF = '\r\n';12const CRLF_BYTES = textEncoder.encode(CRLF);13const CRLF_BYTES_COUNT = 2;14 15class FormDataPart {16  constructor(name, value) {17    const {escapeName} = this.constructor;18    const isStringValue = utils.isString(value);19 20    let headers = `Content-Disposition: form-data; name="${escapeName(name)}"${21      !isStringValue && value.name ? `; filename="${escapeName(value.name)}"` : ''22    }${CRLF}`;23 24    if (isStringValue) {25      value = textEncoder.encode(String(value).replace(/\r?\n|\r\n?/g, CRLF));26    } else {27      headers += `Content-Type: ${value.type || "application/octet-stream"}${CRLF}`28    }29 30    this.headers = textEncoder.encode(headers + CRLF);31 32    this.contentLength = isStringValue ? value.byteLength : value.size;33 34    this.size = this.headers.byteLength + this.contentLength + CRLF_BYTES_COUNT;35 36    this.name = name;37    this.value = value;38  }39 40  async *encode(){41    yield this.headers;42 43    const {value} = this;44 45    if(utils.isTypedArray(value)) {46      yield value;47    } else {48      yield* readBlob(value);49    }50 51    yield CRLF_BYTES;52  }53 54  static escapeName(name) {55      return String(name).replace(/[\r\n"]/g, (match) => ({56        '\r' : '%0D',57        '\n' : '%0A',58        '"' : '%22',59      }[match]));60  }61}62 63const formDataToStream = (form, headersHandler, options) => {64  const {65    tag = 'form-data-boundary',66    size = 25,67    boundary = tag + '-' + platform.generateString(size, BOUNDARY_ALPHABET)68  } = options || {};69 70  if(!utils.isFormData(form)) {71    throw TypeError('FormData instance required');72  }73 74  if (boundary.length < 1 || boundary.length > 70) {75    throw Error('boundary must be 10-70 characters long')76  }77 78  const boundaryBytes = textEncoder.encode('--' + boundary + CRLF);79  const footerBytes = textEncoder.encode('--' + boundary + '--' + CRLF);80  let contentLength = footerBytes.byteLength;81 82  const parts = Array.from(form.entries()).map(([name, value]) => {83    const part = new FormDataPart(name, value);84    contentLength += part.size;85    return part;86  });87 88  contentLength += boundaryBytes.byteLength * parts.length;89 90  contentLength = utils.toFiniteNumber(contentLength);91 92  const computedHeaders = {93    'Content-Type': `multipart/form-data; boundary=${boundary}`94  }95 96  if (Number.isFinite(contentLength)) {97    computedHeaders['Content-Length'] = contentLength;98  }99 100  headersHandler && headersHandler(computedHeaders);101 102  return Readable.from((async function *() {103    for(const part of parts) {104      yield boundaryBytes;105      yield* part.encode();106    }107 108    yield footerBytes;109  })());110};111 112export default formDataToStream;113