ethercalc
Version:
Multi-User Spreadsheet Server — TypeScript rewrite (Cloudflare fullstack)
40 lines (37 loc) • 1.77 kB
text/typescript
/**
* CSV helpers for the Room HTTP layer.
*
* - `csvToSocialCalc` converts a CSV string to a SocialCalc save string by
* delegating to the `csvToSave` wrapper in `@ethercalc/socialcalc-headless`,
* which calls `SocialCalc.ConvertOtherFormatToSave(csv, 'csv')`. This is
* the same entry point the legacy `src/main.ls` used via the `J` library
* (J.utils.to_socialcalc produces an object keyed by sheet name — we only
* consume the primary sheet here, matching the legacy `for k, save of …`
* first-iteration behavior).
*
* - `decodeDoubleEncoded` reproduces the `text/x-ethercalc-csv-double-encoded`
* round-trip (§7 item 4 in AGENTS.md): UTF-8 decode → Latin-1 encode →
* UTF-8 decode. Legacy did this via `iconv-lite` (Node-only); we use the
* platform `TextDecoder('latin1')` / `TextEncoder` which is available in
* both workerd and Node 20+.
*/
import { csvToSave } from '@ethercalc/socialcalc-headless';
/** Convert a CSV string to a SocialCalc save string. */
export function csvToSocialCalc(csv: string): string {
return csvToSave(csv);
}
/**
* Re-encode a UTF-8 → Latin-1 → UTF-8 double-encoded body. Equivalent to
* the legacy `iconv-lite` triple-step.
*/
export function decodeDoubleEncoded(bytes: Uint8Array): string {
const first = new TextDecoder('utf-8').decode(bytes);
const latin1Bytes = new Uint8Array(first.length);
for (let i = 0; i < first.length; i++) {
// Latin-1 is a strict subset of Unicode up to U+00FF; code points above
// that never survived the original round-trip either (legacy would have
// produced the same replacement character). Mask to 8 bits.
latin1Bytes[i] = first.charCodeAt(i) & 0xff;
}
return new TextDecoder('utf-8').decode(latin1Bytes);
}