You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					258 lines
				
				9.7 KiB
			
		
		
			
		
	
	
					258 lines
				
				9.7 KiB
			| 
											3 years ago
										 | "use strict"; | ||
|  | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
|  |     function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
|  |     return new (P || (P = Promise))(function (resolve, reject) { | ||
|  |         function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
|  |         function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
|  |         function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
|  |         step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
|  |     }); | ||
|  | }; | ||
|  | var __importDefault = (this && this.__importDefault) || function (mod) { | ||
|  |     return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
|  | }; | ||
|  | Object.defineProperty(exports, "__esModule", { value: true }); | ||
|  | exports.Cache = void 0; | ||
|  | /** | ||
|  |  * Storage cache | ||
|  |  * @module multer-gridfs-storage/cache | ||
|  |  */ | ||
|  | const events_1 = require("events"); | ||
|  | const mongodb_uri_1 = __importDefault(require("mongodb-uri")); | ||
|  | const utils_1 = require("./utils"); | ||
|  | /** | ||
|  |  * Plugin cached connection handling class. | ||
|  |  * @version 3.1.0 | ||
|  |  */ | ||
|  | class Cache { | ||
|  |     constructor() { | ||
|  |         this.store = new Map(); | ||
|  |         this.emitter = new events_1.EventEmitter(); | ||
|  |         this.emitter.setMaxListeners(0); | ||
|  |     } | ||
|  |     /** | ||
|  |      * Handles creating a new connection from an url and caching if necessary | ||
|  |      * @param {object} options - Options to initialize the cache | ||
|  |      * @param {string} options.url - The url to cache | ||
|  |      * @param {string} options.cacheName - The name of the cache to use | ||
|  |      * @param {any} options.init - The connection options provided | ||
|  |      **/ | ||
|  |     initialize(options) { | ||
|  |         let { url, cacheName: name } = options; | ||
|  |         // If the option is a falsey value or empty object use null as initial value
 | ||
|  |         const init = utils_1.compare(options.init, null) ? null : options.init; | ||
|  |         // If a cache under that name does not exist create one
 | ||
|  |         if (!this.store.has(name)) { | ||
|  |             this.store.set(name, new Map()); | ||
|  |         } | ||
|  |         // Check if the url has been used for that cache before
 | ||
|  |         let cached = this.store.get(name).get(url); | ||
|  |         if (!this.store.get(name).has(url)) { | ||
|  |             // If the url matches any equivalent url used before use that connection instead
 | ||
|  |             const eqUrl = this.findUri(name, url); | ||
|  |             if (!eqUrl) { | ||
|  |                 const store = new Map(); | ||
|  |                 store.set(0, { | ||
|  |                     db: null, | ||
|  |                     client: null, | ||
|  |                     pending: true, | ||
|  |                     opening: false, | ||
|  |                     init, | ||
|  |                 }); | ||
|  |                 this.store.get(name).set(url, store); | ||
|  |                 return { | ||
|  |                     url, | ||
|  |                     name, | ||
|  |                     index: 0, | ||
|  |                 }; | ||
|  |             } | ||
|  |             url = eqUrl; | ||
|  |             cached = this.store.get(name).get(url); | ||
|  |         } | ||
|  |         // Compare connection options to create more only if they are semantically different
 | ||
|  |         for (const [index, value] of cached) { | ||
|  |             if (utils_1.compare(value.init, options.init)) { | ||
|  |                 return { | ||
|  |                     url, | ||
|  |                     name, | ||
|  |                     index, | ||
|  |                 }; | ||
|  |             } | ||
|  |         } | ||
|  |         cached.set(cached.size, { | ||
|  |             db: null, | ||
|  |             client: null, | ||
|  |             pending: true, | ||
|  |             opening: false, | ||
|  |             init, | ||
|  |         }); | ||
|  |         return { | ||
|  |             url, | ||
|  |             name, | ||
|  |             index: cached.size - 1, | ||
|  |         }; | ||
|  |     } | ||
|  |     /** | ||
|  |      * Search the cache for a space stored under an equivalent url. | ||
|  |      * | ||
|  |      * Just swapping parameters can cause two url to be deemed different when in fact they are not. | ||
|  |      * This method finds an url in the cache where another url could be stored even when they are not strictly equal | ||
|  |      * @param cacheName The name of the cache to search | ||
|  |      * @param url The mongodb url to compare | ||
|  |      * @return The similar url already in the cache | ||
|  |      */ | ||
|  |     findUri(cacheName, url) { | ||
|  |         for (const [storedUrl] of this.store.get(cacheName)) { | ||
|  |             const parsedUri = mongodb_uri_1.default.parse(storedUrl); | ||
|  |             const parsedCache = mongodb_uri_1.default.parse(url); | ||
|  |             if (utils_1.compareUris(parsedUri, parsedCache)) { | ||
|  |                 return storedUrl; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |     /** | ||
|  |      * Returns true if the cache has an entry matching the given index | ||
|  |      * @param cacheIndex The index to look for | ||
|  |      * @return Returns if the cache was found | ||
|  |      */ | ||
|  |     has(cacheIndex) { | ||
|  |         return Boolean(this.get(cacheIndex)); | ||
|  |     } | ||
|  |     /** | ||
|  |      * Returns the contents of the cache in a given index | ||
|  |      * @param cacheIndex {object} The index to look for | ||
|  |      * @return {object} The cache contents or null if was not found | ||
|  |      */ | ||
|  |     get(cacheIndex) { | ||
|  |         const { name, url, index } = cacheIndex; | ||
|  |         if (!this.store.has(name)) { | ||
|  |             return null; | ||
|  |         } | ||
|  |         if (!this.store.get(name).has(url)) { | ||
|  |             return null; | ||
|  |         } | ||
|  |         if (!this.store.get(name).get(url).has(index)) { | ||
|  |             return null; | ||
|  |         } | ||
|  |         return this.store.get(name).get(url).get(index); | ||
|  |     } | ||
|  |     /** | ||
|  |      * Sets the contents of the cache in a given index | ||
|  |      * @param cacheIndex The index to look for | ||
|  |      * @param value The value to set | ||
|  |      */ | ||
|  |     set(cacheIndex, value) { | ||
|  |         const { name, url, index } = cacheIndex; | ||
|  |         this.store.get(name).get(url).set(index, value); | ||
|  |     } | ||
|  |     /** | ||
|  |      * Returns true if a given cache is resolving its associated connection | ||
|  |      * @param cacheIndex {object} The index to look for | ||
|  |      * @return Return true if the connection is not found yet | ||
|  |      */ | ||
|  |     isPending(cacheIndex) { | ||
|  |         const cached = this.get(cacheIndex); | ||
|  |         return Boolean(cached) && cached.pending; | ||
|  |     } | ||
|  |     /** | ||
|  |      * Return true if a given cache started resolving a connection for itself | ||
|  |      * @param cacheIndex {object} The index to look for | ||
|  |      * @return Return true if no instances have started creating a connection for this cache | ||
|  |      */ | ||
|  |     isOpening(cacheIndex) { | ||
|  |         const cached = this.get(cacheIndex); | ||
|  |         return Boolean(cached === null || cached === void 0 ? void 0 : cached.opening); | ||
|  |     } | ||
|  |     /** | ||
|  |      * Sets the database and client for a given cache and resolves all instances waiting for it | ||
|  |      * @param cacheIndex {object} The index to look for | ||
|  |      * @param db  The database used to store files | ||
|  |      * @param [client] The client used to open the connection or null if none is provided | ||
|  |      */ | ||
|  |     resolve(cacheIndex, db, client) { | ||
|  |         const cached = this.get(cacheIndex); | ||
|  |         cached.db = db; | ||
|  |         cached.client = client; | ||
|  |         cached.pending = false; | ||
|  |         cached.opening = false; | ||
|  |         this.emitter.emit('resolve', cacheIndex); | ||
|  |     } | ||
|  |     /** | ||
|  |      * Rejects all instances waiting for this connections | ||
|  |      * @param cacheIndex The index to look for | ||
|  |      * @param err The error thrown by the driver | ||
|  |      */ | ||
|  |     reject(cacheIndex, error) { | ||
|  |         const cached = this.get(cacheIndex); | ||
|  |         cached.pending = false; | ||
|  |         this.emitter.emit('reject', cacheIndex, error); | ||
|  |         this.remove(cacheIndex); | ||
|  |     } | ||
|  |     /** | ||
|  |      * Allows waiting for a connection associated to a given cache | ||
|  |      * @param cacheIndex The index to look for | ||
|  |      * @return A promise that will resolve when the connection for this cache is created | ||
|  |      */ | ||
|  |     waitFor(cacheIndex) { | ||
|  |         return __awaiter(this, void 0, void 0, function* () { | ||
|  |             if (!this.isPending(cacheIndex) && !this.isOpening(cacheIndex)) { | ||
|  |                 return this.get(cacheIndex); | ||
|  |             } | ||
|  |             return new Promise((resolve, reject) => { | ||
|  |                 const _resolve = (index) => { | ||
|  |                     if (utils_1.compare(cacheIndex, index)) { | ||
|  |                         this.emitter.removeListener('resolve', _resolve); | ||
|  |                         this.emitter.removeListener('reject', _reject); | ||
|  |                         resolve(this.get(cacheIndex)); | ||
|  |                     } | ||
|  |                 }; | ||
|  |                 const _reject = (index, error) => { | ||
|  |                     if (utils_1.compare(cacheIndex, index)) { | ||
|  |                         this.emitter.removeListener('resolve', _resolve); | ||
|  |                         this.emitter.removeListener('reject', _reject); | ||
|  |                         reject(error); | ||
|  |                     } | ||
|  |                 }; | ||
|  |                 this.emitter.on('resolve', _resolve); | ||
|  |                 this.emitter.on('reject', _reject); | ||
|  |             }); | ||
|  |         }); | ||
|  |     } | ||
|  |     /** | ||
|  |      * Gives the number of connections created by all cache instances | ||
|  |      * @return {number} The number of created connections | ||
|  |      */ | ||
|  |     connections() { | ||
|  |         let total = 0; | ||
|  |         for (const urlStore of this.store.values()) { | ||
|  |             for (const store of urlStore.values()) { | ||
|  |                 total += store.size; | ||
|  |             } | ||
|  |         } | ||
|  |         return total; | ||
|  |     } | ||
|  |     /** | ||
|  |      * Removes a cache entry. | ||
|  |      * | ||
|  |      * > If the cache hasn't resolved yet it will be rejected. | ||
|  |      * @param cacheIndex The index to look for | ||
|  |      */ | ||
|  |     remove(cacheIndex) { | ||
|  |         if (this.has(cacheIndex)) { | ||
|  |             if (this.isPending(cacheIndex)) { | ||
|  |                 this.emitter.emit('reject', cacheIndex, new Error('The cache entry was deleted')); | ||
|  |             } | ||
|  |             const { name, url, index } = cacheIndex; | ||
|  |             this.store.get(name).get(url).delete(index); | ||
|  |         } | ||
|  |     } | ||
|  |     /** | ||
|  |      * Removes all entries in the cache and all listeners | ||
|  |      */ | ||
|  |     clear() { | ||
|  |         this.store = new Map(); | ||
|  |         this.emitter.removeAllListeners(); | ||
|  |     } | ||
|  | } | ||
|  | exports.Cache = Cache; | ||
|  | //# sourceMappingURL=cache.js.map
 |