@wixc3/patterns
Advanced tools
Comparing version 11.3.0 to 12.0.1
@@ -1,2 +0,2 @@ | ||
import type { Disposables } from './disposable'; | ||
import type { DisposablesGroup } from './disposables-group'; | ||
export type GroupConstraints = { | ||
@@ -11,5 +11,10 @@ before: string; | ||
name: string; | ||
disposables: Disposables; | ||
disposables: DisposablesGroup; | ||
} | ||
export declare const getValidatedConstantsGroups: (_constraints: GroupConstraints[], groups: DisposalGroup[]) => { | ||
/** | ||
* @internal | ||
* given groups and a lists of constraints, throws if the constraints contradict each other | ||
* @returns the indices of the dominant constrains | ||
*/ | ||
export declare const getGroupConstrainedIndex: (newGroupConstrains: GroupConstraints[], existingGroups: DisposalGroup[]) => { | ||
lastAfter: number; | ||
@@ -16,0 +21,0 @@ firstBefore: number; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.normalizeConstraints = exports.getValidatedConstantsGroups = void 0; | ||
const getValidatedConstantsGroups = (_constraints, groups) => { | ||
exports.normalizeConstraints = exports.getGroupConstrainedIndex = void 0; | ||
/** | ||
* @internal | ||
* given groups and a lists of constraints, throws if the constraints contradict each other | ||
* @returns the indices of the dominant constrains | ||
*/ | ||
const getGroupConstrainedIndex = (newGroupConstrains, existingGroups) => { | ||
var _a, _b; | ||
let lastAfter = -1, firstBefore = Number.MAX_SAFE_INTEGER; | ||
_constraints.forEach(({ before, after }) => { | ||
newGroupConstrains.forEach(({ before, after }) => { | ||
if (before) { | ||
const index = groups.findIndex((g) => g.name === before); | ||
const index = existingGroups.findIndex((g) => g.name === before); | ||
if (index === -1) { | ||
@@ -16,3 +21,3 @@ throw new Error(`Invalid constraint: "before: ${before}" - group not found`); | ||
if (after) { | ||
const index = groups.findIndex((g) => g.name === after); | ||
const index = existingGroups.findIndex((g) => g.name === after); | ||
if (index === -1) { | ||
@@ -26,3 +31,3 @@ throw new Error(`Invalid constraint: "after: ${after}" - group not found`); | ||
if (lastAfter >= firstBefore) { | ||
throw new Error(`Invalid constraints: ${(_a = groups[lastAfter]) === null || _a === void 0 ? void 0 : _a.name} runs after ${(_b = groups[firstBefore]) === null || _b === void 0 ? void 0 : _b.name}, which contradicts prior constraints`); | ||
throw new Error(`Invalid constraints: ${(_a = existingGroups[lastAfter]) === null || _a === void 0 ? void 0 : _a.name} runs after ${(_b = existingGroups[firstBefore]) === null || _b === void 0 ? void 0 : _b.name}, which contradicts prior constraints`); | ||
} | ||
@@ -32,3 +37,3 @@ } | ||
}; | ||
exports.getValidatedConstantsGroups = getValidatedConstantsGroups; | ||
exports.getGroupConstrainedIndex = getGroupConstrainedIndex; | ||
const normalizeConstraints = (constraints, name, groups) => { | ||
@@ -35,0 +40,0 @@ const _constraints = Array.isArray(constraints) ? constraints : [constraints]; |
@@ -0,24 +1,95 @@ | ||
import { Disposables } from '.'; | ||
declare const DISPOSAL_GUARD_DEFAULTS: { | ||
name: string; | ||
timeout: number; | ||
usedWhileDisposing: boolean; | ||
}; | ||
/** | ||
* Disposables allow adding of disposal async functions, | ||
* when dispose is called, these functions will be run sequentially | ||
* A base class for disposable objects | ||
* @example | ||
* ```ts | ||
* class MyDisposable extends Disposable { | ||
* constructor() { | ||
* super(); | ||
* this.disposables.add(() => console.log('disposed')); | ||
* this.setTimeout(() => console.log('will be canceled upon disposal'), 1000); | ||
* } | ||
* async doSomething() { | ||
* // will throw if disposed, delays disposal until done is called | ||
* const done = this.disposalGuard(false, true); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* } | ||
* } | ||
* } | ||
*/ | ||
export declare function createSimpleDisposable(): Disposables; | ||
export declare class Disposables { | ||
private disposables; | ||
export declare class Disposable { | ||
private _isDisposed; | ||
private _isDisposing; | ||
readonly disposables: Disposables; | ||
private timeouts; | ||
private intervals; | ||
constructor(); | ||
/** | ||
* Starts instance disposal: | ||
* | ||
* **phase 1: disposing** | ||
* - isDisposed === true | ||
* - disposalGuard() will throw | ||
* - disposalGuard(true) will not throw (for methods that are used in the disposal process) | ||
* - disposable.dispose is awaited | ||
* | ||
* **phase 2: disposed done** | ||
* - disposalGuard(true) will throw | ||
*/ | ||
dispose(): Promise<void>; | ||
add(disposable: Disposable, timeout: number, name: string): () => boolean; | ||
remove(target: Disposable): void; | ||
list(): { | ||
name: string; | ||
timeout: number; | ||
}[]; | ||
/** | ||
* returns true if the disposal process started | ||
*/ | ||
get isDisposed(): boolean; | ||
/** | ||
* - throws if disposal started/finished | ||
* - in async mode, delays disposal until the returned fn called | ||
* @example async mode | ||
* ```ts | ||
* // this will throw if disposed | ||
* const done = this.disposalGuard({timeout: 1000, name:'something'}); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* } | ||
* @example sync mode | ||
* ```ts | ||
* // will throw if disposed | ||
* this.disposalGuard({async:false}); | ||
* @example usedWhileDisposing | ||
* ```ts | ||
* // will not throw if disposal didn't finished yet, even if dispose was called | ||
* this.disposalGuard({usedWhileDisposing:true, async:false}); | ||
*/ | ||
disposalGuard(options: { | ||
async: never; | ||
} & Partial<typeof DISPOSAL_GUARD_DEFAULTS>): () => void; | ||
disposalGuard(): () => void; | ||
disposalGuard(options: { | ||
async: false; | ||
usedWhileDisposing?: boolean; | ||
}): void; | ||
/** | ||
* a disposal safe setTimeout | ||
* checks disposal before execution and clears the timeout when the instance is disposed | ||
*/ | ||
setTimeout(fn: () => void, timeout: number): ReturnType<typeof setTimeout>; | ||
/** | ||
* a disposal safe setInterval | ||
* checks disposal before execution and clears the interval when the instance is disposed | ||
*/ | ||
setInterval(fn: () => void, interval: number): ReturnType<typeof setInterval>; | ||
} | ||
export type DisposeFunction = () => unknown; | ||
export type Disposable = { | ||
dispose: DisposeFunction; | ||
} | DisposeFunction; | ||
export type NamedDisposable = { | ||
timeout: number; | ||
name: string; | ||
}; | ||
export {}; | ||
//# sourceMappingURL=disposable.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Disposables = exports.createSimpleDisposable = void 0; | ||
exports.Disposable = void 0; | ||
const common_1 = require("@wixc3/common"); | ||
const _1 = require("."); | ||
const promise_assist_1 = require("promise-assist"); | ||
const DELAY_DISPOSAL = 'DELAY_DISPOSAL'; | ||
const DISPOSAL_GUARD_DEFAULTS = { | ||
name: 'disposalGuard', | ||
timeout: 5000, | ||
usedWhileDisposing: false, | ||
}; | ||
/** | ||
* Disposables allow adding of disposal async functions, | ||
* when dispose is called, these functions will be run sequentially | ||
* A base class for disposable objects | ||
* @example | ||
* ```ts | ||
* class MyDisposable extends Disposable { | ||
* constructor() { | ||
* super(); | ||
* this.disposables.add(() => console.log('disposed')); | ||
* this.setTimeout(() => console.log('will be canceled upon disposal'), 1000); | ||
* } | ||
* async doSomething() { | ||
* // will throw if disposed, delays disposal until done is called | ||
* const done = this.disposalGuard(false, true); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* } | ||
* } | ||
* } | ||
*/ | ||
function createSimpleDisposable() { | ||
return new Disposables(); | ||
} | ||
exports.createSimpleDisposable = createSimpleDisposable; | ||
class Disposables { | ||
class Disposable { | ||
constructor() { | ||
this.disposables = new Map(); | ||
this._isDisposed = false; | ||
this._isDisposing = false; | ||
this.disposables = new _1.Disposables(); | ||
this.timeouts = new Set(); | ||
this.intervals = new Set(); | ||
this.disposables.registerGroup(DELAY_DISPOSAL, { before: 'default' }); | ||
this.disposables.add(() => { | ||
this.timeouts.forEach((t) => clearTimeout(t)); | ||
this.intervals.forEach((i) => clearInterval(i)); | ||
}); | ||
} | ||
/** | ||
* Starts instance disposal: | ||
* | ||
* **phase 1: disposing** | ||
* - isDisposed === true | ||
* - disposalGuard() will throw | ||
* - disposalGuard(true) will not throw (for methods that are used in the disposal process) | ||
* - disposable.dispose is awaited | ||
* | ||
* **phase 2: disposed done** | ||
* - disposalGuard(true) will throw | ||
*/ | ||
async dispose() { | ||
const _disposables = Array.from(this.disposables).reverse(); | ||
this.disposables.clear(); | ||
for (const [disposable, details] of _disposables) { | ||
await (0, promise_assist_1.timeout)(disposeOf(disposable), details.timeout, `Disposal timed out: "${details.name}" after ${details.timeout}ms`); | ||
if (!this.isDisposed && !this._isDisposing) { | ||
this._isDisposing = true; | ||
await this.disposables.dispose(); | ||
this._isDisposed = true; | ||
this._isDisposing = false; | ||
} | ||
} | ||
add(disposable, timeout, name) { | ||
if (this.disposables.has(disposable)) { | ||
throw new Error(`Disposable already added`); | ||
/** | ||
* returns true if the disposal process started | ||
*/ | ||
get isDisposed() { | ||
return this._isDisposed || this._isDisposing; | ||
} | ||
disposalGuard(options) { | ||
const { async, usedWhileDisposing, name, timeout } = (0, common_1.defaults)(options || {}, { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
async: true, | ||
}); | ||
if (this.isDisposed && !(this._isDisposing && usedWhileDisposing)) { | ||
throw new Error('Instance was disposed'); | ||
} | ||
this.disposables.set(disposable, { timeout, name }); | ||
return () => this.disposables.delete(disposable); | ||
if (async) { | ||
const { promise: canDispose, resolve: done } = (0, promise_assist_1.deferred)(); | ||
const remove = this.disposables.add(() => canDispose, { | ||
group: DELAY_DISPOSAL, | ||
name, | ||
timeout, | ||
}); | ||
canDispose.then(remove).catch(common_1.noop); | ||
return done; | ||
} | ||
return; | ||
} | ||
remove(target) { | ||
this.disposables.delete(target); | ||
/** | ||
* a disposal safe setTimeout | ||
* checks disposal before execution and clears the timeout when the instance is disposed | ||
*/ | ||
setTimeout(fn, timeout) { | ||
this.disposalGuard({ async: false }); | ||
const handle = globalThis.setTimeout(() => { | ||
this.timeouts.delete(handle); | ||
if (!this.isDisposed) { | ||
fn(); | ||
} | ||
}, timeout); | ||
this.timeouts.add(handle); | ||
return handle; | ||
} | ||
list() { | ||
return Array.from(this.disposables.values()).map((d) => ({ name: d.name, timeout: d.timeout })); | ||
/** | ||
* a disposal safe setInterval | ||
* checks disposal before execution and clears the interval when the instance is disposed | ||
*/ | ||
setInterval(fn, interval) { | ||
this.disposalGuard({ async: false }); | ||
const handle = globalThis.setInterval(() => { | ||
if (!this.isDisposed) { | ||
fn(); | ||
} | ||
}, interval); | ||
this.intervals.add(handle); | ||
return handle; | ||
} | ||
} | ||
exports.Disposables = Disposables; | ||
async function disposeOf(dispose) { | ||
if (typeof dispose === 'function') { | ||
await dispose(); | ||
} | ||
else { | ||
await dispose.dispose(); | ||
} | ||
} | ||
exports.Disposable = Disposable; | ||
//# sourceMappingURL=disposable.js.map |
@@ -1,4 +0,5 @@ | ||
export * from './disposal-groups'; | ||
export * from './create-disposables'; | ||
export * from './disposable'; | ||
export type { DisposableItem } from './disposables-group'; | ||
export { DisposalGroup, GroupConstraints } from './constraints'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -17,4 +17,4 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__exportStar(require("./disposal-groups"), exports); | ||
__exportStar(require("./create-disposables"), exports); | ||
__exportStar(require("./disposable"), exports); | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,2 @@ | ||
import type { Disposables } from './disposable'; | ||
import type { DisposablesGroup } from './disposables-group'; | ||
export type GroupConstraints = { | ||
@@ -11,5 +11,10 @@ before: string; | ||
name: string; | ||
disposables: Disposables; | ||
disposables: DisposablesGroup; | ||
} | ||
export declare const getValidatedConstantsGroups: (_constraints: GroupConstraints[], groups: DisposalGroup[]) => { | ||
/** | ||
* @internal | ||
* given groups and a lists of constraints, throws if the constraints contradict each other | ||
* @returns the indices of the dominant constrains | ||
*/ | ||
export declare const getGroupConstrainedIndex: (newGroupConstrains: GroupConstraints[], existingGroups: DisposalGroup[]) => { | ||
lastAfter: number; | ||
@@ -16,0 +21,0 @@ firstBefore: number; |
@@ -1,7 +0,12 @@ | ||
export const getValidatedConstantsGroups = (_constraints, groups) => { | ||
/** | ||
* @internal | ||
* given groups and a lists of constraints, throws if the constraints contradict each other | ||
* @returns the indices of the dominant constrains | ||
*/ | ||
export const getGroupConstrainedIndex = (newGroupConstrains, existingGroups) => { | ||
var _a, _b; | ||
let lastAfter = -1, firstBefore = Number.MAX_SAFE_INTEGER; | ||
_constraints.forEach(({ before, after }) => { | ||
newGroupConstrains.forEach(({ before, after }) => { | ||
if (before) { | ||
const index = groups.findIndex((g) => g.name === before); | ||
const index = existingGroups.findIndex((g) => g.name === before); | ||
if (index === -1) { | ||
@@ -13,3 +18,3 @@ throw new Error(`Invalid constraint: "before: ${before}" - group not found`); | ||
if (after) { | ||
const index = groups.findIndex((g) => g.name === after); | ||
const index = existingGroups.findIndex((g) => g.name === after); | ||
if (index === -1) { | ||
@@ -23,3 +28,3 @@ throw new Error(`Invalid constraint: "after: ${after}" - group not found`); | ||
if (lastAfter >= firstBefore) { | ||
throw new Error(`Invalid constraints: ${(_a = groups[lastAfter]) === null || _a === void 0 ? void 0 : _a.name} runs after ${(_b = groups[firstBefore]) === null || _b === void 0 ? void 0 : _b.name}, which contradicts prior constraints`); | ||
throw new Error(`Invalid constraints: ${(_a = existingGroups[lastAfter]) === null || _a === void 0 ? void 0 : _a.name} runs after ${(_b = existingGroups[firstBefore]) === null || _b === void 0 ? void 0 : _b.name}, which contradicts prior constraints`); | ||
} | ||
@@ -26,0 +31,0 @@ } |
@@ -0,24 +1,95 @@ | ||
import { Disposables } from '.'; | ||
declare const DISPOSAL_GUARD_DEFAULTS: { | ||
name: string; | ||
timeout: number; | ||
usedWhileDisposing: boolean; | ||
}; | ||
/** | ||
* Disposables allow adding of disposal async functions, | ||
* when dispose is called, these functions will be run sequentially | ||
* A base class for disposable objects | ||
* @example | ||
* ```ts | ||
* class MyDisposable extends Disposable { | ||
* constructor() { | ||
* super(); | ||
* this.disposables.add(() => console.log('disposed')); | ||
* this.setTimeout(() => console.log('will be canceled upon disposal'), 1000); | ||
* } | ||
* async doSomething() { | ||
* // will throw if disposed, delays disposal until done is called | ||
* const done = this.disposalGuard(false, true); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* } | ||
* } | ||
* } | ||
*/ | ||
export declare function createSimpleDisposable(): Disposables; | ||
export declare class Disposables { | ||
private disposables; | ||
export declare class Disposable { | ||
private _isDisposed; | ||
private _isDisposing; | ||
readonly disposables: Disposables; | ||
private timeouts; | ||
private intervals; | ||
constructor(); | ||
/** | ||
* Starts instance disposal: | ||
* | ||
* **phase 1: disposing** | ||
* - isDisposed === true | ||
* - disposalGuard() will throw | ||
* - disposalGuard(true) will not throw (for methods that are used in the disposal process) | ||
* - disposable.dispose is awaited | ||
* | ||
* **phase 2: disposed done** | ||
* - disposalGuard(true) will throw | ||
*/ | ||
dispose(): Promise<void>; | ||
add(disposable: Disposable, timeout: number, name: string): () => boolean; | ||
remove(target: Disposable): void; | ||
list(): { | ||
name: string; | ||
timeout: number; | ||
}[]; | ||
/** | ||
* returns true if the disposal process started | ||
*/ | ||
get isDisposed(): boolean; | ||
/** | ||
* - throws if disposal started/finished | ||
* - in async mode, delays disposal until the returned fn called | ||
* @example async mode | ||
* ```ts | ||
* // this will throw if disposed | ||
* const done = this.disposalGuard({timeout: 1000, name:'something'}); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* } | ||
* @example sync mode | ||
* ```ts | ||
* // will throw if disposed | ||
* this.disposalGuard({async:false}); | ||
* @example usedWhileDisposing | ||
* ```ts | ||
* // will not throw if disposal didn't finished yet, even if dispose was called | ||
* this.disposalGuard({usedWhileDisposing:true, async:false}); | ||
*/ | ||
disposalGuard(options: { | ||
async: never; | ||
} & Partial<typeof DISPOSAL_GUARD_DEFAULTS>): () => void; | ||
disposalGuard(): () => void; | ||
disposalGuard(options: { | ||
async: false; | ||
usedWhileDisposing?: boolean; | ||
}): void; | ||
/** | ||
* a disposal safe setTimeout | ||
* checks disposal before execution and clears the timeout when the instance is disposed | ||
*/ | ||
setTimeout(fn: () => void, timeout: number): ReturnType<typeof setTimeout>; | ||
/** | ||
* a disposal safe setInterval | ||
* checks disposal before execution and clears the interval when the instance is disposed | ||
*/ | ||
setInterval(fn: () => void, interval: number): ReturnType<typeof setInterval>; | ||
} | ||
export type DisposeFunction = () => unknown; | ||
export type Disposable = { | ||
dispose: DisposeFunction; | ||
} | DisposeFunction; | ||
export type NamedDisposable = { | ||
timeout: number; | ||
name: string; | ||
}; | ||
export {}; | ||
//# sourceMappingURL=disposable.d.ts.map |
@@ -1,42 +0,121 @@ | ||
import { timeout } from 'promise-assist'; | ||
import { defaults, noop } from '@wixc3/common'; | ||
import { Disposables } from '.'; | ||
import { deferred } from 'promise-assist'; | ||
const DELAY_DISPOSAL = 'DELAY_DISPOSAL'; | ||
const DISPOSAL_GUARD_DEFAULTS = { | ||
name: 'disposalGuard', | ||
timeout: 5000, | ||
usedWhileDisposing: false, | ||
}; | ||
/** | ||
* Disposables allow adding of disposal async functions, | ||
* when dispose is called, these functions will be run sequentially | ||
* A base class for disposable objects | ||
* @example | ||
* ```ts | ||
* class MyDisposable extends Disposable { | ||
* constructor() { | ||
* super(); | ||
* this.disposables.add(() => console.log('disposed')); | ||
* this.setTimeout(() => console.log('will be canceled upon disposal'), 1000); | ||
* } | ||
* async doSomething() { | ||
* // will throw if disposed, delays disposal until done is called | ||
* const done = this.disposalGuard(false, true); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* } | ||
* } | ||
* } | ||
*/ | ||
export function createSimpleDisposable() { | ||
return new Disposables(); | ||
} | ||
export class Disposables { | ||
export class Disposable { | ||
constructor() { | ||
this.disposables = new Map(); | ||
this._isDisposed = false; | ||
this._isDisposing = false; | ||
this.disposables = new Disposables(); | ||
this.timeouts = new Set(); | ||
this.intervals = new Set(); | ||
this.disposables.registerGroup(DELAY_DISPOSAL, { before: 'default' }); | ||
this.disposables.add(() => { | ||
this.timeouts.forEach((t) => clearTimeout(t)); | ||
this.intervals.forEach((i) => clearInterval(i)); | ||
}); | ||
} | ||
/** | ||
* Starts instance disposal: | ||
* | ||
* **phase 1: disposing** | ||
* - isDisposed === true | ||
* - disposalGuard() will throw | ||
* - disposalGuard(true) will not throw (for methods that are used in the disposal process) | ||
* - disposable.dispose is awaited | ||
* | ||
* **phase 2: disposed done** | ||
* - disposalGuard(true) will throw | ||
*/ | ||
async dispose() { | ||
const _disposables = Array.from(this.disposables).reverse(); | ||
this.disposables.clear(); | ||
for (const [disposable, details] of _disposables) { | ||
await timeout(disposeOf(disposable), details.timeout, `Disposal timed out: "${details.name}" after ${details.timeout}ms`); | ||
if (!this.isDisposed && !this._isDisposing) { | ||
this._isDisposing = true; | ||
await this.disposables.dispose(); | ||
this._isDisposed = true; | ||
this._isDisposing = false; | ||
} | ||
} | ||
add(disposable, timeout, name) { | ||
if (this.disposables.has(disposable)) { | ||
throw new Error(`Disposable already added`); | ||
/** | ||
* returns true if the disposal process started | ||
*/ | ||
get isDisposed() { | ||
return this._isDisposed || this._isDisposing; | ||
} | ||
disposalGuard(options) { | ||
const { async, usedWhileDisposing, name, timeout } = defaults(options || {}, { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
async: true, | ||
}); | ||
if (this.isDisposed && !(this._isDisposing && usedWhileDisposing)) { | ||
throw new Error('Instance was disposed'); | ||
} | ||
this.disposables.set(disposable, { timeout, name }); | ||
return () => this.disposables.delete(disposable); | ||
if (async) { | ||
const { promise: canDispose, resolve: done } = deferred(); | ||
const remove = this.disposables.add(() => canDispose, { | ||
group: DELAY_DISPOSAL, | ||
name, | ||
timeout, | ||
}); | ||
canDispose.then(remove).catch(noop); | ||
return done; | ||
} | ||
return; | ||
} | ||
remove(target) { | ||
this.disposables.delete(target); | ||
/** | ||
* a disposal safe setTimeout | ||
* checks disposal before execution and clears the timeout when the instance is disposed | ||
*/ | ||
setTimeout(fn, timeout) { | ||
this.disposalGuard({ async: false }); | ||
const handle = globalThis.setTimeout(() => { | ||
this.timeouts.delete(handle); | ||
if (!this.isDisposed) { | ||
fn(); | ||
} | ||
}, timeout); | ||
this.timeouts.add(handle); | ||
return handle; | ||
} | ||
list() { | ||
return Array.from(this.disposables.values()).map((d) => ({ name: d.name, timeout: d.timeout })); | ||
/** | ||
* a disposal safe setInterval | ||
* checks disposal before execution and clears the interval when the instance is disposed | ||
*/ | ||
setInterval(fn, interval) { | ||
this.disposalGuard({ async: false }); | ||
const handle = globalThis.setInterval(() => { | ||
if (!this.isDisposed) { | ||
fn(); | ||
} | ||
}, interval); | ||
this.intervals.add(handle); | ||
return handle; | ||
} | ||
} | ||
async function disposeOf(dispose) { | ||
if (typeof dispose === 'function') { | ||
await dispose(); | ||
} | ||
else { | ||
await dispose.dispose(); | ||
} | ||
} | ||
//# sourceMappingURL=disposable.js.map |
@@ -1,4 +0,5 @@ | ||
export * from './disposal-groups'; | ||
export * from './create-disposables'; | ||
export * from './disposable'; | ||
export type { DisposableItem } from './disposables-group'; | ||
export { DisposalGroup, GroupConstraints } from './constraints'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -1,3 +0,3 @@ | ||
export * from './disposal-groups'; | ||
export * from './create-disposables'; | ||
export * from './disposable'; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@wixc3/patterns", | ||
"version": "11.3.0", | ||
"version": "12.0.1", | ||
"description": "A utility for saving objects to be disposed", | ||
@@ -21,5 +21,5 @@ "main": "dist/cjs/index.js", | ||
"dependencies": { | ||
"@wixc3/common": "^11.1.1", | ||
"@wixc3/common": "^12.0.1", | ||
"promise-assist": "^2.0.1" | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
import type { Disposables } from './disposable'; | ||
import type { DisposablesGroup } from './disposables-group'; | ||
@@ -7,11 +7,16 @@ export type GroupConstraints = { before: string; after?: string } | { after: string; before?: string }; | ||
name: string; | ||
disposables: Disposables; | ||
disposables: DisposablesGroup; | ||
} | ||
export const getValidatedConstantsGroups = (_constraints: GroupConstraints[], groups: DisposalGroup[]) => { | ||
/** | ||
* @internal | ||
* given groups and a lists of constraints, throws if the constraints contradict each other | ||
* @returns the indices of the dominant constrains | ||
*/ | ||
export const getGroupConstrainedIndex = (newGroupConstrains: GroupConstraints[], existingGroups: DisposalGroup[]) => { | ||
let lastAfter = -1, | ||
firstBefore = Number.MAX_SAFE_INTEGER; | ||
_constraints.forEach(({ before, after }) => { | ||
newGroupConstrains.forEach(({ before, after }) => { | ||
if (before) { | ||
const index = groups.findIndex((g) => g.name === before); | ||
const index = existingGroups.findIndex((g) => g.name === before); | ||
if (index === -1) { | ||
@@ -23,3 +28,3 @@ throw new Error(`Invalid constraint: "before: ${before}" - group not found`); | ||
if (after) { | ||
const index = groups.findIndex((g) => g.name === after); | ||
const index = existingGroups.findIndex((g) => g.name === after); | ||
if (index === -1) { | ||
@@ -34,3 +39,3 @@ throw new Error(`Invalid constraint: "after: ${after}" - group not found`); | ||
throw new Error( | ||
`Invalid constraints: ${groups[lastAfter]?.name} runs after ${groups[firstBefore]?.name}, which contradicts prior constraints` | ||
`Invalid constraints: ${existingGroups[lastAfter]?.name} runs after ${existingGroups[firstBefore]?.name}, which contradicts prior constraints` | ||
); | ||
@@ -37,0 +42,0 @@ } |
@@ -1,56 +0,156 @@ | ||
import { timeout } from 'promise-assist'; | ||
import { defaults, noop } from '@wixc3/common'; | ||
import { Disposables } from '.'; | ||
import { deferred } from 'promise-assist'; | ||
const DELAY_DISPOSAL = 'DELAY_DISPOSAL'; | ||
const DISPOSAL_GUARD_DEFAULTS = { | ||
name: 'disposalGuard', | ||
timeout: 5_000, | ||
usedWhileDisposing: false, | ||
}; | ||
/** | ||
* Disposables allow adding of disposal async functions, | ||
* when dispose is called, these functions will be run sequentially | ||
* A base class for disposable objects | ||
* @example | ||
* ```ts | ||
* class MyDisposable extends Disposable { | ||
* constructor() { | ||
* super(); | ||
* this.disposables.add(() => console.log('disposed')); | ||
* this.setTimeout(() => console.log('will be canceled upon disposal'), 1000); | ||
* } | ||
* async doSomething() { | ||
* // will throw if disposed, delays disposal until done is called | ||
* const done = this.disposalGuard(false, true); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* } | ||
* } | ||
* } | ||
*/ | ||
export function createSimpleDisposable() { | ||
return new Disposables(); | ||
} | ||
export class Disposable { | ||
private _isDisposed = false; | ||
private _isDisposing = false; | ||
public readonly disposables = new Disposables(); | ||
private timeouts = new Set<ReturnType<typeof setTimeout>>(); | ||
private intervals = new Set<ReturnType<typeof setInterval>>(); | ||
constructor() { | ||
this.disposables.registerGroup(DELAY_DISPOSAL, { before: 'default' }); | ||
this.disposables.add(() => { | ||
this.timeouts.forEach((t) => clearTimeout(t)); | ||
this.intervals.forEach((i) => clearInterval(i)); | ||
}); | ||
} | ||
export class Disposables { | ||
private disposables = new Map<Disposable, NamedDisposable>(); | ||
/** | ||
* Starts instance disposal: | ||
* | ||
* **phase 1: disposing** | ||
* - isDisposed === true | ||
* - disposalGuard() will throw | ||
* - disposalGuard(true) will not throw (for methods that are used in the disposal process) | ||
* - disposable.dispose is awaited | ||
* | ||
* **phase 2: disposed done** | ||
* - disposalGuard(true) will throw | ||
*/ | ||
async dispose() { | ||
const _disposables = Array.from(this.disposables).reverse(); | ||
this.disposables.clear(); | ||
for (const [disposable, details] of _disposables) { | ||
await timeout( | ||
disposeOf(disposable), | ||
details.timeout, | ||
`Disposal timed out: "${details.name}" after ${details.timeout}ms` | ||
); | ||
if (!this.isDisposed && !this._isDisposing) { | ||
this._isDisposing = true; | ||
await this.disposables.dispose(); | ||
this._isDisposed = true; | ||
this._isDisposing = false; | ||
} | ||
} | ||
add(disposable: Disposable, timeout: number, name: string) { | ||
if (this.disposables.has(disposable)) { | ||
throw new Error(`Disposable already added`); | ||
} | ||
this.disposables.set(disposable, { timeout, name }); | ||
return () => this.disposables.delete(disposable); | ||
/** | ||
* returns true if the disposal process started | ||
*/ | ||
get isDisposed(): boolean { | ||
return this._isDisposed || this._isDisposing; | ||
} | ||
remove(target: Disposable): void { | ||
this.disposables.delete(target); | ||
/** | ||
* - throws if disposal started/finished | ||
* - in async mode, delays disposal until the returned fn called | ||
* @example async mode | ||
* ```ts | ||
* // this will throw if disposed | ||
* const done = this.disposalGuard({timeout: 1000, name:'something'}); | ||
* try { | ||
* // do something | ||
* } finally { | ||
* // disposal can begin (if dispose was called) | ||
* done(); | ||
* } | ||
* @example sync mode | ||
* ```ts | ||
* // will throw if disposed | ||
* this.disposalGuard({async:false}); | ||
* @example usedWhileDisposing | ||
* ```ts | ||
* // will not throw if disposal didn't finished yet, even if dispose was called | ||
* this.disposalGuard({usedWhileDisposing:true, async:false}); | ||
*/ | ||
disposalGuard( | ||
options: { | ||
async: never; | ||
} & Partial<typeof DISPOSAL_GUARD_DEFAULTS> | ||
): () => void; | ||
disposalGuard(): () => void; | ||
disposalGuard(options: { async: false; usedWhileDisposing?: boolean }): void; | ||
disposalGuard(options?: { async?: boolean } & Partial<typeof DISPOSAL_GUARD_DEFAULTS>) { | ||
const { async, usedWhileDisposing, name, timeout } = defaults(options || {}, { | ||
...DISPOSAL_GUARD_DEFAULTS, | ||
async: true, | ||
}); | ||
if (this.isDisposed && !(this._isDisposing && usedWhileDisposing)) { | ||
throw new Error('Instance was disposed'); | ||
} | ||
if (async) { | ||
const { promise: canDispose, resolve: done } = deferred(); | ||
const remove = this.disposables.add(() => canDispose, { | ||
group: DELAY_DISPOSAL, | ||
name, | ||
timeout, | ||
}); | ||
canDispose.then(remove).catch(noop); | ||
return done; | ||
} | ||
return; | ||
} | ||
list() { | ||
return Array.from(this.disposables.values()).map((d) => ({ name: d.name, timeout: d.timeout })); | ||
/** | ||
* a disposal safe setTimeout | ||
* checks disposal before execution and clears the timeout when the instance is disposed | ||
*/ | ||
setTimeout(fn: () => void, timeout: number): ReturnType<typeof setTimeout> { | ||
this.disposalGuard({ async: false }); | ||
const handle = globalThis.setTimeout(() => { | ||
this.timeouts.delete(handle); | ||
if (!this.isDisposed) { | ||
fn(); | ||
} | ||
}, timeout); | ||
this.timeouts.add(handle); | ||
return handle; | ||
} | ||
} | ||
export type DisposeFunction = () => unknown; | ||
export type Disposable = { dispose: DisposeFunction } | DisposeFunction; | ||
export type NamedDisposable = { | ||
timeout: number; | ||
name: string; | ||
}; | ||
async function disposeOf(dispose: Disposable) { | ||
if (typeof dispose === 'function') { | ||
await dispose(); | ||
} else { | ||
await dispose.dispose(); | ||
/** | ||
* a disposal safe setInterval | ||
* checks disposal before execution and clears the interval when the instance is disposed | ||
*/ | ||
setInterval(fn: () => void, interval: number): ReturnType<typeof setInterval> { | ||
this.disposalGuard({ async: false }); | ||
const handle = globalThis.setInterval(() => { | ||
if (!this.isDisposed) { | ||
fn(); | ||
} | ||
}, interval); | ||
this.intervals.add(handle); | ||
return handle; | ||
} | ||
} |
@@ -1,3 +0,4 @@ | ||
export * from './disposal-groups'; | ||
export * from './create-disposables'; | ||
export * from './disposable'; | ||
export type { DisposableItem } from './disposables-group'; | ||
export { DisposalGroup, GroupConstraints } from './constraints'; |
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
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
197491
111
3778
+ Added@wixc3/common@12.1.0(transitive)
- Removed@wixc3/common@11.1.1(transitive)
Updated@wixc3/common@^12.0.1