@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.
225 lines • 7.69 kB
JavaScript
/**
* @module Lock
*/
import {} from "../../../../event-bus/contracts/_module.js";
import {} from "../../../../execution-context/contracts/_module.js";
import { FailedAcquireLockError, LOCK_EVENTS, FailedReleaseLockError, FailedRefreshLockError, LOCK_STATE, } from "../../../../lock/contracts/_module.js";
import { handleDispatch, handleUnexpectedError, } from "../../../../lock/implementations/derivables/lock-factory/event-helpers.js";
import {} from "../../../../middleware/contracts/_module.js";
import {} from "../../../../namespace/contracts/_module.js";
import {} from "../../../../time-span/contracts/_module.js";
import { TimeSpan } from "../../../../time-span/implementations/_module.js";
import { callInvokable, resolveLazyable, } from "../../../../utilities/_module.js";
/**
* @internal
*/
export class Lock {
/**
* @internal
*/
static _serialize(deserializedValue) {
return {
version: "1",
key: deserializedValue._key.get(),
lockId: deserializedValue.lockId,
ttlInMs: deserializedValue._ttl?.toMilliseconds() ?? null,
};
}
namespace;
adapter;
originalAdapter;
eventDispatcher;
_key;
lockId;
_ttl;
defaultRefreshTime;
serdeTransformerName;
waitUntil;
executionContext;
use;
constructor(settings) {
const { namespace, adapter, originalAdapter, eventDispatcher, key, lockId, ttl, serdeTransformerName, defaultRefreshTime, waitUntil, executionContext, use, } = settings;
this.use = use;
this.executionContext = executionContext;
this.waitUntil = waitUntil;
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.defaultRefreshTime = defaultRefreshTime;
}
_getNamespace() {
return this.namespace;
}
_getSerdeTransformerName() {
return this.serdeTransformerName;
}
_getAdapter() {
return this.originalAdapter;
}
async runOrFail(asyncFn) {
await this.acquireOrFail();
try {
return await resolveLazyable(asyncFn);
}
finally {
await this.release();
}
}
async acquire() {
return this.use(async () => {
return await this.adapter.acquire(this.executionContext, this._key.toString(), this.lockId, this._ttl);
}, [
handleUnexpectedError(this.waitUntil, this.eventDispatcher, this),
handleDispatch({
on: "true",
eventName: LOCK_EVENTS.ACQUIRED,
eventData: {
lock: this,
},
waitUntil: this.waitUntil,
eventDispatcher: this.eventDispatcher,
}),
handleDispatch({
on: "false",
eventName: LOCK_EVENTS.UNAVAILABLE,
eventData: {
lock: this,
},
waitUntil: this.waitUntil,
eventDispatcher: this.eventDispatcher,
}),
])();
}
async acquireOrFail() {
const hasAquired = await this.acquire();
if (!hasAquired) {
throw FailedAcquireLockError.create(this._key);
}
}
async release() {
return this.use(async () => {
return await this.adapter.release(this.executionContext, this._key.toString(), this.lockId);
}, [
handleUnexpectedError(this.waitUntil, this.eventDispatcher, this),
handleDispatch({
on: "true",
eventName: LOCK_EVENTS.RELEASED,
eventData: {
lock: this,
},
waitUntil: this.waitUntil,
eventDispatcher: this.eventDispatcher,
}),
handleDispatch({
on: "false",
eventName: LOCK_EVENTS.FAILED_RELEASE,
eventData: {
lock: this,
},
waitUntil: this.waitUntil,
eventDispatcher: this.eventDispatcher,
}),
])();
}
async releaseOrFail() {
const hasRelased = await this.release();
if (!hasRelased) {
throw FailedReleaseLockError.create(this._key, this.lockId);
}
}
async forceRelease() {
return this.use(async () => {
return await this.adapter.forceRelease(this.executionContext, this._key.toString());
}, [
handleUnexpectedError(this.waitUntil, this.eventDispatcher, this),
async ({ next }) => {
const hasReleased = await next();
callInvokable(this.waitUntil, this.eventDispatcher.dispatch(LOCK_EVENTS.FORCE_RELEASED, {
lock: this,
hasReleased,
}));
return hasReleased;
},
])();
}
async refresh(ttl = this.defaultRefreshTime) {
return this.use(async () => {
return await this.adapter.refresh(this.executionContext, this._key.toString(), this.lockId, TimeSpan.fromTimeSpan(ttl));
}, [
handleUnexpectedError(this.waitUntil, this.eventDispatcher, this),
handleDispatch({
on: "true",
eventName: LOCK_EVENTS.REFRESHED,
eventData: {
lock: this,
},
waitUntil: this.waitUntil,
eventDispatcher: this.eventDispatcher,
}),
handleDispatch({
on: "false",
eventName: LOCK_EVENTS.FAILED_REFRESH,
eventData: {
lock: this,
},
waitUntil: this.waitUntil,
eventDispatcher: this.eventDispatcher,
}),
async ({ next }) => {
const hasRefreshed = await next();
if (hasRefreshed) {
this._ttl = TimeSpan.fromTimeSpan(ttl);
}
return hasRefreshed;
},
])();
}
async refreshOrFail(ttl) {
const hasRefreshed = await this.refresh(ttl);
if (!hasRefreshed) {
throw FailedRefreshLockError.create(this._key, this.lockId);
}
}
get key() {
return this._key;
}
get id() {
return this.lockId;
}
get ttl() {
return this._ttl;
}
async getState() {
return this.use(async () => {
const state = await this.adapter.getState(this.executionContext, 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,
};
}, [
handleUnexpectedError(this.waitUntil, this.eventDispatcher, this),
])();
}
}
//# sourceMappingURL=lock.js.map