Best design for upload/download functions within Typescript npm package (Browser + Node)
05:59 09 Apr 2026

I'm putting together a small npm software package that migrates a C++ bayesian network (BN) package to web environments using TypeScript and Emscripten. The package itself contains functions to build a network, analyse it, perform inference, etc. I'm designing the package to work for both Node.js and on browser.

My question relates specifically to the best design for the upload/download functions. Node.js and browser obviously have quite different i/o operations, so I can't fully abstract away environment details within a single "loadBN/saveBN" function pair.

At present, I have two functions "loadBNFromBytes" and "saveBNToBytes" which accept/return UInt8Array buffers (declarations below). This leaves any and all actual i/o work up to the user. However, I could add or substitute in environment-specific functions — i.e. "loadBNNode", "saveBNBrowser", etc. — which implement that i/o logic under the hood. I took a look at other npm packages using Emscripten and saw both options: sql.js uses the buffer approach, while tensorflow.js opts to provide environment-specific functions.

This is my first npm package and I'm unfamiliar with the best practices for these types of interface choices. On the one hand, implementing environment-specific functions reduces work for the user who, in my case, may be a C++ programmer wanting to show off their BN model on the web and know very little about JavaScript. On the other hand, environment-specific functions increase complexity (since now some functions only work for some environments) and reduce flexibility for experienced users. Any thoughts or insight on how to make this decision would be deeply appreciated.

Declarations for loadBNFromBytes and saveBNToBytes below. You can see within "examples" the differing i/o workflows I could potentially hide behind environment-specific functions.

/**
 * Populates the current BN object from bytes (BIFXML file content). Erases existing structure, if any.
 * Bytes must have been read in from a BIFXML file. 
 * 
 * @param __{ ArrayBuffer | ArrayBufferView }__ bytes — BIFXML file content
 * @throws If the bytes are invalid or sourced from invalid filetype.
 * @example
 * // Node
 * import fs from 'fs/promises';
 * const bytes = await fs.readFile('path/to/file.bifxml');
 * await bn.loadBNFromBytes(bytes);
 * 
 * // Browser
 * const reader = new FileReader();
 * reader.onload = async () => {
 *   const bytes = reader.result;
 *   await bn.loadBNFromBytes(bytes);
 * }
 * reader.readAsArrayBuffer('/path/to/file.bifxml');
 */ 
loadBNFromBytes(bytes: ArrayBuffer | ArrayBufferView): Promise;

/**
 * Exports the current BN to binary encoded bytes (BIFXML file content). 
 * 
 * @returns __{Uint8Array}__ the exported bytes to save to BIFXML file
 * @example
 * // Node
 * import fs from 'fs/promises'; 
 * const bytes = await bn.saveBNToBytes();
 * await fs.writeFile("/path/to/file.bifxml", Buffer.from(bytes));
 * 
 * // Browser
 * const bytes = await bn.saveBNToBytes();
 * const blob = new Blob([bytes], {type: "application/octet-stream"});
 * const a = document.createElement("a");
 * a.href = URL.createObjectURL(blob);
 * a.download = "model_out.bifxml";
 * a.click();
 * URL.revokeObjectURL(a.href);
 */
saveBNToBytes(): Promise;
node.js typescript npm io