From 074793f7c9ea3dec7cd3b759de1d94dca0e1e82d Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:39:24 +1000 Subject: [PATCH 01/15] Add Worker All existing asyncronouse methods is `file-system` are dispatched to this Worker. --- .../file-system/file-system-worker/worker.ts | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 tns-core-modules/file-system/file-system-worker/worker.ts diff --git a/tns-core-modules/file-system/file-system-worker/worker.ts b/tns-core-modules/file-system/file-system-worker/worker.ts new file mode 100644 index 0000000000..7318ed7ddd --- /dev/null +++ b/tns-core-modules/file-system/file-system-worker/worker.ts @@ -0,0 +1,215 @@ +import "globals"; +import { FileSystemAccess } from "tns-core-modules/file-system/file-system-access"; +import { FSWorker, WorkerTimer } from "./components"; + +namespace Worker { + declare let self: any; + declare let fileAccess: FileSystemAccess; + + export const Jobs: string[] = []; + export const context: FSWorker.Context = self; + export const Timer = new WorkerTimer(context.close); + + export function postResult(id, message) { + Jobs.splice(Jobs.indexOf(id), 1); + context.postMessage({ id, message }); + if (Jobs.length < 1) this.Timer.startTimer(); + } + + export function onError(error: T): void { + if (error instanceof Error) throw error; + if (typeof error === "string") throw new Error(error); + } + + export function throwWorkerError(id: string, error: Error) { + try { + Jobs.splice(Jobs.indexOf(id), 1); + throw JSON.stringify({ id, message: error.message }); + } finally { + if (Jobs.length < 1) this.Timer.startTimer(); + } + } + + export function performWork(work) { + switch (work.task) { + case FSWorker.WorkTask.closeWorker: + context.close(); + break; + case FSWorker.WorkTask.turnOffActivityObserver: + this.Timer.stopObserving(); + this.postResult(work.id, "Stopped observing"); + break; + } + } + + export function getFileAccess() { + if (!(this.fileAccess instanceof FileSystemAccess)) { + this.fileAccess = new FileSystemAccess(); + } + return this.fileAccess; + } +} + +namespace EntityWorker { + export function performWork(work) { + try { + let result; + switch (work.task) { + case FSWorker.WorkTask.removeEntity: + result = removeEntity(work.object); + break; + case FSWorker.WorkTask.renameEntity: + result = renameEntity(work.object); + break; + default: + throw new Error(`Unrecognised task ${work.task}`); + } + Worker.postResult(work.id, result); + } catch (error) { + Worker.throwWorkerError(work.id, error); + } + } + + function removeEntity(object: { path: string; isFile: boolean }) { + object.isFile + ? Worker.getFileAccess().deleteFile(object.path, Worker.onError) + : Worker.getFileAccess().deleteFolder(object.path, Worker.onError); + return `${object.isFile ? "File" : "Folder"} removed.`; + } + + function renameEntity(object: { + name: string; + path: string; + parentPath: string; + }) { + const path = Worker.getFileAccess().joinPath( + object.parentPath, + object.name + ); + const extension = Worker.getFileAccess().getFileExtension(path); + Worker.getFileAccess().rename(object.path, path, Worker.onError); + return { name: object.name, path, extension }; + } +} + +namespace FileWorker { + export function performWork(work) { + try { + let result; + switch (work.task) { + case FSWorker.WorkTask.readText: + result = readText(work.object); + break; + case FSWorker.WorkTask.writeText: + result = writeText(work.object); + break; + default: + throw new Error(`Unrecognised task ${work.task}`); + } + Worker.postResult(work.id, result); + } catch (error) { + Worker.throwWorkerError(work.id, error); + } + } + + function readText(object: { path: string; encoding?: string }) { + const fileAccess = Worker.getFileAccess(); + const content = object.encoding + ? fileAccess.readText(object.path, Worker.onError, object.encoding) + : fileAccess.readText(object.path, Worker.onError); + return content; + } + + function writeText(object: { + path: string; + content: string; + encoding?: string; + }) { + const fileAccess = Worker.getFileAccess(); + !object.encoding + ? fileAccess.writeText(object.path, object.content, Worker.onError) + : fileAccess.writeText( + object.path, + object.content, + Worker.onError, + object.encoding + ); + return "Content written to file."; + } +} + +namespace FolderWorker { + export function performWork(work) { + try { + let result; + switch (work.task) { + case FSWorker.WorkTask.clearFolder: + result = clearFolder(work.object); + break; + case FSWorker.WorkTask.getEntities: + result = getEntities(work.object); + break; + default: + throw new Error(`Unrecognised task ${work.task}`); + } + Worker.postResult(work.id, result); + } catch (error) { + Worker.throwWorkerError(work.id, error); + } + } + + function clearFolder(object: { path: string }) { + Worker.getFileAccess().emptyFolder(object.path, Worker.onError); + return "Folder emptied"; + } + + function getEntities(object: { path: string }) { + let i; + const entities = []; + const fileInfos = Worker.getFileAccess().getEntities( + object.path, + Worker.onError + ); + if (!fileInfos) throw new Error("Path has no entities"); + for (i = 0; i < fileInfos.length; i++) { + if (fileInfos[i].extension) { + entities.push(fileInfos[i]); + } else { + entities.push(fileInfos[i]); + } + } + + return entities; + } +} + +Worker.context.onmessage = function(message) { + const work = message.data; + Worker.Timer.stopTimer(); + Worker.Jobs.push(work.id); + switch (work.type) { + case FSWorker.WorkType.entity: + EntityWorker.performWork(work); + break; + case FSWorker.WorkType.file: + FileWorker.performWork(work); + break; + case FSWorker.WorkType.folder: + FolderWorker.performWork(work); + break; + case FSWorker.WorkType.worker: + Worker.performWork(work); + break; + } +}; + +Worker.context.onclose = function() { + const jobId = Worker.Jobs.find(id => FSWorker.State.closed === id); + if (jobId) Worker.postResult(jobId, "Worker closed"); + if (Worker.Timer.becameInactive) { + Worker.context.postMessage({ + id: FSWorker.State.inactive, + message: "Worker did become inactive" + }); + } +}; From 3a5f8bcc6a8f1b089f76d704683bc4173e4343f8 Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:40:49 +1000 Subject: [PATCH 02/15] Add Worker components Components for the `file system` Worker. --- .../file-system-worker/components.ts | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 tns-core-modules/file-system/file-system-worker/components.ts diff --git a/tns-core-modules/file-system/file-system-worker/components.ts b/tns-core-modules/file-system/file-system-worker/components.ts new file mode 100644 index 0000000000..d19f54a667 --- /dev/null +++ b/tns-core-modules/file-system/file-system-worker/components.ts @@ -0,0 +1,192 @@ +import { on, off, lowMemoryEvent } from "tns-core-modules/application"; + +export namespace FSWorker { + export interface Context extends Worker { + close: { (): void }; + onclose: { (): void }; + } + + export interface FSJob { + id: string; + onError: { (error: any): void }; + onSuccess: { (message: any): void }; + } + + export enum WorkType { + file, + folder, + entity, + worker + } + + export enum WorkTask { + readText, + writeText, + clearFolder, + getEntities, + removeEntity, + renameEntity, + closeWorker, + turnOffActivityObserver + } + + export enum State { + inactive = "WorkerBecameInactive", + closed = "WorkerCloseRequestFulfilled" + } + + export let fileWorker: Instance; + export const isRunning = (): boolean => fileWorker instanceof Instance; + export const getWorkerInstance = (): Instance => fileWorker; + export const createWorkerInstance = (): Instance => + (fileWorker = new Instance()); + + export const onMemoryEvent = (callback, thisArg) => + on(lowMemoryEvent, callback, thisArg); + export const offMemoryEvent = (callback, thisArg) => + off(lowMemoryEvent, callback, thisArg); + + export class Job { + id: string; + object: any; + task: WorkTask; + type: WorkType; + + constructor(type: WorkType, task: WorkTask, object?: any) { + this.id = `${type}-${task}-${Date.now()}`; + this.object = object; + this.task = task; + this.type = type; + } + } + + export class Instance { + private _worker: Worker; + private _jobs: FSJob[]; + + constructor() { + this._jobs = []; + this._worker = new Worker("./worker"); + this._worker.onerror = error => { + const data = JSON.parse(error.message); + this.getJob(data.id).onError(new Error(data.message)); + }; + this._worker.onmessage = message => { + const data = message.data; + switch (data.id) { + case "WorkerCloseRequestFulfilled": + this.onWorkerClosed(); + break; + case "WorkerBecameInactive": + this.onWorkerClosed(); + break; + default: + this.getJob(data.id).onSuccess(data.message); + } + }; + FSWorker.onMemoryEvent(this.closeWorker, this); + } + + public get jobs() { + return this._jobs; + } + + public getJob(id: string): FSJob { + const job = this.jobs.find(job => job.id === id); + this.jobs.splice(this.jobs.indexOf(job), 1); + return job; + } + + public postJob(job: Job, onSuccess, onError): void { + this.jobs.push({ id: job.id, onSuccess, onError }); + this._worker.postMessage(job); + } + + keepAlive(callback: { (message: string): void } = (): void => {}) { + this.postJob( + new Job(WorkType.worker, WorkTask.turnOffActivityObserver), + callback, + callback + ); + } + + terminate() { + this.keepAlive(() => { + this._worker.terminate(); + this.onWorkerClosed(); + }); + } + + closeWorker() { + this._worker.postMessage({ + id: State.closed, + type: WorkType.worker, + task: WorkTask.closeWorker + }); + } + + onWorkerClosed() { + FSWorker.offMemoryEvent(this.closeWorker, this); + this.jobs.splice(0, this.jobs.length - 1); + fileWorker = null; + } + } +} + +export class WorkerTimer { + private _id: any; + private _time: number; + private _duration: number; + private _observing: boolean; + private _onEnd: { (): void }; + private _becameInactive: boolean; + + constructor(onEnd) { + this._becameInactive = false; + this._duration = 120000; + this._observing = true; + this._onEnd = onEnd; + this._time = 0; + + this.startTimer(); + } + + private _incrementTime() { + this._time += 1000; + if (this._time >= this._duration) { + this.stopTimer(); + this._becameInactive = true; + this._onEnd(); + } + } + + private get _isRunning() { + return typeof this._id === "number"; + } + + private get _isObserving() { + return this._observing; + } + + public get becameInactive() { + return this._becameInactive; + } + + public stopObserving() { + this.stopTimer(); + this._observing = false; + } + + public startTimer() { + if (this._isObserving && !this._isRunning) { + this._id = setInterval(() => this._incrementTime(), 1000); + } + } + + public stopTimer() { + if (this._isObserving && this._isRunning) { + clearInterval(this._id); + this._id = null; + } + } +} From f03ef6ffaa3392676d1fb968da6202475631f101 Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:47:13 +1000 Subject: [PATCH 03/15] Add FSWorker Import the FSWorker object from components. --- tns-core-modules/file-system/file-system.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tns-core-modules/file-system/file-system.ts b/tns-core-modules/file-system/file-system.ts index aa1209716b..d3aeda2493 100644 --- a/tns-core-modules/file-system/file-system.ts +++ b/tns-core-modules/file-system/file-system.ts @@ -1,6 +1,6 @@ // imported for definition purposes only import * as platformModule from "../platform"; - +import { FSWorker } from "./file-system-worker/components"; import { FileSystemAccess } from "./file-system-access"; import { profile } from "../profiling"; From d3e9d96c76585a783af0ccef900b3549f36deddc Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:49:15 +1000 Subject: [PATCH 04/15] Refactor Move globally declared, but local file-system methods and variables to a _FileSystem object. --- tns-core-modules/file-system/file-system.ts | 43 +++++++++++++-------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/tns-core-modules/file-system/file-system.ts b/tns-core-modules/file-system/file-system.ts index d3aeda2493..3b2e6b19ef 100644 --- a/tns-core-modules/file-system/file-system.ts +++ b/tns-core-modules/file-system/file-system.ts @@ -4,41 +4,51 @@ import { FSWorker } from "./file-system-worker/components"; import { FileSystemAccess } from "./file-system-access"; import { profile } from "../profiling"; -// The FileSystemAccess implementation, used through all the APIs. -let fileAccess: FileSystemAccess; -function getFileAccess(): FileSystemAccess { - if (!fileAccess) { - fileAccess = new FileSystemAccess(); - } +namespace _FileSystem { + let fileAccess: FileSystemAccess; + let platform: typeof platformModule; + + export function postJob(job: FSWorker.Job, resolve, reject): void { + if (!FSWorker.isRunning()) FSWorker.createWorkerInstance(); + FSWorker.getWorkerInstance().postJob(job, resolve, reject); + } + export function getFileAccess() { + if (!(fileAccess instanceof FileSystemAccess)) { + fileAccess = new FileSystemAccess(); + } return fileAccess; -} + } -let platform: typeof platformModule; -function ensurePlatform() { + export function getPlatform() { if (!platform) { - platform = require("../platform"); + platform = require("../platfrom"); } -} + return platform; + } -function createFile(info: { path: string; name: string; extension: string }) { + export function createFile(info: { + path: string; + name: string; + extension: string; + }) { const file = new File(); file._path = info.path; file._name = info.name; file._extension = info.extension; return file; -} + } -function createFolder(info: { path: string; name: string; }) { + export function createFolder(info: { path: string; name: string }) { const documents = knownFolders.documents(); if (info.path === documents.path) { - return documents; + return documents; } const temp = knownFolders.temp(); if (info.path === temp.path) { - return temp; + return temp; } const folder = new Folder(); @@ -47,6 +57,7 @@ function createFolder(info: { path: string; name: string; }) { folder._name = info.name; return folder; + } } export class FileSystemEntity { From 3e47d83141a5c13718fb77bb722b78b3abe6caaf Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:52:23 +1000 Subject: [PATCH 05/15] Refactor Update all methods using asynchronous methods to run on Worker thread. And call local methods from new _FileSystem object. --- tns-core-modules/file-system/file-system.ts | 1066 ++++++++++--------- 1 file changed, 581 insertions(+), 485 deletions(-) diff --git a/tns-core-modules/file-system/file-system.ts b/tns-core-modules/file-system/file-system.ts index 3b2e6b19ef..7cdae4236b 100644 --- a/tns-core-modules/file-system/file-system.ts +++ b/tns-core-modules/file-system/file-system.ts @@ -61,625 +61,721 @@ namespace _FileSystem { } export class FileSystemEntity { - _path: string; - _name: string; - _extension: string; - _locked: boolean; - _lastModified: Date; - _isKnown: boolean; - - get parent(): Folder { - const onError = function (error) { - throw error; - }; - - const folderInfo = getFileAccess().getParent(this.path, onError); - if (!folderInfo) { - return undefined; - } + _path: string; + _name: string; + _extension: string; + _locked: boolean; + _lastModified: Date; + _isKnown: boolean; + + get parent(): Folder { + const onError = function(error) { + throw error; + }; + + const folderInfo = _FileSystem + .getFileAccess() + .getParent(this.path, onError); + if (!folderInfo) { + return undefined; + } + + return _FileSystem.createFolder(folderInfo); + } - return createFolder(folderInfo); - } + public remove(): Promise { + return new Promise((resolve, reject) => { + this._isKnown + ? reject(new Error("Cannot delete known folder.")) + : _FileSystem.postJob( + new FSWorker.Job( + FSWorker.WorkType.entity, + FSWorker.WorkTask.removeEntity, + { path: this.path, isFile: this instanceof File } + ), + resolve, + reject + ); + }); + } - public remove(): Promise { - return new Promise((resolve, reject) => { - let hasError = false; - const localError = function (error: any) { - hasError = true; - reject(error); - }; + public removeSync(onError?: (error: any) => any): void { + if (this._isKnown) { + if (onError) { + onError({ message: "Cannot delete known folder." }); + } - this.removeSync(localError); - if (!hasError) { - resolve(); - } - }); + return; } - public removeSync(onError?: (error: any) => any): void { - if (this._isKnown) { - if (onError) { - onError({ message: "Cannot delete known folder." }); - } - - return; - } - - const fileAccess = getFileAccess(); + const fileAccess = _FileSystem.getFileAccess(); - if (this instanceof File) { - fileAccess.deleteFile(this.path, onError); - } else if (this instanceof Folder) { - fileAccess.deleteFolder(this.path, onError); - } + if (this instanceof File) { + fileAccess.deleteFile(this.path, onError); + } else if (this instanceof Folder) { + fileAccess.deleteFolder(this.path, onError); } + } - public rename(newName: string): Promise { - return new Promise((resolve, reject) => { - let hasError = false; - const localError = function (error) { - hasError = true; - reject(error); - }; + public rename(newName: string): Promise { + return new Promise((resolve, reject) => { + if (this._isKnown || !this.parent) { + reject( + new Error( + this._isKnown ? "Cannot delete known folder" : "No parent folder" + ) + ); + } + + _FileSystem.postJob( + new FSWorker.Job( + FSWorker.WorkType.entity, + FSWorker.WorkTask.renameEntity, + { + name: name, + path: this.path, + parentPath: this.parent._path + } + ), + (result: { name: string; path: string; extension: string }) => { + this._name = result.name; + this._path = result.path; + if (this instanceof File) { + this._extension = result.extension; + } + resolve(result); + }, + reject + ); + }); + } - this.renameSync(newName, localError); + public renameSync(newName: string, onError?: (error: any) => any): void { + if (this._isKnown) { + if (onError) { + onError(new Error("Cannot rename known folder.")); + } - if (!hasError) { - resolve(); - } - }); + return; } - public renameSync(newName: string, onError?: (error: any) => any): void { - if (this._isKnown) { - if (onError) { - onError(new Error("Cannot rename known folder.")); - } - - return; - } - - const parentFolder = this.parent; - if (!parentFolder) { - if (onError) { - onError(new Error("No parent folder.")); - } - - return; - } + const parentFolder = this.parent; + if (!parentFolder) { + if (onError) { + onError(new Error("No parent folder.")); + } - const fileAccess = getFileAccess(); - const path = parentFolder.path; - const newPath = fileAccess.joinPath(path, newName); + return; + } - const localError = function (error) { - if (onError) { - onError(error); - } + const fileAccess = _FileSystem.getFileAccess(); + const path = parentFolder.path; + const newPath = fileAccess.joinPath(path, newName); - return null; - }; + const localError = function(error) { + if (onError) { + onError(error); + } - fileAccess.rename(this.path, newPath, localError); - this._path = newPath; - this._name = newName; + return null; + }; - if (this instanceof File) { - this._extension = fileAccess.getFileExtension(newPath); - } - } + fileAccess.rename(this.path, newPath, localError); + this._path = newPath; + this._name = newName; - get name(): string { - return this._name; + if (this instanceof File) { + this._extension = fileAccess.getFileExtension(newPath); } + } - get path(): string { - return this._path; - } + get name(): string { + return this._name; + } - get lastModified(): Date { - let value = this._lastModified; - if (!this._lastModified) { - value = this._lastModified = getFileAccess().getLastModified(this.path); - } + get path(): string { + return this._path; + } - return value; + get lastModified(): Date { + let value = this._lastModified; + if (!this._lastModified) { + value = this._lastModified = _FileSystem + .getFileAccess() + .getLastModified(this.path); } + + return value; + } } export class File extends FileSystemEntity { - public static fromPath(path: string) { - const onError = function (error) { - throw error; - }; + public static fromPath(path: string) { + const onError = function(error) { + throw error; + }; - const fileInfo = getFileAccess().getFile(path, onError); - if (!fileInfo) { - return undefined; - } - - return createFile(fileInfo); + const fileInfo = _FileSystem.getFileAccess().getFile(path, onError); + if (!fileInfo) { + return undefined; } - public static exists(path: string): boolean { - return getFileAccess().fileExists(path); - } - - get extension(): string { - return this._extension; - } + return _FileSystem.createFile(fileInfo); + } - get isLocked(): boolean { - // !! is a boolean conversion/cast, handling undefined as well - return !!this._locked; - } + public static exists(path: string): boolean { + return _FileSystem.getFileAccess().fileExists(path); + } - get size(): number { - return getFileAccess().getFileSize(this.path); - } + get extension(): string { + return this._extension; + } - public readSync(onError?: (error: any) => any): any { - this.checkAccess(); + get isLocked(): boolean { + // !! is a boolean conversion/cast, handling undefined as well + return !!this._locked; + } - this._locked = true; + get size(): number { + return _FileSystem.getFileAccess().getFileSize(this.path); + } - const that = this; - const localError = (error) => { - that._locked = false; - if (onError) { - onError(error); - } - }; + public readSync(onError?: (error: any) => any): any { + this.checkAccess(); - const content = getFileAccess().read(this.path, localError); + this._locked = true; - this._locked = false; + const that = this; + const localError = error => { + that._locked = false; + if (onError) { + onError(error); + } + }; - return content; + const content = _FileSystem.getFileAccess().read(this.path, localError); - } + this._locked = false; - public writeSync(content: any, onError?: (error: any) => any): void { - this.checkAccess(); + return content; + } - try { - this._locked = true; + public writeSync(content: any, onError?: (error: any) => any): void { + this.checkAccess(); - const that = this; - const localError = function (error) { - that._locked = false; - if (onError) { - onError(error); - } - }; + try { + this._locked = true; - getFileAccess().write(this.path, content, localError); - } finally { - this._locked = false; + const that = this; + const localError = function(error) { + that._locked = false; + if (onError) { + onError(error); } - } + }; - public readText(encoding?: string): Promise { - return new Promise((resolve, reject) => { - let hasError = false; - const localError = (error) => { - hasError = true; - reject(error); - }; - - const content = this.readTextSync(localError, encoding); - if (!hasError) { - resolve(content); - } - }); + _FileSystem.getFileAccess().write(this.path, content, localError); + } finally { + this._locked = false; } + } - @profile - public readTextSync(onError?: (error: any) => any, encoding?: string): string { + public readText(encoding?: string): Promise { + return new Promise((resolve, reject) => { + try { this.checkAccess(); - this._locked = true; - - const that = this; - const localError = (error) => { - that._locked = false; - if (onError) { - onError(error); - } + const onResult = (result: any) => { + this._locked = false; + result instanceof Error ? reject(result) : resolve(result); }; + _FileSystem.postJob( + new FSWorker.Job(FSWorker.WorkType.file, FSWorker.WorkTask.readText, { + path: this.path, + encoding: encoding + }), + onResult, + onResult + ); + } catch (error) { + reject(error); + } + }); + } - const content = getFileAccess().readText(this.path, localError, encoding); - this._locked = false; - - return content; - } + @profile + public readTextSync( + onError?: (error: any) => any, + encoding?: string + ): string { + this.checkAccess(); + + this._locked = true; + + const that = this; + const localError = error => { + that._locked = false; + if (onError) { + onError(error); + } + }; + + const content = _FileSystem + .getFileAccess() + .readText(this.path, localError, encoding); + this._locked = false; + + return content; + } - public writeText(content: string, encoding?: string): Promise { - return new Promise((resolve, reject) => { - let hasError = false; - const localError = function (error) { - hasError = true; - reject(error); - }; + public writeText(content: string, encoding?: string): Promise { + return new Promise((resolve, reject) => { + try { + this.checkAccess(); + this._locked = true; + const onResult = (result: any) => { + this._locked = false; + result instanceof Error ? reject(result) : resolve(result); + }; - this.writeTextSync(content, localError, encoding); - if (!hasError) { - resolve(); + _FileSystem.postJob( + new FSWorker.Job( + FSWorker.WorkType.file, + FSWorker.WorkTask.writeText, + { + content, + encoding, + path: this.path } - }); - } - - public writeTextSync(content: string, onError?: (error: any) => any, encoding?: string): void { - this.checkAccess(); + ), + onResult, + onResult + ); + } catch (error) { + reject(error); + } + }); + } - try { - this._locked = true; - - const that = this; - const localError = function (error) { - that._locked = false; - if (onError) { - onError(error); - } - }; - - // TODO: Asyncronous - getFileAccess().writeText(this.path, content, localError, encoding); - } finally { - this._locked = false; + public writeTextSync( + content: string, + onError?: (error: any) => any, + encoding?: string + ): void { + this.checkAccess(); + + try { + this._locked = true; + + const that = this; + const localError = function(error) { + that._locked = false; + if (onError) { + onError(error); } + }; + + // TODO: Asyncronous + _FileSystem + .getFileAccess() + .writeText(this.path, content, localError, encoding); + } finally { + this._locked = false; } + } - private checkAccess() { - if (this.isLocked) { - throw new Error("Cannot access a locked file."); - } + private checkAccess() { + if (this.isLocked) { + throw new Error("Cannot access a locked file."); } + } } export class Folder extends FileSystemEntity { - public static fromPath(path: string): Folder { - const onError = function (error) { - throw error; - }; - - const folderInfo = getFileAccess().getFolder(path, onError); - if (!folderInfo) { - return undefined; - } + public static fromPath(path: string): Folder { + const onError = function(error) { + throw error; + }; - return createFolder(folderInfo); + const folderInfo = _FileSystem.getFileAccess().getFolder(path, onError); + if (!folderInfo) { + return undefined; } - public static exists(path: string): boolean { - return getFileAccess().folderExists(path); - } + return _FileSystem.createFolder(folderInfo); + } - public contains(name: string): boolean { - const fileAccess = getFileAccess(); - const path = fileAccess.joinPath(this.path, name); + public static exists(path: string): boolean { + return _FileSystem.getFileAccess().folderExists(path); + } - if (fileAccess.fileExists(path)) { - return true; - } + public contains(name: string): boolean { + const fileAccess = _FileSystem.getFileAccess(); + const path = fileAccess.joinPath(this.path, name); - return fileAccess.folderExists(path); + if (fileAccess.fileExists(path)) { + return true; } - public clear(): Promise { - return new Promise((resolve, reject) => { - let hasError = false; - const onError = function (error) { - hasError = true; - reject(error); - }; - - this.clearSync(onError); - if (!hasError) { - resolve(); - } - }); - } + return fileAccess.folderExists(path); + } - public clearSync(onError?: (error: any) => void): void { - getFileAccess().emptyFolder(this.path, onError); - } + public clear(): Promise { + return new Promise((resolve, reject) => { + _FileSystem.postJob( + new FSWorker.Job( + FSWorker.WorkType.folder, + FSWorker.WorkTask.clearFolder, + { path: this.path } + ), + resolve, + reject + ); + }); + } - get isKnown(): boolean { - return this._isKnown; - } + public clearSync(onError?: (error: any) => void): void { + _FileSystem.getFileAccess().emptyFolder(this.path, onError); + } - public getFile(name: string): File { - const fileAccess = getFileAccess(); - const path = fileAccess.joinPath(this.path, name); + get isKnown(): boolean { + return this._isKnown; + } - const onError = function (error) { - throw error; - }; + public getFile(name: string): File { + const fileAccess = _FileSystem.getFileAccess(); + const path = fileAccess.joinPath(this.path, name); - const fileInfo = fileAccess.getFile(path, onError); - if (!fileInfo) { - return undefined; - } + const onError = function(error) { + throw error; + }; - return createFile(fileInfo); + const fileInfo = fileAccess.getFile(path, onError); + if (!fileInfo) { + return undefined; } - public getFolder(name: string): Folder { - const fileAccess = getFileAccess(); - const path = fileAccess.joinPath(this.path, name); + return _FileSystem.createFile(fileInfo); + } - const onError = function (error) { - throw error; - }; + public getFolder(name: string): Folder { + const fileAccess = _FileSystem.getFileAccess(); + const path = fileAccess.joinPath(this.path, name); - const folderInfo = fileAccess.getFolder(path, onError); - if (!folderInfo) { - return undefined; - } + const onError = function(error) { + throw error; + }; - return createFolder(folderInfo); + const folderInfo = fileAccess.getFolder(path, onError); + if (!folderInfo) { + return undefined; } - public getEntities(): Promise> { - return new Promise((resolve, reject) => { - let hasError = false; - const localError = function (error) { - hasError = true; - reject(error); - }; + return _FileSystem.createFolder(folderInfo); + } - const entities = this.getEntitiesSync(localError); - if (!hasError) { - resolve(entities); - } - }); + public getEntities(): Promise> { + return new Promise((resolve, reject) => { + _FileSystem.postJob( + new FSWorker.Job( + FSWorker.WorkType.folder, + FSWorker.WorkTask.getEntities, + { path: this.path } + ), + entities => { + const _entities = entities.map( + entity => + entity.extension + ? _FileSystem.createFile(entity) + : _FileSystem.createFolder(entity) + ); + resolve(_entities); + }, + reject + ); + }); + } + + public getEntitiesSync( + onError?: (error: any) => any + ): Array { + const fileInfos = _FileSystem + .getFileAccess() + .getEntities(this.path, onError); + if (!fileInfos) { + return null; } - public getEntitiesSync(onError?: (error: any) => any): Array { - const fileInfos = getFileAccess().getEntities(this.path, onError); - if (!fileInfos) { - return null; - } + const entities = new Array(); + for (let i = 0; i < fileInfos.length; i++) { + if (fileInfos[i].extension) { + entities.push(_FileSystem.createFile(fileInfos[i])); + } else { + entities.push(_FileSystem.createFolder(fileInfos[i])); + } + } - const entities = new Array(); - for (let i = 0; i < fileInfos.length; i++) { - if (fileInfos[i].extension) { - entities.push(createFile(fileInfos[i])); - } else { - entities.push(createFolder(fileInfos[i])); - } - } + return entities; + } - return entities; + public eachEntity(onEntity: (entity: FileSystemEntity) => boolean) { + if (!onEntity) { + return; } - public eachEntity(onEntity: (entity: FileSystemEntity) => boolean) { - if (!onEntity) { - return; - } - - const onSuccess = function (fileInfo: { path: string; name: string; extension: string }): boolean { - let entity; - if (fileInfo.extension) { - entity = createFile(fileInfo); - } else { - entity = createFolder(fileInfo); - } + const onSuccess = function(fileInfo: { + path: string; + name: string; + extension: string; + }): boolean { + let entity; + if (fileInfo.extension) { + entity = _FileSystem.createFile(fileInfo); + } else { + entity = _FileSystem.createFolder(fileInfo); + } - return onEntity(entity); - }; + return onEntity(entity); + }; - const onError = function (error) { - throw error; - }; + const onError = function(error) { + throw error; + }; - getFileAccess().eachEntity(this.path, onSuccess, onError); - } + _FileSystem.getFileAccess().eachEntity(this.path, onSuccess, onError); + } } -export module knownFolders { - let _documents: Folder; - let _temp: Folder; - let _app: Folder; - - export function documents(): Folder { - if (!_documents) { - const path = getFileAccess().getDocumentsFolderPath(); - _documents = new Folder(); - _documents._path = path; - _documents._isKnown = true; - } +export namespace knownFolders { + let _documents: Folder; + let _temp: Folder; + let _app: Folder; - return _documents; + export function documents(): Folder { + if (!_documents) { + const path = _FileSystem.getFileAccess().getDocumentsFolderPath(); + _documents = new Folder(); + _documents._path = path; + _documents._isKnown = true; } - export function temp(): Folder { - if (!_temp) { - const path = getFileAccess().getTempFolderPath(); - _temp = new Folder(); - _temp._path = path; - _temp._isKnown = true; - } + return _documents; + } - return _temp; + export function temp(): Folder { + if (!_temp) { + const path = _FileSystem.getFileAccess().getTempFolderPath(); + _temp = new Folder(); + _temp._path = path; + _temp._isKnown = true; } - export function currentApp(): Folder { - if (!_app) { - const path = getFileAccess().getCurrentAppPath(); - _app = new Folder(); - _app._path = path; - _app._isKnown = true; - } + return _temp; + } - return _app; + export function currentApp(): Folder { + if (!_app) { + const path = _FileSystem.getFileAccess().getCurrentAppPath(); + _app = new Folder(); + _app._path = path; + _app._isKnown = true; } - export module ios { - function _checkPlatform(knownFolderName: string) { - ensurePlatform(); - if (!platform.isIOS) { - throw new Error(`The "${knownFolderName}" known folder is available on iOS only!`); - } - } - - let _library: Folder; - export function library(): Folder { - _checkPlatform("library"); - if (!_library) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.LibraryDirectory); - - if (existingFolderInfo) { - _library = existingFolderInfo.folder; - _library._path = existingFolderInfo.path; - _library._isKnown = true; - } - } + return _app; + } - return _library; + export namespace ios { + function _checkPlatform(knownFolderName: string) { + if (_FileSystem.getPlatform().isAndroid) { + throw new Error( + `The "${knownFolderName}" known folder is available on iOS only!` + ); + } + } + + let _library: Folder; + export function library(): Folder { + _checkPlatform("library"); + if (!_library) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.LibraryDirectory + ); + + if (existingFolderInfo) { + _library = existingFolderInfo.folder; + _library._path = existingFolderInfo.path; + _library._isKnown = true; } + } - let _developer: Folder; - export function developer(): Folder { - _checkPlatform("developer"); - if (!_developer) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.DeveloperDirectory); - - if (existingFolderInfo) { - _developer = existingFolderInfo.folder; - _developer._path = existingFolderInfo.path; - _developer._isKnown = true; - } - } + return _library; + } + + let _developer: Folder; + export function developer(): Folder { + _checkPlatform("developer"); + if (!_developer) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.DeveloperDirectory + ); - return _developer; + if (existingFolderInfo) { + _developer = existingFolderInfo.folder; + _developer._path = existingFolderInfo.path; + _developer._isKnown = true; } + } - let _desktop: Folder; - export function desktop(): Folder { - _checkPlatform("desktop"); - if (!_desktop) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.DesktopDirectory); - - if (existingFolderInfo) { - _desktop = existingFolderInfo.folder; - _desktop._path = existingFolderInfo.path; - _desktop._isKnown = true; - } - } + return _developer; + } + + let _desktop: Folder; + export function desktop(): Folder { + _checkPlatform("desktop"); + if (!_desktop) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.DesktopDirectory + ); - return _desktop; + if (existingFolderInfo) { + _desktop = existingFolderInfo.folder; + _desktop._path = existingFolderInfo.path; + _desktop._isKnown = true; } + } - let _downloads: Folder; - export function downloads(): Folder { - _checkPlatform("downloads"); - if (!_downloads) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.DownloadsDirectory); - - if (existingFolderInfo) { - _downloads = existingFolderInfo.folder; - _downloads._path = existingFolderInfo.path; - _downloads._isKnown = true; - } - } + return _desktop; + } + + let _downloads: Folder; + export function downloads(): Folder { + _checkPlatform("downloads"); + if (!_downloads) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.DownloadsDirectory + ); - return _downloads; + if (existingFolderInfo) { + _downloads = existingFolderInfo.folder; + _downloads._path = existingFolderInfo.path; + _downloads._isKnown = true; } + } - let _movies: Folder; - export function movies(): Folder { - _checkPlatform("movies"); - if (!_movies) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.MoviesDirectory); - - if (existingFolderInfo) { - _movies = existingFolderInfo.folder; - _movies._path = existingFolderInfo.path; - _movies._isKnown = true; - } - } + return _downloads; + } - return _movies; + let _movies: Folder; + export function movies(): Folder { + _checkPlatform("movies"); + if (!_movies) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.MoviesDirectory + ); + + if (existingFolderInfo) { + _movies = existingFolderInfo.folder; + _movies._path = existingFolderInfo.path; + _movies._isKnown = true; } + } - let _music: Folder; - export function music(): Folder { - _checkPlatform("music"); - if (!_music) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.MusicDirectory); - - if (existingFolderInfo) { - _music = existingFolderInfo.folder; - _music._path = existingFolderInfo.path; - _music._isKnown = true; - } - } + return _movies; + } - return _music; + let _music: Folder; + export function music(): Folder { + _checkPlatform("music"); + if (!_music) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.MusicDirectory + ); + + if (existingFolderInfo) { + _music = existingFolderInfo.folder; + _music._path = existingFolderInfo.path; + _music._isKnown = true; } + } - let _pictures: Folder; - export function pictures(): Folder { - _checkPlatform("pictures"); - if (!_pictures) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.PicturesDirectory); - - if (existingFolderInfo) { - _pictures = existingFolderInfo.folder; - _pictures._path = existingFolderInfo.path; - _pictures._isKnown = true; - } - } + return _music; + } + + let _pictures: Folder; + export function pictures(): Folder { + _checkPlatform("pictures"); + if (!_pictures) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.PicturesDirectory + ); - return _pictures; + if (existingFolderInfo) { + _pictures = existingFolderInfo.folder; + _pictures._path = existingFolderInfo.path; + _pictures._isKnown = true; } + } - let _sharedPublic: Folder; - export function sharedPublic(): Folder { - _checkPlatform("sharedPublic"); - if (!_sharedPublic) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.SharedPublicDirectory); - - if (existingFolderInfo) { - _sharedPublic = existingFolderInfo.folder; - _sharedPublic._path = existingFolderInfo.path; - _sharedPublic._isKnown = true; - } - } + return _pictures; + } + + let _sharedPublic: Folder; + export function sharedPublic(): Folder { + _checkPlatform("sharedPublic"); + if (!_sharedPublic) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.SharedPublicDirectory + ); - return _sharedPublic; + if (existingFolderInfo) { + _sharedPublic = existingFolderInfo.folder; + _sharedPublic._path = existingFolderInfo.path; + _sharedPublic._isKnown = true; } + } - function getExistingFolderInfo(pathDirectory: any /* NSSearchPathDirectory */): { folder: Folder; path: string } { - const fileAccess = (getFileAccess()); - const folderPath = fileAccess.getKnownPath(pathDirectory); - const folderInfo = fileAccess.getExistingFolder(folderPath); + return _sharedPublic; + } - if (folderInfo) { - return { - folder: createFolder(folderInfo), - path: folderPath - }; - } + function getExistingFolderInfo( + pathDirectory: any /* NSSearchPathDirectory */ + ): { folder: Folder; path: string } { + const fileAccess = _FileSystem.getFileAccess(); + const folderPath = fileAccess.getKnownPath(pathDirectory); + const folderInfo = fileAccess.getExistingFolder(folderPath); - return undefined; - } + if (folderInfo) { + return { + folder: _FileSystem.createFolder(folderInfo), + path: folderPath + }; + } + + return undefined; } + } } -export module path { +export namespace path { + export function normalize(path: string): string { + return _FileSystem.getFileAccess().normalizePath(path); + } - export function normalize(path: string): string { - return getFileAccess().normalizePath(path); - } + export function join(...paths: string[]): string { + const fileAccess = _FileSystem.getFileAccess(); - export function join(...paths: string[]): string { - const fileAccess = getFileAccess(); + return fileAccess.joinPaths(paths); + } - return fileAccess.joinPaths(paths); - } + export const separator = _FileSystem.getFileAccess().getPathSeparator(); +} + +export namespace worker { + export function terminate(): void { + if (FSWorker.isRunning()) FSWorker.getWorkerInstance().terminate(); + } - export const separator = getFileAccess().getPathSeparator(); + export function keepAlive(): void { + if (FSWorker.isRunning()) FSWorker.getWorkerInstance().keepAlive(); + } } From 873405b5e22fcef0f7efd55e099dc7d58467dd10 Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:54:55 +1000 Subject: [PATCH 06/15] Add worker namespace declaration --- tns-core-modules/file-system/file-system.d.ts | 211 ++++++++++-------- 1 file changed, 118 insertions(+), 93 deletions(-) diff --git a/tns-core-modules/file-system/file-system.d.ts b/tns-core-modules/file-system/file-system.d.ts index d3c6e10232..d19dbc057d 100644 --- a/tns-core-modules/file-system/file-system.d.ts +++ b/tns-core-modules/file-system/file-system.d.ts @@ -7,277 +7,302 @@ * Represents a single entity on the file system. */ export class FileSystemEntity { - /** + /** * Gets the Date object specifying the last time this entity was modified. */ - lastModified: Date; + lastModified: Date; - /** + /** * Gets the name of the entity. */ - name: string; + name: string; - /** + /** * Gets the fully-qualified path (including the extension for a File) of the entity. */ - path: string; + path: string; - /** + /** * Gets the Folder object representing the parent of this entity. * Will be null for a root folder like Documents or Temporary. * This property is readonly. */ - parent: Folder; + parent: Folder; - /** + /** * Removes (deletes) the current Entity from the file system. */ - remove(): Promise; + remove(): Promise; - /** + /** * Removes (deletes) the current Entity from the file system synchronously. */ - removeSync(onError?: (error: any) => any): void; + removeSync(onError?: (error: any) => any): void; - /** + /** * Renames the current entity using the specified name. * @param newName The new name to be applied to the entity. */ - rename(newName: string): Promise; + rename(newName: string): Promise; - /** + /** * Renames the current entity synchronously, using the specified name. * @param newName The new name to be applied to the entity. */ - renameSync(newName: string, onError?: (error: any) => any): void; + renameSync(newName: string, onError?: (error: any) => any): void; } /** * Represents a File entity on the file system. */ export class File extends FileSystemEntity { - /** + /** * Checks whether a File with the specified path already exists. * @param path The path to check for. */ - static exists(path: string): boolean; + static exists(path: string): boolean; - /** + /** * Gets the extension of the file. */ - extension: string; + extension: string; - /** + /** * Gets the size in bytes of the file. */ - size: number; + size: number; - /** + /** * Gets a value indicating whether the file is currently locked, meaning a background operation associated with this file is running. */ - isLocked: boolean; + isLocked: boolean; - /** + /** * Gets or creates a File entity at the specified path. * @param path The path to get/create the file at. */ - static fromPath(path: string): File; + static fromPath(path: string): File; - /** + /** * Reads the content of the file as a string using the specified encoding (defaults to UTF-8). * @param encoding An optional value specifying the preferred encoding (defaults to UTF-8). */ - readText(encoding?: string): Promise; + readText(encoding?: string): Promise; - /** + /** * Reads the content of the file as a string synchronously, using the specified encoding (defaults to UTF-8). * @param onError An optional function to be called if some IO-error occurs. * @param encoding An optional value specifying the preferred encoding (defaults to UTF-8). */ - readTextSync(onError?: (error: any) => any, encoding?: string): string; + readTextSync(onError?: (error: any) => any, encoding?: string): string; - /** + /** * Reads the binary content of the file synchronously. * @param onError An optional function to be called if some IO-error occurs. */ - readSync(onError?: (error: any) => any): any; + readSync(onError?: (error: any) => any): any; - /** + /** * Writes the provided string to the file, using the specified encoding (defaults to UTF-8). * @param content The content to be saved to the file. * @param encoding An optional value specifying the preferred encoding (defaults to UTF-8). */ - writeText(content: string, encoding?: string): Promise; + writeText(content: string, encoding?: string): Promise; - /** + /** * Writes the provided string to the file synchronously, using the specified encoding (defaults to UTF-8). * @param content The content to be saved to the file. * @param onError An optional function to be called if some IO-error occurs. * @param encoding An optional value specifying the preferred encoding (defaults to UTF-8). */ - writeTextSync(content: string, onError?: (error: any) => any, encoding?: string): void; + writeTextSync( + content: string, + onError?: (error: any) => any, + encoding?: string + ): void; - /** + /** * Writes the provided binary content to the file synchronously. * @param content The binary content to be saved to the file. * @param onError An optional function to be called if some IO-error occurs. */ - writeSync(content: any, onError?: (error: any) => any): void; + writeSync(content: any, onError?: (error: any) => any): void; } /** * Represents a Folder (directory) entity on the file system. */ export class Folder extends FileSystemEntity { - /** + /** * Determines whether this instance is a KnownFolder (accessed through the KnownFolders object). */ - isKnown: boolean; + isKnown: boolean; - /** + /** * Gets or creates a Folder entity at the specified path. * @param path The path to get/create the folder at. */ - static fromPath(path: string): Folder; + static fromPath(path: string): Folder; - /** + /** * Checks whether a Folder with the specified path already exists. * @param path The path to check for. */ - static exists(path: string): boolean; + static exists(path: string): boolean; - /** + /** * Checks whether this Folder contains an Entity with the specified name. * The path of the folder is added to the name to resolve the complete path to check for. * @param name The name of the entity to check for. */ - contains(name: string): boolean; + contains(name: string): boolean; - /** + /** * Deletes all the files and folders (recursively), contained within this Folder. */ - clear(): Promise; + clear(): Promise; - /** + /** * Deletes all the files and folders (recursively), contained within this Folder synchronously. * @param onError An optional function to be called if some error occurs. */ - clearSync(onError?: (error: any) => void): void; + clearSync(onError?: (error: any) => void): void; - /** + /** * Gets or creates a File entity with the specified name within this Folder. * @param name The name of the file to get/create. */ - getFile(name: string): File; + getFile(name: string): File; - /** + /** * Gets or creates a Folder entity with the specified name within this Folder. * @param name The name of the folder to get/create. */ - getFolder(name: string): Folder; + getFolder(name: string): Folder; - /** + /** * Gets all the top-level entities residing within this folder. */ - getEntities(): Promise>; + getEntities(): Promise>; - /** + /** * Gets all the top-level entities residing within this folder synchronously. * @param onError An optional function to be called if some error occurs. */ - getEntitiesSync(onError?: (error: any) => any): Promise>; + getEntitiesSync( + onError?: (error: any) => any + ): Promise>; - /** + /** * Enumerates all the top-level FileSystem entities residing within this folder. * @param onEntity A callback that receives the current entity. If the callback returns false this will mean for the iteration to stop. */ - eachEntity(onEntity: (entity: FileSystemEntity) => boolean); + eachEntity(onEntity: (entity: FileSystemEntity) => boolean); } /** * Provides access to the top-level Folders instances that are accessible from the application. Use these as entry points to access the FileSystem. */ -export module knownFolders { - /** +export namespace knownFolders { + /** * Gets the Documents folder available for the current application. This Folder is private for the application and not accessible from Users/External apps. */ - export function documents(): Folder; + export function documents(): Folder; - /** + /** * Gets the Temporary (Caches) folder available for the current application. This Folder is private for the application and not accessible from Users/External apps. */ - export function temp(): Folder; + export function temp(): Folder; - /** + /** * Gets the root folder for the current application. This Folder is private for the application and not accessible from Users/External apps. * iOS - this folder is read-only and contains the app and all its resources. */ - export function currentApp(): Folder; + export function currentApp(): Folder; - /** + /** * Contains iOS-specific known folders. */ - module ios { - /** + namespace ios { + /** * Gets the NSLibraryDirectory. Note that the folder will not be created if it did not exist. */ - export function library(): Folder; + export function library(): Folder; - /** + /** * Gets the NSDeveloperDirectory. Note that the folder will not be created if it did not exist. */ - export function developer(): Folder; + export function developer(): Folder; - /** + /** * Gets the NSDesktopDirectory. Note that the folder will not be created if it did not exist. */ - export function desktop(): Folder; + export function desktop(): Folder; - /** + /** * Gets the NSDownloadsDirectory. Note that the folder will not be created if it did not exist. */ - export function downloads(): Folder; + export function downloads(): Folder; - /** + /** * Gets the NSMoviesDirectory. Note that the folder will not be created if it did not exist. */ - export function movies(): Folder; + export function movies(): Folder; - /** + /** * Gets the NSMusicDirectory. Note that the folder will not be created if it did not exist. */ - export function music(): Folder; + export function music(): Folder; - /** + /** * Gets the NSPicturesDirectory. Note that the folder will not be created if it did not exist. */ - export function pictures(): Folder; + export function pictures(): Folder; - /** + /** * Gets the NSSharedPublicDirectory. Note that the folder will not be created if it did not exist. */ - export function sharedPublic(): Folder; - } + export function sharedPublic(): Folder; + } } /** * Enables path-specific operations like join, extension, etc. */ -export module path { - /** +export namespace path { + /** * Normalizes a path, taking care of occurrances like ".." and "//". * @param path The path to be normalized. */ - export function normalize(path: string): string; + export function normalize(path: string): string; - /** + /** * Joins all the provided string components, forming a valid and normalized path. * @param paths An array of string components to be joined. */ - export function join(...paths: string[]): string; + export function join(...paths: string[]): string; - /** + /** * Gets the string used to separate file paths. */ - export const separator: string; + export const separator: string; +} + +/** + * Enables control over the lifespane of a File System Worker. + */ +export namespace worker { + /** + * Terminates the Worker on its next run loop. + */ + export function terminate(): void; + /** + * Prevents the Worker from closing due to inactivity. + * + * By default File System Workers are closed after a period of inactivity. After calling this method the Worker will only close if; + * * The application raises a `lowMemoryEvent` + * * The `terminate`method is called. + * * The application is closed. + */ + export function keepAlive(): void; } From 659155a8717e053a4d9427dd2f96df154ad71a36 Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:39:24 +1000 Subject: [PATCH 07/15] Add Worker All existing asyncronouse methods is `file-system` are dispatched to this Worker. --- .../file-system/file-system-worker/worker.ts | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 tns-core-modules/file-system/file-system-worker/worker.ts diff --git a/tns-core-modules/file-system/file-system-worker/worker.ts b/tns-core-modules/file-system/file-system-worker/worker.ts new file mode 100644 index 0000000000..7318ed7ddd --- /dev/null +++ b/tns-core-modules/file-system/file-system-worker/worker.ts @@ -0,0 +1,215 @@ +import "globals"; +import { FileSystemAccess } from "tns-core-modules/file-system/file-system-access"; +import { FSWorker, WorkerTimer } from "./components"; + +namespace Worker { + declare let self: any; + declare let fileAccess: FileSystemAccess; + + export const Jobs: string[] = []; + export const context: FSWorker.Context = self; + export const Timer = new WorkerTimer(context.close); + + export function postResult(id, message) { + Jobs.splice(Jobs.indexOf(id), 1); + context.postMessage({ id, message }); + if (Jobs.length < 1) this.Timer.startTimer(); + } + + export function onError(error: T): void { + if (error instanceof Error) throw error; + if (typeof error === "string") throw new Error(error); + } + + export function throwWorkerError(id: string, error: Error) { + try { + Jobs.splice(Jobs.indexOf(id), 1); + throw JSON.stringify({ id, message: error.message }); + } finally { + if (Jobs.length < 1) this.Timer.startTimer(); + } + } + + export function performWork(work) { + switch (work.task) { + case FSWorker.WorkTask.closeWorker: + context.close(); + break; + case FSWorker.WorkTask.turnOffActivityObserver: + this.Timer.stopObserving(); + this.postResult(work.id, "Stopped observing"); + break; + } + } + + export function getFileAccess() { + if (!(this.fileAccess instanceof FileSystemAccess)) { + this.fileAccess = new FileSystemAccess(); + } + return this.fileAccess; + } +} + +namespace EntityWorker { + export function performWork(work) { + try { + let result; + switch (work.task) { + case FSWorker.WorkTask.removeEntity: + result = removeEntity(work.object); + break; + case FSWorker.WorkTask.renameEntity: + result = renameEntity(work.object); + break; + default: + throw new Error(`Unrecognised task ${work.task}`); + } + Worker.postResult(work.id, result); + } catch (error) { + Worker.throwWorkerError(work.id, error); + } + } + + function removeEntity(object: { path: string; isFile: boolean }) { + object.isFile + ? Worker.getFileAccess().deleteFile(object.path, Worker.onError) + : Worker.getFileAccess().deleteFolder(object.path, Worker.onError); + return `${object.isFile ? "File" : "Folder"} removed.`; + } + + function renameEntity(object: { + name: string; + path: string; + parentPath: string; + }) { + const path = Worker.getFileAccess().joinPath( + object.parentPath, + object.name + ); + const extension = Worker.getFileAccess().getFileExtension(path); + Worker.getFileAccess().rename(object.path, path, Worker.onError); + return { name: object.name, path, extension }; + } +} + +namespace FileWorker { + export function performWork(work) { + try { + let result; + switch (work.task) { + case FSWorker.WorkTask.readText: + result = readText(work.object); + break; + case FSWorker.WorkTask.writeText: + result = writeText(work.object); + break; + default: + throw new Error(`Unrecognised task ${work.task}`); + } + Worker.postResult(work.id, result); + } catch (error) { + Worker.throwWorkerError(work.id, error); + } + } + + function readText(object: { path: string; encoding?: string }) { + const fileAccess = Worker.getFileAccess(); + const content = object.encoding + ? fileAccess.readText(object.path, Worker.onError, object.encoding) + : fileAccess.readText(object.path, Worker.onError); + return content; + } + + function writeText(object: { + path: string; + content: string; + encoding?: string; + }) { + const fileAccess = Worker.getFileAccess(); + !object.encoding + ? fileAccess.writeText(object.path, object.content, Worker.onError) + : fileAccess.writeText( + object.path, + object.content, + Worker.onError, + object.encoding + ); + return "Content written to file."; + } +} + +namespace FolderWorker { + export function performWork(work) { + try { + let result; + switch (work.task) { + case FSWorker.WorkTask.clearFolder: + result = clearFolder(work.object); + break; + case FSWorker.WorkTask.getEntities: + result = getEntities(work.object); + break; + default: + throw new Error(`Unrecognised task ${work.task}`); + } + Worker.postResult(work.id, result); + } catch (error) { + Worker.throwWorkerError(work.id, error); + } + } + + function clearFolder(object: { path: string }) { + Worker.getFileAccess().emptyFolder(object.path, Worker.onError); + return "Folder emptied"; + } + + function getEntities(object: { path: string }) { + let i; + const entities = []; + const fileInfos = Worker.getFileAccess().getEntities( + object.path, + Worker.onError + ); + if (!fileInfos) throw new Error("Path has no entities"); + for (i = 0; i < fileInfos.length; i++) { + if (fileInfos[i].extension) { + entities.push(fileInfos[i]); + } else { + entities.push(fileInfos[i]); + } + } + + return entities; + } +} + +Worker.context.onmessage = function(message) { + const work = message.data; + Worker.Timer.stopTimer(); + Worker.Jobs.push(work.id); + switch (work.type) { + case FSWorker.WorkType.entity: + EntityWorker.performWork(work); + break; + case FSWorker.WorkType.file: + FileWorker.performWork(work); + break; + case FSWorker.WorkType.folder: + FolderWorker.performWork(work); + break; + case FSWorker.WorkType.worker: + Worker.performWork(work); + break; + } +}; + +Worker.context.onclose = function() { + const jobId = Worker.Jobs.find(id => FSWorker.State.closed === id); + if (jobId) Worker.postResult(jobId, "Worker closed"); + if (Worker.Timer.becameInactive) { + Worker.context.postMessage({ + id: FSWorker.State.inactive, + message: "Worker did become inactive" + }); + } +}; From 55a9d5629feaf2e9e5f5c7f08b651e26bce2602a Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:40:49 +1000 Subject: [PATCH 08/15] Add Worker components Components for the `file system` Worker. --- .../file-system-worker/components.ts | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 tns-core-modules/file-system/file-system-worker/components.ts diff --git a/tns-core-modules/file-system/file-system-worker/components.ts b/tns-core-modules/file-system/file-system-worker/components.ts new file mode 100644 index 0000000000..d19f54a667 --- /dev/null +++ b/tns-core-modules/file-system/file-system-worker/components.ts @@ -0,0 +1,192 @@ +import { on, off, lowMemoryEvent } from "tns-core-modules/application"; + +export namespace FSWorker { + export interface Context extends Worker { + close: { (): void }; + onclose: { (): void }; + } + + export interface FSJob { + id: string; + onError: { (error: any): void }; + onSuccess: { (message: any): void }; + } + + export enum WorkType { + file, + folder, + entity, + worker + } + + export enum WorkTask { + readText, + writeText, + clearFolder, + getEntities, + removeEntity, + renameEntity, + closeWorker, + turnOffActivityObserver + } + + export enum State { + inactive = "WorkerBecameInactive", + closed = "WorkerCloseRequestFulfilled" + } + + export let fileWorker: Instance; + export const isRunning = (): boolean => fileWorker instanceof Instance; + export const getWorkerInstance = (): Instance => fileWorker; + export const createWorkerInstance = (): Instance => + (fileWorker = new Instance()); + + export const onMemoryEvent = (callback, thisArg) => + on(lowMemoryEvent, callback, thisArg); + export const offMemoryEvent = (callback, thisArg) => + off(lowMemoryEvent, callback, thisArg); + + export class Job { + id: string; + object: any; + task: WorkTask; + type: WorkType; + + constructor(type: WorkType, task: WorkTask, object?: any) { + this.id = `${type}-${task}-${Date.now()}`; + this.object = object; + this.task = task; + this.type = type; + } + } + + export class Instance { + private _worker: Worker; + private _jobs: FSJob[]; + + constructor() { + this._jobs = []; + this._worker = new Worker("./worker"); + this._worker.onerror = error => { + const data = JSON.parse(error.message); + this.getJob(data.id).onError(new Error(data.message)); + }; + this._worker.onmessage = message => { + const data = message.data; + switch (data.id) { + case "WorkerCloseRequestFulfilled": + this.onWorkerClosed(); + break; + case "WorkerBecameInactive": + this.onWorkerClosed(); + break; + default: + this.getJob(data.id).onSuccess(data.message); + } + }; + FSWorker.onMemoryEvent(this.closeWorker, this); + } + + public get jobs() { + return this._jobs; + } + + public getJob(id: string): FSJob { + const job = this.jobs.find(job => job.id === id); + this.jobs.splice(this.jobs.indexOf(job), 1); + return job; + } + + public postJob(job: Job, onSuccess, onError): void { + this.jobs.push({ id: job.id, onSuccess, onError }); + this._worker.postMessage(job); + } + + keepAlive(callback: { (message: string): void } = (): void => {}) { + this.postJob( + new Job(WorkType.worker, WorkTask.turnOffActivityObserver), + callback, + callback + ); + } + + terminate() { + this.keepAlive(() => { + this._worker.terminate(); + this.onWorkerClosed(); + }); + } + + closeWorker() { + this._worker.postMessage({ + id: State.closed, + type: WorkType.worker, + task: WorkTask.closeWorker + }); + } + + onWorkerClosed() { + FSWorker.offMemoryEvent(this.closeWorker, this); + this.jobs.splice(0, this.jobs.length - 1); + fileWorker = null; + } + } +} + +export class WorkerTimer { + private _id: any; + private _time: number; + private _duration: number; + private _observing: boolean; + private _onEnd: { (): void }; + private _becameInactive: boolean; + + constructor(onEnd) { + this._becameInactive = false; + this._duration = 120000; + this._observing = true; + this._onEnd = onEnd; + this._time = 0; + + this.startTimer(); + } + + private _incrementTime() { + this._time += 1000; + if (this._time >= this._duration) { + this.stopTimer(); + this._becameInactive = true; + this._onEnd(); + } + } + + private get _isRunning() { + return typeof this._id === "number"; + } + + private get _isObserving() { + return this._observing; + } + + public get becameInactive() { + return this._becameInactive; + } + + public stopObserving() { + this.stopTimer(); + this._observing = false; + } + + public startTimer() { + if (this._isObserving && !this._isRunning) { + this._id = setInterval(() => this._incrementTime(), 1000); + } + } + + public stopTimer() { + if (this._isObserving && this._isRunning) { + clearInterval(this._id); + this._id = null; + } + } +} From a2ab5a455a3371df0e8e3541a795cf9b05c4bd12 Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:47:13 +1000 Subject: [PATCH 09/15] Add FSWorker Import the FSWorker object from components. --- tns-core-modules/file-system/file-system.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tns-core-modules/file-system/file-system.ts b/tns-core-modules/file-system/file-system.ts index aa1209716b..d3aeda2493 100644 --- a/tns-core-modules/file-system/file-system.ts +++ b/tns-core-modules/file-system/file-system.ts @@ -1,6 +1,6 @@ // imported for definition purposes only import * as platformModule from "../platform"; - +import { FSWorker } from "./file-system-worker/components"; import { FileSystemAccess } from "./file-system-access"; import { profile } from "../profiling"; From 5f08ecb20a0dd6b43bbb4b0ee43a7e531c327a49 Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:49:15 +1000 Subject: [PATCH 10/15] Refactor Move globally declared, but local file-system methods and variables to a _FileSystem object. --- tns-core-modules/file-system/file-system.ts | 43 +++++++++++++-------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/tns-core-modules/file-system/file-system.ts b/tns-core-modules/file-system/file-system.ts index d3aeda2493..3b2e6b19ef 100644 --- a/tns-core-modules/file-system/file-system.ts +++ b/tns-core-modules/file-system/file-system.ts @@ -4,41 +4,51 @@ import { FSWorker } from "./file-system-worker/components"; import { FileSystemAccess } from "./file-system-access"; import { profile } from "../profiling"; -// The FileSystemAccess implementation, used through all the APIs. -let fileAccess: FileSystemAccess; -function getFileAccess(): FileSystemAccess { - if (!fileAccess) { - fileAccess = new FileSystemAccess(); - } +namespace _FileSystem { + let fileAccess: FileSystemAccess; + let platform: typeof platformModule; + + export function postJob(job: FSWorker.Job, resolve, reject): void { + if (!FSWorker.isRunning()) FSWorker.createWorkerInstance(); + FSWorker.getWorkerInstance().postJob(job, resolve, reject); + } + export function getFileAccess() { + if (!(fileAccess instanceof FileSystemAccess)) { + fileAccess = new FileSystemAccess(); + } return fileAccess; -} + } -let platform: typeof platformModule; -function ensurePlatform() { + export function getPlatform() { if (!platform) { - platform = require("../platform"); + platform = require("../platfrom"); } -} + return platform; + } -function createFile(info: { path: string; name: string; extension: string }) { + export function createFile(info: { + path: string; + name: string; + extension: string; + }) { const file = new File(); file._path = info.path; file._name = info.name; file._extension = info.extension; return file; -} + } -function createFolder(info: { path: string; name: string; }) { + export function createFolder(info: { path: string; name: string }) { const documents = knownFolders.documents(); if (info.path === documents.path) { - return documents; + return documents; } const temp = knownFolders.temp(); if (info.path === temp.path) { - return temp; + return temp; } const folder = new Folder(); @@ -47,6 +57,7 @@ function createFolder(info: { path: string; name: string; }) { folder._name = info.name; return folder; + } } export class FileSystemEntity { From 25d7ecd785a2d89650a02b0be7bfb51bbdbc1ac9 Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:52:23 +1000 Subject: [PATCH 11/15] Refactor Update all methods using asynchronous methods to run on Worker thread. And call local methods from new _FileSystem object. --- tns-core-modules/file-system/file-system.ts | 1066 ++++++++++--------- 1 file changed, 581 insertions(+), 485 deletions(-) diff --git a/tns-core-modules/file-system/file-system.ts b/tns-core-modules/file-system/file-system.ts index 3b2e6b19ef..7cdae4236b 100644 --- a/tns-core-modules/file-system/file-system.ts +++ b/tns-core-modules/file-system/file-system.ts @@ -61,625 +61,721 @@ namespace _FileSystem { } export class FileSystemEntity { - _path: string; - _name: string; - _extension: string; - _locked: boolean; - _lastModified: Date; - _isKnown: boolean; - - get parent(): Folder { - const onError = function (error) { - throw error; - }; - - const folderInfo = getFileAccess().getParent(this.path, onError); - if (!folderInfo) { - return undefined; - } + _path: string; + _name: string; + _extension: string; + _locked: boolean; + _lastModified: Date; + _isKnown: boolean; + + get parent(): Folder { + const onError = function(error) { + throw error; + }; + + const folderInfo = _FileSystem + .getFileAccess() + .getParent(this.path, onError); + if (!folderInfo) { + return undefined; + } + + return _FileSystem.createFolder(folderInfo); + } - return createFolder(folderInfo); - } + public remove(): Promise { + return new Promise((resolve, reject) => { + this._isKnown + ? reject(new Error("Cannot delete known folder.")) + : _FileSystem.postJob( + new FSWorker.Job( + FSWorker.WorkType.entity, + FSWorker.WorkTask.removeEntity, + { path: this.path, isFile: this instanceof File } + ), + resolve, + reject + ); + }); + } - public remove(): Promise { - return new Promise((resolve, reject) => { - let hasError = false; - const localError = function (error: any) { - hasError = true; - reject(error); - }; + public removeSync(onError?: (error: any) => any): void { + if (this._isKnown) { + if (onError) { + onError({ message: "Cannot delete known folder." }); + } - this.removeSync(localError); - if (!hasError) { - resolve(); - } - }); + return; } - public removeSync(onError?: (error: any) => any): void { - if (this._isKnown) { - if (onError) { - onError({ message: "Cannot delete known folder." }); - } - - return; - } - - const fileAccess = getFileAccess(); + const fileAccess = _FileSystem.getFileAccess(); - if (this instanceof File) { - fileAccess.deleteFile(this.path, onError); - } else if (this instanceof Folder) { - fileAccess.deleteFolder(this.path, onError); - } + if (this instanceof File) { + fileAccess.deleteFile(this.path, onError); + } else if (this instanceof Folder) { + fileAccess.deleteFolder(this.path, onError); } + } - public rename(newName: string): Promise { - return new Promise((resolve, reject) => { - let hasError = false; - const localError = function (error) { - hasError = true; - reject(error); - }; + public rename(newName: string): Promise { + return new Promise((resolve, reject) => { + if (this._isKnown || !this.parent) { + reject( + new Error( + this._isKnown ? "Cannot delete known folder" : "No parent folder" + ) + ); + } + + _FileSystem.postJob( + new FSWorker.Job( + FSWorker.WorkType.entity, + FSWorker.WorkTask.renameEntity, + { + name: name, + path: this.path, + parentPath: this.parent._path + } + ), + (result: { name: string; path: string; extension: string }) => { + this._name = result.name; + this._path = result.path; + if (this instanceof File) { + this._extension = result.extension; + } + resolve(result); + }, + reject + ); + }); + } - this.renameSync(newName, localError); + public renameSync(newName: string, onError?: (error: any) => any): void { + if (this._isKnown) { + if (onError) { + onError(new Error("Cannot rename known folder.")); + } - if (!hasError) { - resolve(); - } - }); + return; } - public renameSync(newName: string, onError?: (error: any) => any): void { - if (this._isKnown) { - if (onError) { - onError(new Error("Cannot rename known folder.")); - } - - return; - } - - const parentFolder = this.parent; - if (!parentFolder) { - if (onError) { - onError(new Error("No parent folder.")); - } - - return; - } + const parentFolder = this.parent; + if (!parentFolder) { + if (onError) { + onError(new Error("No parent folder.")); + } - const fileAccess = getFileAccess(); - const path = parentFolder.path; - const newPath = fileAccess.joinPath(path, newName); + return; + } - const localError = function (error) { - if (onError) { - onError(error); - } + const fileAccess = _FileSystem.getFileAccess(); + const path = parentFolder.path; + const newPath = fileAccess.joinPath(path, newName); - return null; - }; + const localError = function(error) { + if (onError) { + onError(error); + } - fileAccess.rename(this.path, newPath, localError); - this._path = newPath; - this._name = newName; + return null; + }; - if (this instanceof File) { - this._extension = fileAccess.getFileExtension(newPath); - } - } + fileAccess.rename(this.path, newPath, localError); + this._path = newPath; + this._name = newName; - get name(): string { - return this._name; + if (this instanceof File) { + this._extension = fileAccess.getFileExtension(newPath); } + } - get path(): string { - return this._path; - } + get name(): string { + return this._name; + } - get lastModified(): Date { - let value = this._lastModified; - if (!this._lastModified) { - value = this._lastModified = getFileAccess().getLastModified(this.path); - } + get path(): string { + return this._path; + } - return value; + get lastModified(): Date { + let value = this._lastModified; + if (!this._lastModified) { + value = this._lastModified = _FileSystem + .getFileAccess() + .getLastModified(this.path); } + + return value; + } } export class File extends FileSystemEntity { - public static fromPath(path: string) { - const onError = function (error) { - throw error; - }; + public static fromPath(path: string) { + const onError = function(error) { + throw error; + }; - const fileInfo = getFileAccess().getFile(path, onError); - if (!fileInfo) { - return undefined; - } - - return createFile(fileInfo); + const fileInfo = _FileSystem.getFileAccess().getFile(path, onError); + if (!fileInfo) { + return undefined; } - public static exists(path: string): boolean { - return getFileAccess().fileExists(path); - } - - get extension(): string { - return this._extension; - } + return _FileSystem.createFile(fileInfo); + } - get isLocked(): boolean { - // !! is a boolean conversion/cast, handling undefined as well - return !!this._locked; - } + public static exists(path: string): boolean { + return _FileSystem.getFileAccess().fileExists(path); + } - get size(): number { - return getFileAccess().getFileSize(this.path); - } + get extension(): string { + return this._extension; + } - public readSync(onError?: (error: any) => any): any { - this.checkAccess(); + get isLocked(): boolean { + // !! is a boolean conversion/cast, handling undefined as well + return !!this._locked; + } - this._locked = true; + get size(): number { + return _FileSystem.getFileAccess().getFileSize(this.path); + } - const that = this; - const localError = (error) => { - that._locked = false; - if (onError) { - onError(error); - } - }; + public readSync(onError?: (error: any) => any): any { + this.checkAccess(); - const content = getFileAccess().read(this.path, localError); + this._locked = true; - this._locked = false; + const that = this; + const localError = error => { + that._locked = false; + if (onError) { + onError(error); + } + }; - return content; + const content = _FileSystem.getFileAccess().read(this.path, localError); - } + this._locked = false; - public writeSync(content: any, onError?: (error: any) => any): void { - this.checkAccess(); + return content; + } - try { - this._locked = true; + public writeSync(content: any, onError?: (error: any) => any): void { + this.checkAccess(); - const that = this; - const localError = function (error) { - that._locked = false; - if (onError) { - onError(error); - } - }; + try { + this._locked = true; - getFileAccess().write(this.path, content, localError); - } finally { - this._locked = false; + const that = this; + const localError = function(error) { + that._locked = false; + if (onError) { + onError(error); } - } + }; - public readText(encoding?: string): Promise { - return new Promise((resolve, reject) => { - let hasError = false; - const localError = (error) => { - hasError = true; - reject(error); - }; - - const content = this.readTextSync(localError, encoding); - if (!hasError) { - resolve(content); - } - }); + _FileSystem.getFileAccess().write(this.path, content, localError); + } finally { + this._locked = false; } + } - @profile - public readTextSync(onError?: (error: any) => any, encoding?: string): string { + public readText(encoding?: string): Promise { + return new Promise((resolve, reject) => { + try { this.checkAccess(); - this._locked = true; - - const that = this; - const localError = (error) => { - that._locked = false; - if (onError) { - onError(error); - } + const onResult = (result: any) => { + this._locked = false; + result instanceof Error ? reject(result) : resolve(result); }; + _FileSystem.postJob( + new FSWorker.Job(FSWorker.WorkType.file, FSWorker.WorkTask.readText, { + path: this.path, + encoding: encoding + }), + onResult, + onResult + ); + } catch (error) { + reject(error); + } + }); + } - const content = getFileAccess().readText(this.path, localError, encoding); - this._locked = false; - - return content; - } + @profile + public readTextSync( + onError?: (error: any) => any, + encoding?: string + ): string { + this.checkAccess(); + + this._locked = true; + + const that = this; + const localError = error => { + that._locked = false; + if (onError) { + onError(error); + } + }; + + const content = _FileSystem + .getFileAccess() + .readText(this.path, localError, encoding); + this._locked = false; + + return content; + } - public writeText(content: string, encoding?: string): Promise { - return new Promise((resolve, reject) => { - let hasError = false; - const localError = function (error) { - hasError = true; - reject(error); - }; + public writeText(content: string, encoding?: string): Promise { + return new Promise((resolve, reject) => { + try { + this.checkAccess(); + this._locked = true; + const onResult = (result: any) => { + this._locked = false; + result instanceof Error ? reject(result) : resolve(result); + }; - this.writeTextSync(content, localError, encoding); - if (!hasError) { - resolve(); + _FileSystem.postJob( + new FSWorker.Job( + FSWorker.WorkType.file, + FSWorker.WorkTask.writeText, + { + content, + encoding, + path: this.path } - }); - } - - public writeTextSync(content: string, onError?: (error: any) => any, encoding?: string): void { - this.checkAccess(); + ), + onResult, + onResult + ); + } catch (error) { + reject(error); + } + }); + } - try { - this._locked = true; - - const that = this; - const localError = function (error) { - that._locked = false; - if (onError) { - onError(error); - } - }; - - // TODO: Asyncronous - getFileAccess().writeText(this.path, content, localError, encoding); - } finally { - this._locked = false; + public writeTextSync( + content: string, + onError?: (error: any) => any, + encoding?: string + ): void { + this.checkAccess(); + + try { + this._locked = true; + + const that = this; + const localError = function(error) { + that._locked = false; + if (onError) { + onError(error); } + }; + + // TODO: Asyncronous + _FileSystem + .getFileAccess() + .writeText(this.path, content, localError, encoding); + } finally { + this._locked = false; } + } - private checkAccess() { - if (this.isLocked) { - throw new Error("Cannot access a locked file."); - } + private checkAccess() { + if (this.isLocked) { + throw new Error("Cannot access a locked file."); } + } } export class Folder extends FileSystemEntity { - public static fromPath(path: string): Folder { - const onError = function (error) { - throw error; - }; - - const folderInfo = getFileAccess().getFolder(path, onError); - if (!folderInfo) { - return undefined; - } + public static fromPath(path: string): Folder { + const onError = function(error) { + throw error; + }; - return createFolder(folderInfo); + const folderInfo = _FileSystem.getFileAccess().getFolder(path, onError); + if (!folderInfo) { + return undefined; } - public static exists(path: string): boolean { - return getFileAccess().folderExists(path); - } + return _FileSystem.createFolder(folderInfo); + } - public contains(name: string): boolean { - const fileAccess = getFileAccess(); - const path = fileAccess.joinPath(this.path, name); + public static exists(path: string): boolean { + return _FileSystem.getFileAccess().folderExists(path); + } - if (fileAccess.fileExists(path)) { - return true; - } + public contains(name: string): boolean { + const fileAccess = _FileSystem.getFileAccess(); + const path = fileAccess.joinPath(this.path, name); - return fileAccess.folderExists(path); + if (fileAccess.fileExists(path)) { + return true; } - public clear(): Promise { - return new Promise((resolve, reject) => { - let hasError = false; - const onError = function (error) { - hasError = true; - reject(error); - }; - - this.clearSync(onError); - if (!hasError) { - resolve(); - } - }); - } + return fileAccess.folderExists(path); + } - public clearSync(onError?: (error: any) => void): void { - getFileAccess().emptyFolder(this.path, onError); - } + public clear(): Promise { + return new Promise((resolve, reject) => { + _FileSystem.postJob( + new FSWorker.Job( + FSWorker.WorkType.folder, + FSWorker.WorkTask.clearFolder, + { path: this.path } + ), + resolve, + reject + ); + }); + } - get isKnown(): boolean { - return this._isKnown; - } + public clearSync(onError?: (error: any) => void): void { + _FileSystem.getFileAccess().emptyFolder(this.path, onError); + } - public getFile(name: string): File { - const fileAccess = getFileAccess(); - const path = fileAccess.joinPath(this.path, name); + get isKnown(): boolean { + return this._isKnown; + } - const onError = function (error) { - throw error; - }; + public getFile(name: string): File { + const fileAccess = _FileSystem.getFileAccess(); + const path = fileAccess.joinPath(this.path, name); - const fileInfo = fileAccess.getFile(path, onError); - if (!fileInfo) { - return undefined; - } + const onError = function(error) { + throw error; + }; - return createFile(fileInfo); + const fileInfo = fileAccess.getFile(path, onError); + if (!fileInfo) { + return undefined; } - public getFolder(name: string): Folder { - const fileAccess = getFileAccess(); - const path = fileAccess.joinPath(this.path, name); + return _FileSystem.createFile(fileInfo); + } - const onError = function (error) { - throw error; - }; + public getFolder(name: string): Folder { + const fileAccess = _FileSystem.getFileAccess(); + const path = fileAccess.joinPath(this.path, name); - const folderInfo = fileAccess.getFolder(path, onError); - if (!folderInfo) { - return undefined; - } + const onError = function(error) { + throw error; + }; - return createFolder(folderInfo); + const folderInfo = fileAccess.getFolder(path, onError); + if (!folderInfo) { + return undefined; } - public getEntities(): Promise> { - return new Promise((resolve, reject) => { - let hasError = false; - const localError = function (error) { - hasError = true; - reject(error); - }; + return _FileSystem.createFolder(folderInfo); + } - const entities = this.getEntitiesSync(localError); - if (!hasError) { - resolve(entities); - } - }); + public getEntities(): Promise> { + return new Promise((resolve, reject) => { + _FileSystem.postJob( + new FSWorker.Job( + FSWorker.WorkType.folder, + FSWorker.WorkTask.getEntities, + { path: this.path } + ), + entities => { + const _entities = entities.map( + entity => + entity.extension + ? _FileSystem.createFile(entity) + : _FileSystem.createFolder(entity) + ); + resolve(_entities); + }, + reject + ); + }); + } + + public getEntitiesSync( + onError?: (error: any) => any + ): Array { + const fileInfos = _FileSystem + .getFileAccess() + .getEntities(this.path, onError); + if (!fileInfos) { + return null; } - public getEntitiesSync(onError?: (error: any) => any): Array { - const fileInfos = getFileAccess().getEntities(this.path, onError); - if (!fileInfos) { - return null; - } + const entities = new Array(); + for (let i = 0; i < fileInfos.length; i++) { + if (fileInfos[i].extension) { + entities.push(_FileSystem.createFile(fileInfos[i])); + } else { + entities.push(_FileSystem.createFolder(fileInfos[i])); + } + } - const entities = new Array(); - for (let i = 0; i < fileInfos.length; i++) { - if (fileInfos[i].extension) { - entities.push(createFile(fileInfos[i])); - } else { - entities.push(createFolder(fileInfos[i])); - } - } + return entities; + } - return entities; + public eachEntity(onEntity: (entity: FileSystemEntity) => boolean) { + if (!onEntity) { + return; } - public eachEntity(onEntity: (entity: FileSystemEntity) => boolean) { - if (!onEntity) { - return; - } - - const onSuccess = function (fileInfo: { path: string; name: string; extension: string }): boolean { - let entity; - if (fileInfo.extension) { - entity = createFile(fileInfo); - } else { - entity = createFolder(fileInfo); - } + const onSuccess = function(fileInfo: { + path: string; + name: string; + extension: string; + }): boolean { + let entity; + if (fileInfo.extension) { + entity = _FileSystem.createFile(fileInfo); + } else { + entity = _FileSystem.createFolder(fileInfo); + } - return onEntity(entity); - }; + return onEntity(entity); + }; - const onError = function (error) { - throw error; - }; + const onError = function(error) { + throw error; + }; - getFileAccess().eachEntity(this.path, onSuccess, onError); - } + _FileSystem.getFileAccess().eachEntity(this.path, onSuccess, onError); + } } -export module knownFolders { - let _documents: Folder; - let _temp: Folder; - let _app: Folder; - - export function documents(): Folder { - if (!_documents) { - const path = getFileAccess().getDocumentsFolderPath(); - _documents = new Folder(); - _documents._path = path; - _documents._isKnown = true; - } +export namespace knownFolders { + let _documents: Folder; + let _temp: Folder; + let _app: Folder; - return _documents; + export function documents(): Folder { + if (!_documents) { + const path = _FileSystem.getFileAccess().getDocumentsFolderPath(); + _documents = new Folder(); + _documents._path = path; + _documents._isKnown = true; } - export function temp(): Folder { - if (!_temp) { - const path = getFileAccess().getTempFolderPath(); - _temp = new Folder(); - _temp._path = path; - _temp._isKnown = true; - } + return _documents; + } - return _temp; + export function temp(): Folder { + if (!_temp) { + const path = _FileSystem.getFileAccess().getTempFolderPath(); + _temp = new Folder(); + _temp._path = path; + _temp._isKnown = true; } - export function currentApp(): Folder { - if (!_app) { - const path = getFileAccess().getCurrentAppPath(); - _app = new Folder(); - _app._path = path; - _app._isKnown = true; - } + return _temp; + } - return _app; + export function currentApp(): Folder { + if (!_app) { + const path = _FileSystem.getFileAccess().getCurrentAppPath(); + _app = new Folder(); + _app._path = path; + _app._isKnown = true; } - export module ios { - function _checkPlatform(knownFolderName: string) { - ensurePlatform(); - if (!platform.isIOS) { - throw new Error(`The "${knownFolderName}" known folder is available on iOS only!`); - } - } - - let _library: Folder; - export function library(): Folder { - _checkPlatform("library"); - if (!_library) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.LibraryDirectory); - - if (existingFolderInfo) { - _library = existingFolderInfo.folder; - _library._path = existingFolderInfo.path; - _library._isKnown = true; - } - } + return _app; + } - return _library; + export namespace ios { + function _checkPlatform(knownFolderName: string) { + if (_FileSystem.getPlatform().isAndroid) { + throw new Error( + `The "${knownFolderName}" known folder is available on iOS only!` + ); + } + } + + let _library: Folder; + export function library(): Folder { + _checkPlatform("library"); + if (!_library) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.LibraryDirectory + ); + + if (existingFolderInfo) { + _library = existingFolderInfo.folder; + _library._path = existingFolderInfo.path; + _library._isKnown = true; } + } - let _developer: Folder; - export function developer(): Folder { - _checkPlatform("developer"); - if (!_developer) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.DeveloperDirectory); - - if (existingFolderInfo) { - _developer = existingFolderInfo.folder; - _developer._path = existingFolderInfo.path; - _developer._isKnown = true; - } - } + return _library; + } + + let _developer: Folder; + export function developer(): Folder { + _checkPlatform("developer"); + if (!_developer) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.DeveloperDirectory + ); - return _developer; + if (existingFolderInfo) { + _developer = existingFolderInfo.folder; + _developer._path = existingFolderInfo.path; + _developer._isKnown = true; } + } - let _desktop: Folder; - export function desktop(): Folder { - _checkPlatform("desktop"); - if (!_desktop) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.DesktopDirectory); - - if (existingFolderInfo) { - _desktop = existingFolderInfo.folder; - _desktop._path = existingFolderInfo.path; - _desktop._isKnown = true; - } - } + return _developer; + } + + let _desktop: Folder; + export function desktop(): Folder { + _checkPlatform("desktop"); + if (!_desktop) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.DesktopDirectory + ); - return _desktop; + if (existingFolderInfo) { + _desktop = existingFolderInfo.folder; + _desktop._path = existingFolderInfo.path; + _desktop._isKnown = true; } + } - let _downloads: Folder; - export function downloads(): Folder { - _checkPlatform("downloads"); - if (!_downloads) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.DownloadsDirectory); - - if (existingFolderInfo) { - _downloads = existingFolderInfo.folder; - _downloads._path = existingFolderInfo.path; - _downloads._isKnown = true; - } - } + return _desktop; + } + + let _downloads: Folder; + export function downloads(): Folder { + _checkPlatform("downloads"); + if (!_downloads) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.DownloadsDirectory + ); - return _downloads; + if (existingFolderInfo) { + _downloads = existingFolderInfo.folder; + _downloads._path = existingFolderInfo.path; + _downloads._isKnown = true; } + } - let _movies: Folder; - export function movies(): Folder { - _checkPlatform("movies"); - if (!_movies) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.MoviesDirectory); - - if (existingFolderInfo) { - _movies = existingFolderInfo.folder; - _movies._path = existingFolderInfo.path; - _movies._isKnown = true; - } - } + return _downloads; + } - return _movies; + let _movies: Folder; + export function movies(): Folder { + _checkPlatform("movies"); + if (!_movies) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.MoviesDirectory + ); + + if (existingFolderInfo) { + _movies = existingFolderInfo.folder; + _movies._path = existingFolderInfo.path; + _movies._isKnown = true; } + } - let _music: Folder; - export function music(): Folder { - _checkPlatform("music"); - if (!_music) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.MusicDirectory); - - if (existingFolderInfo) { - _music = existingFolderInfo.folder; - _music._path = existingFolderInfo.path; - _music._isKnown = true; - } - } + return _movies; + } - return _music; + let _music: Folder; + export function music(): Folder { + _checkPlatform("music"); + if (!_music) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.MusicDirectory + ); + + if (existingFolderInfo) { + _music = existingFolderInfo.folder; + _music._path = existingFolderInfo.path; + _music._isKnown = true; } + } - let _pictures: Folder; - export function pictures(): Folder { - _checkPlatform("pictures"); - if (!_pictures) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.PicturesDirectory); - - if (existingFolderInfo) { - _pictures = existingFolderInfo.folder; - _pictures._path = existingFolderInfo.path; - _pictures._isKnown = true; - } - } + return _music; + } + + let _pictures: Folder; + export function pictures(): Folder { + _checkPlatform("pictures"); + if (!_pictures) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.PicturesDirectory + ); - return _pictures; + if (existingFolderInfo) { + _pictures = existingFolderInfo.folder; + _pictures._path = existingFolderInfo.path; + _pictures._isKnown = true; } + } - let _sharedPublic: Folder; - export function sharedPublic(): Folder { - _checkPlatform("sharedPublic"); - if (!_sharedPublic) { - let existingFolderInfo = getExistingFolderInfo(NSSearchPathDirectory.SharedPublicDirectory); - - if (existingFolderInfo) { - _sharedPublic = existingFolderInfo.folder; - _sharedPublic._path = existingFolderInfo.path; - _sharedPublic._isKnown = true; - } - } + return _pictures; + } + + let _sharedPublic: Folder; + export function sharedPublic(): Folder { + _checkPlatform("sharedPublic"); + if (!_sharedPublic) { + let existingFolderInfo = getExistingFolderInfo( + NSSearchPathDirectory.SharedPublicDirectory + ); - return _sharedPublic; + if (existingFolderInfo) { + _sharedPublic = existingFolderInfo.folder; + _sharedPublic._path = existingFolderInfo.path; + _sharedPublic._isKnown = true; } + } - function getExistingFolderInfo(pathDirectory: any /* NSSearchPathDirectory */): { folder: Folder; path: string } { - const fileAccess = (getFileAccess()); - const folderPath = fileAccess.getKnownPath(pathDirectory); - const folderInfo = fileAccess.getExistingFolder(folderPath); + return _sharedPublic; + } - if (folderInfo) { - return { - folder: createFolder(folderInfo), - path: folderPath - }; - } + function getExistingFolderInfo( + pathDirectory: any /* NSSearchPathDirectory */ + ): { folder: Folder; path: string } { + const fileAccess = _FileSystem.getFileAccess(); + const folderPath = fileAccess.getKnownPath(pathDirectory); + const folderInfo = fileAccess.getExistingFolder(folderPath); - return undefined; - } + if (folderInfo) { + return { + folder: _FileSystem.createFolder(folderInfo), + path: folderPath + }; + } + + return undefined; } + } } -export module path { +export namespace path { + export function normalize(path: string): string { + return _FileSystem.getFileAccess().normalizePath(path); + } - export function normalize(path: string): string { - return getFileAccess().normalizePath(path); - } + export function join(...paths: string[]): string { + const fileAccess = _FileSystem.getFileAccess(); - export function join(...paths: string[]): string { - const fileAccess = getFileAccess(); + return fileAccess.joinPaths(paths); + } - return fileAccess.joinPaths(paths); - } + export const separator = _FileSystem.getFileAccess().getPathSeparator(); +} + +export namespace worker { + export function terminate(): void { + if (FSWorker.isRunning()) FSWorker.getWorkerInstance().terminate(); + } - export const separator = getFileAccess().getPathSeparator(); + export function keepAlive(): void { + if (FSWorker.isRunning()) FSWorker.getWorkerInstance().keepAlive(); + } } From e2c8a6bb338f62d6cb980240a793f039bdf61991 Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 11:54:55 +1000 Subject: [PATCH 12/15] Add worker namespace declaration --- tns-core-modules/file-system/file-system.d.ts | 211 ++++++++++-------- 1 file changed, 118 insertions(+), 93 deletions(-) diff --git a/tns-core-modules/file-system/file-system.d.ts b/tns-core-modules/file-system/file-system.d.ts index d3c6e10232..d19dbc057d 100644 --- a/tns-core-modules/file-system/file-system.d.ts +++ b/tns-core-modules/file-system/file-system.d.ts @@ -7,277 +7,302 @@ * Represents a single entity on the file system. */ export class FileSystemEntity { - /** + /** * Gets the Date object specifying the last time this entity was modified. */ - lastModified: Date; + lastModified: Date; - /** + /** * Gets the name of the entity. */ - name: string; + name: string; - /** + /** * Gets the fully-qualified path (including the extension for a File) of the entity. */ - path: string; + path: string; - /** + /** * Gets the Folder object representing the parent of this entity. * Will be null for a root folder like Documents or Temporary. * This property is readonly. */ - parent: Folder; + parent: Folder; - /** + /** * Removes (deletes) the current Entity from the file system. */ - remove(): Promise; + remove(): Promise; - /** + /** * Removes (deletes) the current Entity from the file system synchronously. */ - removeSync(onError?: (error: any) => any): void; + removeSync(onError?: (error: any) => any): void; - /** + /** * Renames the current entity using the specified name. * @param newName The new name to be applied to the entity. */ - rename(newName: string): Promise; + rename(newName: string): Promise; - /** + /** * Renames the current entity synchronously, using the specified name. * @param newName The new name to be applied to the entity. */ - renameSync(newName: string, onError?: (error: any) => any): void; + renameSync(newName: string, onError?: (error: any) => any): void; } /** * Represents a File entity on the file system. */ export class File extends FileSystemEntity { - /** + /** * Checks whether a File with the specified path already exists. * @param path The path to check for. */ - static exists(path: string): boolean; + static exists(path: string): boolean; - /** + /** * Gets the extension of the file. */ - extension: string; + extension: string; - /** + /** * Gets the size in bytes of the file. */ - size: number; + size: number; - /** + /** * Gets a value indicating whether the file is currently locked, meaning a background operation associated with this file is running. */ - isLocked: boolean; + isLocked: boolean; - /** + /** * Gets or creates a File entity at the specified path. * @param path The path to get/create the file at. */ - static fromPath(path: string): File; + static fromPath(path: string): File; - /** + /** * Reads the content of the file as a string using the specified encoding (defaults to UTF-8). * @param encoding An optional value specifying the preferred encoding (defaults to UTF-8). */ - readText(encoding?: string): Promise; + readText(encoding?: string): Promise; - /** + /** * Reads the content of the file as a string synchronously, using the specified encoding (defaults to UTF-8). * @param onError An optional function to be called if some IO-error occurs. * @param encoding An optional value specifying the preferred encoding (defaults to UTF-8). */ - readTextSync(onError?: (error: any) => any, encoding?: string): string; + readTextSync(onError?: (error: any) => any, encoding?: string): string; - /** + /** * Reads the binary content of the file synchronously. * @param onError An optional function to be called if some IO-error occurs. */ - readSync(onError?: (error: any) => any): any; + readSync(onError?: (error: any) => any): any; - /** + /** * Writes the provided string to the file, using the specified encoding (defaults to UTF-8). * @param content The content to be saved to the file. * @param encoding An optional value specifying the preferred encoding (defaults to UTF-8). */ - writeText(content: string, encoding?: string): Promise; + writeText(content: string, encoding?: string): Promise; - /** + /** * Writes the provided string to the file synchronously, using the specified encoding (defaults to UTF-8). * @param content The content to be saved to the file. * @param onError An optional function to be called if some IO-error occurs. * @param encoding An optional value specifying the preferred encoding (defaults to UTF-8). */ - writeTextSync(content: string, onError?: (error: any) => any, encoding?: string): void; + writeTextSync( + content: string, + onError?: (error: any) => any, + encoding?: string + ): void; - /** + /** * Writes the provided binary content to the file synchronously. * @param content The binary content to be saved to the file. * @param onError An optional function to be called if some IO-error occurs. */ - writeSync(content: any, onError?: (error: any) => any): void; + writeSync(content: any, onError?: (error: any) => any): void; } /** * Represents a Folder (directory) entity on the file system. */ export class Folder extends FileSystemEntity { - /** + /** * Determines whether this instance is a KnownFolder (accessed through the KnownFolders object). */ - isKnown: boolean; + isKnown: boolean; - /** + /** * Gets or creates a Folder entity at the specified path. * @param path The path to get/create the folder at. */ - static fromPath(path: string): Folder; + static fromPath(path: string): Folder; - /** + /** * Checks whether a Folder with the specified path already exists. * @param path The path to check for. */ - static exists(path: string): boolean; + static exists(path: string): boolean; - /** + /** * Checks whether this Folder contains an Entity with the specified name. * The path of the folder is added to the name to resolve the complete path to check for. * @param name The name of the entity to check for. */ - contains(name: string): boolean; + contains(name: string): boolean; - /** + /** * Deletes all the files and folders (recursively), contained within this Folder. */ - clear(): Promise; + clear(): Promise; - /** + /** * Deletes all the files and folders (recursively), contained within this Folder synchronously. * @param onError An optional function to be called if some error occurs. */ - clearSync(onError?: (error: any) => void): void; + clearSync(onError?: (error: any) => void): void; - /** + /** * Gets or creates a File entity with the specified name within this Folder. * @param name The name of the file to get/create. */ - getFile(name: string): File; + getFile(name: string): File; - /** + /** * Gets or creates a Folder entity with the specified name within this Folder. * @param name The name of the folder to get/create. */ - getFolder(name: string): Folder; + getFolder(name: string): Folder; - /** + /** * Gets all the top-level entities residing within this folder. */ - getEntities(): Promise>; + getEntities(): Promise>; - /** + /** * Gets all the top-level entities residing within this folder synchronously. * @param onError An optional function to be called if some error occurs. */ - getEntitiesSync(onError?: (error: any) => any): Promise>; + getEntitiesSync( + onError?: (error: any) => any + ): Promise>; - /** + /** * Enumerates all the top-level FileSystem entities residing within this folder. * @param onEntity A callback that receives the current entity. If the callback returns false this will mean for the iteration to stop. */ - eachEntity(onEntity: (entity: FileSystemEntity) => boolean); + eachEntity(onEntity: (entity: FileSystemEntity) => boolean); } /** * Provides access to the top-level Folders instances that are accessible from the application. Use these as entry points to access the FileSystem. */ -export module knownFolders { - /** +export namespace knownFolders { + /** * Gets the Documents folder available for the current application. This Folder is private for the application and not accessible from Users/External apps. */ - export function documents(): Folder; + export function documents(): Folder; - /** + /** * Gets the Temporary (Caches) folder available for the current application. This Folder is private for the application and not accessible from Users/External apps. */ - export function temp(): Folder; + export function temp(): Folder; - /** + /** * Gets the root folder for the current application. This Folder is private for the application and not accessible from Users/External apps. * iOS - this folder is read-only and contains the app and all its resources. */ - export function currentApp(): Folder; + export function currentApp(): Folder; - /** + /** * Contains iOS-specific known folders. */ - module ios { - /** + namespace ios { + /** * Gets the NSLibraryDirectory. Note that the folder will not be created if it did not exist. */ - export function library(): Folder; + export function library(): Folder; - /** + /** * Gets the NSDeveloperDirectory. Note that the folder will not be created if it did not exist. */ - export function developer(): Folder; + export function developer(): Folder; - /** + /** * Gets the NSDesktopDirectory. Note that the folder will not be created if it did not exist. */ - export function desktop(): Folder; + export function desktop(): Folder; - /** + /** * Gets the NSDownloadsDirectory. Note that the folder will not be created if it did not exist. */ - export function downloads(): Folder; + export function downloads(): Folder; - /** + /** * Gets the NSMoviesDirectory. Note that the folder will not be created if it did not exist. */ - export function movies(): Folder; + export function movies(): Folder; - /** + /** * Gets the NSMusicDirectory. Note that the folder will not be created if it did not exist. */ - export function music(): Folder; + export function music(): Folder; - /** + /** * Gets the NSPicturesDirectory. Note that the folder will not be created if it did not exist. */ - export function pictures(): Folder; + export function pictures(): Folder; - /** + /** * Gets the NSSharedPublicDirectory. Note that the folder will not be created if it did not exist. */ - export function sharedPublic(): Folder; - } + export function sharedPublic(): Folder; + } } /** * Enables path-specific operations like join, extension, etc. */ -export module path { - /** +export namespace path { + /** * Normalizes a path, taking care of occurrances like ".." and "//". * @param path The path to be normalized. */ - export function normalize(path: string): string; + export function normalize(path: string): string; - /** + /** * Joins all the provided string components, forming a valid and normalized path. * @param paths An array of string components to be joined. */ - export function join(...paths: string[]): string; + export function join(...paths: string[]): string; - /** + /** * Gets the string used to separate file paths. */ - export const separator: string; + export const separator: string; +} + +/** + * Enables control over the lifespane of a File System Worker. + */ +export namespace worker { + /** + * Terminates the Worker on its next run loop. + */ + export function terminate(): void; + /** + * Prevents the Worker from closing due to inactivity. + * + * By default File System Workers are closed after a period of inactivity. After calling this method the Worker will only close if; + * * The application raises a `lowMemoryEvent` + * * The `terminate`method is called. + * * The application is closed. + */ + export function keepAlive(): void; } From 6da6b9f6a83406da7fe682a48afe5f929876c2bc Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 15:08:42 +1000 Subject: [PATCH 13/15] Refactor Context is only used in worker so moved interface to worker.ts --- .../file-system/file-system-worker/components.ts | 4 ---- tns-core-modules/file-system/file-system-worker/worker.ts | 7 ++++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tns-core-modules/file-system/file-system-worker/components.ts b/tns-core-modules/file-system/file-system-worker/components.ts index d19f54a667..3cf0b7faa3 100644 --- a/tns-core-modules/file-system/file-system-worker/components.ts +++ b/tns-core-modules/file-system/file-system-worker/components.ts @@ -1,10 +1,6 @@ import { on, off, lowMemoryEvent } from "tns-core-modules/application"; export namespace FSWorker { - export interface Context extends Worker { - close: { (): void }; - onclose: { (): void }; - } export interface FSJob { id: string; diff --git a/tns-core-modules/file-system/file-system-worker/worker.ts b/tns-core-modules/file-system/file-system-worker/worker.ts index 7318ed7ddd..1785e8c887 100644 --- a/tns-core-modules/file-system/file-system-worker/worker.ts +++ b/tns-core-modules/file-system/file-system-worker/worker.ts @@ -2,12 +2,17 @@ import "globals"; import { FileSystemAccess } from "tns-core-modules/file-system/file-system-access"; import { FSWorker, WorkerTimer } from "./components"; +interface Context extends Worker { + close: { (): void }; + onclose: { (): void }; +} + namespace Worker { declare let self: any; declare let fileAccess: FileSystemAccess; export const Jobs: string[] = []; - export const context: FSWorker.Context = self; + export const context: Context = self; export const Timer = new WorkerTimer(context.close); export function postResult(id, message) { From 98b2f94383edf7a97a029b2bb2ab7a945d30a7f7 Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 15:11:06 +1000 Subject: [PATCH 14/15] Refactor components.ts FSJobs interface is only worker and not needed to be exported. removed from FSWorker namespace and renamed to WorkerJobs (for semantics). --- .../file-system-worker/components.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/tns-core-modules/file-system/file-system-worker/components.ts b/tns-core-modules/file-system/file-system-worker/components.ts index 3cf0b7faa3..917af2bb39 100644 --- a/tns-core-modules/file-system/file-system-worker/components.ts +++ b/tns-core-modules/file-system/file-system-worker/components.ts @@ -1,13 +1,12 @@ import { on, off, lowMemoryEvent } from "tns-core-modules/application"; -export namespace FSWorker { - - export interface FSJob { - id: string; - onError: { (error: any): void }; - onSuccess: { (message: any): void }; - } +interface WorkerJob { + id: string; + onError: { (error: any): void }; + onSuccess: { (message: any): void }; +} +export namespace FSWorker { export enum WorkType { file, folder, @@ -31,7 +30,7 @@ export namespace FSWorker { closed = "WorkerCloseRequestFulfilled" } - export let fileWorker: Instance; + let fileWorker: Instance; export const isRunning = (): boolean => fileWorker instanceof Instance; export const getWorkerInstance = (): Instance => fileWorker; export const createWorkerInstance = (): Instance => @@ -58,7 +57,7 @@ export namespace FSWorker { export class Instance { private _worker: Worker; - private _jobs: FSJob[]; + private _jobs: WorkerJob[]; constructor() { this._jobs = []; @@ -87,7 +86,7 @@ export namespace FSWorker { return this._jobs; } - public getJob(id: string): FSJob { + public getJob(id: string): WorkerJob { const job = this.jobs.find(job => job.id === id); this.jobs.splice(this.jobs.indexOf(job), 1); return job; From be6605ec81fb6fc52e283a97e2aa263ba0d73dc9 Mon Sep 17 00:00:00 2001 From: mudlabs Date: Sat, 13 Jul 2019 15:11:42 +1000 Subject: [PATCH 15/15] Update import paths (relative) --- tns-core-modules/file-system/file-system-worker/components.ts | 2 +- tns-core-modules/file-system/file-system-worker/worker.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tns-core-modules/file-system/file-system-worker/components.ts b/tns-core-modules/file-system/file-system-worker/components.ts index 917af2bb39..cd9f8708d2 100644 --- a/tns-core-modules/file-system/file-system-worker/components.ts +++ b/tns-core-modules/file-system/file-system-worker/components.ts @@ -1,4 +1,4 @@ -import { on, off, lowMemoryEvent } from "tns-core-modules/application"; +import { on, off, lowMemoryEvent } from "../../application"; interface WorkerJob { id: string; diff --git a/tns-core-modules/file-system/file-system-worker/worker.ts b/tns-core-modules/file-system/file-system-worker/worker.ts index 1785e8c887..4ebf2c84c4 100644 --- a/tns-core-modules/file-system/file-system-worker/worker.ts +++ b/tns-core-modules/file-system/file-system-worker/worker.ts @@ -1,5 +1,5 @@ import "globals"; -import { FileSystemAccess } from "tns-core-modules/file-system/file-system-access"; +import { FileSystemAccess } from "../file-system-access"; import { FSWorker, WorkerTimer } from "./components"; interface Context extends Worker {