// electron/main/redis/format.ts // 值格式转换(解压/编码/自定义) import { gunzipSync, inflateSync, brotliDecompressSync } from 'zlib' import { execFile } from 'child_process' import { writeFile, unlink, mkdtemp } from 'fs/promises' import { tmpdir } from 'os' import { join } from 'path' export type FormatType = 'text' | 'hex' | 'json' | 'binary' | 'gzip' | 'deflate' | 'brotli' | 'deflateRaw' export function decodeValue(value: string, format: FormatType): string { const buf = Buffer.from(value, 'utf-8') switch (format) { case 'hex': return buf.toString('hex').match(/.{1,2}/g)?.join(' ') ?? '' case 'binary': return buf.toString('binary') case 'json': try { return JSON.stringify(JSON.parse(value), null, 2) } catch { return value } case 'gzip': try { return gunzipSync(buf).toString('utf-8') } catch { return '[Gzip decode failed]' } case 'deflate': try { return inflateSync(buf).toString('utf-8') } catch { return '[Deflate decode failed]' } case 'brotli': try { return brotliDecompressSync(buf).toString('utf-8') } catch { return '[Brotli decode failed]' } case 'deflateRaw': try { return inflateSync(buf).toString('utf-8') } catch { return '[DeflateRaw decode failed]' } default: return value } } export function detectFormat(value: string): FormatType { if (!value) return 'text' // JSON try { JSON.parse(value); return 'json' } catch { /* not json */ } // Gzip magic: 1f 8b if (value.charCodeAt(0) === 0x1f && value.charCodeAt(1) === 0x8b) return 'gzip' // Brotli magic (less reliable, check common patterns) // Deflate (zlib) magic: 78 01/9c/da if (value.charCodeAt(0) === 0x78 && [0x01, 0x9c, 0xda, 0x5e].includes(value.charCodeAt(1))) return 'deflate' // Check for non-printable characters → binary/hex const hasNonPrintable = /[\x00-\x08\x0e-\x1f]/.test(value.substring(0, 100)) if (hasNonPrintable) return 'hex' return 'text' } export async function customFormat( value: string, command: string, key?: string ): Promise { const tmpDir = await mkdtemp(join(tmpdir(), 'jrdm-fmt-')) const tmpFile = join(tmpDir, 'value.bin') await writeFile(tmpFile, value, 'utf-8') return new Promise((resolve) => { const cmd = command .replace(/\{KEY\}/g, key || '') .replace(/\{VALUE\}/g, value) .replace(/\{HEX_FILE\}/g, tmpFile) const parts = cmd.split(/\s+/) const exe = parts[0] const args = parts.slice(1) execFile(exe, args, { timeout: 10000, maxBuffer: 10 * 1024 * 1024 }, async (err, stdout) => { await unlink(tmpFile).catch(() => {}) if (err) { resolve(`[Custom formatter error: ${err.message}]`) } else { resolve(stdout) } }) }) }