All files memoryKv.ts

74.39% Statements 61/82
62.07% Branches 18/29
75% Functions 6/8
74.39% Lines 61/82

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 831x 1x                       1x 1x 5x 5x 5x 5x 20x 20x 20x 20x 20x 5360x             5360x       5360x 5360x 230x 230x 5360x 20x 20x 20x 20x 20x 20x 5x 1342x 1342x 1342x 216x 846x 12x 12x 12x 12x 12x 5x 1100x 1100x 1100x   1100x 1100x 1100x 1100x 1100x 5x 5x 20x 20x 20x 5x 5x 14x 14x 14x 14x 14x 5x 5x 5x  
import { KvListReturn, KV, ValueType, ListEntry, GetResultType, KvDataTypes } from './backendKv.js'
 
function ab2str (buf:ArrayBuffer):string {
  return String.fromCharCode.apply(null, (new Uint16Array(buf) as any))
}
function str2ab (str:string):ArrayBuffer {
  var buf = new ArrayBuffer(str.length * 2) // 2 bytes for each char
  var bufView = new Uint16Array(buf)
  for (var i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i)
  }
  return buf
}
 
export const memoryKV = (): KV => {
  const db = new Map<string, string>()
  const dbMeta = new Map<string, ListEntry>()
 
  const list = async (options?: {limit?: number, cursor?: string, prefix?: string}):Promise<KvListReturn> => {
    let currentCursor = ''
    let cursorFound = false
    const { cursor, limit, prefix } = options || {}
    const result = []
    for (const value of dbMeta.values()) {
      if (cursor && !cursorFound) {
        if (value.name === cursor) {
          cursorFound = true
        } else {
          continue
        }
      }
      if (limit && result.length === limit) {
        currentCursor = value.name
        break
      }
 
      if (!prefix || value.name.startsWith(prefix)) {
        result.push(value)
      }
    }
    return {
      keys: result,
      cursor: currentCursor,
      list_complete: true
    }
  }
  const get = async <T extends KvDataTypes>(key:string, type?:T) : Promise<GetResultType<T>|null> => {
    const returned = db.get(key)
 
    if (returned == null) return null
 
    if (type === 'json') return JSON.parse(returned)
    else if (type === 'arrayBuffer') return str2ab(returned) as GetResultType<T>
    else if (type === 'stream') return returned == null ? null : JSON.parse(returned)
 
    return returned as GetResultType<T>
  }
  const put = async (key:string, value:ValueType, additional?: {metadata?:any, expiration?:number, expirationTtl?:number}): Promise<void> => {
    const { expiration, expirationTtl } = additional || {}
    const exp = expiration || (expirationTtl ? (Date.now() / 1000) + expirationTtl : undefined)
    if (value instanceof ArrayBuffer) {
      db.set(key, ab2str(value))
    } else {
      db.set(key, typeof value === 'string' ? value : JSON.stringify(value))
    }
    if (additional) { dbMeta.set(key, { name: key, metadata: additional.metadata, expiration: exp }) }
  }
 
  const destroy = async (key:string):Promise<void> => {
    db.delete(key)
    dbMeta.delete(key)
  }
 
  const getWithMetadata = async <T extends KvDataTypes>(key:string, type?:T) : Promise<{value:GetResultType<T> | null, metadata: object | null}> => {
    return {
      value: await get(key, type),
      metadata: (await list({ prefix: key }))?.keys[0]?.metadata || null
    }
  }
 
  return { list, get, put, delete: destroy, getWithMetadata }
}