@wixc3/patterns
Advanced tools
Comparing version 14.1.3 to 15.0.0
@@ -77,8 +77,13 @@ import { GroupConstraints } from './constraints'; | ||
/** | ||
* @param disposable a function or object with a dispose method | ||
* @param options if string, will be used as group name | ||
* @param name - disposable name for error messages | ||
* @param disposable - a function or object with a dispose method | ||
* @returns a function to remove the disposable | ||
*/ | ||
add(...[nameOrOptions, disposable]: [id: string, disposable: DisposableItem] | [options: DisposableOptions]): () => void; | ||
add(name: string, disposable: DisposableItem): () => void; | ||
/** | ||
* @param options must include a [dispose] function or object with a dispose method | ||
* @returns a function to remove the disposable | ||
*/ | ||
add(options: DisposableOptions): () => void; | ||
/** | ||
* removes a disposable from all disposal group | ||
@@ -85,0 +90,0 @@ */ |
@@ -85,7 +85,3 @@ "use strict"; | ||
} | ||
/** | ||
* @param disposable a function or object with a dispose method | ||
* @param options if string, will be used as group name | ||
* @returns a function to remove the disposable | ||
*/ | ||
// @internal | ||
add(...[nameOrOptions, disposable]) { | ||
@@ -92,0 +88,0 @@ if (typeof nameOrOptions === 'string') { |
@@ -10,2 +10,5 @@ import { Disposables } from '.'; | ||
}; | ||
type OPTIONS = Partial<typeof DISPOSAL_GUARD_DEFAULTS>; | ||
type GUARDED_FN_ASYNC<T> = () => Promise<T>; | ||
type GUARDED_FN_SYNC<T> = () => T; | ||
/** | ||
@@ -30,5 +33,8 @@ * Adds dispose-safe methods to Disposables: | ||
* // will throw if disposed, delays disposal until done is called | ||
* using _ = this.disposables.guard() | ||
* await somePromise // if dispose is called while the code awaits, new guards will throw, but actual disposal will not begin | ||
* } // after the method exists, disposal may begin | ||
* return await this.disposables.guard(async () =>{ | ||
* // do something | ||
* return await somePromise // if dispose is called while the code awaits, new guards will throw, but actual disposal will not begin | ||
* }) | ||
* // disposal may begin | ||
* } | ||
* } | ||
@@ -70,28 +76,17 @@ * ``` | ||
* - delays disposal actual until the current flow is done | ||
* @example with "using" keyword | ||
* ```ts | ||
* { | ||
* // this will throw if disposed | ||
* using _ = this.guard({timeout: 1000, name:'something'}); | ||
* // do something | ||
* } | ||
* // disposal may begin | ||
* ``` | ||
* | ||
* @example without THE "using" keyword | ||
* @example | ||
* ```ts | ||
* // this will throw if disposed | ||
* const done = this.guard({timeout: 1000, name:'something'}); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* // disposal may begin | ||
* } | ||
* this.guard(()=> { | ||
* // do something | ||
* // if dispose is called while the code executes, | ||
* // new guards will throw, but actual disposal will not begin | ||
* }, {timeout: 1000, name:'something'}); | ||
* // disposal may begin | ||
* ``` | ||
*/ | ||
guard(options?: Partial<typeof DISPOSAL_GUARD_DEFAULTS>): import("promise-assist").PromiseResolveCb<void> & { | ||
[Symbol.dispose]: import("promise-assist").PromiseResolveCb<void>; | ||
}; | ||
guard<T>(fn: GUARDED_FN_ASYNC<T>, options?: OPTIONS): Promise<T>; | ||
guard<T>(fn: GUARDED_FN_SYNC<T>, options?: OPTIONS): T; | ||
guard<_T>(options?: OPTIONS): void; | ||
/** | ||
@@ -107,8 +102,4 @@ * a disposal safe setTimeout | ||
setInterval(fn: () => void, interval: number): ReturnType<typeof setInterval>; | ||
/** | ||
* Support for the "using" keyword | ||
*/ | ||
[Symbol.asyncDispose]: () => Promise<void>; | ||
} | ||
export {}; | ||
//# sourceMappingURL=safe-disposable.d.ts.map |
"use strict"; | ||
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { | ||
if (value !== null && value !== void 0) { | ||
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); | ||
var dispose; | ||
if (async) { | ||
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); | ||
dispose = value[Symbol.asyncDispose]; | ||
} | ||
if (dispose === void 0) { | ||
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); | ||
dispose = value[Symbol.dispose]; | ||
} | ||
if (typeof dispose !== "function") throw new TypeError("Object not disposable."); | ||
env.stack.push({ value: value, dispose: dispose, async: async }); | ||
} | ||
else if (async) { | ||
env.stack.push({ async: true }); | ||
} | ||
return value; | ||
}; | ||
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) { | ||
return function (env) { | ||
function fail(e) { | ||
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; | ||
env.hasError = true; | ||
} | ||
function next() { | ||
while (env.stack.length) { | ||
var rec = env.stack.pop(); | ||
try { | ||
var result = rec.dispose && rec.dispose.call(rec.value); | ||
if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); | ||
} | ||
catch (e) { | ||
fail(e); | ||
} | ||
} | ||
if (env.hasError) throw env.error; | ||
} | ||
return next(); | ||
}; | ||
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { | ||
var e = new Error(message); | ||
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; | ||
}); | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -77,5 +31,8 @@ exports.SafeDisposable = void 0; | ||
* // will throw if disposed, delays disposal until done is called | ||
* using _ = this.disposables.guard() | ||
* await somePromise // if dispose is called while the code awaits, new guards will throw, but actual disposal will not begin | ||
* } // after the method exists, disposal may begin | ||
* return await this.disposables.guard(async () =>{ | ||
* // do something | ||
* return await somePromise // if dispose is called while the code awaits, new guards will throw, but actual disposal will not begin | ||
* }) | ||
* // disposal may begin | ||
* } | ||
* } | ||
@@ -91,6 +48,2 @@ * ``` | ||
this.intervals = new Set(); | ||
/** | ||
* Support for the "using" keyword | ||
*/ | ||
this[_a] = () => this.dispose(); | ||
this.registerGroup(DELAY_DISPOSAL, { before: 'default' }); | ||
@@ -129,39 +82,6 @@ this.add('dispose timeouts and intervals', () => { | ||
} | ||
/** | ||
* After disposal starts, it's necessary to avoid executing some code. `guard` is used for those cases. | ||
* | ||
* <b>for example:</b> after fileRemover.dispose(), fileRemover.remove() should throw. | ||
* | ||
* `guard` will: | ||
* | ||
* - throws if disposal started/finished | ||
* - delays disposal actual until the current flow is done | ||
* @example with "using" keyword | ||
* ```ts | ||
* { | ||
* // this will throw if disposed | ||
* using _ = this.guard({timeout: 1000, name:'something'}); | ||
* // do something | ||
* } | ||
* // disposal may begin | ||
* ``` | ||
* | ||
* @example without THE "using" keyword | ||
* ```ts | ||
* // this will throw if disposed | ||
* const done = this.guard({timeout: 1000, name:'something'}); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* // disposal may begin | ||
* } | ||
* ``` | ||
*/ | ||
guard(options) { | ||
const { usedWhileDisposing, name, timeout } = { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
...(options !== null && options !== void 0 ? options : {}), | ||
}; | ||
// guard<T>(options?: OPTIONS): { [Symbol.dispose]: () => void }; | ||
// @internal | ||
guard(fnOrOptions, options) { | ||
const { fn, options: { name, timeout, usedWhileDisposing }, } = extractArgs(fnOrOptions, options); | ||
if (this.isDisposed && !(this._isDisposing && usedWhileDisposing)) { | ||
@@ -178,3 +98,8 @@ throw new Error('Instance was disposed'); | ||
canDispose.then(removeGuard, removeGuard); | ||
return Object.assign(done, { [Symbol.dispose]: done }); | ||
return executeCode(fn, done); | ||
/** | ||
* Support for the "using" keyword | ||
* uncomment when supported in browsers | ||
*/ | ||
// || { [Symbol.dispose]: done }; | ||
} | ||
@@ -186,5 +111,3 @@ /** | ||
setTimeout(fn, timeout) { | ||
const env_1 = { stack: [], error: void 0, hasError: false }; | ||
try { | ||
const _ = __addDisposableResource(env_1, this.guard(), false); | ||
return this.guard(() => { | ||
const handle = setTimeout(() => { | ||
@@ -198,10 +121,3 @@ this.timeouts.delete(handle); | ||
return handle; | ||
} | ||
catch (e_1) { | ||
env_1.error = e_1; | ||
env_1.hasError = true; | ||
} | ||
finally { | ||
__disposeResources(env_1); | ||
} | ||
}); | ||
} | ||
@@ -213,5 +129,3 @@ /** | ||
setInterval(fn, interval) { | ||
const env_2 = { stack: [], error: void 0, hasError: false }; | ||
try { | ||
const _ = __addDisposableResource(env_2, this.guard(), false); | ||
return this.guard(() => { | ||
const handle = setInterval(() => { | ||
@@ -224,14 +138,50 @@ if (!this.isDisposed) { | ||
return handle; | ||
}); | ||
} | ||
} | ||
exports.SafeDisposable = SafeDisposable; | ||
function extractArgs(fnOrOptions, options) { | ||
if (fnOrOptions instanceof Function) { | ||
return { | ||
fn: fnOrOptions, | ||
options: { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
...(options !== null && options !== void 0 ? options : {}), | ||
}, | ||
}; | ||
} | ||
else { | ||
return { | ||
fn: null, | ||
options: { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
...(fnOrOptions !== null && fnOrOptions !== void 0 ? fnOrOptions : {}), | ||
}, | ||
}; | ||
} | ||
} | ||
function executeCode(fn, done) { | ||
let result; | ||
if (fn) { | ||
try { | ||
result = fn(); | ||
if (!(result instanceof Promise)) { | ||
done(); | ||
return result; | ||
} | ||
} | ||
catch (e_2) { | ||
env_2.error = e_2; | ||
env_2.hasError = true; | ||
catch (e) { | ||
done(); | ||
throw e; | ||
} | ||
finally { | ||
__disposeResources(env_2); | ||
} | ||
return result.finally(done); | ||
} | ||
/** | ||
* Support for the "using" keyword | ||
* uncomment when supported in browsers | ||
*/ | ||
// return | ||
// TODO remove when "using" is supported in browsers | ||
return done(); | ||
} | ||
exports.SafeDisposable = SafeDisposable; | ||
_a = Symbol.asyncDispose; | ||
//# sourceMappingURL=safe-disposable.js.map |
@@ -77,8 +77,13 @@ import { GroupConstraints } from './constraints'; | ||
/** | ||
* @param disposable a function or object with a dispose method | ||
* @param options if string, will be used as group name | ||
* @param name - disposable name for error messages | ||
* @param disposable - a function or object with a dispose method | ||
* @returns a function to remove the disposable | ||
*/ | ||
add(...[nameOrOptions, disposable]: [id: string, disposable: DisposableItem] | [options: DisposableOptions]): () => void; | ||
add(name: string, disposable: DisposableItem): () => void; | ||
/** | ||
* @param options must include a [dispose] function or object with a dispose method | ||
* @returns a function to remove the disposable | ||
*/ | ||
add(options: DisposableOptions): () => void; | ||
/** | ||
* removes a disposable from all disposal group | ||
@@ -85,0 +90,0 @@ */ |
@@ -81,7 +81,3 @@ import { getGroupConstrainedIndex, normalizeConstraints } from './constraints'; | ||
} | ||
/** | ||
* @param disposable a function or object with a dispose method | ||
* @param options if string, will be used as group name | ||
* @returns a function to remove the disposable | ||
*/ | ||
// @internal | ||
add(...[nameOrOptions, disposable]) { | ||
@@ -88,0 +84,0 @@ if (typeof nameOrOptions === 'string') { |
@@ -10,2 +10,5 @@ import { Disposables } from '.'; | ||
}; | ||
type OPTIONS = Partial<typeof DISPOSAL_GUARD_DEFAULTS>; | ||
type GUARDED_FN_ASYNC<T> = () => Promise<T>; | ||
type GUARDED_FN_SYNC<T> = () => T; | ||
/** | ||
@@ -30,5 +33,8 @@ * Adds dispose-safe methods to Disposables: | ||
* // will throw if disposed, delays disposal until done is called | ||
* using _ = this.disposables.guard() | ||
* await somePromise // if dispose is called while the code awaits, new guards will throw, but actual disposal will not begin | ||
* } // after the method exists, disposal may begin | ||
* return await this.disposables.guard(async () =>{ | ||
* // do something | ||
* return await somePromise // if dispose is called while the code awaits, new guards will throw, but actual disposal will not begin | ||
* }) | ||
* // disposal may begin | ||
* } | ||
* } | ||
@@ -70,28 +76,17 @@ * ``` | ||
* - delays disposal actual until the current flow is done | ||
* @example with "using" keyword | ||
* ```ts | ||
* { | ||
* // this will throw if disposed | ||
* using _ = this.guard({timeout: 1000, name:'something'}); | ||
* // do something | ||
* } | ||
* // disposal may begin | ||
* ``` | ||
* | ||
* @example without THE "using" keyword | ||
* @example | ||
* ```ts | ||
* // this will throw if disposed | ||
* const done = this.guard({timeout: 1000, name:'something'}); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* // disposal may begin | ||
* } | ||
* this.guard(()=> { | ||
* // do something | ||
* // if dispose is called while the code executes, | ||
* // new guards will throw, but actual disposal will not begin | ||
* }, {timeout: 1000, name:'something'}); | ||
* // disposal may begin | ||
* ``` | ||
*/ | ||
guard(options?: Partial<typeof DISPOSAL_GUARD_DEFAULTS>): import("promise-assist").PromiseResolveCb<void> & { | ||
[Symbol.dispose]: import("promise-assist").PromiseResolveCb<void>; | ||
}; | ||
guard<T>(fn: GUARDED_FN_ASYNC<T>, options?: OPTIONS): Promise<T>; | ||
guard<T>(fn: GUARDED_FN_SYNC<T>, options?: OPTIONS): T; | ||
guard<_T>(options?: OPTIONS): void; | ||
/** | ||
@@ -107,8 +102,4 @@ * a disposal safe setTimeout | ||
setInterval(fn: () => void, interval: number): ReturnType<typeof setInterval>; | ||
/** | ||
* Support for the "using" keyword | ||
*/ | ||
[Symbol.asyncDispose]: () => Promise<void>; | ||
} | ||
export {}; | ||
//# sourceMappingURL=safe-disposable.d.ts.map |
@@ -1,47 +0,1 @@ | ||
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { | ||
if (value !== null && value !== void 0) { | ||
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); | ||
var dispose; | ||
if (async) { | ||
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); | ||
dispose = value[Symbol.asyncDispose]; | ||
} | ||
if (dispose === void 0) { | ||
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); | ||
dispose = value[Symbol.dispose]; | ||
} | ||
if (typeof dispose !== "function") throw new TypeError("Object not disposable."); | ||
env.stack.push({ value: value, dispose: dispose, async: async }); | ||
} | ||
else if (async) { | ||
env.stack.push({ async: true }); | ||
} | ||
return value; | ||
}; | ||
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) { | ||
return function (env) { | ||
function fail(e) { | ||
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; | ||
env.hasError = true; | ||
} | ||
function next() { | ||
while (env.stack.length) { | ||
var rec = env.stack.pop(); | ||
try { | ||
var result = rec.dispose && rec.dispose.call(rec.value); | ||
if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); | ||
} | ||
catch (e) { | ||
fail(e); | ||
} | ||
} | ||
if (env.hasError) throw env.error; | ||
} | ||
return next(); | ||
}; | ||
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { | ||
var e = new Error(message); | ||
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; | ||
}); | ||
var _a; | ||
import { Disposables } from '.'; | ||
@@ -74,5 +28,8 @@ import { deferred } from 'promise-assist'; | ||
* // will throw if disposed, delays disposal until done is called | ||
* using _ = this.disposables.guard() | ||
* await somePromise // if dispose is called while the code awaits, new guards will throw, but actual disposal will not begin | ||
* } // after the method exists, disposal may begin | ||
* return await this.disposables.guard(async () =>{ | ||
* // do something | ||
* return await somePromise // if dispose is called while the code awaits, new guards will throw, but actual disposal will not begin | ||
* }) | ||
* // disposal may begin | ||
* } | ||
* } | ||
@@ -88,6 +45,2 @@ * ``` | ||
this.intervals = new Set(); | ||
/** | ||
* Support for the "using" keyword | ||
*/ | ||
this[_a] = () => this.dispose(); | ||
this.registerGroup(DELAY_DISPOSAL, { before: 'default' }); | ||
@@ -126,39 +79,6 @@ this.add('dispose timeouts and intervals', () => { | ||
} | ||
/** | ||
* After disposal starts, it's necessary to avoid executing some code. `guard` is used for those cases. | ||
* | ||
* <b>for example:</b> after fileRemover.dispose(), fileRemover.remove() should throw. | ||
* | ||
* `guard` will: | ||
* | ||
* - throws if disposal started/finished | ||
* - delays disposal actual until the current flow is done | ||
* @example with "using" keyword | ||
* ```ts | ||
* { | ||
* // this will throw if disposed | ||
* using _ = this.guard({timeout: 1000, name:'something'}); | ||
* // do something | ||
* } | ||
* // disposal may begin | ||
* ``` | ||
* | ||
* @example without THE "using" keyword | ||
* ```ts | ||
* // this will throw if disposed | ||
* const done = this.guard({timeout: 1000, name:'something'}); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* // disposal may begin | ||
* } | ||
* ``` | ||
*/ | ||
guard(options) { | ||
const { usedWhileDisposing, name, timeout } = { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
...(options !== null && options !== void 0 ? options : {}), | ||
}; | ||
// guard<T>(options?: OPTIONS): { [Symbol.dispose]: () => void }; | ||
// @internal | ||
guard(fnOrOptions, options) { | ||
const { fn, options: { name, timeout, usedWhileDisposing }, } = extractArgs(fnOrOptions, options); | ||
if (this.isDisposed && !(this._isDisposing && usedWhileDisposing)) { | ||
@@ -175,3 +95,8 @@ throw new Error('Instance was disposed'); | ||
canDispose.then(removeGuard, removeGuard); | ||
return Object.assign(done, { [Symbol.dispose]: done }); | ||
return executeCode(fn, done); | ||
/** | ||
* Support for the "using" keyword | ||
* uncomment when supported in browsers | ||
*/ | ||
// || { [Symbol.dispose]: done }; | ||
} | ||
@@ -183,5 +108,3 @@ /** | ||
setTimeout(fn, timeout) { | ||
const env_1 = { stack: [], error: void 0, hasError: false }; | ||
try { | ||
const _ = __addDisposableResource(env_1, this.guard(), false); | ||
return this.guard(() => { | ||
const handle = setTimeout(() => { | ||
@@ -195,10 +118,3 @@ this.timeouts.delete(handle); | ||
return handle; | ||
} | ||
catch (e_1) { | ||
env_1.error = e_1; | ||
env_1.hasError = true; | ||
} | ||
finally { | ||
__disposeResources(env_1); | ||
} | ||
}); | ||
} | ||
@@ -210,5 +126,3 @@ /** | ||
setInterval(fn, interval) { | ||
const env_2 = { stack: [], error: void 0, hasError: false }; | ||
try { | ||
const _ = __addDisposableResource(env_2, this.guard(), false); | ||
return this.guard(() => { | ||
const handle = setInterval(() => { | ||
@@ -221,13 +135,49 @@ if (!this.isDisposed) { | ||
return handle; | ||
}); | ||
} | ||
} | ||
function extractArgs(fnOrOptions, options) { | ||
if (fnOrOptions instanceof Function) { | ||
return { | ||
fn: fnOrOptions, | ||
options: { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
...(options !== null && options !== void 0 ? options : {}), | ||
}, | ||
}; | ||
} | ||
else { | ||
return { | ||
fn: null, | ||
options: { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
...(fnOrOptions !== null && fnOrOptions !== void 0 ? fnOrOptions : {}), | ||
}, | ||
}; | ||
} | ||
} | ||
function executeCode(fn, done) { | ||
let result; | ||
if (fn) { | ||
try { | ||
result = fn(); | ||
if (!(result instanceof Promise)) { | ||
done(); | ||
return result; | ||
} | ||
} | ||
catch (e_2) { | ||
env_2.error = e_2; | ||
env_2.hasError = true; | ||
catch (e) { | ||
done(); | ||
throw e; | ||
} | ||
finally { | ||
__disposeResources(env_2); | ||
} | ||
return result.finally(done); | ||
} | ||
/** | ||
* Support for the "using" keyword | ||
* uncomment when supported in browsers | ||
*/ | ||
// return | ||
// TODO remove when "using" is supported in browsers | ||
return done(); | ||
} | ||
_a = Symbol.asyncDispose; | ||
//# sourceMappingURL=safe-disposable.js.map |
{ | ||
"name": "@wixc3/patterns", | ||
"version": "14.1.3", | ||
"version": "15.0.0", | ||
"description": "A utility for saving objects to be disposed", | ||
@@ -21,5 +21,5 @@ "main": "dist/cjs/index.js", | ||
"dependencies": { | ||
"@wixc3/common": "^14.1.0", | ||
"@wixc3/common": "^15.0.0", | ||
"promise-assist": "^2.0.1" | ||
} | ||
} |
@@ -107,8 +107,14 @@ import { DisposalGroup, getGroupConstrainedIndex, GroupConstraints, normalizeConstraints } from './constraints'; | ||
} | ||
/** | ||
* @param disposable a function or object with a dispose method | ||
* @param options if string, will be used as group name | ||
* @param name - disposable name for error messages | ||
* @param disposable - a function or object with a dispose method | ||
* @returns a function to remove the disposable | ||
*/ | ||
add(name: string, disposable: DisposableItem): () => void; | ||
/** | ||
* @param options must include a [dispose] function or object with a dispose method | ||
* @returns a function to remove the disposable | ||
*/ | ||
add(options: DisposableOptions): () => void; | ||
// @internal | ||
add(...[nameOrOptions, disposable]: [id: string, disposable: DisposableItem] | [options: DisposableOptions]) { | ||
@@ -115,0 +121,0 @@ if (typeof nameOrOptions === 'string') { |
@@ -14,2 +14,8 @@ import { Disposables } from '.'; | ||
}; | ||
type OPTIONS = Partial<typeof DISPOSAL_GUARD_DEFAULTS>; | ||
type GUARDED_FN_ASYNC<T> = () => Promise<T>; | ||
type GUARDED_FN_SYNC<T> = () => T; | ||
type GUARDED_FN<T> = GUARDED_FN_SYNC<T> | GUARDED_FN_ASYNC<T>; | ||
/** | ||
@@ -34,5 +40,8 @@ * Adds dispose-safe methods to Disposables: | ||
* // will throw if disposed, delays disposal until done is called | ||
* using _ = this.disposables.guard() | ||
* await somePromise // if dispose is called while the code awaits, new guards will throw, but actual disposal will not begin | ||
* } // after the method exists, disposal may begin | ||
* return await this.disposables.guard(async () =>{ | ||
* // do something | ||
* return await somePromise // if dispose is called while the code awaits, new guards will throw, but actual disposal will not begin | ||
* }) | ||
* // disposal may begin | ||
* } | ||
* } | ||
@@ -93,30 +102,24 @@ * ``` | ||
* - delays disposal actual until the current flow is done | ||
* @example with "using" keyword | ||
* ```ts | ||
* { | ||
* // this will throw if disposed | ||
* using _ = this.guard({timeout: 1000, name:'something'}); | ||
* // do something | ||
* } | ||
* // disposal may begin | ||
* ``` | ||
* | ||
* @example without THE "using" keyword | ||
* @example | ||
* ```ts | ||
* // this will throw if disposed | ||
* const done = this.guard({timeout: 1000, name:'something'}); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* // disposal may begin | ||
* } | ||
* this.guard(()=> { | ||
* // do something | ||
* // if dispose is called while the code executes, | ||
* // new guards will throw, but actual disposal will not begin | ||
* }, {timeout: 1000, name:'something'}); | ||
* // disposal may begin | ||
* ``` | ||
*/ | ||
guard(options?: Partial<typeof DISPOSAL_GUARD_DEFAULTS>) { | ||
const { usedWhileDisposing, name, timeout } = { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
...(options ?? {}), | ||
}; | ||
guard<T>(fn: GUARDED_FN_ASYNC<T>, options?: OPTIONS): Promise<T>; | ||
guard<T>(fn: GUARDED_FN_SYNC<T>, options?: OPTIONS): T; | ||
guard<_T>(options?: OPTIONS): void; | ||
// guard<T>(options?: OPTIONS): { [Symbol.dispose]: () => void }; | ||
// @internal | ||
guard<T>(fnOrOptions?: OPTIONS | GUARDED_FN<T>, options?: OPTIONS) { | ||
const { | ||
fn, | ||
options: { name, timeout, usedWhileDisposing }, | ||
} = extractArgs<T>(fnOrOptions, options); | ||
@@ -135,3 +138,9 @@ if (this.isDisposed && !(this._isDisposing && usedWhileDisposing)) { | ||
return Object.assign(done, { [Symbol.dispose]: done }); | ||
return executeCode(fn, done); | ||
/** | ||
* Support for the "using" keyword | ||
* uncomment when supported in browsers | ||
*/ | ||
// || { [Symbol.dispose]: done }; | ||
} | ||
@@ -144,11 +153,12 @@ | ||
setTimeout(fn: () => void, timeout: number): ReturnType<typeof setTimeout> { | ||
using _ = this.guard(); | ||
const handle = setTimeout(() => { | ||
this.timeouts.delete(handle); | ||
if (!this.isDisposed) { | ||
fn(); | ||
} | ||
}, timeout); | ||
this.timeouts.add(handle); | ||
return handle; | ||
return this.guard(() => { | ||
const handle = setTimeout(() => { | ||
this.timeouts.delete(handle); | ||
if (!this.isDisposed) { | ||
fn(); | ||
} | ||
}, timeout); | ||
this.timeouts.add(handle); | ||
return handle; | ||
}); | ||
} | ||
@@ -161,10 +171,11 @@ | ||
setInterval(fn: () => void, interval: number): ReturnType<typeof setInterval> { | ||
using _ = this.guard(); | ||
const handle = setInterval(() => { | ||
if (!this.isDisposed) { | ||
fn(); | ||
} | ||
}, interval); | ||
this.intervals.add(handle); | ||
return handle; | ||
return this.guard(() => { | ||
const handle = setInterval(() => { | ||
if (!this.isDisposed) { | ||
fn(); | ||
} | ||
}, interval); | ||
this.intervals.add(handle); | ||
return handle; | ||
}); | ||
} | ||
@@ -174,4 +185,50 @@ | ||
* Support for the "using" keyword | ||
* uncomment when supported in browsers | ||
*/ | ||
[Symbol.asyncDispose] = () => this.dispose(); | ||
// [Symbol.asyncDispose] = () => this.dispose(); | ||
} | ||
function extractArgs<T>(fnOrOptions?: OPTIONS | GUARDED_FN<T>, options?: OPTIONS) { | ||
if (fnOrOptions instanceof Function) { | ||
return { | ||
fn: fnOrOptions, | ||
options: { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
...(options ?? {}), | ||
}, | ||
}; | ||
} else { | ||
return { | ||
fn: null, | ||
options: { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
...(fnOrOptions ?? {}), | ||
}, | ||
}; | ||
} | ||
} | ||
function executeCode<T>(fn: GUARDED_FN<T> | null, done: () => void) { | ||
let result: T | Promise<T>; | ||
if (fn) { | ||
try { | ||
result = fn(); | ||
if (!(result instanceof Promise)) { | ||
done(); | ||
return result; | ||
} | ||
} catch (e) { | ||
done(); | ||
throw e; | ||
} | ||
return result.finally(done); | ||
} | ||
/** | ||
* Support for the "using" keyword | ||
* uncomment when supported in browsers | ||
*/ | ||
// return | ||
// TODO remove when "using" is supported in browsers | ||
return done(); | ||
} |
import { expect } from 'chai'; | ||
import { SafeDisposable } from '../disposables/safe-disposable'; | ||
import { deferred, sleep } from 'promise-assist'; | ||
import sinon from 'sinon'; | ||
@@ -41,3 +40,17 @@ describe('SafeDisposable class', () => { | ||
}); | ||
describe('sync/async', () => { | ||
describe('when no function is passed', () => { | ||
it('it does not block disposal', async () => { | ||
const disposable = new SafeDisposable('test'); | ||
disposable.guard(); | ||
await disposable.dispose(); | ||
}); | ||
}); | ||
/* | ||
The following suite tests the behavior of the "using" keyword, | ||
which is not supported in browsers. | ||
Uncomment when they are supported. | ||
describe('sync/async with "using" keyword', () => { | ||
it('sync does not delay disposal', async () => { | ||
@@ -58,3 +71,3 @@ const disposable = new SafeDisposable('name'); | ||
}); | ||
it('async (default) delays disposal until the guard is done', async () => { | ||
it('async delays disposal until the guard is done', async () => { | ||
const disposables = new SafeDisposable('test'); | ||
@@ -79,2 +92,41 @@ let disposeCalled = false; | ||
}); | ||
*/ | ||
describe('sync/async without "using" keyword', () => { | ||
it('sync does not delay disposal', async () => { | ||
const disposable = new SafeDisposable('name'); | ||
let disposeCalled = false; | ||
disposable.add('disposeCalled', () => { | ||
disposeCalled = true; | ||
}); | ||
expect( | ||
disposable.guard(() => 'guarded return value'), | ||
'guard return value', | ||
).to.eql('guarded return value'); | ||
const disposing = disposable.dispose(); | ||
await sleep(1); | ||
expect(disposeCalled).to.be.true; | ||
await disposing; | ||
}); | ||
it('async delays disposal until the guard is done', async () => { | ||
const disposables = new SafeDisposable('test'); | ||
let disposeCalled = false; | ||
disposables.add('disposeCalled', () => { | ||
disposeCalled = true; | ||
}); | ||
let disposing!: Promise<void>; | ||
const result = disposables.guard(async () => { | ||
disposing = disposables.dispose(); | ||
await sleep(1); | ||
expect(disposables.isDisposed, 'isDisposed').to.be.true; | ||
expect(disposeCalled, 'disposeCalled').to.be.false; | ||
return 'guarded return value'; | ||
}); | ||
expect(await result, 'guard return value').to.eql('guarded return value'); | ||
await disposing; | ||
expect(disposeCalled, 'disposeCalled after done()').to.be.true; | ||
}); | ||
}); | ||
}); | ||
@@ -119,12 +171,12 @@ describe('setTimeout', () => { | ||
}); | ||
describe('"using" keyword', () => { | ||
it('disposes when the using block exists', async () => { | ||
const spy = sinon.spy(); | ||
{ | ||
await using disposable = new SafeDisposable('test'); | ||
disposable.add('wasDisposed', spy); | ||
} | ||
expect(spy.callCount).to.equal(1); | ||
}); | ||
}); | ||
// describe('"using" keyword', () => { | ||
// it('disposes when the using block exists', async () => { | ||
// const spy = sinon.spy(); | ||
// { | ||
// await using disposable = new SafeDisposable('test'); | ||
// disposable.add('wasDisposed', spy); | ||
// } | ||
// expect(spy.callCount).to.equal(1); | ||
// }); | ||
// }); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
256003
4948
+ Added@wixc3/common@15.1.1(transitive)
- Removed@wixc3/common@14.1.0(transitive)
Updated@wixc3/common@^15.0.0