UNPKG

@daiso-tech/core

Version:

The library offers flexible, framework-agnostic solutions for modern web applications, built on adaptable components that integrate seamlessly with popular frameworks like Next Js.

369 lines 13.1 kB
/** * @module Lock */ import { TimeSpan } from "../../../../utilities/_module-exports.js"; import { resolveLazyable, resultSuccess, resultFailure, } from "../../../../utilities/_module-exports.js"; import { resolveOneOrMoreStr } from "../../../../utilities/_module-exports.js"; import { KeyAlreadyAcquiredLockError, LOCK_EVENTS, UnownedRefreshLockError, UnownedReleaseLockError, } from "../../../../lock/contracts/_module-exports.js"; import {} from "../../../../lock/contracts/_module-exports.js"; import { LazyPromise } from "../../../../async/_module-exports.js"; /** * @internal */ export class Lock { /** * @internal */ static _internal_serialize(deserializedValue) { return { key: deserializedValue.key.resolved, expirationInMs: deserializedValue.lockState.get()?.toEndDate().getTime() ?? null, owner: deserializedValue.owner, ttlInMs: deserializedValue.ttl?.toMilliseconds() ?? null, }; } createLazyPromise; adapter; lockState; eventDispatcher; key; owner; ttl; defaultBlockingInterval; defaultBlockingTime; defaultRefreshTime; serdeTransformerName; constructor(settings) { const { createLazyPromise, adapter, lockState, eventDispatcher, key, owner, ttl, serdeTransformerName, expirationInMs, defaultBlockingInterval, defaultBlockingTime, defaultRefreshTime, } = settings; this.serdeTransformerName = serdeTransformerName; this.createLazyPromise = createLazyPromise; this.adapter = adapter; this.lockState = lockState; if (expirationInMs !== undefined && expirationInMs === null) { this.lockState.set(null); } if (expirationInMs !== undefined && expirationInMs !== null) { this.lockState.set(TimeSpan.fromDateRange(new Date(), new Date(expirationInMs))); } this.eventDispatcher = eventDispatcher; this.key = key; this.owner = resolveOneOrMoreStr(owner); this.ttl = ttl; this.defaultBlockingInterval = defaultBlockingInterval; this.defaultBlockingTime = defaultBlockingTime; this.defaultRefreshTime = defaultRefreshTime; } _internal_getSerdeTransformerName() { return this.serdeTransformerName; } run(asyncFn) { return this.createLazyPromise(async () => { try { const hasAquired = await this.acquire(); if (!hasAquired) { return resultFailure(new KeyAlreadyAcquiredLockError(`Key "${this.key.resolved}" already acquired`)); } return resultSuccess(await resolveLazyable(asyncFn)); } finally { await this.release(); } }); } runOrFail(asyncFn) { return this.createLazyPromise(async () => { try { await this.acquireOrFail(); return await resolveLazyable(asyncFn); } finally { await this.release(); } }); } runBlocking(asyncFn, settings) { return this.createLazyPromise(async () => { try { const hasAquired = await this.acquireBlocking(settings); if (!hasAquired) { return resultFailure(new KeyAlreadyAcquiredLockError(`Key "${this.key.resolved}" already acquired`)); } return resultSuccess(await resolveLazyable(asyncFn)); } finally { await this.release(); } }); } runBlockingOrFail(asyncFn, settings) { return this.createLazyPromise(async () => { try { await this.acquireBlockingOrFail(settings); return await resolveLazyable(asyncFn); } finally { await this.release(); } }); } acquire() { return this.createLazyPromise(async () => { try { const hasAquired = await this.adapter.acquire(this.key.namespaced, this.owner, this.ttl); this.lockState.remove(); if (hasAquired) { this.lockState.set(this.ttl); const event = { key: this.key.resolved, owner: this.owner, ttl: this.ttl, }; this.eventDispatcher .dispatch(LOCK_EVENTS.ACQUIRED, event) .defer(); } else { const event = { key: this.key.resolved, owner: this.owner, }; this.eventDispatcher .dispatch(LOCK_EVENTS.UNAVAILABLE, event) .defer(); } return hasAquired; } catch (error) { const event = { key: this.key.resolved, owner: this.owner, ttl: this.ttl, error, }; this.eventDispatcher .dispatch(LOCK_EVENTS.UNEXPECTED_ERROR, event) .defer(); throw error; } }); } acquireOrFail() { return this.createLazyPromise(async () => { const hasAquired = await this.acquire(); if (!hasAquired) { throw new KeyAlreadyAcquiredLockError(`Key "${this.key.resolved}" already acquired`); } }); } acquireBlocking(settings = {}) { return this.createLazyPromise(async () => { const { time = this.defaultBlockingTime, interval = this.defaultBlockingInterval, } = settings; const endDate = time.toEndDate(); while (endDate.getTime() > new Date().getTime()) { const hasAquired = await this.acquire(); if (hasAquired) { return true; } await LazyPromise.delay(interval); } return false; }); } acquireBlockingOrFail(settings) { return this.createLazyPromise(async () => { const hasAquired = await this.acquireBlocking(settings); if (!hasAquired) { throw new KeyAlreadyAcquiredLockError(`Key "${this.key.resolved}" already acquired`); } }); } release() { return this.createLazyPromise(async () => { try { const hasReleased = await this.adapter.release(this.key.namespaced, this.owner); if (hasReleased) { this.lockState.remove(); const event = { key: this.key.resolved, owner: this.owner, }; this.eventDispatcher .dispatch(LOCK_EVENTS.RELEASED, event) .defer(); } else { const event = { key: this.key.resolved, owner: this.owner, }; this.eventDispatcher .dispatch(LOCK_EVENTS.UNOWNED_RELEASE_TRY, event) .defer(); } return hasReleased; } catch (error) { const event = { key: this.key.resolved, owner: this.owner, ttl: this.ttl, error, }; this.eventDispatcher .dispatch(LOCK_EVENTS.UNEXPECTED_ERROR, event) .defer(); throw error; } }); } releaseOrFail() { return this.createLazyPromise(async () => { const hasRelased = await this.release(); if (!hasRelased) { throw new UnownedReleaseLockError(`Unonwed release on key "${this.key.resolved}" by owner "${this.owner}"`); } }); } forceRelease() { return this.createLazyPromise(async () => { try { await this.adapter.forceRelease(this.key.namespaced); this.lockState.remove(); const event = { key: this.key.resolved, }; this.eventDispatcher .dispatch(LOCK_EVENTS.FORCE_RELEASED, event) .defer(); } catch (error) { const event = { key: this.key.resolved, owner: this.owner, ttl: this.ttl, error, }; this.eventDispatcher .dispatch(LOCK_EVENTS.UNEXPECTED_ERROR, event) .defer(); throw error; } }); } isExpired() { // eslint-disable-next-line @typescript-eslint/require-await return this.createLazyPromise(async () => { try { return this.lockState.isExpired(); } catch (error) { const event = { key: this.key.resolved, owner: this.owner, ttl: this.ttl, error, }; this.eventDispatcher .dispatch(LOCK_EVENTS.UNEXPECTED_ERROR, event) .defer(); throw error; } }); } isLocked() { return this.createLazyPromise(async () => { const isExpired = await this.isExpired(); return !isExpired; }); } refresh(ttl = this.defaultRefreshTime) { return this.createLazyPromise(async () => { try { const hasRefreshed = await this.adapter.refresh(this.key.namespaced, this.owner, ttl); if (hasRefreshed) { const event = { key: this.key.resolved, owner: this.owner, ttl, }; this.lockState.set(ttl); this.eventDispatcher .dispatch(LOCK_EVENTS.REFRESHED, event) .defer(); } else { const event = { key: this.key.resolved, owner: this.owner, }; this.eventDispatcher .dispatch(LOCK_EVENTS.UNOWNED_REFRESH_TRY, event) .defer(); } return hasRefreshed; } catch (error) { const event = { key: this.key.resolved, owner: this.owner, ttl: this.ttl, error, }; this.eventDispatcher .dispatch(LOCK_EVENTS.UNEXPECTED_ERROR, event) .defer(); throw error; } }); } refreshOrFail(ttl) { return this.createLazyPromise(async () => { const hasRefreshed = await this.refresh(ttl); if (!hasRefreshed) { throw new UnownedRefreshLockError(`Unonwed refresh on key "${this.key.resolved}" by owner "${this.owner}"`); } }); } getRemainingTime() { // eslint-disable-next-line @typescript-eslint/require-await return this.createLazyPromise(async () => { try { return this.lockState.get(); } catch (error) { const event = { key: this.key.resolved, owner: this.owner, ttl: this.ttl, error, }; this.eventDispatcher .dispatch(LOCK_EVENTS.UNEXPECTED_ERROR, event) .defer(); throw error; } }); } getOwner() { // eslint-disable-next-line @typescript-eslint/require-await return this.createLazyPromise(async () => { try { return this.owner; } catch (error) { const event = { key: this.key.resolved, owner: this.owner, ttl: this.ttl, error, }; this.eventDispatcher .dispatch(LOCK_EVENTS.UNEXPECTED_ERROR, event) .defer(); throw error; } }); } } //# sourceMappingURL=lock.js.map