Iniciativa Cidadã Independente · Transparência Parlamentar
TS78 linhas2.237 bytes

apps/worker/src/runtime/node/kv-fs.ts

SHA-256

983eb8d502e266c64646f45f199f538032539535fe73d614d81c1fdba8fb700f

Somente leiturafonte-acf635076da2
import { createHash } from 'node:crypto'
import { readFile, writeFile, unlink, mkdir } from 'node:fs/promises'
import { dirname, join } from 'node:path'

type PutOptions = { expirationTtl?: number; expiration?: number }

interface CacheEntry {
  value: unknown
  expiresAt: number | null
}

export class FsKV {
  constructor(private readonly baseDir: string) {}

  private path(key: string): string {
    const hash = createHash('sha1').update(key).digest('hex')
    return join(this.baseDir, hash.slice(0, 2), hash + '.json')
  }

  async get<T = unknown>(key: string, type?: 'json' | 'text'): Promise<T | string | null> {
    const file = this.path(key)
    let raw: string
    try {
      raw = await readFile(file, 'utf8')
    } catch (err) {
      if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null
      throw err
    }
    let entry: CacheEntry
    try {
      entry = JSON.parse(raw) as CacheEntry
    } catch {
      return null
    }
    if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {
      void unlink(file).catch(() => {})
      return null
    }
    if (type === 'text') return typeof entry.value === 'string' ? entry.value : JSON.stringify(entry.value)
    return entry.value as T
  }

  async put(key: string, value: string | ArrayBuffer | ReadableStream, opts: PutOptions = {}): Promise<void> {
    let parsed: unknown
    if (typeof value === 'string') {
      try {
        parsed = JSON.parse(value)
      } catch {
        parsed = value
      }
    } else {
      throw new Error('FsKV.put: only string values supported')
    }
    const expiresAt = opts.expirationTtl
      ? Date.now() + opts.expirationTtl * 1000
      : opts.expiration
        ? opts.expiration * 1000
        : null
    const entry: CacheEntry = { value: parsed, expiresAt }
    const file = this.path(key)
    await mkdir(dirname(file), { recursive: true })
    await writeFile(file, JSON.stringify(entry))
  }

  async delete(key: string): Promise<void> {
    const file = this.path(key)
    try {
      await unlink(file)
    } catch (err) {
      if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err
    }
  }

  async list(): Promise<{ keys: { name: string }[] }> {
    return { keys: [] }
  }
}