@asaidimu/utils-sync 2.2.2 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.mts CHANGED
@@ -373,6 +373,13 @@ declare class Once<T = void> {
373
373
  retry?: boolean;
374
374
  throws?: boolean;
375
375
  });
376
+ /**
377
+ * Manually sets a value, marking the operation as done.
378
+ * Throws if the operation is already done or currently running.
379
+ *
380
+ * @param value - The value to resolve to.
381
+ */
382
+ resolve(value: T): void;
376
383
  /**
377
384
  * Execute the function if it hasn't been executed yet.
378
385
  * Subsequent calls return the result of the first execution.
package/index.d.ts CHANGED
@@ -373,6 +373,13 @@ declare class Once<T = void> {
373
373
  retry?: boolean;
374
374
  throws?: boolean;
375
375
  });
376
+ /**
377
+ * Manually sets a value, marking the operation as done.
378
+ * Throws if the operation is already done or currently running.
379
+ *
380
+ * @param value - The value to resolve to.
381
+ */
382
+ resolve(value: T): void;
376
383
  /**
377
384
  * Execute the function if it hasn't been executed yet.
378
385
  * Subsequent calls return the result of the first execution.
package/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e={VALIDATION_FAILED:{code:"VAL-001",name:"VALIDATION_FAILED",description:"Input validation failed due to one or more invalid fields",category:"validation",httpStatus:400,logLevel:"warn"},REQUIRED_FIELD_MISSING:{code:"VAL-002",name:"REQUIRED_FIELD_MISSING",description:"A required field was not provided in the request",category:"validation",httpStatus:400,logLevel:"warn"},INVALID_FORMAT:{code:"VAL-003",name:"INVALID_FORMAT",description:"Field value does not match expected format",category:"validation",httpStatus:400,logLevel:"warn"},NOT_FOUND:{code:"DB-001-NF",name:"NOT_FOUND",description:"The requested resource could not be found",category:"database",httpStatus:404,logLevel:"info",action:"Verify the resource identifier exists before accessing"},DUPLICATE_KEY:{code:"DB-002-DUP",name:"DUPLICATE_KEY",description:"A unique constraint violation occurred",category:"database",httpStatus:409,logLevel:"warn",action:"Check if the resource already exists before creation"},RESOURCE_LOCKED:{code:"DB-003-LOCK",name:"RESOURCE_LOCKED",description:"The resource is currently locked by another operation",category:"database",httpStatus:409,logLevel:"warn",action:"Retry the operation after a brief delay"},PERMISSION_DENIED:{code:"AUTH-001-DENIED",name:"PERMISSION_DENIED",description:"The authenticated user lacks permission for this operation",category:"auth",httpStatus:403,logLevel:"warn",action:"Check user roles and permissions"},UNAUTHENTICATED:{code:"AUTH-002-UNAUTH",name:"UNAUTHENTICATED",description:"Authentication is required for this operation",category:"auth",httpStatus:401,logLevel:"info",action:"Provide valid authentication credentials"},INVALID_COMMAND:{code:"BUS-001",name:"INVALID_COMMAND",description:"The command or operation is invalid for the current state",category:"business",httpStatus:400,logLevel:"warn"},OPERATION_ABORTED:{code:"BUS-002-ABORT",name:"OPERATION_ABORTED",description:"The operation was explicitly aborted",category:"business",httpStatus:409,logLevel:"info"},INTERNAL_ERROR:{code:"SYS-001",name:"INTERNAL_ERROR",description:"An unexpected internal error occurred",category:"system",httpStatus:500,logLevel:"error",action:"Check system logs for stack traces and diagnostic information"},BACKEND_ERROR:{code:"SYS-002",name:"BACKEND_ERROR",description:"An error occurred in a backend service",category:"system",httpStatus:502,logLevel:"error"},CONCURRENCY_ERROR:{code:"CON-001",name:"CONCURRENCY_ERROR",description:"A concurrency conflict occurred during the operation",category:"concurrency",httpStatus:409,logLevel:"warn",action:"Retry the operation with updated data"}},t=new Map;var i=class i extends Error{code;codeMetadata;severity;path;operation;issues;cause;constructor(s){const r={...function(i){const s=Object.values(e).find((e=>e.code===i));if(s)return s;return t.get(i)||{code:i,name:i.replace(/-/g,"_"),description:`Unknown error: ${i}`,category:"custom",httpStatus:500,logLevel:"error",action:"Check if this error code is properly registered or handle as unknown error"}}(s.code),...s.metadata,code:s.code};super(s.message??r.description),this.name="SystemError",this.code=s.code,this.codeMetadata=r,this.severity=s.severity??"error",this.path=s.path,this.operation=s.operation,this.issues=s.issues??[],this.cause=s.cause,Object.setPrototypeOf(this,i.prototype),Error.captureStackTrace&&Error.captureStackTrace(this,i)}withPath(e){return new i({code:this.code,message:this.message,severity:this.severity,path:e,operation:this.operation,issues:this.issues,cause:this.cause,metadata:this.codeMetadata})}withOperation(e){return new i({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:e,issues:this.issues,cause:this.cause,metadata:this.codeMetadata})}withIssue(e){return new i({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:[...this.issues,e],cause:this.cause,metadata:this.codeMetadata})}withIssues(e){return new i({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:[...this.issues,...e],cause:this.cause,metadata:this.codeMetadata})}withCause(e){return new i({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:this.issues,cause:e,metadata:this.codeMetadata})}getHttpStatus(){return this.codeMetadata.httpStatus}toLogEntry(){return{name:this.name,code:this.code,category:this.codeMetadata.category,message:this.message,path:this.path,operation:this.operation,issues:this.issues,cause:this.cause instanceof Error?this.cause.message:this.cause,stack:this.stack,timestamp:(new Date).toISOString()}}toString(){let e=`[${this.code}] ${this.message} (${this.codeMetadata.category})`;return this.path&&(e+=` at '${this.path}'`),this.operation&&(e+=` during '${this.operation}'`),this.issues.length>0&&(e+="\nIssues:\n"+this.issues.map(((e,t)=>` ${t+1}. ${e.message} [${e.code}]`)).join("\n")),this.cause&&(e+=`\nCause: ${this.cause instanceof Error?this.cause.message:String(this.cause)}`),e}},s=class e extends i{constructor(t,i){super({code:"SYNC_ERROR",message:t,cause:i}),this.name="SyncError",Object.setPrototypeOf(this,e.prototype)}},r=class e extends s{constructor(t){super(`[ArtifactContainer] Operation timed out: ${t}`),this.name="TimeoutError",Object.setPrototypeOf(this,e.prototype)}},o=class e extends s{constructor(t){super("[Serializer] The serializer has been marked as done!",t),this.name="SerializerExecutionDone",Object.setPrototypeOf(this,e.prototype)}},a=class{_locked=!1;_capacity;_yieldMode;waiters=[];constructor(e){this._capacity=e?.capacity??1/0,this._yieldMode=e?.yieldMode??"macrotask"}async lock(e){if(!this._locked)return void(this._locked=!0);if(this.waiters.length>=this._capacity)throw new Error(`Mutex queue is full (capacity: ${this._capacity})`);let t;const i=new Promise((e=>t=e));if(this.waiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,o)=>{s=setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),o(new r("Mutex lock timed out"))}),e)}))])}tryLock(){return!this._locked&&(this._locked=!0,!0)}unlock(){if(!this._locked)throw new Error("Mutex is not locked");const e=this.waiters.shift();e?"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0):this._locked=!1}locked(){return this._locked}pending(){return this.waiters.length}},n=class{mutex=new a({yieldMode:"microtask"});promise=null;_value=null;_error;_done=!1;retry;throws;constructor({retry:e,throws:t}={}){this.retry=Boolean(e),this.throws=Boolean(t)}async do(e,t){return this._done?this.peek():this.promise?this._awaitWithTimeout(this.promise,t,"Once do() timed out"):(await this.mutex.lock(),this.promise?(this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")):(this.promise=(async()=>{try{this._value=await e(),this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.retry&&!this._done&&(this.promise=null)}return this.peek()})(),this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")))}doSync(e){if(this._done){if(this.throws&&this._error)throw this._error;return this.peek()}if(this.promise){const e=new Error("Cannot execute doSync while an async operation is pending.");if(this.throws)throw e;return{value:null,error:e}}if(!this.mutex.tryLock()){const e=new Error("Cannot execute doSync: lock is currently held.");if(this.throws)throw e;return{value:null,error:e}}if(this.promise||this._done){if(this.mutex.unlock(),this._done){if(this.throws&&this._error)throw this._error;return this.peek()}const e=new Error("Cannot execute doSync while an async operation is pending.");if(this.throws)throw e;return{value:null,error:e}}try{const t=e();this._value=t,this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.mutex.unlock()}return this.peek()}running(){return null!==this.promise&&!this.done()}peek(){return{value:this._value,error:this._error}}get(){if(!this._done)throw new Error("Once operation is not yet complete");if(this._error)throw this._error;return this._value}reset(){if(this.running())throw new Error("Cannot reset Once while an operation is in progress.");this._done=!1,this.promise=null,this._value=null,this._error=void 0}done(){return this._done}current(){return this.promise}_awaitWithTimeout(e,t,i="Operation timed out"){if(null==t)return e;let s;return Promise.race([e.then((e=>(clearTimeout(s),e))),new Promise(((e,o)=>{s=setTimeout((()=>o(new r(i))),t)}))])}};exports.Debouncer=class{_delay;_leading;_timer;_pendingFn;_pendingResolvers=[];_leadingFired=!1;constructor(e){this._delay=e?.delay??300,this._leading=e?.leading??!1}do(e){return new Promise((t=>{this._enqueue(e,t)}))}fire(e){this._enqueue(e,void 0)}_enqueue(e,t){if(this._pendingFn=e,t&&this._pendingResolvers.push(t),this._leading&&!this._leadingFired)return this._leadingFired=!0,this._fire(),clearTimeout(this._timer),void(this._timer=setTimeout((()=>{this._leadingFired=!1,void 0!==this._pendingFn&&this._fire()}),this._delay));clearTimeout(this._timer),this._timer=setTimeout((()=>{this._leadingFired=!1,this._fire()}),this._delay)}cancel(){clearTimeout(this._timer),this._timer=void 0,this._pendingFn=void 0,this._leadingFired=!1;const e=this._pendingResolvers.splice(0);for(const t of e)t({status:"cancelled"})}async flush(){return this._pendingFn?(clearTimeout(this._timer),this._timer=void 0,this._leadingFired=!1,this._fire()):null}pending(){return void 0!==this._pendingFn}async _fire(){const e=this._pendingFn,t=this._pendingResolvers.splice(0);let i;this._pendingFn=void 0;try{i={status:"ok",value:await e()}}catch(e){i={status:"error",error:e}}for(const e of t)e(i);return i}},exports.Latch=class{_open=!1;_resolve;_promise;constructor(){this._promise=new Promise((e=>{this._resolve=e}))}open(){this._open||(this._open=!0,this._resolve())}async wait(e){if(this._open)return;if(null==e)return this._promise;let t;await Promise.race([this._promise.then((()=>clearTimeout(t))),new Promise(((i,s)=>{t=setTimeout((()=>s(new r("Latch timed out"))),e)}))])}isOpen(){return this._open}},exports.Mutex=a,exports.Once=n,exports.OnceExecutionConflict=class e extends s{constructor(t){super("[Once] A method has already been registered!",t),this.name="OnceExecutionConflict",Object.setPrototypeOf(this,e.prototype)}},exports.RWMutex=class{_readers=0;_writeLocked=!1;_pendingWriters=0;_yieldMode;readerWaiters=[];writerWaiters=[];constructor(e){this._yieldMode=e?.yieldMode??"microtask"}async rlock(e){if(!this._writeLocked&&0===this._pendingWriters)return void this._readers++;let t;const i=new Promise((e=>t=e));if(this.readerWaiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,o)=>{s=setTimeout((()=>{const e=this.readerWaiters.indexOf(t);-1!==e&&this.readerWaiters.splice(e,1),o(new r("RWMutex rlock timed out"))}),e)}))])}runlock(){if(this._readers<=0)throw new Error("RWMutex: runlock called without a matching rlock");this._readers--,this._tryWakeWriter()}async lock(e){if(!this._writeLocked&&0===this._readers)return void(this._writeLocked=!0);let t;this._pendingWriters++;const i=new Promise((e=>t=e));if(this.writerWaiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,o)=>{s=setTimeout((()=>{const e=this.writerWaiters.indexOf(t);-1!==e&&(this.writerWaiters.splice(e,1),this._pendingWriters--),o(new r("RWMutex lock timed out"))}),e)}))])}unlock(){if(!this._writeLocked)throw new Error("RWMutex: unlock called without a matching lock");this._writeLocked=!1,this._tryWakeWriter()||this._wakeAllReaders()}async read(e,t){await this.rlock(t);try{return await e()}finally{this.runlock()}}async write(e,t){await this.lock(t);try{return await e()}finally{this.unlock()}}writeLocked(){return this._writeLocked}readers(){return this._readers}pendingWriters(){return this._pendingWriters}pendingReaders(){return this.readerWaiters.length}_tryWakeWriter(){if(this._writeLocked||this._readers>0)return!1;const e=this.writerWaiters.shift();return!!e&&(this._writeLocked=!0,this._pendingWriters--,this._schedule(e),!0)}_wakeAllReaders(){const e=this.readerWaiters.splice(0);if(0!==e.length){this._readers+=e.length;for(const t of e)this._schedule(t)}}_schedule(e){"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0)}},exports.Semaphore=class{_slots;_available;_capacity;_yieldMode;waiters=[];constructor(e){if(this._slots=e?.slots??1,this._available=this._slots,this._capacity=e?.capacity??1/0,this._yieldMode=e?.yieldMode??"macrotask",this._slots<1)throw new Error("Semaphore slots must be >= 1")}async acquire(e){if(this._available>0)return void this._available--;if(this.waiters.length>=this._capacity)throw new Error(`Semaphore queue is full (capacity: ${this._capacity})`);let t;const i=new Promise((e=>t=e));if(this.waiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,o)=>{s=setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),o(new r("Semaphore acquire timed out"))}),e)}))])}tryAcquire(){return this._available>0&&(this._available--,!0)}release(){if(this._available>=this._slots)throw new Error("Semaphore released more times than acquired");const e=this.waiters.shift();e?"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0):this._available++}async run(e,t){await this.acquire(t);try{return await e()}finally{this.release()}}available(){return this._available}pending(){return this.waiters.length}slots(){return this._slots}},exports.Serializer=class{mutex;_done=!1;_lastValue=null;_lastError=void 0;_hasRun=!1;constructor(e){this.mutex=new a({capacity:e?.capacity??1e3,yieldMode:e?.yieldMode??"macrotask"})}async do(e,t){if(this._done)return{value:null,error:new o};try{await this.mutex.lock(t)}catch(e){return{value:null,error:e}}let i,s=null;try{if(this._done)throw new o;s=await e(),this._lastValue=s,this._lastError=void 0,this._hasRun=!0}catch(e){i=e,this._lastError=e,this._hasRun=!0}finally{this.mutex.unlock()}return{value:s,error:i}}peek(){return{value:this._lastValue,error:this._lastError}}hasRun(){return this._hasRun}close(){this._done=!0}pending(){return this.mutex.pending()}running(){return this.mutex.locked()}},exports.SerializerExecutionDone=o,exports.SharedResource=class{constructor(e,t,i={}){this.factory=e,this.onCleanup=t,this.options=i}_count=0;init=new n({retry:!1,throws:!1});pendingMicrotask=!1;cleanupTimer;get subscribers(){return this._count}async acquire(){this.cancelPendingCleanup(),this._count++;const e=await this.init.do(this.factory);if(e.error)throw e.error;return e.value}release(){this._count<=0?console.warn("SharedResource.release() called, but count is already 0."):(this._count--,0===this._count&&this.scheduleCleanup())}peek(){const e=this.init.peek();return e.error?null:e.value}forceCleanup(){this.cancelPendingCleanup(),this._count=0,this.executeCleanup()}cancelPendingCleanup(){this.pendingMicrotask=!1,void 0!==this.cleanupTimer&&(clearTimeout(this.cleanupTimer),this.cleanupTimer=void 0)}scheduleCleanup(){const e=this.options.gracePeriod??"microtask";if("sync"!==e)return"microtask"===e?(this.pendingMicrotask=!0,void queueMicrotask((()=>{!this.pendingMicrotask||this._count>0||(this.pendingMicrotask=!1,this.executeCleanup())}))):void(this.cleanupTimer=setTimeout((()=>{this.cleanupTimer=void 0,this._count>0||this.executeCleanup()}),e));this.executeCleanup()}executeCleanup(){const e=this.init.peek();try{this.onCleanup(e.value)}catch(e){console.error("[SharedResource] Error during cleanup callback:",e)}this.init.running()||this.init.reset()}},exports.SyncError=s,exports.TimeoutError=r;
1
+ "use strict";var e={VALIDATION_FAILED:{code:"VAL-001",name:"VALIDATION_FAILED",description:"Input validation failed due to one or more invalid fields",category:"validation",httpStatus:400,logLevel:"warn"},REQUIRED_FIELD_MISSING:{code:"VAL-002",name:"REQUIRED_FIELD_MISSING",description:"A required field was not provided in the request",category:"validation",httpStatus:400,logLevel:"warn"},INVALID_FORMAT:{code:"VAL-003",name:"INVALID_FORMAT",description:"Field value does not match expected format",category:"validation",httpStatus:400,logLevel:"warn"},NOT_FOUND:{code:"DB-001-NF",name:"NOT_FOUND",description:"The requested resource could not be found",category:"database",httpStatus:404,logLevel:"info",action:"Verify the resource identifier exists before accessing"},DUPLICATE_KEY:{code:"DB-002-DUP",name:"DUPLICATE_KEY",description:"A unique constraint violation occurred",category:"database",httpStatus:409,logLevel:"warn",action:"Check if the resource already exists before creation"},RESOURCE_LOCKED:{code:"DB-003-LOCK",name:"RESOURCE_LOCKED",description:"The resource is currently locked by another operation",category:"database",httpStatus:409,logLevel:"warn",action:"Retry the operation after a brief delay"},PERMISSION_DENIED:{code:"AUTH-001-DENIED",name:"PERMISSION_DENIED",description:"The authenticated user lacks permission for this operation",category:"auth",httpStatus:403,logLevel:"warn",action:"Check user roles and permissions"},UNAUTHENTICATED:{code:"AUTH-002-UNAUTH",name:"UNAUTHENTICATED",description:"Authentication is required for this operation",category:"auth",httpStatus:401,logLevel:"info",action:"Provide valid authentication credentials"},INVALID_COMMAND:{code:"BUS-001",name:"INVALID_COMMAND",description:"The command or operation is invalid for the current state",category:"business",httpStatus:400,logLevel:"warn"},OPERATION_ABORTED:{code:"BUS-002-ABORT",name:"OPERATION_ABORTED",description:"The operation was explicitly aborted",category:"business",httpStatus:409,logLevel:"info"},INTERNAL_ERROR:{code:"SYS-001",name:"INTERNAL_ERROR",description:"An unexpected internal error occurred",category:"system",httpStatus:500,logLevel:"error",action:"Check system logs for stack traces and diagnostic information"},BACKEND_ERROR:{code:"SYS-002",name:"BACKEND_ERROR",description:"An error occurred in a backend service",category:"system",httpStatus:502,logLevel:"error"},CONCURRENCY_ERROR:{code:"CON-001",name:"CONCURRENCY_ERROR",description:"A concurrency conflict occurred during the operation",category:"concurrency",httpStatus:409,logLevel:"warn",action:"Retry the operation with updated data"}},t=new Map;var i=class i extends Error{code;codeMetadata;severity;path;operation;issues;cause;constructor(s){const r={...function(i){const s=Object.values(e).find((e=>e.code===i));if(s)return s;return t.get(i)||{code:i,name:i.replace(/-/g,"_"),description:`Unknown error: ${i}`,category:"custom",httpStatus:500,logLevel:"error",action:"Check if this error code is properly registered or handle as unknown error"}}(s.code),...s.metadata,code:s.code};super(s.message??r.description),this.name="SystemError",this.code=s.code,this.codeMetadata=r,this.severity=s.severity??"error",this.path=s.path,this.operation=s.operation,this.issues=s.issues??[],this.cause=s.cause,Object.setPrototypeOf(this,i.prototype),Error.captureStackTrace&&Error.captureStackTrace(this,i)}withPath(e){return new i({code:this.code,message:this.message,severity:this.severity,path:e,operation:this.operation,issues:this.issues,cause:this.cause,metadata:this.codeMetadata})}withOperation(e){return new i({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:e,issues:this.issues,cause:this.cause,metadata:this.codeMetadata})}withIssue(e){return new i({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:[...this.issues,e],cause:this.cause,metadata:this.codeMetadata})}withIssues(e){return new i({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:[...this.issues,...e],cause:this.cause,metadata:this.codeMetadata})}withCause(e){return new i({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:this.issues,cause:e,metadata:this.codeMetadata})}getHttpStatus(){return this.codeMetadata.httpStatus}toLogEntry(){return{name:this.name,code:this.code,category:this.codeMetadata.category,message:this.message,path:this.path,operation:this.operation,issues:this.issues,cause:this.cause instanceof Error?this.cause.message:this.cause,stack:this.stack,timestamp:(new Date).toISOString()}}toString(){let e=`[${this.code}] ${this.message} (${this.codeMetadata.category})`;return this.path&&(e+=` at '${this.path}'`),this.operation&&(e+=` during '${this.operation}'`),this.issues.length>0&&(e+="\nIssues:\n"+this.issues.map(((e,t)=>` ${t+1}. ${e.message} [${e.code}]`)).join("\n")),this.cause&&(e+=`\nCause: ${this.cause instanceof Error?this.cause.message:String(this.cause)}`),e}},s=class e extends i{constructor(t,i){super({code:"SYNC_ERROR",message:t,cause:i}),this.name="SyncError",Object.setPrototypeOf(this,e.prototype)}},r=class e extends s{constructor(t){super(`[ArtifactContainer] Operation timed out: ${t}`),this.name="TimeoutError",Object.setPrototypeOf(this,e.prototype)}},o=class e extends s{constructor(t){super("[Serializer] The serializer has been marked as done!",t),this.name="SerializerExecutionDone",Object.setPrototypeOf(this,e.prototype)}},a=class{_locked=!1;_capacity;_yieldMode;waiters=[];constructor(e){this._capacity=e?.capacity??1/0,this._yieldMode=e?.yieldMode??"macrotask"}async lock(e){if(!this._locked)return void(this._locked=!0);if(this.waiters.length>=this._capacity)throw new Error(`Mutex queue is full (capacity: ${this._capacity})`);let t;const i=new Promise((e=>t=e));if(this.waiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,o)=>{s=setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),o(new r("Mutex lock timed out"))}),e)}))])}tryLock(){return!this._locked&&(this._locked=!0,!0)}unlock(){if(!this._locked)throw new Error("Mutex is not locked");const e=this.waiters.shift();e?"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0):this._locked=!1}locked(){return this._locked}pending(){return this.waiters.length}},n=class{mutex=new a({yieldMode:"microtask"});promise=null;_value=null;_error;_done=!1;retry;throws;constructor({retry:e,throws:t}={}){this.retry=Boolean(e),this.throws=Boolean(t)}resolve(e){if(this._done)throw new Error("Cannot resolve: operation is already completed.");if(this.running())throw new Error("Cannot resolve: operation is currently running.");this._value=e,this._done=!0}async do(e,t){return this._done?this.peek():this.promise?this._awaitWithTimeout(this.promise,t,"Once do() timed out"):(await this.mutex.lock(),this.promise?(this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")):(this.promise=(async()=>{try{this._value=await e(),this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.retry&&!this._done&&(this.promise=null)}return this.peek()})(),this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")))}doSync(e){if(this._done){if(this.throws&&this._error)throw this._error;return this.peek()}if(this.promise){const e=new Error("Cannot execute doSync while an async operation is pending.");if(this.throws)throw e;return{value:null,error:e}}if(!this.mutex.tryLock()){const e=new Error("Cannot execute doSync: lock is currently held.");if(this.throws)throw e;return{value:null,error:e}}if(this.promise||this._done){if(this.mutex.unlock(),this._done){if(this.throws&&this._error)throw this._error;return this.peek()}const e=new Error("Cannot execute doSync while an async operation is pending.");if(this.throws)throw e;return{value:null,error:e}}try{const t=e();this._value=t,this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.mutex.unlock()}return this.peek()}running(){return null!==this.promise&&!this.done()}peek(){return{value:this._value,error:this._error}}get(){if(!this._done)throw new Error("Once operation is not yet complete");if(this._error)throw this._error;return this._value}reset(){if(this.running())throw new Error("Cannot reset Once while an operation is in progress.");this._done=!1,this.promise=null,this._value=null,this._error=void 0}done(){return this._done}current(){return this.promise}_awaitWithTimeout(e,t,i="Operation timed out"){if(null==t)return e;let s;return Promise.race([e.then((e=>(clearTimeout(s),e))),new Promise(((e,o)=>{s=setTimeout((()=>o(new r(i))),t)}))])}};exports.Debouncer=class{_delay;_leading;_timer;_pendingFn;_pendingResolvers=[];_leadingFired=!1;constructor(e){this._delay=e?.delay??300,this._leading=e?.leading??!1}do(e){return new Promise((t=>{this._enqueue(e,t)}))}fire(e){this._enqueue(e,void 0)}_enqueue(e,t){if(this._pendingFn=e,t&&this._pendingResolvers.push(t),this._leading&&!this._leadingFired)return this._leadingFired=!0,this._fire(),clearTimeout(this._timer),void(this._timer=setTimeout((()=>{this._leadingFired=!1,void 0!==this._pendingFn&&this._fire()}),this._delay));clearTimeout(this._timer),this._timer=setTimeout((()=>{this._leadingFired=!1,this._fire()}),this._delay)}cancel(){clearTimeout(this._timer),this._timer=void 0,this._pendingFn=void 0,this._leadingFired=!1;const e=this._pendingResolvers.splice(0);for(const t of e)t({status:"cancelled"})}async flush(){return this._pendingFn?(clearTimeout(this._timer),this._timer=void 0,this._leadingFired=!1,this._fire()):null}pending(){return void 0!==this._pendingFn}async _fire(){const e=this._pendingFn,t=this._pendingResolvers.splice(0);let i;this._pendingFn=void 0;try{i={status:"ok",value:await e()}}catch(e){i={status:"error",error:e}}for(const e of t)e(i);return i}},exports.Latch=class{_open=!1;_resolve;_promise;constructor(){this._promise=new Promise((e=>{this._resolve=e}))}open(){this._open||(this._open=!0,this._resolve())}async wait(e){if(this._open)return;if(null==e)return this._promise;let t;await Promise.race([this._promise.then((()=>clearTimeout(t))),new Promise(((i,s)=>{t=setTimeout((()=>s(new r("Latch timed out"))),e)}))])}isOpen(){return this._open}},exports.Mutex=a,exports.Once=n,exports.OnceExecutionConflict=class e extends s{constructor(t){super("[Once] A method has already been registered!",t),this.name="OnceExecutionConflict",Object.setPrototypeOf(this,e.prototype)}},exports.RWMutex=class{_readers=0;_writeLocked=!1;_pendingWriters=0;_yieldMode;readerWaiters=[];writerWaiters=[];constructor(e){this._yieldMode=e?.yieldMode??"microtask"}async rlock(e){if(!this._writeLocked&&0===this._pendingWriters)return void this._readers++;let t;const i=new Promise((e=>t=e));if(this.readerWaiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,o)=>{s=setTimeout((()=>{const e=this.readerWaiters.indexOf(t);-1!==e&&this.readerWaiters.splice(e,1),o(new r("RWMutex rlock timed out"))}),e)}))])}runlock(){if(this._readers<=0)throw new Error("RWMutex: runlock called without a matching rlock");this._readers--,this._tryWakeWriter()}async lock(e){if(!this._writeLocked&&0===this._readers)return void(this._writeLocked=!0);let t;this._pendingWriters++;const i=new Promise((e=>t=e));if(this.writerWaiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,o)=>{s=setTimeout((()=>{const e=this.writerWaiters.indexOf(t);-1!==e&&(this.writerWaiters.splice(e,1),this._pendingWriters--),o(new r("RWMutex lock timed out"))}),e)}))])}unlock(){if(!this._writeLocked)throw new Error("RWMutex: unlock called without a matching lock");this._writeLocked=!1,this._tryWakeWriter()||this._wakeAllReaders()}async read(e,t){await this.rlock(t);try{return await e()}finally{this.runlock()}}async write(e,t){await this.lock(t);try{return await e()}finally{this.unlock()}}writeLocked(){return this._writeLocked}readers(){return this._readers}pendingWriters(){return this._pendingWriters}pendingReaders(){return this.readerWaiters.length}_tryWakeWriter(){if(this._writeLocked||this._readers>0)return!1;const e=this.writerWaiters.shift();return!!e&&(this._writeLocked=!0,this._pendingWriters--,this._schedule(e),!0)}_wakeAllReaders(){const e=this.readerWaiters.splice(0);if(0!==e.length){this._readers+=e.length;for(const t of e)this._schedule(t)}}_schedule(e){"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0)}},exports.Semaphore=class{_slots;_available;_capacity;_yieldMode;waiters=[];constructor(e){if(this._slots=e?.slots??1,this._available=this._slots,this._capacity=e?.capacity??1/0,this._yieldMode=e?.yieldMode??"macrotask",this._slots<1)throw new Error("Semaphore slots must be >= 1")}async acquire(e){if(this._available>0)return void this._available--;if(this.waiters.length>=this._capacity)throw new Error(`Semaphore queue is full (capacity: ${this._capacity})`);let t;const i=new Promise((e=>t=e));if(this.waiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,o)=>{s=setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),o(new r("Semaphore acquire timed out"))}),e)}))])}tryAcquire(){return this._available>0&&(this._available--,!0)}release(){if(this._available>=this._slots)throw new Error("Semaphore released more times than acquired");const e=this.waiters.shift();e?"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0):this._available++}async run(e,t){await this.acquire(t);try{return await e()}finally{this.release()}}available(){return this._available}pending(){return this.waiters.length}slots(){return this._slots}},exports.Serializer=class{mutex;_done=!1;_lastValue=null;_lastError=void 0;_hasRun=!1;constructor(e){this.mutex=new a({capacity:e?.capacity??1e3,yieldMode:e?.yieldMode??"macrotask"})}async do(e,t){if(this._done)return{value:null,error:new o};try{await this.mutex.lock(t)}catch(e){return{value:null,error:e}}let i,s=null;try{if(this._done)throw new o;s=await e(),this._lastValue=s,this._lastError=void 0,this._hasRun=!0}catch(e){i=e,this._lastError=e,this._hasRun=!0}finally{this.mutex.unlock()}return{value:s,error:i}}peek(){return{value:this._lastValue,error:this._lastError}}hasRun(){return this._hasRun}close(){this._done=!0}pending(){return this.mutex.pending()}running(){return this.mutex.locked()}},exports.SerializerExecutionDone=o,exports.SharedResource=class{constructor(e,t,i={}){this.factory=e,this.onCleanup=t,this.options=i}_count=0;init=new n({retry:!1,throws:!1});pendingMicrotask=!1;cleanupTimer;get subscribers(){return this._count}async acquire(){this.cancelPendingCleanup(),this._count++;const e=await this.init.do(this.factory);if(e.error)throw e.error;return e.value}release(){this._count<=0?console.warn("SharedResource.release() called, but count is already 0."):(this._count--,0===this._count&&this.scheduleCleanup())}peek(){const e=this.init.peek();return e.error?null:e.value}forceCleanup(){this.cancelPendingCleanup(),this._count=0,this.executeCleanup()}cancelPendingCleanup(){this.pendingMicrotask=!1,void 0!==this.cleanupTimer&&(clearTimeout(this.cleanupTimer),this.cleanupTimer=void 0)}scheduleCleanup(){const e=this.options.gracePeriod??"microtask";if("sync"!==e)return"microtask"===e?(this.pendingMicrotask=!0,void queueMicrotask((()=>{!this.pendingMicrotask||this._count>0||(this.pendingMicrotask=!1,this.executeCleanup())}))):void(this.cleanupTimer=setTimeout((()=>{this.cleanupTimer=void 0,this._count>0||this.executeCleanup()}),e));this.executeCleanup()}executeCleanup(){const e=this.init.peek();try{this.onCleanup(e.value)}catch(e){console.error("[SharedResource] Error during cleanup callback:",e)}this.init.running()||this.init.reset()}},exports.SyncError=s,exports.TimeoutError=r;
package/index.mjs CHANGED
@@ -1 +1 @@
1
- var e=class{_delay;_leading;_timer;_pendingFn;_pendingResolvers=[];_leadingFired=!1;constructor(e){this._delay=e?.delay??300,this._leading=e?.leading??!1}do(e){return new Promise((t=>{this._enqueue(e,t)}))}fire(e){this._enqueue(e,void 0)}_enqueue(e,t){if(this._pendingFn=e,t&&this._pendingResolvers.push(t),this._leading&&!this._leadingFired)return this._leadingFired=!0,this._fire(),clearTimeout(this._timer),void(this._timer=setTimeout((()=>{this._leadingFired=!1,void 0!==this._pendingFn&&this._fire()}),this._delay));clearTimeout(this._timer),this._timer=setTimeout((()=>{this._leadingFired=!1,this._fire()}),this._delay)}cancel(){clearTimeout(this._timer),this._timer=void 0,this._pendingFn=void 0,this._leadingFired=!1;const e=this._pendingResolvers.splice(0);for(const t of e)t({status:"cancelled"})}async flush(){return this._pendingFn?(clearTimeout(this._timer),this._timer=void 0,this._leadingFired=!1,this._fire()):null}pending(){return void 0!==this._pendingFn}async _fire(){const e=this._pendingFn,t=this._pendingResolvers.splice(0);let i;this._pendingFn=void 0;try{i={status:"ok",value:await e()}}catch(e){i={status:"error",error:e}}for(const e of t)e(i);return i}},t={VALIDATION_FAILED:{code:"VAL-001",name:"VALIDATION_FAILED",description:"Input validation failed due to one or more invalid fields",category:"validation",httpStatus:400,logLevel:"warn"},REQUIRED_FIELD_MISSING:{code:"VAL-002",name:"REQUIRED_FIELD_MISSING",description:"A required field was not provided in the request",category:"validation",httpStatus:400,logLevel:"warn"},INVALID_FORMAT:{code:"VAL-003",name:"INVALID_FORMAT",description:"Field value does not match expected format",category:"validation",httpStatus:400,logLevel:"warn"},NOT_FOUND:{code:"DB-001-NF",name:"NOT_FOUND",description:"The requested resource could not be found",category:"database",httpStatus:404,logLevel:"info",action:"Verify the resource identifier exists before accessing"},DUPLICATE_KEY:{code:"DB-002-DUP",name:"DUPLICATE_KEY",description:"A unique constraint violation occurred",category:"database",httpStatus:409,logLevel:"warn",action:"Check if the resource already exists before creation"},RESOURCE_LOCKED:{code:"DB-003-LOCK",name:"RESOURCE_LOCKED",description:"The resource is currently locked by another operation",category:"database",httpStatus:409,logLevel:"warn",action:"Retry the operation after a brief delay"},PERMISSION_DENIED:{code:"AUTH-001-DENIED",name:"PERMISSION_DENIED",description:"The authenticated user lacks permission for this operation",category:"auth",httpStatus:403,logLevel:"warn",action:"Check user roles and permissions"},UNAUTHENTICATED:{code:"AUTH-002-UNAUTH",name:"UNAUTHENTICATED",description:"Authentication is required for this operation",category:"auth",httpStatus:401,logLevel:"info",action:"Provide valid authentication credentials"},INVALID_COMMAND:{code:"BUS-001",name:"INVALID_COMMAND",description:"The command or operation is invalid for the current state",category:"business",httpStatus:400,logLevel:"warn"},OPERATION_ABORTED:{code:"BUS-002-ABORT",name:"OPERATION_ABORTED",description:"The operation was explicitly aborted",category:"business",httpStatus:409,logLevel:"info"},INTERNAL_ERROR:{code:"SYS-001",name:"INTERNAL_ERROR",description:"An unexpected internal error occurred",category:"system",httpStatus:500,logLevel:"error",action:"Check system logs for stack traces and diagnostic information"},BACKEND_ERROR:{code:"SYS-002",name:"BACKEND_ERROR",description:"An error occurred in a backend service",category:"system",httpStatus:502,logLevel:"error"},CONCURRENCY_ERROR:{code:"CON-001",name:"CONCURRENCY_ERROR",description:"A concurrency conflict occurred during the operation",category:"concurrency",httpStatus:409,logLevel:"warn",action:"Retry the operation with updated data"}},i=new Map;var s=class e extends Error{code;codeMetadata;severity;path;operation;issues;cause;constructor(s){const r={...function(e){const s=Object.values(t).find((t=>t.code===e));if(s)return s;return i.get(e)||{code:e,name:e.replace(/-/g,"_"),description:`Unknown error: ${e}`,category:"custom",httpStatus:500,logLevel:"error",action:"Check if this error code is properly registered or handle as unknown error"}}(s.code),...s.metadata,code:s.code};super(s.message??r.description),this.name="SystemError",this.code=s.code,this.codeMetadata=r,this.severity=s.severity??"error",this.path=s.path,this.operation=s.operation,this.issues=s.issues??[],this.cause=s.cause,Object.setPrototypeOf(this,e.prototype),Error.captureStackTrace&&Error.captureStackTrace(this,e)}withPath(t){return new e({code:this.code,message:this.message,severity:this.severity,path:t,operation:this.operation,issues:this.issues,cause:this.cause,metadata:this.codeMetadata})}withOperation(t){return new e({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:t,issues:this.issues,cause:this.cause,metadata:this.codeMetadata})}withIssue(t){return new e({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:[...this.issues,t],cause:this.cause,metadata:this.codeMetadata})}withIssues(t){return new e({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:[...this.issues,...t],cause:this.cause,metadata:this.codeMetadata})}withCause(t){return new e({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:this.issues,cause:t,metadata:this.codeMetadata})}getHttpStatus(){return this.codeMetadata.httpStatus}toLogEntry(){return{name:this.name,code:this.code,category:this.codeMetadata.category,message:this.message,path:this.path,operation:this.operation,issues:this.issues,cause:this.cause instanceof Error?this.cause.message:this.cause,stack:this.stack,timestamp:(new Date).toISOString()}}toString(){let e=`[${this.code}] ${this.message} (${this.codeMetadata.category})`;return this.path&&(e+=` at '${this.path}'`),this.operation&&(e+=` during '${this.operation}'`),this.issues.length>0&&(e+="\nIssues:\n"+this.issues.map(((e,t)=>` ${t+1}. ${e.message} [${e.code}]`)).join("\n")),this.cause&&(e+=`\nCause: ${this.cause instanceof Error?this.cause.message:String(this.cause)}`),e}},r=class e extends s{constructor(t,i){super({code:"SYNC_ERROR",message:t,cause:i}),this.name="SyncError",Object.setPrototypeOf(this,e.prototype)}},o=class e extends r{constructor(t){super(`[ArtifactContainer] Operation timed out: ${t}`),this.name="TimeoutError",Object.setPrototypeOf(this,e.prototype)}},a=class e extends r{constructor(t){super("[Once] A method has already been registered!",t),this.name="OnceExecutionConflict",Object.setPrototypeOf(this,e.prototype)}},n=class e extends r{constructor(t){super("[Serializer] The serializer has been marked as done!",t),this.name="SerializerExecutionDone",Object.setPrototypeOf(this,e.prototype)}},h=class{_open=!1;_resolve;_promise;constructor(){this._promise=new Promise((e=>{this._resolve=e}))}open(){this._open||(this._open=!0,this._resolve())}async wait(e){if(this._open)return;if(null==e)return this._promise;let t;await Promise.race([this._promise.then((()=>clearTimeout(t))),new Promise(((i,s)=>{t=setTimeout((()=>s(new o("Latch timed out"))),e)}))])}isOpen(){return this._open}},c=class{_locked=!1;_capacity;_yieldMode;waiters=[];constructor(e){this._capacity=e?.capacity??1/0,this._yieldMode=e?.yieldMode??"macrotask"}async lock(e){if(!this._locked)return void(this._locked=!0);if(this.waiters.length>=this._capacity)throw new Error(`Mutex queue is full (capacity: ${this._capacity})`);let t;const i=new Promise((e=>t=e));if(this.waiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,r)=>{s=setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),r(new o("Mutex lock timed out"))}),e)}))])}tryLock(){return!this._locked&&(this._locked=!0,!0)}unlock(){if(!this._locked)throw new Error("Mutex is not locked");const e=this.waiters.shift();e?"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0):this._locked=!1}locked(){return this._locked}pending(){return this.waiters.length}},u=class{mutex=new c({yieldMode:"microtask"});promise=null;_value=null;_error;_done=!1;retry;throws;constructor({retry:e,throws:t}={}){this.retry=Boolean(e),this.throws=Boolean(t)}async do(e,t){return this._done?this.peek():this.promise?this._awaitWithTimeout(this.promise,t,"Once do() timed out"):(await this.mutex.lock(),this.promise?(this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")):(this.promise=(async()=>{try{this._value=await e(),this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.retry&&!this._done&&(this.promise=null)}return this.peek()})(),this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")))}doSync(e){if(this._done){if(this.throws&&this._error)throw this._error;return this.peek()}if(this.promise){const e=new Error("Cannot execute doSync while an async operation is pending.");if(this.throws)throw e;return{value:null,error:e}}if(!this.mutex.tryLock()){const e=new Error("Cannot execute doSync: lock is currently held.");if(this.throws)throw e;return{value:null,error:e}}if(this.promise||this._done){if(this.mutex.unlock(),this._done){if(this.throws&&this._error)throw this._error;return this.peek()}const e=new Error("Cannot execute doSync while an async operation is pending.");if(this.throws)throw e;return{value:null,error:e}}try{const t=e();this._value=t,this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.mutex.unlock()}return this.peek()}running(){return null!==this.promise&&!this.done()}peek(){return{value:this._value,error:this._error}}get(){if(!this._done)throw new Error("Once operation is not yet complete");if(this._error)throw this._error;return this._value}reset(){if(this.running())throw new Error("Cannot reset Once while an operation is in progress.");this._done=!1,this.promise=null,this._value=null,this._error=void 0}done(){return this._done}current(){return this.promise}_awaitWithTimeout(e,t,i="Operation timed out"){if(null==t)return e;let s;return Promise.race([e.then((e=>(clearTimeout(s),e))),new Promise(((e,r)=>{s=setTimeout((()=>r(new o(i))),t)}))])}},l=class{_readers=0;_writeLocked=!1;_pendingWriters=0;_yieldMode;readerWaiters=[];writerWaiters=[];constructor(e){this._yieldMode=e?.yieldMode??"microtask"}async rlock(e){if(!this._writeLocked&&0===this._pendingWriters)return void this._readers++;let t;const i=new Promise((e=>t=e));if(this.readerWaiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,r)=>{s=setTimeout((()=>{const e=this.readerWaiters.indexOf(t);-1!==e&&this.readerWaiters.splice(e,1),r(new o("RWMutex rlock timed out"))}),e)}))])}runlock(){if(this._readers<=0)throw new Error("RWMutex: runlock called without a matching rlock");this._readers--,this._tryWakeWriter()}async lock(e){if(!this._writeLocked&&0===this._readers)return void(this._writeLocked=!0);let t;this._pendingWriters++;const i=new Promise((e=>t=e));if(this.writerWaiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,r)=>{s=setTimeout((()=>{const e=this.writerWaiters.indexOf(t);-1!==e&&(this.writerWaiters.splice(e,1),this._pendingWriters--),r(new o("RWMutex lock timed out"))}),e)}))])}unlock(){if(!this._writeLocked)throw new Error("RWMutex: unlock called without a matching lock");this._writeLocked=!1,this._tryWakeWriter()||this._wakeAllReaders()}async read(e,t){await this.rlock(t);try{return await e()}finally{this.runlock()}}async write(e,t){await this.lock(t);try{return await e()}finally{this.unlock()}}writeLocked(){return this._writeLocked}readers(){return this._readers}pendingWriters(){return this._pendingWriters}pendingReaders(){return this.readerWaiters.length}_tryWakeWriter(){if(this._writeLocked||this._readers>0)return!1;const e=this.writerWaiters.shift();return!!e&&(this._writeLocked=!0,this._pendingWriters--,this._schedule(e),!0)}_wakeAllReaders(){const e=this.readerWaiters.splice(0);if(0!==e.length){this._readers+=e.length;for(const t of e)this._schedule(t)}}_schedule(e){"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0)}},d=class{_slots;_available;_capacity;_yieldMode;waiters=[];constructor(e){if(this._slots=e?.slots??1,this._available=this._slots,this._capacity=e?.capacity??1/0,this._yieldMode=e?.yieldMode??"macrotask",this._slots<1)throw new Error("Semaphore slots must be >= 1")}async acquire(e){if(this._available>0)return void this._available--;if(this.waiters.length>=this._capacity)throw new Error(`Semaphore queue is full (capacity: ${this._capacity})`);let t;const i=new Promise((e=>t=e));if(this.waiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,r)=>{s=setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),r(new o("Semaphore acquire timed out"))}),e)}))])}tryAcquire(){return this._available>0&&(this._available--,!0)}release(){if(this._available>=this._slots)throw new Error("Semaphore released more times than acquired");const e=this.waiters.shift();e?"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0):this._available++}async run(e,t){await this.acquire(t);try{return await e()}finally{this.release()}}available(){return this._available}pending(){return this.waiters.length}slots(){return this._slots}},p=class{mutex;_done=!1;_lastValue=null;_lastError=void 0;_hasRun=!1;constructor(e){this.mutex=new c({capacity:e?.capacity??1e3,yieldMode:e?.yieldMode??"macrotask"})}async do(e,t){if(this._done)return{value:null,error:new n};try{await this.mutex.lock(t)}catch(e){return{value:null,error:e}}let i,s=null;try{if(this._done)throw new n;s=await e(),this._lastValue=s,this._lastError=void 0,this._hasRun=!0}catch(e){i=e,this._lastError=e,this._hasRun=!0}finally{this.mutex.unlock()}return{value:s,error:i}}peek(){return{value:this._lastValue,error:this._lastError}}hasRun(){return this._hasRun}close(){this._done=!0}pending(){return this.mutex.pending()}running(){return this.mutex.locked()}},_=class{constructor(e,t,i={}){this.factory=e,this.onCleanup=t,this.options=i}_count=0;init=new u({retry:!1,throws:!1});pendingMicrotask=!1;cleanupTimer;get subscribers(){return this._count}async acquire(){this.cancelPendingCleanup(),this._count++;const e=await this.init.do(this.factory);if(e.error)throw e.error;return e.value}release(){this._count<=0?console.warn("SharedResource.release() called, but count is already 0."):(this._count--,0===this._count&&this.scheduleCleanup())}peek(){const e=this.init.peek();return e.error?null:e.value}forceCleanup(){this.cancelPendingCleanup(),this._count=0,this.executeCleanup()}cancelPendingCleanup(){this.pendingMicrotask=!1,void 0!==this.cleanupTimer&&(clearTimeout(this.cleanupTimer),this.cleanupTimer=void 0)}scheduleCleanup(){const e=this.options.gracePeriod??"microtask";if("sync"!==e)return"microtask"===e?(this.pendingMicrotask=!0,void queueMicrotask((()=>{!this.pendingMicrotask||this._count>0||(this.pendingMicrotask=!1,this.executeCleanup())}))):void(this.cleanupTimer=setTimeout((()=>{this.cleanupTimer=void 0,this._count>0||this.executeCleanup()}),e));this.executeCleanup()}executeCleanup(){const e=this.init.peek();try{this.onCleanup(e.value)}catch(e){console.error("[SharedResource] Error during cleanup callback:",e)}this.init.running()||this.init.reset()}};export{e as Debouncer,h as Latch,c as Mutex,u as Once,a as OnceExecutionConflict,l as RWMutex,d as Semaphore,p as Serializer,n as SerializerExecutionDone,_ as SharedResource,r as SyncError,o as TimeoutError};
1
+ var e=class{_delay;_leading;_timer;_pendingFn;_pendingResolvers=[];_leadingFired=!1;constructor(e){this._delay=e?.delay??300,this._leading=e?.leading??!1}do(e){return new Promise((t=>{this._enqueue(e,t)}))}fire(e){this._enqueue(e,void 0)}_enqueue(e,t){if(this._pendingFn=e,t&&this._pendingResolvers.push(t),this._leading&&!this._leadingFired)return this._leadingFired=!0,this._fire(),clearTimeout(this._timer),void(this._timer=setTimeout((()=>{this._leadingFired=!1,void 0!==this._pendingFn&&this._fire()}),this._delay));clearTimeout(this._timer),this._timer=setTimeout((()=>{this._leadingFired=!1,this._fire()}),this._delay)}cancel(){clearTimeout(this._timer),this._timer=void 0,this._pendingFn=void 0,this._leadingFired=!1;const e=this._pendingResolvers.splice(0);for(const t of e)t({status:"cancelled"})}async flush(){return this._pendingFn?(clearTimeout(this._timer),this._timer=void 0,this._leadingFired=!1,this._fire()):null}pending(){return void 0!==this._pendingFn}async _fire(){const e=this._pendingFn,t=this._pendingResolvers.splice(0);let i;this._pendingFn=void 0;try{i={status:"ok",value:await e()}}catch(e){i={status:"error",error:e}}for(const e of t)e(i);return i}},t={VALIDATION_FAILED:{code:"VAL-001",name:"VALIDATION_FAILED",description:"Input validation failed due to one or more invalid fields",category:"validation",httpStatus:400,logLevel:"warn"},REQUIRED_FIELD_MISSING:{code:"VAL-002",name:"REQUIRED_FIELD_MISSING",description:"A required field was not provided in the request",category:"validation",httpStatus:400,logLevel:"warn"},INVALID_FORMAT:{code:"VAL-003",name:"INVALID_FORMAT",description:"Field value does not match expected format",category:"validation",httpStatus:400,logLevel:"warn"},NOT_FOUND:{code:"DB-001-NF",name:"NOT_FOUND",description:"The requested resource could not be found",category:"database",httpStatus:404,logLevel:"info",action:"Verify the resource identifier exists before accessing"},DUPLICATE_KEY:{code:"DB-002-DUP",name:"DUPLICATE_KEY",description:"A unique constraint violation occurred",category:"database",httpStatus:409,logLevel:"warn",action:"Check if the resource already exists before creation"},RESOURCE_LOCKED:{code:"DB-003-LOCK",name:"RESOURCE_LOCKED",description:"The resource is currently locked by another operation",category:"database",httpStatus:409,logLevel:"warn",action:"Retry the operation after a brief delay"},PERMISSION_DENIED:{code:"AUTH-001-DENIED",name:"PERMISSION_DENIED",description:"The authenticated user lacks permission for this operation",category:"auth",httpStatus:403,logLevel:"warn",action:"Check user roles and permissions"},UNAUTHENTICATED:{code:"AUTH-002-UNAUTH",name:"UNAUTHENTICATED",description:"Authentication is required for this operation",category:"auth",httpStatus:401,logLevel:"info",action:"Provide valid authentication credentials"},INVALID_COMMAND:{code:"BUS-001",name:"INVALID_COMMAND",description:"The command or operation is invalid for the current state",category:"business",httpStatus:400,logLevel:"warn"},OPERATION_ABORTED:{code:"BUS-002-ABORT",name:"OPERATION_ABORTED",description:"The operation was explicitly aborted",category:"business",httpStatus:409,logLevel:"info"},INTERNAL_ERROR:{code:"SYS-001",name:"INTERNAL_ERROR",description:"An unexpected internal error occurred",category:"system",httpStatus:500,logLevel:"error",action:"Check system logs for stack traces and diagnostic information"},BACKEND_ERROR:{code:"SYS-002",name:"BACKEND_ERROR",description:"An error occurred in a backend service",category:"system",httpStatus:502,logLevel:"error"},CONCURRENCY_ERROR:{code:"CON-001",name:"CONCURRENCY_ERROR",description:"A concurrency conflict occurred during the operation",category:"concurrency",httpStatus:409,logLevel:"warn",action:"Retry the operation with updated data"}},i=new Map;var s=class e extends Error{code;codeMetadata;severity;path;operation;issues;cause;constructor(s){const r={...function(e){const s=Object.values(t).find((t=>t.code===e));if(s)return s;return i.get(e)||{code:e,name:e.replace(/-/g,"_"),description:`Unknown error: ${e}`,category:"custom",httpStatus:500,logLevel:"error",action:"Check if this error code is properly registered or handle as unknown error"}}(s.code),...s.metadata,code:s.code};super(s.message??r.description),this.name="SystemError",this.code=s.code,this.codeMetadata=r,this.severity=s.severity??"error",this.path=s.path,this.operation=s.operation,this.issues=s.issues??[],this.cause=s.cause,Object.setPrototypeOf(this,e.prototype),Error.captureStackTrace&&Error.captureStackTrace(this,e)}withPath(t){return new e({code:this.code,message:this.message,severity:this.severity,path:t,operation:this.operation,issues:this.issues,cause:this.cause,metadata:this.codeMetadata})}withOperation(t){return new e({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:t,issues:this.issues,cause:this.cause,metadata:this.codeMetadata})}withIssue(t){return new e({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:[...this.issues,t],cause:this.cause,metadata:this.codeMetadata})}withIssues(t){return new e({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:[...this.issues,...t],cause:this.cause,metadata:this.codeMetadata})}withCause(t){return new e({code:this.code,message:this.message,severity:this.severity,path:this.path,operation:this.operation,issues:this.issues,cause:t,metadata:this.codeMetadata})}getHttpStatus(){return this.codeMetadata.httpStatus}toLogEntry(){return{name:this.name,code:this.code,category:this.codeMetadata.category,message:this.message,path:this.path,operation:this.operation,issues:this.issues,cause:this.cause instanceof Error?this.cause.message:this.cause,stack:this.stack,timestamp:(new Date).toISOString()}}toString(){let e=`[${this.code}] ${this.message} (${this.codeMetadata.category})`;return this.path&&(e+=` at '${this.path}'`),this.operation&&(e+=` during '${this.operation}'`),this.issues.length>0&&(e+="\nIssues:\n"+this.issues.map(((e,t)=>` ${t+1}. ${e.message} [${e.code}]`)).join("\n")),this.cause&&(e+=`\nCause: ${this.cause instanceof Error?this.cause.message:String(this.cause)}`),e}},r=class e extends s{constructor(t,i){super({code:"SYNC_ERROR",message:t,cause:i}),this.name="SyncError",Object.setPrototypeOf(this,e.prototype)}},o=class e extends r{constructor(t){super(`[ArtifactContainer] Operation timed out: ${t}`),this.name="TimeoutError",Object.setPrototypeOf(this,e.prototype)}},a=class e extends r{constructor(t){super("[Once] A method has already been registered!",t),this.name="OnceExecutionConflict",Object.setPrototypeOf(this,e.prototype)}},n=class e extends r{constructor(t){super("[Serializer] The serializer has been marked as done!",t),this.name="SerializerExecutionDone",Object.setPrototypeOf(this,e.prototype)}},h=class{_open=!1;_resolve;_promise;constructor(){this._promise=new Promise((e=>{this._resolve=e}))}open(){this._open||(this._open=!0,this._resolve())}async wait(e){if(this._open)return;if(null==e)return this._promise;let t;await Promise.race([this._promise.then((()=>clearTimeout(t))),new Promise(((i,s)=>{t=setTimeout((()=>s(new o("Latch timed out"))),e)}))])}isOpen(){return this._open}},c=class{_locked=!1;_capacity;_yieldMode;waiters=[];constructor(e){this._capacity=e?.capacity??1/0,this._yieldMode=e?.yieldMode??"macrotask"}async lock(e){if(!this._locked)return void(this._locked=!0);if(this.waiters.length>=this._capacity)throw new Error(`Mutex queue is full (capacity: ${this._capacity})`);let t;const i=new Promise((e=>t=e));if(this.waiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,r)=>{s=setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),r(new o("Mutex lock timed out"))}),e)}))])}tryLock(){return!this._locked&&(this._locked=!0,!0)}unlock(){if(!this._locked)throw new Error("Mutex is not locked");const e=this.waiters.shift();e?"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0):this._locked=!1}locked(){return this._locked}pending(){return this.waiters.length}},u=class{mutex=new c({yieldMode:"microtask"});promise=null;_value=null;_error;_done=!1;retry;throws;constructor({retry:e,throws:t}={}){this.retry=Boolean(e),this.throws=Boolean(t)}resolve(e){if(this._done)throw new Error("Cannot resolve: operation is already completed.");if(this.running())throw new Error("Cannot resolve: operation is currently running.");this._value=e,this._done=!0}async do(e,t){return this._done?this.peek():this.promise?this._awaitWithTimeout(this.promise,t,"Once do() timed out"):(await this.mutex.lock(),this.promise?(this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")):(this.promise=(async()=>{try{this._value=await e(),this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.retry&&!this._done&&(this.promise=null)}return this.peek()})(),this.mutex.unlock(),this._awaitWithTimeout(this.promise,t,"Once do() timed out")))}doSync(e){if(this._done){if(this.throws&&this._error)throw this._error;return this.peek()}if(this.promise){const e=new Error("Cannot execute doSync while an async operation is pending.");if(this.throws)throw e;return{value:null,error:e}}if(!this.mutex.tryLock()){const e=new Error("Cannot execute doSync: lock is currently held.");if(this.throws)throw e;return{value:null,error:e}}if(this.promise||this._done){if(this.mutex.unlock(),this._done){if(this.throws&&this._error)throw this._error;return this.peek()}const e=new Error("Cannot execute doSync while an async operation is pending.");if(this.throws)throw e;return{value:null,error:e}}try{const t=e();this._value=t,this._done=!0}catch(e){if(this._error=e,this.retry||(this._done=!0),this.throws)throw e}finally{this.mutex.unlock()}return this.peek()}running(){return null!==this.promise&&!this.done()}peek(){return{value:this._value,error:this._error}}get(){if(!this._done)throw new Error("Once operation is not yet complete");if(this._error)throw this._error;return this._value}reset(){if(this.running())throw new Error("Cannot reset Once while an operation is in progress.");this._done=!1,this.promise=null,this._value=null,this._error=void 0}done(){return this._done}current(){return this.promise}_awaitWithTimeout(e,t,i="Operation timed out"){if(null==t)return e;let s;return Promise.race([e.then((e=>(clearTimeout(s),e))),new Promise(((e,r)=>{s=setTimeout((()=>r(new o(i))),t)}))])}},l=class{_readers=0;_writeLocked=!1;_pendingWriters=0;_yieldMode;readerWaiters=[];writerWaiters=[];constructor(e){this._yieldMode=e?.yieldMode??"microtask"}async rlock(e){if(!this._writeLocked&&0===this._pendingWriters)return void this._readers++;let t;const i=new Promise((e=>t=e));if(this.readerWaiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,r)=>{s=setTimeout((()=>{const e=this.readerWaiters.indexOf(t);-1!==e&&this.readerWaiters.splice(e,1),r(new o("RWMutex rlock timed out"))}),e)}))])}runlock(){if(this._readers<=0)throw new Error("RWMutex: runlock called without a matching rlock");this._readers--,this._tryWakeWriter()}async lock(e){if(!this._writeLocked&&0===this._readers)return void(this._writeLocked=!0);let t;this._pendingWriters++;const i=new Promise((e=>t=e));if(this.writerWaiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,r)=>{s=setTimeout((()=>{const e=this.writerWaiters.indexOf(t);-1!==e&&(this.writerWaiters.splice(e,1),this._pendingWriters--),r(new o("RWMutex lock timed out"))}),e)}))])}unlock(){if(!this._writeLocked)throw new Error("RWMutex: unlock called without a matching lock");this._writeLocked=!1,this._tryWakeWriter()||this._wakeAllReaders()}async read(e,t){await this.rlock(t);try{return await e()}finally{this.runlock()}}async write(e,t){await this.lock(t);try{return await e()}finally{this.unlock()}}writeLocked(){return this._writeLocked}readers(){return this._readers}pendingWriters(){return this._pendingWriters}pendingReaders(){return this.readerWaiters.length}_tryWakeWriter(){if(this._writeLocked||this._readers>0)return!1;const e=this.writerWaiters.shift();return!!e&&(this._writeLocked=!0,this._pendingWriters--,this._schedule(e),!0)}_wakeAllReaders(){const e=this.readerWaiters.splice(0);if(0!==e.length){this._readers+=e.length;for(const t of e)this._schedule(t)}}_schedule(e){"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0)}},d=class{_slots;_available;_capacity;_yieldMode;waiters=[];constructor(e){if(this._slots=e?.slots??1,this._available=this._slots,this._capacity=e?.capacity??1/0,this._yieldMode=e?.yieldMode??"macrotask",this._slots<1)throw new Error("Semaphore slots must be >= 1")}async acquire(e){if(this._available>0)return void this._available--;if(this.waiters.length>=this._capacity)throw new Error(`Semaphore queue is full (capacity: ${this._capacity})`);let t;const i=new Promise((e=>t=e));if(this.waiters.push(t),null==e)return void await i;let s;await Promise.race([i.then((()=>clearTimeout(s))),new Promise(((i,r)=>{s=setTimeout((()=>{const e=this.waiters.indexOf(t);-1!==e&&this.waiters.splice(e,1),r(new o("Semaphore acquire timed out"))}),e)}))])}tryAcquire(){return this._available>0&&(this._available--,!0)}release(){if(this._available>=this._slots)throw new Error("Semaphore released more times than acquired");const e=this.waiters.shift();e?"microtask"===this._yieldMode?queueMicrotask(e):setTimeout(e,0):this._available++}async run(e,t){await this.acquire(t);try{return await e()}finally{this.release()}}available(){return this._available}pending(){return this.waiters.length}slots(){return this._slots}},p=class{mutex;_done=!1;_lastValue=null;_lastError=void 0;_hasRun=!1;constructor(e){this.mutex=new c({capacity:e?.capacity??1e3,yieldMode:e?.yieldMode??"macrotask"})}async do(e,t){if(this._done)return{value:null,error:new n};try{await this.mutex.lock(t)}catch(e){return{value:null,error:e}}let i,s=null;try{if(this._done)throw new n;s=await e(),this._lastValue=s,this._lastError=void 0,this._hasRun=!0}catch(e){i=e,this._lastError=e,this._hasRun=!0}finally{this.mutex.unlock()}return{value:s,error:i}}peek(){return{value:this._lastValue,error:this._lastError}}hasRun(){return this._hasRun}close(){this._done=!0}pending(){return this.mutex.pending()}running(){return this.mutex.locked()}},_=class{constructor(e,t,i={}){this.factory=e,this.onCleanup=t,this.options=i}_count=0;init=new u({retry:!1,throws:!1});pendingMicrotask=!1;cleanupTimer;get subscribers(){return this._count}async acquire(){this.cancelPendingCleanup(),this._count++;const e=await this.init.do(this.factory);if(e.error)throw e.error;return e.value}release(){this._count<=0?console.warn("SharedResource.release() called, but count is already 0."):(this._count--,0===this._count&&this.scheduleCleanup())}peek(){const e=this.init.peek();return e.error?null:e.value}forceCleanup(){this.cancelPendingCleanup(),this._count=0,this.executeCleanup()}cancelPendingCleanup(){this.pendingMicrotask=!1,void 0!==this.cleanupTimer&&(clearTimeout(this.cleanupTimer),this.cleanupTimer=void 0)}scheduleCleanup(){const e=this.options.gracePeriod??"microtask";if("sync"!==e)return"microtask"===e?(this.pendingMicrotask=!0,void queueMicrotask((()=>{!this.pendingMicrotask||this._count>0||(this.pendingMicrotask=!1,this.executeCleanup())}))):void(this.cleanupTimer=setTimeout((()=>{this.cleanupTimer=void 0,this._count>0||this.executeCleanup()}),e));this.executeCleanup()}executeCleanup(){const e=this.init.peek();try{this.onCleanup(e.value)}catch(e){console.error("[SharedResource] Error during cleanup callback:",e)}this.init.running()||this.init.reset()}};export{e as Debouncer,h as Latch,c as Mutex,u as Once,a as OnceExecutionConflict,l as RWMutex,d as Semaphore,p as Serializer,n as SerializerExecutionDone,_ as SharedResource,r as SyncError,o as TimeoutError};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asaidimu/utils-sync",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
4
4
  "description": "A collection of sync utilities.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",