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.

289 lines 9.44 kB
/** * @module Lock */ import {} from "../../../../event-bus/contracts/_module.js"; import {} from "../../../../hooks/_module.js"; import { FailedAcquireLockError, LOCK_EVENTS, FailedReleaseLockError, FailedRefreshLockError, LOCK_STATE, isLockError, } from "../../../../lock/contracts/_module.js"; import {} from "../../../../namespace/_module.js"; import {} from "../../../../task/contracts/_module.js"; import { Task } from "../../../../task/implementations/_module.js"; import {} from "../../../../time-span/contracts/_module.js"; import { TimeSpan } from "../../../../time-span/implementations/_module.js"; import { resolveLazyable } from "../../../../utilities/_module.js"; /** * @internal */ export class Lock { /** * @internal */ static _internal_serialize(deserializedValue) { return { version: "1", key: deserializedValue._key.get(), lockId: deserializedValue.lockId, ttlInMs: deserializedValue._ttl?.toMilliseconds() ?? null, }; } namespace; adapter; originalAdapter; eventDispatcher; _key; lockId; _ttl; defaultBlockingInterval; defaultBlockingTime; defaultRefreshTime; serdeTransformerName; constructor(settings) { const { namespace, adapter, originalAdapter, eventDispatcher, key, lockId, ttl, serdeTransformerName, defaultBlockingInterval, defaultBlockingTime, defaultRefreshTime, } = settings; this.namespace = namespace; this.originalAdapter = originalAdapter; this.serdeTransformerName = serdeTransformerName; this.adapter = adapter; this.eventDispatcher = eventDispatcher; this._key = key; this.lockId = lockId; this._ttl = ttl; this.defaultBlockingInterval = defaultBlockingInterval; this.defaultBlockingTime = defaultBlockingTime; this.defaultRefreshTime = defaultRefreshTime; } _internal_getNamespace() { return this.namespace; } _internal_getSerdeTransformerName() { return this.serdeTransformerName; } _internal_getAdapter() { return this.originalAdapter; } runOrFail(asyncFn) { return new Task(async () => { try { await this.acquireOrFail(); return await resolveLazyable(asyncFn); } finally { await this.release(); } }); } runBlockingOrFail(asyncFn, settings) { return new Task(async () => { try { await this.acquireBlockingOrFail(settings); return await resolveLazyable(asyncFn); } finally { await this.release(); } }); } handleUnexpectedError = () => { return async (args, next) => { try { return await next(...args); } catch (error) { if (isLockError(error)) { throw error; } this.eventDispatcher .dispatch(LOCK_EVENTS.UNEXPECTED_ERROR, { error, lock: this, }) .detach(); throw error; } }; }; handleDispatch = (settings) => { return async (args, next) => { const result = await next(...args); if (result && settings.on === "true") { this.eventDispatcher .dispatch(settings.eventName, settings.eventData) .detach(); } if (!result && settings.on === "false") { this.eventDispatcher .dispatch(settings.eventName, settings.eventData) .detach(); } return result; }; }; acquire() { return new Task(async () => { return await this.adapter.acquire(this._key.toString(), this.lockId, this._ttl); }).pipe([ this.handleUnexpectedError(), this.handleDispatch({ on: "true", eventName: LOCK_EVENTS.ACQUIRED, eventData: { lock: this, }, }), this.handleDispatch({ on: "false", eventName: LOCK_EVENTS.UNAVAILABLE, eventData: { lock: this, }, }), ]); } acquireOrFail() { return new Task(async () => { const hasAquired = await this.acquire(); if (!hasAquired) { throw FailedAcquireLockError.create(this._key); } }); } acquireBlocking(settings = {}) { return new Task(async () => { const { time = this.defaultBlockingTime, interval = this.defaultBlockingInterval, } = settings; const timeAsTimeSpan = TimeSpan.fromTimeSpan(time); const intervalAsTimeSpan = TimeSpan.fromTimeSpan(interval); const endDate = timeAsTimeSpan.toEndDate(); while (endDate > new Date()) { const hasAquired = await this.acquire(); if (hasAquired) { return true; } await Task.delay(intervalAsTimeSpan); } return false; }); } acquireBlockingOrFail(settings) { return new Task(async () => { const hasAquired = await this.acquireBlocking(settings); if (!hasAquired) { throw FailedAcquireLockError.create(this._key); } }); } release() { return new Task(async () => { return await this.adapter.release(this._key.toString(), this.lockId); }).pipe([ this.handleUnexpectedError(), this.handleDispatch({ on: "true", eventName: LOCK_EVENTS.RELEASED, eventData: { lock: this, }, }), this.handleDispatch({ on: "false", eventName: LOCK_EVENTS.FAILED_RELEASE, eventData: { lock: this, }, }), ]); } releaseOrFail() { return new Task(async () => { const hasRelased = await this.release(); if (!hasRelased) { throw FailedReleaseLockError.create(this._key, this.lockId); } }); } forceRelease() { return new Task(async () => { return await this.adapter.forceRelease(this._key.toString()); }).pipe([ this.handleUnexpectedError(), async (args, next) => { const hasReleased = await next(...args); this.eventDispatcher .dispatch(LOCK_EVENTS.FORCE_RELEASED, { lock: this, hasReleased, }) .detach(); return hasReleased; }, ]); } refresh(ttl = this.defaultRefreshTime) { return new Task(async () => { return await this.adapter.refresh(this._key.toString(), this.lockId, TimeSpan.fromTimeSpan(ttl)); }).pipe([ this.handleUnexpectedError(), this.handleDispatch({ on: "true", eventName: LOCK_EVENTS.REFRESHED, eventData: { lock: this, }, }), this.handleDispatch({ on: "false", eventName: LOCK_EVENTS.FAILED_REFRESH, eventData: { lock: this, }, }), async (args, next) => { const hasRefreshed = await next(...args); if (hasRefreshed) { this._ttl = TimeSpan.fromTimeSpan(ttl); } return hasRefreshed; }, ]); } refreshOrFail(ttl) { return new Task(async () => { const hasRefreshed = await this.refresh(ttl); if (!hasRefreshed) { throw FailedRefreshLockError.create(this._key, this.lockId); } }); } get key() { return this._key.get(); } get id() { return this.lockId; } get ttl() { return this._ttl; } getState() { return new Task(async () => { const state = await this.adapter.getState(this._key.toString()); if (state === null) { return { type: LOCK_STATE.EXPIRED, }; } if (state.owner === this.lockId) { return { type: LOCK_STATE.ACQUIRED, remainingTime: state.expiration === null ? null : TimeSpan.fromDateRange({ start: new Date(), end: state.expiration, }), }; } return { type: LOCK_STATE.UNAVAILABLE, owner: state.owner, }; }).pipe(this.handleUnexpectedError()); } } //# sourceMappingURL=lock.js.map