var capacitorFilesystemPluginCapacitor = (function (exports, core, synapse) { 'use strict'; exports.Directory = void 0; (function (Directory) { /** * The Documents directory. * On iOS it's the app's documents directory. * Use this directory to store user-generated content. * On Android it's the Public Documents folder, so it's accessible from other apps. * It's not accessible on Android 10 unless the app enables legacy External Storage * by adding `android:requestLegacyExternalStorage="true"` in the `application` tag * in the `AndroidManifest.xml`. * On Android 11 or newer the app can only access the files/folders the app created. * * @since 1.0.0 */ Directory["Documents"] = "DOCUMENTS"; /** * The Data directory. * On iOS it will use the Documents directory. * On Android it's the directory holding application files. * Files will be deleted when the application is uninstalled. * * @since 1.0.0 */ Directory["Data"] = "DATA"; /** * The Library directory. * On iOS it will use the Library directory. * On Android it's the directory holding application files. * Files will be deleted when the application is uninstalled. * * @since 1.1.0 */ Directory["Library"] = "LIBRARY"; /** * The Cache directory. * Can be deleted in cases of low memory, so use this directory to write app-specific files. * that your app can re-create easily. * * @since 1.0.0 */ Directory["Cache"] = "CACHE"; /** * The external directory. * On iOS it will use the Documents directory. * On Android it's the directory on the primary shared/external * storage device where the application can place persistent files it owns. * These files are internal to the applications, and not typically visible * to the user as media. * Files will be deleted when the application is uninstalled. * * @since 1.0.0 */ Directory["External"] = "EXTERNAL"; /** * The external storage directory. * On iOS it will use the Documents directory. * On Android it's the primary shared/external storage directory. * It's not accessible on Android 10 unless the app enables legacy External Storage * by adding `android:requestLegacyExternalStorage="true"` in the `application` tag * in the `AndroidManifest.xml`. * It's not accessible on Android 11 or newer. * * @since 1.0.0 */ Directory["ExternalStorage"] = "EXTERNAL_STORAGE"; /** * The external cache directory. * On iOS it will use the Documents directory. * On Android it's the primary shared/external cache. * * @since 7.1.0 */ Directory["ExternalCache"] = "EXTERNAL_CACHE"; /** * The Library directory without cloud backup. Used in iOS. * On Android it's the directory holding application files. * * @since 7.1.0 */ Directory["LibraryNoCloud"] = "LIBRARY_NO_CLOUD"; /** * A temporary directory for iOS. * On Android it's the directory holding the application cache. * * @since 7.1.0 */ Directory["Temporary"] = "TEMPORARY"; })(exports.Directory || (exports.Directory = {})); exports.Encoding = void 0; (function (Encoding) { /** * Eight-bit UCS Transformation Format * * @since 1.0.0 */ Encoding["UTF8"] = "utf8"; /** * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the * Unicode character set * This encoding is only supported on Android. * * @since 1.0.0 */ Encoding["ASCII"] = "ascii"; /** * Sixteen-bit UCS Transformation Format, byte order identified by an * optional byte-order mark * This encoding is only supported on Android. * * @since 1.0.0 */ Encoding["UTF16"] = "utf16"; })(exports.Encoding || (exports.Encoding = {})); /** * @deprecated Use `Directory`. * @since 1.0.0 */ const FilesystemDirectory = exports.Directory; /** * @deprecated Use `Encoding`. * @since 1.0.0 */ const FilesystemEncoding = exports.Encoding; const Filesystem = core.registerPlugin('Filesystem', { web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.FilesystemWeb()), }); synapse.exposeSynapse(); function resolve(path) { const posix = path.split('/').filter((item) => item !== '.'); const newPosix = []; posix.forEach((item) => { if (item === '..' && newPosix.length > 0 && newPosix[newPosix.length - 1] !== '..') { newPosix.pop(); } else { newPosix.push(item); } }); return newPosix.join('/'); } function isPathParent(parent, children) { parent = resolve(parent); children = resolve(children); const pathsA = parent.split('/'); const pathsB = children.split('/'); return parent !== children && pathsA.every((value, index) => value === pathsB[index]); } class FilesystemWeb extends core.WebPlugin { constructor() { super(...arguments); this.DB_VERSION = 1; this.DB_NAME = 'Disc'; this._writeCmds = ['add', 'put', 'delete']; /** * Function that performs a http request to a server and downloads the file to the specified destination * * @deprecated Use the @capacitor/file-transfer plugin instead. * @param options the options for the download operation * @returns a promise that resolves with the download file result */ this.downloadFile = async (options) => { var _a, _b; const requestInit = core.buildRequestInit(options, options.webFetchExtra); const response = await fetch(options.url, requestInit); let blob; if (!options.progress) blob = await response.blob(); else if (!(response === null || response === void 0 ? void 0 : response.body)) blob = new Blob(); else { const reader = response.body.getReader(); let bytes = 0; const chunks = []; const contentType = response.headers.get('content-type'); const contentLength = parseInt(response.headers.get('content-length') || '0', 10); while (true) { const { done, value } = await reader.read(); if (done) break; chunks.push(value); bytes += (value === null || value === void 0 ? void 0 : value.length) || 0; const status = { url: options.url, bytes, contentLength, }; this.notifyListeners('progress', status); } const allChunks = new Uint8Array(bytes); let position = 0; for (const chunk of chunks) { if (typeof chunk === 'undefined') continue; allChunks.set(chunk, position); position += chunk.length; } blob = new Blob([allChunks.buffer], { type: contentType || undefined }); } const result = await this.writeFile({ path: options.path, directory: (_a = options.directory) !== null && _a !== void 0 ? _a : undefined, recursive: (_b = options.recursive) !== null && _b !== void 0 ? _b : false, data: blob, }); return { path: result.uri, blob }; }; } readFileInChunks(_options, _callback) { throw this.unavailable('Method not implemented.'); } async initDb() { if (this._db !== undefined) { return this._db; } if (!('indexedDB' in window)) { throw this.unavailable("This browser doesn't support IndexedDB"); } return new Promise((resolve, reject) => { const request = indexedDB.open(this.DB_NAME, this.DB_VERSION); request.onupgradeneeded = FilesystemWeb.doUpgrade; request.onsuccess = () => { this._db = request.result; resolve(request.result); }; request.onerror = () => reject(request.error); request.onblocked = () => { console.warn('db blocked'); }; }); } static doUpgrade(event) { const eventTarget = event.target; const db = eventTarget.result; switch (event.oldVersion) { case 0: case 1: default: { if (db.objectStoreNames.contains('FileStorage')) { db.deleteObjectStore('FileStorage'); } const store = db.createObjectStore('FileStorage', { keyPath: 'path' }); store.createIndex('by_folder', 'folder'); } } } async dbRequest(cmd, args) { const readFlag = this._writeCmds.indexOf(cmd) !== -1 ? 'readwrite' : 'readonly'; return this.initDb().then((conn) => { return new Promise((resolve, reject) => { const tx = conn.transaction(['FileStorage'], readFlag); const store = tx.objectStore('FileStorage'); const req = store[cmd](...args); req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); }); } async dbIndexRequest(indexName, cmd, args) { const readFlag = this._writeCmds.indexOf(cmd) !== -1 ? 'readwrite' : 'readonly'; return this.initDb().then((conn) => { return new Promise((resolve, reject) => { const tx = conn.transaction(['FileStorage'], readFlag); const store = tx.objectStore('FileStorage'); const index = store.index(indexName); const req = index[cmd](...args); req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); }); } getPath(directory, uriPath) { const cleanedUriPath = uriPath !== undefined ? uriPath.replace(/^[/]+|[/]+$/g, '') : ''; let fsPath = ''; if (directory !== undefined) fsPath += '/' + directory; if (uriPath !== '') fsPath += '/' + cleanedUriPath; return fsPath; } async clear() { const conn = await this.initDb(); const tx = conn.transaction(['FileStorage'], 'readwrite'); const store = tx.objectStore('FileStorage'); store.clear(); } /** * Read a file from disk * @param options options for the file read * @return a promise that resolves with the read file data result */ async readFile(options) { const path = this.getPath(options.directory, options.path); // const encoding = options.encoding; const entry = (await this.dbRequest('get', [path])); if (entry === undefined) throw Error('File does not exist.'); return { data: entry.content ? entry.content : '' }; } /** * Write a file to disk in the specified location on device * @param options options for the file write * @return a promise that resolves with the file write result */ async writeFile(options) { const path = this.getPath(options.directory, options.path); let data = options.data; const encoding = options.encoding; const doRecursive = options.recursive; const occupiedEntry = (await this.dbRequest('get', [path])); if (occupiedEntry && occupiedEntry.type === 'directory') throw Error('The supplied path is a directory.'); const parentPath = path.substr(0, path.lastIndexOf('/')); const parentEntry = (await this.dbRequest('get', [parentPath])); if (parentEntry === undefined) { const subDirIndex = parentPath.indexOf('/', 1); if (subDirIndex !== -1) { const parentArgPath = parentPath.substr(subDirIndex); await this.mkdir({ path: parentArgPath, directory: options.directory, recursive: doRecursive, }); } } if (!encoding && !(data instanceof Blob)) { data = data.indexOf(',') >= 0 ? data.split(',')[1] : data; if (!this.isBase64String(data)) throw Error('The supplied data is not valid base64 content.'); } const now = Date.now(); const pathObj = { path: path, folder: parentPath, type: 'file', size: data instanceof Blob ? data.size : data.length, ctime: now, mtime: now, content: data, }; await this.dbRequest('put', [pathObj]); return { uri: pathObj.path, }; } /** * Append to a file on disk in the specified location on device * @param options options for the file append * @return a promise that resolves with the file write result */ async appendFile(options) { const path = this.getPath(options.directory, options.path); let data = options.data; const encoding = options.encoding; const parentPath = path.substr(0, path.lastIndexOf('/')); const now = Date.now(); let ctime = now; const occupiedEntry = (await this.dbRequest('get', [path])); if (occupiedEntry && occupiedEntry.type === 'directory') throw Error('The supplied path is a directory.'); const parentEntry = (await this.dbRequest('get', [parentPath])); if (parentEntry === undefined) { const subDirIndex = parentPath.indexOf('/', 1); if (subDirIndex !== -1) { const parentArgPath = parentPath.substr(subDirIndex); await this.mkdir({ path: parentArgPath, directory: options.directory, recursive: true, }); } } if (!encoding && !this.isBase64String(data)) throw Error('The supplied data is not valid base64 content.'); if (occupiedEntry !== undefined) { if (occupiedEntry.content instanceof Blob) { throw Error('The occupied entry contains a Blob object which cannot be appended to.'); } if (occupiedEntry.content !== undefined && !encoding) { data = btoa(atob(occupiedEntry.content) + atob(data)); } else { data = occupiedEntry.content + data; } ctime = occupiedEntry.ctime; } const pathObj = { path: path, folder: parentPath, type: 'file', size: data.length, ctime: ctime, mtime: now, content: data, }; await this.dbRequest('put', [pathObj]); } /** * Delete a file from disk * @param options options for the file delete * @return a promise that resolves with the deleted file data result */ async deleteFile(options) { const path = this.getPath(options.directory, options.path); const entry = (await this.dbRequest('get', [path])); if (entry === undefined) throw Error('File does not exist.'); const entries = await this.dbIndexRequest('by_folder', 'getAllKeys', [IDBKeyRange.only(path)]); if (entries.length !== 0) throw Error('Folder is not empty.'); await this.dbRequest('delete', [path]); } /** * Create a directory. * @param options options for the mkdir * @return a promise that resolves with the mkdir result */ async mkdir(options) { const path = this.getPath(options.directory, options.path); const doRecursive = options.recursive; const parentPath = path.substr(0, path.lastIndexOf('/')); const depth = (path.match(/\//g) || []).length; const parentEntry = (await this.dbRequest('get', [parentPath])); const occupiedEntry = (await this.dbRequest('get', [path])); if (depth === 1) throw Error('Cannot create Root directory'); if (occupiedEntry !== undefined) throw Error('Current directory does already exist.'); if (!doRecursive && depth !== 2 && parentEntry === undefined) throw Error('Parent directory must exist'); if (doRecursive && depth !== 2 && parentEntry === undefined) { const parentArgPath = parentPath.substr(parentPath.indexOf('/', 1)); await this.mkdir({ path: parentArgPath, directory: options.directory, recursive: doRecursive, }); } const now = Date.now(); const pathObj = { path: path, folder: parentPath, type: 'directory', size: 0, ctime: now, mtime: now, }; await this.dbRequest('put', [pathObj]); } /** * Remove a directory * @param options the options for the directory remove */ async rmdir(options) { const { path, directory, recursive } = options; const fullPath = this.getPath(directory, path); const entry = (await this.dbRequest('get', [fullPath])); if (entry === undefined) throw Error('Folder does not exist.'); if (entry.type !== 'directory') throw Error('Requested path is not a directory'); const readDirResult = await this.readdir({ path, directory }); if (readDirResult.files.length !== 0 && !recursive) throw Error('Folder is not empty'); for (const entry of readDirResult.files) { const entryPath = `${path}/${entry.name}`; const entryObj = await this.stat({ path: entryPath, directory }); if (entryObj.type === 'file') { await this.deleteFile({ path: entryPath, directory }); } else { await this.rmdir({ path: entryPath, directory, recursive }); } } await this.dbRequest('delete', [fullPath]); } /** * Return a list of files from the directory (not recursive) * @param options the options for the readdir operation * @return a promise that resolves with the readdir directory listing result */ async readdir(options) { const path = this.getPath(options.directory, options.path); const entry = (await this.dbRequest('get', [path])); if (options.path !== '' && entry === undefined) throw Error('Folder does not exist.'); const entries = await this.dbIndexRequest('by_folder', 'getAllKeys', [IDBKeyRange.only(path)]); const files = await Promise.all(entries.map(async (e) => { let subEntry = (await this.dbRequest('get', [e])); if (subEntry === undefined) { subEntry = (await this.dbRequest('get', [e + '/'])); } return { name: e.substring(path.length + 1), type: subEntry.type, size: subEntry.size, ctime: subEntry.ctime, mtime: subEntry.mtime, uri: subEntry.path, }; })); return { files: files }; } /** * Return full File URI for a path and directory * @param options the options for the stat operation * @return a promise that resolves with the file stat result */ async getUri(options) { const path = this.getPath(options.directory, options.path); let entry = (await this.dbRequest('get', [path])); if (entry === undefined) { entry = (await this.dbRequest('get', [path + '/'])); } return { uri: (entry === null || entry === void 0 ? void 0 : entry.path) || path, }; } /** * Return data about a file * @param options the options for the stat operation * @return a promise that resolves with the file stat result */ async stat(options) { const path = this.getPath(options.directory, options.path); let entry = (await this.dbRequest('get', [path])); if (entry === undefined) { entry = (await this.dbRequest('get', [path + '/'])); } if (entry === undefined) throw Error('Entry does not exist.'); return { name: entry.path.substring(path.length + 1), type: entry.type, size: entry.size, ctime: entry.ctime, mtime: entry.mtime, uri: entry.path, }; } /** * Rename a file or directory * @param options the options for the rename operation * @return a promise that resolves with the rename result */ async rename(options) { await this._copy(options, true); return; } /** * Copy a file or directory * @param options the options for the copy operation * @return a promise that resolves with the copy result */ async copy(options) { return this._copy(options, false); } async requestPermissions() { return { publicStorage: 'granted' }; } async checkPermissions() { return { publicStorage: 'granted' }; } /** * Function that can perform a copy or a rename * @param options the options for the rename operation * @param doRename whether to perform a rename or copy operation * @return a promise that resolves with the result */ async _copy(options, doRename = false) { let { toDirectory } = options; const { to, from, directory: fromDirectory } = options; if (!to || !from) { throw Error('Both to and from must be provided'); } // If no "to" directory is provided, use the "from" directory if (!toDirectory) { toDirectory = fromDirectory; } const fromPath = this.getPath(fromDirectory, from); const toPath = this.getPath(toDirectory, to); // Test that the "to" and "from" locations are different if (fromPath === toPath) { return { uri: toPath, }; } if (isPathParent(fromPath, toPath)) { throw Error('To path cannot contain the from path'); } // Check the state of the "to" location let toObj; try { toObj = await this.stat({ path: to, directory: toDirectory, }); } catch (e) { // To location does not exist, ensure the directory containing "to" location exists and is a directory const toPathComponents = to.split('/'); toPathComponents.pop(); const toPath = toPathComponents.join('/'); // Check the containing directory of the "to" location exists if (toPathComponents.length > 0) { const toParentDirectory = await this.stat({ path: toPath, directory: toDirectory, }); if (toParentDirectory.type !== 'directory') { throw new Error('Parent directory of the to path is a file'); } } } // Cannot overwrite a directory if (toObj && toObj.type === 'directory') { throw new Error('Cannot overwrite a directory with a file'); } // Ensure the "from" object exists const fromObj = await this.stat({ path: from, directory: fromDirectory, }); // Set the mtime/ctime of the supplied path const updateTime = async (path, ctime, mtime) => { const fullPath = this.getPath(toDirectory, path); const entry = (await this.dbRequest('get', [fullPath])); entry.ctime = ctime; entry.mtime = mtime; await this.dbRequest('put', [entry]); }; const ctime = fromObj.ctime ? fromObj.ctime : Date.now(); switch (fromObj.type) { // The "from" object is a file case 'file': { // Read the file const file = await this.readFile({ path: from, directory: fromDirectory, }); // Optionally remove the file if (doRename) { await this.deleteFile({ path: from, directory: fromDirectory, }); } let encoding; if (!(file.data instanceof Blob) && !this.isBase64String(file.data)) { encoding = exports.Encoding.UTF8; } // Write the file to the new location const writeResult = await this.writeFile({ path: to, directory: toDirectory, data: file.data, encoding: encoding, }); // Copy the mtime/ctime of a renamed file if (doRename) { await updateTime(to, ctime, fromObj.mtime); } // Resolve promise return writeResult; } case 'directory': { if (toObj) { throw Error('Cannot move a directory over an existing object'); } try { // Create the to directory await this.mkdir({ path: to, directory: toDirectory, recursive: false, }); // Copy the mtime/ctime of a renamed directory if (doRename) { await updateTime(to, ctime, fromObj.mtime); } } catch (e) { // ignore } // Iterate over the contents of the from location const contents = (await this.readdir({ path: from, directory: fromDirectory, })).files; for (const filename of contents) { // Move item from the from directory to the to directory await this._copy({ from: `${from}/${filename.name}`, to: `${to}/${filename.name}`, directory: fromDirectory, toDirectory, }, doRename); } // Optionally remove the original from directory if (doRename) { await this.rmdir({ path: from, directory: fromDirectory, }); } } } return { uri: toPath, }; } isBase64String(str) { try { return btoa(atob(str)) == str; } catch (err) { return false; } } } FilesystemWeb._debug = true; var web = /*#__PURE__*/Object.freeze({ __proto__: null, FilesystemWeb: FilesystemWeb }); exports.Filesystem = Filesystem; exports.FilesystemDirectory = FilesystemDirectory; exports.FilesystemEncoding = FilesystemEncoding; Object.defineProperty(exports, '__esModule', { value: true }); return exports; })({}, capacitorExports, synapse); //# sourceMappingURL=plugin.js.map