| 'use strict'; |
| |
| const { |
| DateNow, |
| FunctionPrototypeApply, |
| NumberIsNaN, |
| ObjectDefineProperties, |
| ObjectSetPrototypeOf, |
| StringPrototypeToWellFormed, |
| Symbol, |
| SymbolToStringTag, |
| } = primordials; |
| |
| const { |
| Blob, |
| TransferableBlob, |
| } = require('internal/blob'); |
| |
| const { |
| customInspectSymbol: kInspect, |
| kEnumerableProperty, |
| kEmptyObject, |
| } = require('internal/util'); |
| |
| const { |
| codes: { |
| ERR_INVALID_THIS, |
| ERR_MISSING_ARGS, |
| }, |
| } = require('internal/errors'); |
| |
| const { |
| inspect, |
| } = require('internal/util/inspect'); |
| |
| const { |
| kClone, |
| kDeserialize, |
| } = require('internal/worker/js_transferable'); |
| |
| const kState = Symbol('state'); |
| |
| function isFile(object) { |
| return object?.[kState] !== undefined; |
| } |
| |
| class FileState { |
| name; |
| lastModified; |
| |
| /** |
| * @param {string} name |
| * @param {number} lastModified |
| */ |
| constructor(name, lastModified) { |
| this.name = name; |
| this.lastModified = lastModified; |
| } |
| } |
| |
| class File extends Blob { |
| constructor(fileBits, fileName, options = kEmptyObject) { |
| if (arguments.length < 2) { |
| throw new ERR_MISSING_ARGS('fileBits', 'fileName'); |
| } |
| |
| super(fileBits, options); |
| |
| let { lastModified } = options ?? kEmptyObject; |
| |
| if (lastModified !== undefined) { |
| // Using Number(...) will not throw an error for bigints. |
| lastModified = +lastModified; |
| |
| if (NumberIsNaN(lastModified)) { |
| lastModified = 0; |
| } |
| } else { |
| lastModified = DateNow(); |
| } |
| |
| this[kState] = new FileState(StringPrototypeToWellFormed(`${fileName}`), lastModified); |
| } |
| |
| get name() { |
| if (!isFile(this)) |
| throw new ERR_INVALID_THIS('File'); |
| |
| return this[kState].name; |
| } |
| |
| get lastModified() { |
| if (!isFile(this)) |
| throw new ERR_INVALID_THIS('File'); |
| |
| return this[kState].lastModified; |
| } |
| |
| [kInspect](depth, options) { |
| if (depth < 0) { |
| return this; |
| } |
| |
| const opts = { |
| ...options, |
| depth: options.depth == null ? null : options.depth - 1, |
| }; |
| |
| return `File ${inspect({ |
| size: this.size, |
| type: this.type, |
| name: this[kState].name, |
| lastModified: this[kState].lastModified, |
| }, opts)}`; |
| } |
| |
| [kClone]() { |
| return { |
| data: { ...super[kClone]().data, ...this[kState] }, |
| deserializeInfo: 'internal/file:TransferableFile', |
| }; |
| } |
| |
| [kDeserialize](data) { |
| super[kDeserialize](data); |
| |
| this[kState] = new FileState(data.name, data.lastModified); |
| } |
| } |
| |
| function TransferableFile(handle, length, type = '') { |
| FunctionPrototypeApply(TransferableBlob, this, [handle, length, type]); |
| ObjectSetPrototypeOf(this, File.prototype); |
| } |
| |
| ObjectSetPrototypeOf(TransferableFile.prototype, File.prototype); |
| ObjectSetPrototypeOf(TransferableFile, File); |
| |
| ObjectDefineProperties(File.prototype, { |
| name: kEnumerableProperty, |
| lastModified: kEnumerableProperty, |
| [SymbolToStringTag]: { |
| __proto__: null, |
| configurable: true, |
| value: 'File', |
| }, |
| }); |
| |
| module.exports = { |
| File, |
| TransferableFile, |
| }; |