import { statSync, createReadStream, promises as fs } from 'node:fs' import { basename } from 'node:path' import { MessageChannel } from 'node:worker_threads' import File from './file.js' import Blob from './index.js' const { stat } = fs const DOMException = globalThis.DOMException || (() => { const port = new MessageChannel().port1 const ab = new ArrayBuffer(0) try { port.postMessage(ab, [ab, ab]) } catch (err) { return err.constructor } })() /** * @param {string} path filepath on the disk * @param {string} [type] mimetype to use */ const blobFromSync = (path, type) => fromBlob(statSync(path), path, type) /** * @param {string} path filepath on the disk * @param {string} [type] mimetype to use */ const blobFrom = (path, type) => stat(path).then(stat => fromBlob(stat, path, type)) /** * @param {string} path filepath on the disk * @param {string} [type] mimetype to use */ const fileFrom = (path, type) => stat(path).then(stat => fromFile(stat, path, type)) /** * @param {string} path filepath on the disk * @param {string} [type] mimetype to use */ const fileFromSync = (path, type) => fromFile(statSync(path), path, type) // @ts-ignore const fromBlob = (stat, path, type = '') => new Blob([new BlobDataItem({ path, size: stat.size, lastModified: stat.mtimeMs, start: 0 })], { type }) // @ts-ignore const fromFile = (stat, path, type = '') => new File([new BlobDataItem({ path, size: stat.size, lastModified: stat.mtimeMs, start: 0 })], basename(path), { type, lastModified: stat.mtimeMs }) /** * This is a blob backed up by a file on the disk * with minium requirement. Its wrapped around a Blob as a blobPart * so you have no direct access to this. * * @private */ class BlobDataItem { #path #start constructor (options) { this.#path = options.path this.#start = options.start this.size = options.size this.lastModified = options.lastModified } /** * Slicing arguments is first validated and formatted * to not be out of range by Blob.prototype.slice */ slice (start, end) { return new BlobDataItem({ path: this.#path, lastModified: this.lastModified, size: end - start, start }) } async * stream () { const { mtimeMs } = await stat(this.#path) if (mtimeMs > this.lastModified) { throw new DOMException('The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.', 'NotReadableError') } yield * createReadStream(this.#path, { start: this.#start, end: this.#start + this.size - 1 }) } get [Symbol.toStringTag] () { return 'Blob' } } export default blobFromSync export { File, Blob, blobFrom, blobFromSync, fileFrom, fileFromSync }