{"version":3,"sources":["jsdelivr-header.js","/npm/resource-container@0.11.8/lib/main.js"],"names":[],"mappings":"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","file":"/npm/resource-container@0.11.8/lib/main.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/resource-container@0.11.8/lib/main.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","'use strict';\n\nconst fs = require('fs');\nconst path = require('path');\nconst fileUtils = require('./utils/files');\nconst compressionUtils = require('./utils/compression');\nconst promiseUtils = require('./utils/promises');\nconst YAML = require('yamljs');\nconst rimraf = require('rimraf');\nconst assert = require(\"assert\");\nconst mkdirp = require('mkdirp');\nconst compareVersions = require('compare-versions');\n\n/**\n * Details about the container specification\n */\nconst spec = {\n    version: '0.1',\n    file_ext: 'tsrc',\n    base_mime_type: 'application/tsrc'\n};\n\nconst content_dir = 'content';\n\n/**\n * Returns a properly formatted container slug.\n *\n * @param language_slug {string}\n * @param project_slug {string}\n * @param resource_slug {string}\n * @returns {string}\n */\nfunction containerSlug(language_slug, project_slug, resource_slug) {\n    if(!language_slug || !project_slug || !resource_slug) throw new Error('Invalid resource container slug parameters');\n    return language_slug\n        + '_' + project_slug\n        + '_' + resource_slug;\n}\n\n/**\n * Represents an instance of a resource container.\n *\n * @param container_directory\n * @param package_json {{}}\n * @constructor\n */\nfunction Container(container_directory, package_json) {\n    let config_yaml = {};\n    let toc_yaml = {};\n\n    assert(container_directory, 'resource-container: missing container_directory');\n    assert.equal(typeof container_directory, 'string', 'resource-container: container_directory should be a string');\n\n    try {\n        let configFile = path.join(container_directory, content_dir, 'config.yml');\n        if(fileUtils.fileExists(configFile)) config_yaml = YAML.parse(fs.readFileSync(configFile, {encoding: 'utf8'}));\n    } catch (err){\n        console.warn(err);\n    }\n    try {\n        let tocFile = path.join(container_directory, content_dir, 'toc.yml');\n        if(fileUtils.fileExists(tocFile)) toc_yaml = YAML.parse(fs.readFileSync(tocFile, {encoding: 'utf8'}));\n    } catch (err){\n        console.warn(err);\n    }\n\n    assert(package_json, 'resource-container: missing package json');\n    assert(package_json.language, 'resource-container: missing language');\n    assert(package_json.project, 'resource-container: missing project');\n    assert(package_json.resource, 'resource-container: missing resource');\n    assert(package_json.resource.type, 'resource-container: missing resource type');\n\n    return {\n\n        get language () {\n            return package_json.language;\n        },\n\n        get project () {\n            return package_json.project;\n        },\n\n        get resource () {\n            return package_json.resource;\n        },\n\n        /**\n         * Returns an array of chapters in this resource container\n         */\n        chapters: function () {\n            let dir = path.join(container_directory, content_dir);\n            return fs.readdirSync(dir).filter(function(file) {\n                try {\n                    return fs.statSync(file).isDirectory();\n                } catch (err) {\n                    console.log(err);\n                    return false;\n                }\n            });\n        },\n\n        /**\n         * Returns an array of chunks in the chapter\n         */\n        chunks: function (chapterSlug) {\n            let dir = path.join(container_directory, content_dir, chapterSlug);\n            return fs.readdirSync(dir).filter(function(file) {\n                try {\n                    return fs.statSync(file).isFile();\n                } catch (err) {\n                    console.log(err);\n                    return false;\n                }\n            });\n        },\n\n        /**\n         * Returns the contents of a chunk.\n         * If the chunk does not exist or there is an exception an empty string will be returned.\n         * @param chapterSlug\n         * @param chunkSlug\n         * @returns string the contents of the chunk\n         */\n        readChunk: function(chapterSlug, chunkSlug) {\n            let file = path.join(container_directory, content_dir, chapterSlug, chunkSlug + '.' + this.chunkExt);\n            return fs.readFileSync(file, {encoding: 'utf8'});\n        },\n\n        /**\n         * Returns the file extension to use for content files (chunks)\n         * @returns {*}\n         */\n        get chunkExt() {\n            switch(package_json['content_mime_type']) {\n                case 'text/usx':\n                    return 'usx';\n                case 'text/usfm':\n                    return 'usfm';\n                case 'text/markdown':\n                    return 'md';\n                default:\n                    return 'txt';\n            }\n        },\n\n        /**\n         * Returns the path to the resource container directory\n         * @returns {string}\n         */\n        get path () {\n            return container_directory;\n        },\n\n        /**\n         * Returns the slug of the resource container\n         * @returns {string}\n         */\n        get slug () {\n            return containerSlug(\n                package_json.language.slug,\n                package_json.project.slug,\n                package_json.resource.slug);\n        },\n\n        /**\n         * Returns the type of the resource container.\n         * Shorthand for info.resource.type\n         * @returns {string}\n         */\n        get type() {\n            return package_json.resource.type;\n        },\n\n        /**\n         * Returns the resource container package information.\n         * This is the package.json file\n         * @returns {{}}\n         */\n        get info() {\n            return package_json;\n        },\n\n        /**\n         * Returns the resource container data configuration.\n         * This is the config.yml file under the content/ directory\n         *\n         * @returns {{}}\n         */\n        get config() {\n            return config_yaml;\n        },\n\n        /**\n         * Returns the table of contents.\n         * This is the toc.yml file under the content/ directory\n         *\n         * @returns {{}|[]}\n         */\n        get toc() {\n            return toc_yaml;\n        }\n    };\n}\n\n/**\n * Loads a resource container from the disk.\n * Rejects with an error if the container is not supported. Or does not exist, or is not a directory.\n *\n * @param container_directory {string}\n * @returns {Promise<Container>}\n */\nfunction loadContainer(container_directory) {\n    return new Promise(function(resolve, reject) {\n        let package_json;\n        try {\n            if(!fs.statSync(container_directory).isDirectory()) {\n                reject(new Error('Not an open resource container'));\n                return;\n            }\n        } catch(err) {\n            reject(new Error('The resource container does not exist'));\n            return;\n        }\n        try {\n            package_json = JSON.parse(fs.readFileSync(path.join(container_directory, 'package.json'), {encoding: 'utf8'}));\n        } catch (err) {\n            console.error(err);\n        }\n        if(package_json == null) {\n            reject(new Error('Invalid resource container. Missing package.json'));\n            return;\n        } else if(package_json.package_version == null) {\n            reject(new Error('Invalid resource container. Missing field \"package_version\".'));\n            return;\n        }\n        if (compareVersions(package_json.package_version, spec.version) === 1) {\n            reject(new Error('Unsupported resource container version. This library supports version ' + spec.version + ' but was given ' + package_json.package_version));\n            return;\n        }\n        if(compareVersions(package_json.package_version, spec.version) === -1) {\n            reject(new Error('Outdated resource container version. This library supports version ' + spec.version + ' but was given ' + package_json.package_version));\n            return;\n        }\n\n        resolve(new Container(container_directory, package_json));\n    });\n}\n\n/**\n * Creates a new resource container.\n * Rejects with an error if the container exists.\n *\n * @param container_directory {string}\n * @param opts {{}}\n * @returns {Promise<Container>}\n */\nfunction makeContainer(container_directory, opts) {\n    return new Promise(function(resolve, reject) {\n        if(fileUtils.fileExists(container_directory)) {\n            reject(new Error('Container already exists'));\n            return;\n        }\n\n        let package_json = {};\n        // TODO: build the container\n        reject(new Error('Not implemented yet.'));\n\n        // resolve(new Container(container_directory, package_json));\n    });\n}\n\n/**\n * Opens an archived resource container.\n * If the container is already opened it will be loaded\n *\n * @param container_archive {string}\n * @param container_directory {string}\n * @param opts {{}} extra options such as compression_method (zip or tar)\n * @returns {Promise<Container>}\n */\nfunction openContainer(container_archive, container_directory, opts) {\n    opts = opts || { compression_method : 'tar' };\n    if(fileUtils.fileExists(container_directory)) {\n        return loadContainer(container_directory);\n    }\n    if(!fileUtils.fileExists(container_archive)) return Promise.reject(new Error('Missing resource container'));\n    if(opts.compression_method === 'zip') {\n        return compressionUtils.unzip(container_archive, container_directory)\n            .then(function(dir) {\n                return loadContainer(dir);\n            });\n    } else {\n        return compressionUtils.untar(container_archive, container_directory)\n            .then(function(dir) {\n                return loadContainer(dir);\n            });\n    }\n}\n\n/**\n * Closes (archives) a resource container.\n *\n * @param container_directory {string}\n * @param opts {{}} extra options such as compression_method (zip or tar)\n * @returns {Promise<string>} the path to the resource container archive\n */\nfunction closeContainer(container_directory, opts) {\n    opts = opts || { compression_method : 'tar', clean : true };\n    if(!fileUtils.fileExists(container_directory)) return Promise.reject(new Error('Missing resource container'));\n    var container_archive = container_directory + '.' + spec.file_ext;\n    var compressPromise = Promise.resolve(container_archive);\n\n    // create archive if it's missing\n    if(!fileUtils.fileExists(container_archive)) {\n        if(opts.compression_method === 'zip') {\n            compressPromise = compressionUtils.zip(container_directory, container_archive);\n        } else {\n            compressPromise = compressionUtils.tar(container_directory, container_archive);\n        }\n    }\n    return compressPromise.then(function(path) {\n        // remove directory\n        if(opts.clean) rimraf.sync(container_directory);\n        return Promise.resolve(path);\n    });\n}\n\n/**\n * TODO: this will eventually be abstracted to call makeContainer after processing the data\n * Converts a legacy resource into a resource container.\n * Rejects with an error if the container exists.\n *\n * @param data {string} the raw resource data\n * @param dir {string} the destination directory\n * @param props {{language, project, resource}} properties of the resource content.\n * @returns {Promise.<Container>} the newly converted container\n */\nfunction convertResource(data, dir, props) {\n    // validate opts\n    if(!props.language || !props.project || !props.resource || !props.resource.type) {\n        return Promise.reject(new Error('Missing required parameters'));\n    }\n\n    const writeFileSync = promiseUtils.promisify(fs, 'writeFileSync');\n    // TRICKY: the old content was in usx.\n    const mimeType = props.project.slug !== 'obs' && props.resource.type === 'book' ? 'text/usx' : 'text/markdown';\n    const chunk_ext = mimeType === 'text/usx' ? 'usx' : 'md';\n\n    return new Promise(function(resolve, reject) {\n            if(fileUtils.fileExists(path.join(dir + '.' + spec.file_ext))) {\n                reject(new Error('Resource container already exists'));\n            } else {\n                // clean opened container\n                rimraf.sync(dir);\n                resolve();\n            }\n        })\n        .then(function() {\n            // package.json\n            mkdirp.sync(dir);\n            let packageData = {\n                package_version: spec.version,\n                modified_at: props.modified_at,\n                content_mime_type: mimeType,\n                language: props.language,\n                project: props.project,\n                resource: props.resource,\n                chunk_status: []\n            };\n            return writeFileSync(path.join(dir, 'package.json'), new Buffer(JSON.stringify(packageData, null, 2)));\n        })\n        .then(function() {\n            // license\n            // TODO: use a proper license based on the resource license\n            return writeFileSync(path.join(dir, 'LICENSE.md'), new Buffer(props.resource.status.license));\n        })\n        .then(function() {\n            // content\n            return new Promise(function(resolve, reject) {\n                let contentDir = path.join(dir, 'content');\n                let config = {};\n                let toc = [];\n\n                try {\n                    // front matter\n                    if(props.resource.type !== 'help' && props.resource.type !== 'dict') {\n                        // TRICKY: helps do not have a translatable title\n                        let frontDir = path.join(contentDir, 'front');\n                        mkdirp.sync(frontDir);\n                        fs.writeFileSync(path.join(frontDir, 'title.' + chunk_ext), props.project.name.trim());\n                        toc.push({\n                            chapter: 'front',\n                            chunks: ['title']\n                        });\n                    }\n\n                    // main content\n                    mkdirp.sync(contentDir);\n                    if(props.resource.type === 'book') {\n                        config.content = {};\n                        if (props.project.slug === 'obs') {\n                            // add obs images\n                            if(!config.media) config.media = {};\n                            config.media.image = {\n                                mime_type: 'image/jpg',\n                                size: 37620940,\n                                url: 'https://api.unfoldingword.org/obs/jpg/1/en/obs-images-360px.zip'\n                            };\n                        }\n                        data = JSON.parse(data);\n                        for (let chapter of data.chapters) {\n                            chapter.number = normalizeSlug(chapter.number);\n                            let chapterConfig = {};\n                            let chapterTOC = {\n                                chapter: chapter.number,\n                                chunks: []\n                            };\n                            let chapterDir = path.join(contentDir, chapter.number);\n                            mkdirp.sync(chapterDir);\n\n                            // chapter title\n                            chapterTOC.chunks.push('title');\n                            if (chapter.title) {\n                                fs.writeFileSync(path.join(chapterDir, 'title.' + chunk_ext), chapter.title);\n                            } else {\n                                var title = localizeChapterTitle(props.language.slug, chapter.number);\n                                fs.writeFileSync(path.join(chapterDir, 'title.' + chunk_ext), title);\n                            }\n\n                            // frames\n                            for (let frame of chapter.frames) {\n                                let frameSlug = normalizeSlug(frame.id.split('-')[1].trim());\n                                if(frameSlug.trim() === '00') {\n                                    // fix for chunk 00.txt bug\n                                    let firstVerseRange = /<verse\\s+number=\"(\\d+(-\\d+)?)\"\\s+style=\"v\"\\s*\\/>/.exec(frame.text)[1];\n                                    // TRICKY: verses can be num-num\n                                    if(firstVerseRange) frameSlug = normalizeSlug(firstVerseRange.split('-')[0]);\n                                }\n\n                                // build chunk config\n                                let questions = [];\n                                let notes = [];\n                                let images = [];\n                                let words = [];\n                                // TODO: add questions, notes, and images to the config for the chunk\n                                if(props.tw_assignments) {\n                                    try {\n                                        let temp = props.tw_assignments[chapter.number][frameSlug];\n                                        words = temp ? temp : words;\n                                    } catch(e) {}\n                                }\n                                if(questions.length || notes.length || images.length || words.length) {\n                                    chapterConfig[frameSlug] = {};\n                                }\n                                if(questions.length) {\n                                    chapterConfig[frameSlug]['questions'] = questions;\n                                }\n                                if(notes.length) {\n                                    chapterConfig[frameSlug]['notes'] = notes;\n                                }\n                                if(images.length) {\n                                    chapterConfig[frameSlug]['images'] = images;\n                                }\n                                if(words.length) {\n                                    chapterConfig[frameSlug]['words'] = words;\n                                }\n\n                                chapterTOC.chunks.push(frameSlug);\n                                fs.writeFileSync(path.join(chapterDir, frameSlug + '.' + chunk_ext), frame.text);\n                            }\n\n                            // chapter reference\n                            if (chapter.ref) {\n                                chapterTOC.chunks.push('reference');\n                                fs.writeFileSync(path.join(chapterDir, 'reference.' + chunk_ext), chapter.ref)\n                            }\n                            if(Object.keys(chapterConfig).length) {\n                                // TRICKY: only non-empty config\n                                config.content[chapter.number] = chapterConfig;\n                            }\n                            toc.push(chapterTOC);\n                        }\n                    } else if(props.resource.type === 'help') {\n                        if(props.resource.slug === 'tn') {\n                            data = JSON.parse(data);\n                            for(let chunk of data) {\n                                if(!chunk.tn) continue;\n                                let slugs = chunk.id.split('-');\n                                if(slugs.length !== 2) continue;\n                                let chapterSlug = normalizeSlug(slugs[0]);\n                                let chunkSlug = normalizeSlug(slugs[1]);\n\n                                // fix for chunk 00.txt bug\n                                if(chunkSlug === '00') continue;\n\n                                let chapterDir = path.join(contentDir, chapterSlug);\n                                mkdirp.sync(chapterDir);\n                                let body = '';\n                                for(let note of chunk.tn) {\n                                    body += '\\n\\n#' + note.ref + '\\n\\n' + note.text;\n                                }\n                                if(body.trim() !== '') {\n                                    fs.writeFileSync(path.join(chapterDir, chunkSlug + '.' + chunk_ext), body.trim());\n                                }\n                            }\n                        } else if(props.resource.slug == 'tq') {\n                            data = JSON.parse(data);\n                            for(let chapter of data) {\n                                if(!chapter.cq) continue;\n                                chapter.id = normalizeSlug(chapter.id);\n                                let chapterDir = path.join(contentDir, chapter.id);\n                                mkdirp.sync(chapterDir);\n                                let normalizedChunks = {};\n                                for(let question of chapter.cq) {\n                                    let text = '\\n\\n#' + question.q + '\\n\\n' + question.a;\n                                    for(let slug of question.ref) {\n                                        let slugs = slug.split('-');\n                                        if(slugs.length !== 2) continue;\n                                        let chapterSlug = normalizeSlug(slugs[0]);\n                                        let chunkSlug = normalizeSlug(slugs[1]);\n\n                                        if(!normalizedChunks[chunkSlug]) normalizedChunks[chunkSlug] = '';\n                                        normalizedChunks[chunkSlug] += text;\n                                    }\n                                }\n                                for(let chunk in normalizedChunks) {\n                                    fs.writeFileSync(path.join(chapterDir, chunk + '.' + chunk_ext), normalizedChunks[chunk].trim())\n                                }\n                            }\n                        } else {\n                            reject(new Error('Unsupported resource ' + props.resource.slug));\n                            return;\n                        }\n                    } else if(props.resource.type === 'dict') {\n                        data = JSON.parse(data);\n                        for(let word of data) {\n                            if(!word.id) continue;\n                            let wordDir = path.join(contentDir, word.id);\n                            mkdirp.sync(wordDir);\n                            let body = '#' + word.term + '\\n\\n' + word.def;\n                            fs.writeFileSync(path.join(wordDir, '01.' + chunk_ext), body);\n\n                            config[word.id] = {\n                                def_title: word.def_title\n                            };\n\n                            if(word.cf) {\n                                let foundIds = {}; // track found related id's so we don't get duplicates\n                                for (let related of word.cf) {\n                                    if (!config[word.id]['see_also']) config[word.id]['see_also'] = [];\n                                    let parts = related.split('|');\n                                    let id = parts[0].toLowerCase();\n                                    if(!foundIds[id]) {\n                                        foundIds[id] = true;\n                                        config[word.id]['see_also'].push(id);\n                                    }\n                                }\n                            }\n                            if(word.aliases) {\n                                for (let alias of word.aliases) {\n                                    if (!config[word.id]['aliases']) config[word.id]['aliases'] = [];\n                                    let aliases = alias.split(',');\n                                    for(let a of aliases) {\n                                        config[word.id]['aliases'].push(a.trim());\n                                    }\n                                }\n                            }\n                            if(word.ex) {\n                                for (let example of word.ex) {\n                                    if (!config[word.id]['examples']) config[word.id]['examples'] = [];\n                                    config[word.id]['examples'].push(example.ref);\n                                }\n                            }\n                        }\n                    } else if (props.resource.type === 'man') {\n                        config.content = {};\n                        data = JSON.parse(data);\n\n                        let tocMap = {};\n\n                        for(let article of data.articles) {\n                            let articleConfig = {\n                                recommended: [],\n                                dependencies: []\n                            };\n                            let articleTOC = {\n\n                            };\n\n                            // TRICKY: fix the id's\n                            article.id = article.id.replace(/\\_/g, '-');\n                            if(article.recommend) {\n                                article.recommend.forEach(function (id) {\n                                    articleConfig.recommended.push(id.replace(/\\_/g, '-'));\n                                });\n                            }\n                            if(article.depend) {\n                                article.depend.forEach(function (id) {\n                                    articleConfig.dependencies.push(id.replace(/\\_/g, '-'));\n                                });\n                            }\n\n                            let articleDir = path.join(contentDir, article.id);\n                            mkdirp.sync(articleDir);\n\n                            // article title\n                            fs.writeFileSync(path.join(articleDir, 'title.' + chunk_ext), article.title);\n\n                            // article sub-title\n                            fs.writeFileSync(path.join(articleDir, 'sub-title.' + chunk_ext), article.question);\n\n                            // article body\n                            fs.writeFileSync(path.join(articleDir, '01.' + chunk_ext), article.text);\n\n                            if(articleConfig.recommended.length || articleConfig.dependencies.length) {\n                                // TRICKY: only non-empty config\n                                config.content[article.id] = articleConfig;\n                            }\n\n                            articleTOC['chapter'] = article.id;\n                            articleTOC['chunks'] = [\n                                'title',\n                                'sub-title',\n                                '01'\n                            ];\n                            tocMap[article.id] = articleTOC;\n                        }\n\n                        // build toc from what we see in the api\n                        let tocRegex = /\\[[^\\[\\]]*\\]\\s*\\(([^\\(\\)]*)\\)/g;\n                        let match = tocRegex.exec(data.toc);\n                        while(match != null) {\n                            let key = match[1].replace(/\\_/g, '-');\n                            let val = tocMap[key];\n                            if (val) toc.push(val);\n\n                            match = tocRegex.exec(data.toc);\n                        }\n                    } else {\n                        reject(new Error('Unsupported resource container type ' + props.resource.type));\n                        return;\n                    }\n\n                    // write config\n                    let configPath = path.join(contentDir, 'config.yml');\n                    fs.writeFileSync(configPath, new Buffer(YAML.stringify(config, 10, 2, null, null)));\n                    // write toc\n                    let tocPath = path.join(contentDir, 'toc.yml');\n                    fs.writeFileSync(tocPath, new Buffer(YAML.stringify(toc, 10, 2, null, null)));\n                } catch(err) {\n                    console.error(data);\n                    reject(err);\n                    return;\n                }\n                resolve();\n            });\n        })\n        .then(function() {\n            return loadContainer(dir);\n        })\n        .catch(function(err) {\n            // clean up after an error\n            rimraf.sync(dir);\n            return Promise.reject(err);\n        });\n}\n\n/**\n * Returns a localized chapter title. e.g. \"Chapter 1\"\n * If the language does not have a match a default localization will be used.\n *\n * If chapter_number is a number it will be parsed as an int to strip leading 0's\n *\n * @param language_slug the language into which the chapter title will be localized\n * @param chapter_number the chapter number that is being localized\n */\nfunction localizeChapterTitle(language_slug, chapter_number) {\n    var translations = {\n        'ar': 'الفصل %',\n        'en': 'Chapter %',\n        'ru': 'Глава %',\n        'hu': '%. fejezet',\n        'sr-Latin': 'Поглавље %',\n        'default': 'Chapter %'\n    };\n    var title = translations[language_slug];\n    if(!title) title = translations['default'];\n    var num = parseInt(chapter_number);\n    if (isNaN(num)) num = chapter_number;\n    return title.replace('%', num);\n}\n\n/**\n * Pads a slug to 2 significant digits.\n * Examples:\n * '1'    -> '01'\n * '001'  -> '01'\n * '12'   -> '12'\n * '123'  -> '123'\n * '0123' -> '123'\n * Words are not padded:\n * 'a' -> 'a'\n * '0word' -> '0word'\n * And as a matter of consistency:\n * '0'  -> '00'\n * '00' -> '00'\n *\n * @param slug\n * @returns {*}\n */\nfunction normalizeSlug(slug) {\n    if(typeof slug !== 'string') throw new Error('slug must be a string');\n    if(slug === '') throw new Error('slug cannot be an empty string');\n    if(isNaN(Number(slug))) return slug;\n    slug = slug.replace(/^(0+)/, '').trim();\n    while(slug.length < 2) {\n        slug = '0' + slug;\n    }\n    return slug;\n}\n\n/**\n * Retrieves the resource container type by parsing the resource container mime type.\n *\n * @param mime_type {string} a mime type value e.g. \"application/tsrc+book\". where the type is \"book\"\n * @returns {string} The resource container type\n */\nfunction mimeToType(mime_type) {\n    return mime_type.split('+')[1];\n}\n\n/**\n * Returns a resource container mime type based on the given container type.\n *\n * @param resource_type {string} the resource container type. e.g. \"book\", \"help\" etc.\n * @returns {string} The mime type. e.g. \"application/tsrc+type\"\n */\nfunction typeToMime(resource_type) {\n    return spec.base_mime_type + '+' + resource_type;\n}\n\n/**\n * Reads the resource container info without opening it.\n * This will however, work on containers that are both open and closed.\n *\n * @param container_path {string} path to the container archive or directory\n * @param opts {{}}\n * @returns {Promise.<{}>} the resource container info (package.json)\n */\nfunction inspectContainer(container_path, opts) {\n    return new Promise(function(resolve, reject) {\n            if(path.extname(container_path) !== '.' + spec.file_ext) {\n                reject(new Error('Invalid resource container file extension'));\n                return;\n            }\n            try {\n                resolve(fs.statSync(container_path).isFile());\n            } catch(err) {\n                reject(new Error('The resource container does not exist at', container_path));\n            }\n        })\n        .then(function(isFile) {\n            if(isFile) {\n                // TODO: For now we are just opening the container then closing it.\n                // Eventually it would be nice if we can inspect the archive without extracting everything.\n                let containerDir = path.join(path.dirname(container_path), path.basename(container_path, '.' + spec.file_ext));\n                return openContainer(container_path, containerDir, opts)\n                    .then(function(container) {\n                        return closeContainer(containerDir, opts)\n                            .then(function() {\n                                return Promise.resolve(container.info);\n                            });\n                    });\n            } else {\n                loadContainer(container_path)\n                    .then(function(container) {\n                        return Promise.resolve(container.info);\n                    });\n            }\n        });\n}\n\nmodule.exports = {\n    load: loadContainer,\n    make: makeContainer,\n    open: openContainer,\n    close: closeContainer,\n    tools: {\n        inspect: inspectContainer,\n        convertResource: convertResource,\n        makeSlug: containerSlug,\n        mimeToType: mimeToType,\n        typeToMime: typeToMime,\n        localizeChapterTitle: localizeChapterTitle,\n        normalizeSlug: normalizeSlug,\n        spec: spec\n    }\n};"]}