| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | const createHash = require("../util/createHash"); | 
					
						
							|  |  |  | const { dirname, join, mkdirp } = require("../util/fs"); | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | const memorize = require("../util/memorize"); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | const SerializerMiddleware = require("./SerializerMiddleware"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | /** @typedef {import("../util/fs").IntermediateFileSystem} IntermediateFileSystem */ | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | /** @typedef {import("./types").BufferSerializableType} BufferSerializableType */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-01 00:53:50 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | Format: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | File -> Header Section* | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Version -> u32 | 
					
						
							|  |  |  | AmountOfSections -> u32 | 
					
						
							|  |  |  | SectionSize -> i32 (if less than zero represents lazy value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Header -> Version AmountOfSections SectionSize* | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Buffer -> n bytes | 
					
						
							|  |  |  | Section -> Buffer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // "wpc" + 0 in little-endian
 | 
					
						
							|  |  |  | const VERSION = 0x00637077; | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | const hashForName = buffers => { | 
					
						
							|  |  |  | 	const hash = createHash("md4"); | 
					
						
							|  |  |  | 	for (const buf of buffers) hash.update(buf); | 
					
						
							|  |  |  | 	return /** @type {string} */ (hash.digest("hex")); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} SerializeResult | 
					
						
							|  |  |  |  * @property {string | false} name | 
					
						
							|  |  |  |  * @property {number} size | 
					
						
							|  |  |  |  * @property {Promise=} backgroundJob | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {FileMiddleware} middleware this | 
					
						
							|  |  |  |  * @param {BufferSerializableType[] | Promise<BufferSerializableType[]>} data data to be serialized | 
					
						
							|  |  |  |  * @param {string | boolean} name file base name | 
					
						
							|  |  |  |  * @param {function(string | false, Buffer[]): Promise} writeFile writes a file | 
					
						
							|  |  |  |  * @returns {Promise<SerializeResult>} resulting file pointer and promise | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const serialize = async (middleware, data, name, writeFile) => { | 
					
						
							|  |  |  | 	/** @type {(Buffer[] | Buffer | SerializeResult | Promise<SerializeResult>)[]} */ | 
					
						
							|  |  |  | 	const processedData = []; | 
					
						
							|  |  |  | 	/** @type {WeakMap<SerializeResult, function(): any | Promise<any>>} */ | 
					
						
							|  |  |  | 	const resultToLazy = new WeakMap(); | 
					
						
							|  |  |  | 	/** @type {Buffer[]} */ | 
					
						
							|  |  |  | 	let lastBuffers = undefined; | 
					
						
							|  |  |  | 	for (const item of await data) { | 
					
						
							|  |  |  | 		if (typeof item === "function") { | 
					
						
							|  |  |  | 			if (!SerializerMiddleware.isLazy(item)) | 
					
						
							|  |  |  | 				throw new Error("Unexpected function"); | 
					
						
							|  |  |  | 			if (!SerializerMiddleware.isLazy(item, middleware)) { | 
					
						
							|  |  |  | 				throw new Error( | 
					
						
							|  |  |  | 					"Unexpected lazy value with non-this target (can't pass through lazy values)" | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			lastBuffers = undefined; | 
					
						
							|  |  |  | 			const serializedInfo = SerializerMiddleware.getLazySerializedValue(item); | 
					
						
							|  |  |  | 			if (serializedInfo) { | 
					
						
							|  |  |  | 				if (typeof serializedInfo === "function") { | 
					
						
							|  |  |  | 					throw new Error( | 
					
						
							|  |  |  | 						"Unexpected lazy value with non-this target (can't pass through lazy values)" | 
					
						
							|  |  |  | 					); | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 					processedData.push(serializedInfo); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				const content = item(); | 
					
						
							|  |  |  | 				if (content) { | 
					
						
							|  |  |  | 					const options = SerializerMiddleware.getLazyOptions(item); | 
					
						
							|  |  |  | 					processedData.push( | 
					
						
							|  |  |  | 						serialize( | 
					
						
							|  |  |  | 							middleware, | 
					
						
							|  |  |  | 							content, | 
					
						
							|  |  |  | 							(options && options.name) || true, | 
					
						
							|  |  |  | 							writeFile | 
					
						
							|  |  |  | 						).then(result => { | 
					
						
							|  |  |  | 							/** @type {any} */ (item).options.size = result.size; | 
					
						
							|  |  |  | 							resultToLazy.set(result, item); | 
					
						
							|  |  |  | 							return result; | 
					
						
							|  |  |  | 						}) | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					throw new Error( | 
					
						
							|  |  |  | 						"Unexpected falsy value returned by lazy value function" | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else if (item) { | 
					
						
							|  |  |  | 			if (lastBuffers) { | 
					
						
							|  |  |  | 				lastBuffers.push(item); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				lastBuffers = [item]; | 
					
						
							|  |  |  | 				processedData.push(lastBuffers); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 			throw new Error("Unexpected falsy value in items array"); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	/** @type {Promise<any>[]} */ | 
					
						
							|  |  |  | 	const backgroundJobs = []; | 
					
						
							|  |  |  | 	const resolvedData = ( | 
					
						
							|  |  |  | 		await Promise.all( | 
					
						
							|  |  |  | 			/** @type {Promise<Buffer[] | Buffer | SerializeResult>[]} */ (processedData) | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 	).map(item => { | 
					
						
							| 
									
										
										
										
											2020-02-01 00:53:50 +08:00
										 |  |  | 		if (Array.isArray(item) || Buffer.isBuffer(item)) return item; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		backgroundJobs.push(item.backgroundJob); | 
					
						
							|  |  |  | 		// create pointer buffer from size and name
 | 
					
						
							|  |  |  | 		const name = /** @type {string} */ (item.name); | 
					
						
							|  |  |  | 		const nameBuffer = Buffer.from(name); | 
					
						
							|  |  |  | 		const buf = Buffer.allocUnsafe(4 + nameBuffer.length); | 
					
						
							|  |  |  | 		buf.writeUInt32LE(item.size, 0); | 
					
						
							|  |  |  | 		nameBuffer.copy(buf, 4, 0); | 
					
						
							|  |  |  | 		const lazy = resultToLazy.get(item); | 
					
						
							|  |  |  | 		SerializerMiddleware.setLazySerializedValue(lazy, buf); | 
					
						
							|  |  |  | 		return buf; | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 	const lengths = resolvedData.map(item => { | 
					
						
							|  |  |  | 		if (Array.isArray(item)) { | 
					
						
							|  |  |  | 			let l = 0; | 
					
						
							|  |  |  | 			for (const b of item) l += b.length; | 
					
						
							|  |  |  | 			return l; | 
					
						
							|  |  |  | 		} else if (item) { | 
					
						
							|  |  |  | 			return -item.length; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			throw new Error("Unexpected falsy value in resolved data " + item); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 	const header = Buffer.allocUnsafe(8 + lengths.length * 4); | 
					
						
							| 
									
										
										
										
											2020-02-01 00:53:50 +08:00
										 |  |  | 	header.writeUInt32LE(VERSION, 0); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	header.writeUInt32LE(lengths.length, 4); | 
					
						
							|  |  |  | 	for (let i = 0; i < lengths.length; i++) { | 
					
						
							|  |  |  | 		header.writeInt32LE(lengths[i], 8 + i * 4); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	const buf = [header]; | 
					
						
							|  |  |  | 	for (const item of resolvedData) { | 
					
						
							|  |  |  | 		if (Array.isArray(item)) { | 
					
						
							|  |  |  | 			for (const b of item) buf.push(b); | 
					
						
							|  |  |  | 		} else if (item) { | 
					
						
							|  |  |  | 			buf.push(item); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	if (name === true) { | 
					
						
							|  |  |  | 		name = hashForName(buf); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	backgroundJobs.push(writeFile(name, buf)); | 
					
						
							|  |  |  | 	let size = 0; | 
					
						
							|  |  |  | 	for (const b of buf) size += b.length; | 
					
						
							|  |  |  | 	return { | 
					
						
							|  |  |  | 		size, | 
					
						
							|  |  |  | 		name, | 
					
						
							|  |  |  | 		backgroundJob: | 
					
						
							|  |  |  | 			backgroundJobs.length === 1 | 
					
						
							|  |  |  | 				? backgroundJobs[0] | 
					
						
							|  |  |  | 				: Promise.all(backgroundJobs) | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  |  * @param {FileMiddleware} middleware this | 
					
						
							|  |  |  |  * @param {string | false} name filename | 
					
						
							|  |  |  |  * @param {function(string | false): Promise<Buffer>} readFile read content of a file | 
					
						
							|  |  |  |  * @returns {Promise<BufferSerializableType[]>} deserialized data | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | const deserialize = async (middleware, name, readFile) => { | 
					
						
							|  |  |  | 	const content = await readFile(name); | 
					
						
							|  |  |  | 	if (content.length === 0) throw new Error("Empty file " + name); | 
					
						
							|  |  |  | 	const version = content.readUInt32LE(0); | 
					
						
							| 
									
										
										
										
											2020-02-01 00:53:50 +08:00
										 |  |  | 	if (version !== VERSION) { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		throw new Error("Invalid file version"); | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	const sectionCount = content.readUInt32LE(4); | 
					
						
							|  |  |  | 	const lengths = []; | 
					
						
							|  |  |  | 	for (let i = 0; i < sectionCount; i++) { | 
					
						
							|  |  |  | 		lengths.push(content.readInt32LE(8 + 4 * i)); | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	let position = sectionCount * 4 + 8; | 
					
						
							|  |  |  | 	const result = lengths.map(length => { | 
					
						
							|  |  |  | 		const l = Math.abs(length); | 
					
						
							|  |  |  | 		const section = content.slice(position, position + l); | 
					
						
							|  |  |  | 		position += l; | 
					
						
							|  |  |  | 		if (length < 0) { | 
					
						
							|  |  |  | 			// we clone the buffer here to allow the original content to be garbage collected
 | 
					
						
							|  |  |  | 			const clone = Buffer.from(section); | 
					
						
							|  |  |  | 			const size = section.readUInt32LE(0); | 
					
						
							|  |  |  | 			const nameBuffer = clone.slice(4); | 
					
						
							|  |  |  | 			const name = nameBuffer.toString(); | 
					
						
							|  |  |  | 			return SerializerMiddleware.createLazy( | 
					
						
							|  |  |  | 				memorize(() => deserialize(middleware, name, readFile)), | 
					
						
							|  |  |  | 				middleware, | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					name, | 
					
						
							|  |  |  | 					size | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				clone | 
					
						
							| 
									
										
										
										
											2019-01-19 18:49:30 +08:00
										 |  |  | 			); | 
					
						
							| 
									
										
										
										
											2019-01-15 00:31:33 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 			return section; | 
					
						
							| 
									
										
										
										
											2019-01-15 00:31:33 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 	return result; | 
					
						
							| 
									
										
										
										
											2019-01-15 00:31:33 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {BufferSerializableType[]} DeserializedType | 
					
						
							| 
									
										
										
										
											2019-06-11 19:09:42 +08:00
										 |  |  |  * @typedef {true} SerializedType | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  |  * @extends {SerializerMiddleware<DeserializedType, SerializedType>} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | class FileMiddleware extends SerializerMiddleware { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {IntermediateFileSystem} fs filesystem | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-06-11 19:09:42 +08:00
										 |  |  | 	constructor(fs) { | 
					
						
							|  |  |  | 		super(); | 
					
						
							|  |  |  | 		this.fs = fs; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | 	 * @param {DeserializedType} data data | 
					
						
							|  |  |  | 	 * @param {Object} context context object | 
					
						
							|  |  |  | 	 * @returns {SerializedType|Promise<SerializedType>} serialized data | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	serialize(data, { filename, extension = "" }) { | 
					
						
							|  |  |  | 		return new Promise((resolve, reject) => { | 
					
						
							|  |  |  | 			mkdirp(this.fs, dirname(this.fs, filename), err => { | 
					
						
							|  |  |  | 				if (err) return reject(err); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				// It's important that we don't touch existing files during serialization
 | 
					
						
							|  |  |  | 				// because serialize may read existing files (when deserializing)
 | 
					
						
							|  |  |  | 				const allWrittenFiles = new Set(); | 
					
						
							|  |  |  | 				const writeFile = async (name, content) => { | 
					
						
							|  |  |  | 					const file = name | 
					
						
							|  |  |  | 						? join(this.fs, filename, `../${name}${extension}`) | 
					
						
							|  |  |  | 						: filename; | 
					
						
							|  |  |  | 					await new Promise((resolve, reject) => { | 
					
						
							|  |  |  | 						const stream = this.fs.createWriteStream(file + "_"); | 
					
						
							|  |  |  | 						for (const b of content) stream.write(b); | 
					
						
							|  |  |  | 						stream.end(); | 
					
						
							|  |  |  | 						stream.on("error", err => reject(err)); | 
					
						
							|  |  |  | 						stream.on("finish", () => resolve()); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 					if (name) allWrittenFiles.add(file); | 
					
						
							|  |  |  | 				}; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				resolve( | 
					
						
							|  |  |  | 					serialize(this, data, false, writeFile).then( | 
					
						
							|  |  |  | 						async ({ backgroundJob }) => { | 
					
						
							|  |  |  | 							await backgroundJob; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 							// Rename the index file to disallow access during inconsistent file state
 | 
					
						
							|  |  |  | 							await new Promise(resolve => | 
					
						
							|  |  |  | 								this.fs.rename(filename, filename + ".old", err => { | 
					
						
							|  |  |  | 									resolve(); | 
					
						
							|  |  |  | 								}) | 
					
						
							|  |  |  | 							); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 							// update all written files
 | 
					
						
							|  |  |  | 							await Promise.all( | 
					
						
							|  |  |  | 								Array.from( | 
					
						
							|  |  |  | 									allWrittenFiles, | 
					
						
							|  |  |  | 									file => | 
					
						
							|  |  |  | 										new Promise((resolve, reject) => { | 
					
						
							|  |  |  | 											this.fs.rename(file + "_", file, err => { | 
					
						
							|  |  |  | 												if (err) return reject(err); | 
					
						
							|  |  |  | 												resolve(); | 
					
						
							|  |  |  | 											}); | 
					
						
							|  |  |  | 										}) | 
					
						
							|  |  |  | 								) | 
					
						
							|  |  |  | 							); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							// As final step atomatically update the index file to have a consistent pack again
 | 
					
						
							|  |  |  | 							await new Promise(resolve => { | 
					
						
							|  |  |  | 								this.fs.rename(filename + "_", filename, err => { | 
					
						
							|  |  |  | 									if (err) return reject(err); | 
					
						
							|  |  |  | 									resolve(); | 
					
						
							|  |  |  | 								}); | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 							return /** @type {true} */ (true); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					) | 
					
						
							|  |  |  | 				); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | 	 * @param {SerializedType} data data | 
					
						
							|  |  |  | 	 * @param {Object} context context object | 
					
						
							|  |  |  | 	 * @returns {DeserializedType|Promise<DeserializedType>} deserialized data | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	deserialize(data, { filename, extension = "" }) { | 
					
						
							|  |  |  | 		const readFile = name => | 
					
						
							|  |  |  | 			new Promise((resolve, reject) => { | 
					
						
							|  |  |  | 				const file = name | 
					
						
							|  |  |  | 					? join(this.fs, filename, `../${name}${extension}`) | 
					
						
							|  |  |  | 					: filename; | 
					
						
							|  |  |  | 				return this.fs.readFile(file, (err, content) => { | 
					
						
							|  |  |  | 					if (err) return reject(err); | 
					
						
							|  |  |  | 					resolve(content); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		return deserialize(this, false, readFile); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = FileMiddleware; |