serializeRawParameters.ts•2.99 kB
import Decimal from 'decimal.js'
import { isDate } from './date'
export function serializeRawParameters(parameters: any[]): string {
  try {
    return serializeRawParametersInternal(parameters, 'fast')
  } catch (error) {
    // Got TypeError, try replacing values unsupported by JSON (i.e., BigInts)
    // with strings inside arrays and objects.
    return serializeRawParametersInternal(parameters, 'slow')
  }
}
function serializeRawParametersInternal(parameters: any[], objectSerialization: 'fast' | 'slow'): string {
  return JSON.stringify(parameters.map((parameter) => encodeParameter(parameter, objectSerialization)))
}
function encodeParameter(parameter: any, objectSerialization: 'fast' | 'slow'): unknown {
  if (Array.isArray(parameter)) {
    return parameter.map((item) => encodeParameter(item, objectSerialization))
  }
  if (typeof parameter === 'bigint') {
    return {
      prisma__type: 'bigint',
      prisma__value: parameter.toString(),
    }
  }
  if (isDate(parameter)) {
    return {
      prisma__type: 'date',
      prisma__value: parameter.toJSON(),
    }
  }
  if (Decimal.isDecimal(parameter)) {
    return {
      prisma__type: 'decimal',
      prisma__value: parameter.toJSON(),
    }
  }
  if (Buffer.isBuffer(parameter)) {
    return {
      prisma__type: 'bytes',
      prisma__value: parameter.toString('base64'),
    }
  }
  if (isArrayBufferLike(parameter)) {
    return {
      prisma__type: 'bytes',
      prisma__value: Buffer.from(parameter).toString('base64'),
    }
  }
  if (ArrayBuffer.isView(parameter)) {
    const { buffer, byteOffset, byteLength } = parameter
    return {
      prisma__type: 'bytes',
      prisma__value: Buffer.from(buffer, byteOffset, byteLength).toString('base64'),
    }
  }
  if (typeof parameter === 'object' && objectSerialization === 'slow') {
    return preprocessObject(parameter)
  }
  return parameter
}
function isArrayBufferLike(value: any): value is ArrayBufferLike {
  if (value instanceof ArrayBuffer || value instanceof SharedArrayBuffer) {
    return true
  }
  if (typeof value === 'object' && value !== null) {
    return value[Symbol.toStringTag] === 'ArrayBuffer' || value[Symbol.toStringTag] === 'SharedArrayBuffer'
  }
  return false
}
function preprocessObject(obj: any): unknown {
  if (typeof obj !== 'object' || obj === null) {
    return obj
  }
  if (typeof obj.toJSON === 'function') {
    return obj.toJSON()
  }
  // TODO: map from blaze would've been convenient here to map arrays and objects uniformly,
  // but importing it in this file causes compilation errors in tsd tests.
  if (Array.isArray(obj)) {
    return obj.map(preprocessValueInObject)
  }
  const result = {} as any
  for (const key of Object.keys(obj as object)) {
    result[key] = preprocessValueInObject(obj[key])
  }
  return result
}
function preprocessValueInObject(value: any): unknown {
  if (typeof value === 'bigint') {
    return value.toString()
  }
  return preprocessObject(value)
}