@asaidimu/utils-persistence 6.1.12 → 6.1.14
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.js +1 -1
- package/index.mjs +1 -1
- package/package.json +7 -5
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports);let t=require("@asaidimu/utils-events"),n=require("@asaidimu/utils-database"),r=require("@asaidimu/utils-error");var i=class{eventBus;storage;config;initialized=!1;initializationCallbacks=[];constructor(e){this.config=e,this.storage=e.session?sessionStorage:localStorage,this.eventBus=this.initializeEventBus(),this.initialize(),e.session||this.setupStorageEventListener()}async initialize(){try{let e=this.storage.getItem(this.getStoreName());if(e){let t=JSON.parse(e);if(t.version!==this.config.version&&this.config.onUpgrade){let{state:e}=await this.config.onUpgrade({data:t.state,version:t.version,app:t.app});this.set(`migration`,e)}}this.initialized=!0,this.initializationCallbacks.forEach(e=>e()),this.initializationCallbacks=[]}catch(e){console.error(`Failed to initialize WebStoragePersistence for ${this.config.storageKey}:`,e)}}_onInitialized(e){this.initialized?e():this.initializationCallbacks.push(e)}initializeEventBus(){let e={batch:{size:0,delay:0},errorHandler:e=>console.error(`Event bus error for ${this.config.storageKey}:`,e)};return(typeof process>`u`||process.env.NODE_ENV!==`test`)&&(e.broadcast={channel:`storage_${this.getStoreName()}`}),(0,t.createEventBus)(e)}getStoreName(){return`_${this.config.app}_${this.config.storageKey}_`}setupStorageEventListener(){window.addEventListener(`storage`,e=>{if(!(e.key!==this.getStoreName()||!e.newValue))try{let t=JSON.parse(e.newValue);t&&this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:`external`,state:t.state,version:t.version,app:t.app}})}catch(e){console.error(`Failed to parse storage event data:`,e)}})}set(e,t){try{let n={state:structuredClone(t),version:this.config.version,app:this.config.app},r=JSON.stringify(n);return this.storage.setItem(this.getStoreName(),r),this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:e,state:t,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to persist state to web storage for ${this.config.storageKey}:`,e),!1}}_get(){try{let e=this.storage.getItem(this.getStoreName());return e?JSON.parse(e).state:null}catch(e){return console.error(`Failed to retrieve state from web storage for ${this.config.storageKey}:`,e),null}}async get(){return this.initialized||await new Promise(e=>{this._onInitialized(e)}),this._get()}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.config.storageKey&&r!==e&&t(i)})}clear(){try{return this.storage.removeItem(this.getStoreName()),this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:`clear-initiator`,state:null,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to clear persisted state for ${this.config.storageKey}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}},a=e(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},c=e=>s(n({},`__esModule`,{value:!0}),e),l={};o(l,{QueryBuilder:()=>u,createJoiner:()=>j,createMatcher:()=>v,createPaginator:()=>z,createProjector:()=>L,createSorter:()=>I}),t.exports=c(l);var u=class{query;constructor(){this.query={}}where(e){return this.query.filters=e,this}orderBy(e,t){return this.query.sort||(this.query.sort=[]),this.query.sort.push({field:e,direction:t}),this}offset(e,t){return this.query.pagination={type:`offset`,offset:e,limit:t},this}cursor(e,t,n){return this.query.pagination={type:`cursor`,cursor:e,limit:t,direction:n},this}include(e){return this.query.projection||(this.query.projection={}),this.query.projection.include=e,this}exclude(e){return this.query.projection||(this.query.projection={}),this.query.projection.exclude=e,this}computed(e,t){return this.query.projection||(this.query.projection={}),this.query.projection.computed||(this.query.projection.computed=[]),this.query.projection.computed.push({type:`computed`,expression:e,alias:t}),this}case(e,t,n){return this.query.projection||(this.query.projection={}),this.query.projection.computed||(this.query.projection.computed=[]),this.query.projection.computed.push({type:`case`,conditions:e,else:t,alias:n}),this}join(e,t,n){return this.query.joins||(this.query.joins=[]),this.query.joins.push({relation:e,alias:n,query:t}),this}aggregate(e,t){return this.query.aggregations={groupBy:e,metrics:t},this}window(e){return this.query.window||(this.query.window=[]),this.query.window.push(e),this}hint(e){return this.query.hints||(this.query.hints=[]),this.query.hints.push(e),this}build(){return this.query}};function d(e){function t(e,t){return f(t)?e[t.field]:p(t)?t.value:g(t)?i(t,e):m(t)?i(t.expression,e):h(t)?a(e,t):t}let n=new Map([[`and`,(e,t)=>t.every(t=>o(e,t))],[`or`,(e,t)=>t.some(t=>o(e,t))],[`not`,(e,t)=>!t.every(t=>o(e,t))],[`nor`,(e,t)=>!t.some(t=>o(e,t))],[`xor`,(e,t)=>t.filter(t=>o(e,t)).length===1]]);function r(e,t){let{operator:r,conditions:i}=t,a=n.get(r);if(a)return a(e,i);throw Error(`Unsupported logical operator: ${r}`)}function i(n,r){let i=n.arguments.map(e=>t(r,e));if(e[n.function])return e[n.function](...i);throw Error(`Function ${n.function} not found!`)}function a(e,t){for(let n of t.conditions)if(o(e,n.when))return n.then;return t.else}function o(n,i){if(_(i))return r(n,i);if(!i||!i.field)return!1;let{field:a,operator:o,value:s}=i,c=n[a],l=t(n,s),u=new Map([[`eq`,(e,t)=>e===t],[`neq`,(e,t)=>e!==t],[`lt`,(e,t)=>e<t],[`lte`,(e,t)=>e<=t],[`gt`,(e,t)=>e>t],[`gte`,(e,t)=>e>=t],[`in`,(e,t)=>Array.isArray(t)&&t.includes(e)],[`nin`,(e,t)=>Array.isArray(t)&&!t.includes(e)],[`contains`,(e,t)=>typeof e==`string`?e.includes(t):Array.isArray(e)?e.includes(s):!1],[`ncontains`,(e,t)=>typeof e==`string`&&!e.includes(t)],[`startswith`,(e,t)=>typeof e==`string`&&e.startsWith(t)],[`endswith`,(e,t)=>typeof e==`string`&&e.endsWith(t)],[`exists`,e=>e!=null],[`nexists`,e=>e==null]]),d=e[o]||u.get(o);if(d)return d(c,l);throw Error(`Unsupported comparison operator: ${o}`)}return{resolve:t,evaluate:o}}function f(e){return e?.type===`field`}function p(e){return e?.type===`value`}function m(e){return e?.type===`computed`}function h(e){return e?.type===`case`}function g(e){return e?.type===`function`}function _(e){return e?e.conditions!==void 0:!1}function v(e){let{evaluate:t}=d(e),n=new WeakMap;function r(e,r){let i=n.get(e);i||(i=new Map,n.set(e,i));let a=JSON.stringify(r);if(i.has(a))return i.get(a);let o=t(e,r);return i.set(a,o),o}return{matcher:r,match:r}}var y=class extends Error{constructor(e,t){super(e),this.code=t,this.name=`JoinError`}},b=e=>e&&`field`in e&&`operator`in e&&`value`in e,x=e=>`operator`in e&&`conditions`in e,S=e=>typeof e==`object`&&!!e&&`type`in e&&e.type===`field`,C=(e,t)=>{if(!e.relation)throw new y(`Join configuration must specify a relation`,`INVALID_CONFIG`);if(!t[e.relation])throw new y(`Collection "${e.relation}" not found in database`,`COLLECTION_NOT_FOUND`);if(e.alias&&typeof e.alias!=`string`)throw new y(`Join alias must be a string`,`INVALID_ALIAS`)},w=(e,t)=>t.split(`.`).reduce((e,t)=>e?.[t],e),T=(e,t)=>{if(e){if(b(e)&&e){let n=S(e.value)?w(t,e.value.field):e.value;return{...e,value:n}}if(x(e)){let n={...e};return e.conditions&&(n.conditions=e.conditions.map(e=>T(e,t))),n}return e}},E=(e,t)=>{if(!e)return{};let n=T(e.filters,t);return{...e,filters:n}},D=(e,t,n)=>{let r=n.alias||n.relation;return[{...e,[r]:t}]},O=(e,t)=>{let n=new Map;return e.forEach(e=>{let r=e[t];n.has(r)||n.set(r,[]),n.get(r).push(e)}),n},k=async(e,t,n,r)=>{try{if(C(n,e),!Array.isArray(t))throw new y(`Source data must be an array`,`INVALID_SOURCE_DATA`);let i=e[n.relation],a;return n.query?.filters&&b(n.query.filters)&&(a=O(i,n.query.filters.field)),(await Promise.all(t.map(async e=>{let t=E(n.query,e),o;return o=t.filters&&b(t.filters)&&a?.has(t.filters.value)?a.get(t.filters.value)||[]:i.filter(e=>r.matcher(e,t.filters)),D(e,await[e=>t.sort?r.sorter(e,t.sort):e,e=>t.projection?r.projector(e,t.projection):e,async e=>t.pagination?await r.paginator(e,t.pagination):e].reduce(async(e,t)=>t(await e),Promise.resolve(o)),n)}))).flat()}catch(e){throw e instanceof y?e:new y(`Join operation failed: ${e.message}`,`JOIN_EXECUTION_ERROR`)}},A=async(e,t,n,r)=>n.reduce(async(t,n)=>k(e,await t,n,r),Promise.resolve(t));function j(){return{join:A}}var M=class extends Error{constructor(e,t){super(`Unsupported comparison between ${typeof e} and ${typeof t}`),this.name=`UnsupportedComparisonError`}},N=class extends Error{constructor(e){super(`Unsupported sort direction: ${e}`),this.name=`InvalidSortDirectionError`}};function P(e,t,n){if(e===t)return 0;if(e==null||t==null)return e==null?n===`asc`?-1:1:n===`asc`?1:-1;if(typeof e==`string`&&typeof t==`string`)return n===`asc`?e.localeCompare(t):t.localeCompare(e);if(typeof e==`number`&&typeof t==`number`)return n===`asc`?e-t:t-e;throw new M(e,t)}function F(e,t){let n=Array.from(e);return t.length===0?n:n.sort((e,n)=>{for(let r of t){let{field:t,direction:i}=r,a=e[t],o=n[t];try{if(i!==`asc`&&i!==`desc`)throw new N(i);let e=P(a,o,i);if(e!==0)return e}catch(e){throw e instanceof M||e instanceof N?e:Error(`Error comparing field '${t}': ${e.message}`)}}return 0})}function I(){return{sort:F}}function L(e){let{resolve:t}=d(e);function n(e,t,n){for(let r of t){if(typeof r==`string`){let t=r;n[t]=e[t];continue}for(let[t,i]of Object.entries(r))Object.prototype.hasOwnProperty.call(e,t)&&(n[t]=a(e[t],i))}}function r(e,t,n){Object.keys(n).length===0&&Object.assign(n,e);for(let e of t){if(typeof e==`string`){let t=e;delete n[t];continue}for(let[t,r]of Object.entries(e)){if(!Object.prototype.hasOwnProperty.call(n,t))continue;let e=n[t];e&&typeof e==`object`?n[t]=a(e,r):delete n[t]}}}function i(e,n,r){for(let i of n)r[i.alias]=t(e,i)}function a(e,t){let a={};return t.include?.length&&n(e,t.include,a),t.exclude?.length&&r(e,t.exclude,a),t.computed?.length&&i(e,t.computed,a),a}return{project:a}}async function*R(e,t,n=e=>String(e)){t.type===`offset`?yield*B(e,t):yield*V(e,t,n)}function z(){return{paginate:R}}async function*B(e,t){let{offset:n,limit:r}=t,i=0,a=r,o=[];for await(let t of e){if(a<=0&&(yield o,o=[],a=r),i<n){i++;continue}o.push(t),a--}o.length>0&&(yield o)}async function*V(e,t,n){let{cursor:r,limit:i,direction:a}=t,o=i,s=r===void 0,c=[];if(a===`forward`)for await(let t of e){if(o<=0&&(yield c,c=[],o=i),!s){s=n(t)===r;continue}c.push(t),o--}else{let t=[];for await(let n of e)t.push(n);for(let e=t.length-1;e>=0;e--){let a=t[e];if(!s){s=n(a)===r;continue}c.push(a),o--,o<=0&&(yield c,c=[],o=i)}}c.length>0&&(yield c)}0&&(t.exports={QueryBuilder,createJoiner,createMatcher,createPaginator,createProjector,createSorter})}))(),o=class e extends r.SystemError{constructor(t,n){super({code:`SYNC_ERROR`,message:t,cause:n}),this.name=`SyncError`,Object.setPrototypeOf(this,e.prototype)}},s=class e extends o{constructor(t){super(`[ArtifactContainer] Operation timed out: ${t}`),this.name=`TimeoutError`,Object.setPrototypeOf(this,e.prototype)}},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){this._locked=!0;return}if(this.waiters.length>=this._capacity)throw Error(`Mutex queue is full (capacity: ${this._capacity})`);let t,n=new Promise(e=>t=e);if(this.waiters.push(t),e==null){await n;return}let r;await Promise.race([n.then(()=>clearTimeout(r)),new Promise((n,i)=>{r=setTimeout(()=>{let e=this.waiters.indexOf(t);e!==-1&&this.waiters.splice(e,1),i(new s(`Mutex lock timed out`))},e)})])}tryLock(){return this._locked?!1:(this._locked=!0,!0)}unlock(){if(!this._locked)throw Error(`Mutex is not locked`);let e=this.waiters.shift();e?this._yieldMode===`microtask`?queueMicrotask(e):setTimeout(e,0):this._locked=!1}locked(){return this._locked}pending(){return this.waiters.length}},l=class{mutex=new c({yieldMode:`microtask`});promise=null;_value=null;_error;_done=!1;retry;throws;constructor({retry:e,throws:t}={}){this.retry=!!e,this.throws=!!t}resolve(e){if(this._done)throw Error(`Cannot resolve: operation is already completed.`);if(this.running())throw 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){let e=Error(`Cannot execute doSync while an async operation is pending.`);if(this.throws)throw e;return{value:null,error:e}}if(!this.mutex.tryLock()){let e=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()}let e=Error(`Cannot execute doSync while an async operation is pending.`);if(this.throws)throw e;return{value:null,error:e}}try{let 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 this.promise!==null&&!this.done()}peek(){return{value:this._value,error:this._error}}get(){if(!this._done)throw Error(`Once operation is not yet complete`);if(this._error)throw this._error;return this._value}reset(){if(this.running())throw 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,n=`Operation timed out`){if(t==null)return e;let r;return Promise.race([e.then(e=>(clearTimeout(r),e)),new Promise((e,i)=>{r=setTimeout(()=>i(new s(n)),t)})])}},u=class{factory;onCleanup;options;_count=0;init=new l({retry:!1,throws:!1});pendingMicrotask=!1;cleanupTimer;constructor(e,t,n={}){this.factory=e,this.onCleanup=t,this.options=n}get subscribers(){return this._count}async acquire(){this.cancelPendingCleanup(),this._count++;let e=await this.init.do(this.factory);if(e.error)throw e.error;return e.value}release(){if(this._count<=0){console.warn(`SharedResource.release() called, but count is already 0.`);return}this._count--,this._count===0&&this.scheduleCleanup()}peek(){let e=this.init.peek();return e.error?null:e.value}forceCleanup(){this.cancelPendingCleanup(),this._count=0,this.executeCleanup()}cancelPendingCleanup(){this.pendingMicrotask=!1,this.cleanupTimer!==void 0&&(clearTimeout(this.cleanupTimer),this.cleanupTimer=void 0)}scheduleCleanup(){let e=this.options.gracePeriod??`microtask`;if(e===`sync`){this.executeCleanup();return}if(e===`microtask`){this.pendingMicrotask=!0,queueMicrotask(()=>{!this.pendingMicrotask||this._count>0||(this.pendingMicrotask=!1,this.executeCleanup())});return}this.cleanupTimer=setTimeout(()=>{this.cleanupTimer=void 0,!(this._count>0)&&this.executeCleanup()},e)}executeCleanup(){let 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()}},d=class e{static dbResources=new Map;static eventBusMap=new Map;static createDatabaseFactory(e){return async()=>{let{database:t,enableTelemetry:r=!1,collection:i=`stores`}=e,a=await(0,n.DatabaseConnection)({database:t,enableTelemetry:r},n.createIndexedDbStore);try{await a.createCollection({name:i,version:`1.0.0`,nestedSchemas:{},fields:{store:{name:`store`,type:`string`,required:!0},data:{name:`data`,type:`object`,required:!0},version:{name:`version`,type:`string`,required:!0},app:{name:`app`,type:`string`,required:!0}}})}catch(e){if(e instanceof n.DatabaseError&&e.type!==`SCHEMA_ALREADY_EXISTS`)throw e}return a}}static createDatabaseCleanup(t){return n=>{n?.close();let r=[];e.eventBusMap.forEach((e,n)=>{n.startsWith(`${t}_store_`)&&(e.clear(),r.push(n))}),r.forEach(t=>e.eventBusMap.delete(t))}}static async acquireDatabase(t){let n=t.database;if(!e.dbResources.has(n)){let r=new u(e.createDatabaseFactory(t),e.createDatabaseCleanup(n),{gracePeriod:`microtask`});e.dbResources.set(n,r)}return e.dbResources.get(n).acquire()}static releaseDatabase(t){let n=e.dbResources.get(t);n&&n.release()}static async getCollection(t){let n=await e.acquireDatabase(t),r=t.collection??`stores`;return n.collection(r)}static getEventBus(n,r){let i=`${n}_store_${r}`;return e.eventBusMap.has(i)||e.eventBusMap.set(i,(0,t.createEventBus)({errorHandler:e=>console.error(`Event bus error for ${n}:${r}:`,e),broadcast:{channel:i}})),e.eventBusMap.get(i)}},f=class{collection=null;collectionPromise;config;eventBus;initialized=!1;_initializing=!1;initializationCallbacks=[];doc=null;getStoreName(){return`_${this.config.app}_${this.config.store}_`}constructor(e){this.config=e,this.collectionPromise=d.getCollection(this.config),this.collectionPromise.then(e=>{this.collection=e,this.initialize()}).catch(e=>{console.error(`Failed to initialize collection for store ${this.getStoreName()}:`,e)}),this.eventBus=d.getEventBus(this.config.database,this.getStoreName()),this.eventBus.subscribe(`store:updated`,({storageKey:e})=>{e===this.getStoreName()&&(this.doc=null)})}async initialize(){this._initializing=!0;try{let e=await this._get();if(e&&e.version!==this.config.version&&this.config.onUpgrade){let{state:t}=await this.config.onUpgrade({data:e.data,version:e.version,app:e.app});await this.set(`migration`,t)}this.initialized=!0,this.initializationCallbacks.forEach(e=>e()),this.initializationCallbacks=[]}catch(e){console.error(`Failed to initialize and upgrade store ${this.getStoreName()}:`,e)}finally{this._initializing=!1}}_onInitialized(e){this.initialized?e():this.initializationCallbacks.push(e)}async getCollection(){return this.collection??this.collectionPromise}async set(e,t){!this.initialized&&!this._initializing&&await new Promise(e=>this._onInitialized(e));try{let n=await this.getCollection(),r=await this._read(),i={store:this.getStoreName(),data:t,version:this.config.version,app:this.config.app},a;return r?a=await r.update(i):(this.doc=await n.create(i),a=!0),a&&this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.getStoreName(),instanceId:e,state:t,version:this.config.version,app:this.config.app}}),a}catch(t){return console.error(`Failed to set state for store ${this.getStoreName()} in database ${this.config.database} by instance ${e}:`,t),!1}}async _read(){if(this.doc)return this.doc;let e=await this.getCollection(),t=new a.QueryBuilder().where({field:`store`,operator:`eq`,value:this.getStoreName()}).build();return this.doc=await e.find(t.filters),this.doc}async _get(){try{let e=await this._read();return e?e.read().then(()=>e):null}catch(e){return console.error(`Failed to get state for store ${this.getStoreName()} in database ${this.config.database}:`,e),null}}async get(){this.initialized||await new Promise(e=>this._onInitialized(e));let e=await this._get();return e?e.data:null}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.getStoreName()&&r!==e&&t(i)})}async clear(){this.initialized||await new Promise(e=>this._onInitialized(e));try{let e=await this._read();return e&&await e.delete()?(this.doc=null,this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.getStoreName(),instanceId:``,state:null,version:this.config.version,app:this.config.app}}),!0):!0}catch(e){return console.error(`Failed to clear state for store ${this.getStoreName()} in database ${this.config.database}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}async close(){d.releaseDatabase(this.config.database)}},p=class{eventBus;inMemoryState=null;config;constructor(e){this.config=e,this.eventBus=this.initializeEventBus(),this.setupLwwSynchronizationListener()}initializeEventBus(){let e={errorHandler:e=>console.error(`Event bus error for ${this.config.storageKey} (Ephemeral LWW):`,e)};return(typeof process>`u`||process.env.NODE_ENV!==`test`)&&(e.broadcast={channel:`ephemeral_lww_${this.config.storageKey}`}),(0,t.createEventBus)(e)}setupLwwSynchronizationListener(){this.eventBus.subscribe(`store:updated`,({storageKey:e,instanceId:t,state:n,timestamp:r,version:i})=>{e===this.config.storageKey&&(!this.inMemoryState||r&&r>this.inMemoryState.timestamp)&&(this.inMemoryState={data:n,timestamp:r,instanceId:t,version:i})})}set(e,t){try{let n=Date.now();return this.inMemoryState={data:structuredClone(t),timestamp:n,instanceId:e,version:this.config.version},this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:e,state:t,timestamp:n,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to set state in EphemeralPersistence (LWW) for ${this.config.storageKey}:`,e),!1}}get(){return this.inMemoryState?.data??null}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.config.storageKey&&r!==e&&t(i)})}clear(){try{let e=Date.now(),t=`clear-initiator`;return this.inMemoryState={data:null,timestamp:e,instanceId:t,version:this.config.version},this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:t,state:null,timestamp:e,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to clear state in EphemeralPersistence (LWW) for ${this.config.storageKey}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}};exports.EphemeralPersistence=p,exports.IndexedDBPersistence=f,exports.WebStoragePersistence=i;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports);let t=require("@asaidimu/utils-events"),n=require("@asaidimu/utils-database"),r=require("@asaidimu/utils-sync");var i=class{eventBus;storage;config;initialized=!1;initializationCallbacks=[];constructor(e){this.config=e,this.storage=e.session?sessionStorage:localStorage,this.eventBus=this.initializeEventBus(),this.initialize(),e.session||this.setupStorageEventListener()}async initialize(){try{let e=this.storage.getItem(this.getStoreName());if(e){let t=JSON.parse(e);if(t.version!==this.config.version&&this.config.onUpgrade){let{state:e}=await this.config.onUpgrade({data:t.state,version:t.version,app:t.app});this.set(`migration`,e)}}this.initialized=!0,this.initializationCallbacks.forEach(e=>e()),this.initializationCallbacks=[]}catch(e){console.error(`Failed to initialize WebStoragePersistence for ${this.config.storageKey}:`,e)}}_onInitialized(e){this.initialized?e():this.initializationCallbacks.push(e)}initializeEventBus(){let e={batch:{size:0,delay:0},errorHandler:e=>console.error(`Event bus error for ${this.config.storageKey}:`,e)};return(typeof process>`u`||process.env.NODE_ENV!==`test`)&&(e.broadcast={channel:`storage_${this.getStoreName()}`}),(0,t.createEventBus)(e)}getStoreName(){return`_${this.config.app}_${this.config.storageKey}_`}setupStorageEventListener(){window.addEventListener(`storage`,e=>{if(!(e.key!==this.getStoreName()||!e.newValue))try{let t=JSON.parse(e.newValue);t&&this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:`external`,state:t.state,version:t.version,app:t.app}})}catch(e){console.error(`Failed to parse storage event data:`,e)}})}set(e,t){try{let n={state:structuredClone(t),version:this.config.version,app:this.config.app},r=JSON.stringify(n);return this.storage.setItem(this.getStoreName(),r),this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:e,state:t,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to persist state to web storage for ${this.config.storageKey}:`,e),!1}}_get(){try{let e=this.storage.getItem(this.getStoreName());return e?JSON.parse(e).state:null}catch(e){return console.error(`Failed to retrieve state from web storage for ${this.config.storageKey}:`,e),null}}async get(){return this.initialized||await new Promise(e=>{this._onInitialized(e)}),this._get()}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.config.storageKey&&r!==e&&t(i)})}clear(){try{return this.storage.removeItem(this.getStoreName()),this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:`clear-initiator`,state:null,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to clear persisted state for ${this.config.storageKey}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}},a=e(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},c=e=>s(n({},`__esModule`,{value:!0}),e),l={};o(l,{QueryBuilder:()=>u,createJoiner:()=>j,createMatcher:()=>v,createPaginator:()=>z,createProjector:()=>L,createSorter:()=>I}),t.exports=c(l);var u=class{query;constructor(){this.query={}}where(e){return this.query.filters=e,this}orderBy(e,t){return this.query.sort||(this.query.sort=[]),this.query.sort.push({field:e,direction:t}),this}offset(e,t){return this.query.pagination={type:`offset`,offset:e,limit:t},this}cursor(e,t,n){return this.query.pagination={type:`cursor`,cursor:e,limit:t,direction:n},this}include(e){return this.query.projection||(this.query.projection={}),this.query.projection.include=e,this}exclude(e){return this.query.projection||(this.query.projection={}),this.query.projection.exclude=e,this}computed(e,t){return this.query.projection||(this.query.projection={}),this.query.projection.computed||(this.query.projection.computed=[]),this.query.projection.computed.push({type:`computed`,expression:e,alias:t}),this}case(e,t,n){return this.query.projection||(this.query.projection={}),this.query.projection.computed||(this.query.projection.computed=[]),this.query.projection.computed.push({type:`case`,conditions:e,else:t,alias:n}),this}join(e,t,n){return this.query.joins||(this.query.joins=[]),this.query.joins.push({relation:e,alias:n,query:t}),this}aggregate(e,t){return this.query.aggregations={groupBy:e,metrics:t},this}window(e){return this.query.window||(this.query.window=[]),this.query.window.push(e),this}hint(e){return this.query.hints||(this.query.hints=[]),this.query.hints.push(e),this}build(){return this.query}};function d(e){function t(e,t){return f(t)?e[t.field]:p(t)?t.value:g(t)?i(t,e):m(t)?i(t.expression,e):h(t)?a(e,t):t}let n=new Map([[`and`,(e,t)=>t.every(t=>o(e,t))],[`or`,(e,t)=>t.some(t=>o(e,t))],[`not`,(e,t)=>!t.every(t=>o(e,t))],[`nor`,(e,t)=>!t.some(t=>o(e,t))],[`xor`,(e,t)=>t.filter(t=>o(e,t)).length===1]]);function r(e,t){let{operator:r,conditions:i}=t,a=n.get(r);if(a)return a(e,i);throw Error(`Unsupported logical operator: ${r}`)}function i(n,r){let i=n.arguments.map(e=>t(r,e));if(e[n.function])return e[n.function](...i);throw Error(`Function ${n.function} not found!`)}function a(e,t){for(let n of t.conditions)if(o(e,n.when))return n.then;return t.else}function o(n,i){if(_(i))return r(n,i);if(!i||!i.field)return!1;let{field:a,operator:o,value:s}=i,c=n[a],l=t(n,s),u=new Map([[`eq`,(e,t)=>e===t],[`neq`,(e,t)=>e!==t],[`lt`,(e,t)=>e<t],[`lte`,(e,t)=>e<=t],[`gt`,(e,t)=>e>t],[`gte`,(e,t)=>e>=t],[`in`,(e,t)=>Array.isArray(t)&&t.includes(e)],[`nin`,(e,t)=>Array.isArray(t)&&!t.includes(e)],[`contains`,(e,t)=>typeof e==`string`?e.includes(t):Array.isArray(e)?e.includes(s):!1],[`ncontains`,(e,t)=>typeof e==`string`&&!e.includes(t)],[`startswith`,(e,t)=>typeof e==`string`&&e.startsWith(t)],[`endswith`,(e,t)=>typeof e==`string`&&e.endsWith(t)],[`exists`,e=>e!=null],[`nexists`,e=>e==null]]),d=e[o]||u.get(o);if(d)return d(c,l);throw Error(`Unsupported comparison operator: ${o}`)}return{resolve:t,evaluate:o}}function f(e){return e?.type===`field`}function p(e){return e?.type===`value`}function m(e){return e?.type===`computed`}function h(e){return e?.type===`case`}function g(e){return e?.type===`function`}function _(e){return e?e.conditions!==void 0:!1}function v(e){let{evaluate:t}=d(e),n=new WeakMap;function r(e,r){let i=n.get(e);i||(i=new Map,n.set(e,i));let a=JSON.stringify(r);if(i.has(a))return i.get(a);let o=t(e,r);return i.set(a,o),o}return{matcher:r,match:r}}var y=class extends Error{constructor(e,t){super(e),this.code=t,this.name=`JoinError`}},b=e=>e&&`field`in e&&`operator`in e&&`value`in e,x=e=>`operator`in e&&`conditions`in e,S=e=>typeof e==`object`&&!!e&&`type`in e&&e.type===`field`,C=(e,t)=>{if(!e.relation)throw new y(`Join configuration must specify a relation`,`INVALID_CONFIG`);if(!t[e.relation])throw new y(`Collection "${e.relation}" not found in database`,`COLLECTION_NOT_FOUND`);if(e.alias&&typeof e.alias!=`string`)throw new y(`Join alias must be a string`,`INVALID_ALIAS`)},w=(e,t)=>t.split(`.`).reduce((e,t)=>e?.[t],e),T=(e,t)=>{if(e){if(b(e)&&e){let n=S(e.value)?w(t,e.value.field):e.value;return{...e,value:n}}if(x(e)){let n={...e};return e.conditions&&(n.conditions=e.conditions.map(e=>T(e,t))),n}return e}},E=(e,t)=>{if(!e)return{};let n=T(e.filters,t);return{...e,filters:n}},D=(e,t,n)=>{let r=n.alias||n.relation;return[{...e,[r]:t}]},O=(e,t)=>{let n=new Map;return e.forEach(e=>{let r=e[t];n.has(r)||n.set(r,[]),n.get(r).push(e)}),n},k=async(e,t,n,r)=>{try{if(C(n,e),!Array.isArray(t))throw new y(`Source data must be an array`,`INVALID_SOURCE_DATA`);let i=e[n.relation],a;return n.query?.filters&&b(n.query.filters)&&(a=O(i,n.query.filters.field)),(await Promise.all(t.map(async e=>{let t=E(n.query,e),o;return o=t.filters&&b(t.filters)&&a?.has(t.filters.value)?a.get(t.filters.value)||[]:i.filter(e=>r.matcher(e,t.filters)),D(e,await[e=>t.sort?r.sorter(e,t.sort):e,e=>t.projection?r.projector(e,t.projection):e,async e=>t.pagination?await r.paginator(e,t.pagination):e].reduce(async(e,t)=>t(await e),Promise.resolve(o)),n)}))).flat()}catch(e){throw e instanceof y?e:new y(`Join operation failed: ${e.message}`,`JOIN_EXECUTION_ERROR`)}},A=async(e,t,n,r)=>n.reduce(async(t,n)=>k(e,await t,n,r),Promise.resolve(t));function j(){return{join:A}}var M=class extends Error{constructor(e,t){super(`Unsupported comparison between ${typeof e} and ${typeof t}`),this.name=`UnsupportedComparisonError`}},N=class extends Error{constructor(e){super(`Unsupported sort direction: ${e}`),this.name=`InvalidSortDirectionError`}};function P(e,t,n){if(e===t)return 0;if(e==null||t==null)return e==null?n===`asc`?-1:1:n===`asc`?1:-1;if(typeof e==`string`&&typeof t==`string`)return n===`asc`?e.localeCompare(t):t.localeCompare(e);if(typeof e==`number`&&typeof t==`number`)return n===`asc`?e-t:t-e;throw new M(e,t)}function F(e,t){let n=Array.from(e);return t.length===0?n:n.sort((e,n)=>{for(let r of t){let{field:t,direction:i}=r,a=e[t],o=n[t];try{if(i!==`asc`&&i!==`desc`)throw new N(i);let e=P(a,o,i);if(e!==0)return e}catch(e){throw e instanceof M||e instanceof N?e:Error(`Error comparing field '${t}': ${e.message}`)}}return 0})}function I(){return{sort:F}}function L(e){let{resolve:t}=d(e);function n(e,t,n){for(let r of t){if(typeof r==`string`){let t=r;n[t]=e[t];continue}for(let[t,i]of Object.entries(r))Object.prototype.hasOwnProperty.call(e,t)&&(n[t]=a(e[t],i))}}function r(e,t,n){Object.keys(n).length===0&&Object.assign(n,e);for(let e of t){if(typeof e==`string`){let t=e;delete n[t];continue}for(let[t,r]of Object.entries(e)){if(!Object.prototype.hasOwnProperty.call(n,t))continue;let e=n[t];e&&typeof e==`object`?n[t]=a(e,r):delete n[t]}}}function i(e,n,r){for(let i of n)r[i.alias]=t(e,i)}function a(e,t){let a={};return t.include?.length&&n(e,t.include,a),t.exclude?.length&&r(e,t.exclude,a),t.computed?.length&&i(e,t.computed,a),a}return{project:a}}async function*R(e,t,n=e=>String(e)){t.type===`offset`?yield*B(e,t):yield*V(e,t,n)}function z(){return{paginate:R}}async function*B(e,t){let{offset:n,limit:r}=t,i=0,a=r,o=[];for await(let t of e){if(a<=0&&(yield o,o=[],a=r),i<n){i++;continue}o.push(t),a--}o.length>0&&(yield o)}async function*V(e,t,n){let{cursor:r,limit:i,direction:a}=t,o=i,s=r===void 0,c=[];if(a===`forward`)for await(let t of e){if(o<=0&&(yield c,c=[],o=i),!s){s=n(t)===r;continue}c.push(t),o--}else{let t=[];for await(let n of e)t.push(n);for(let e=t.length-1;e>=0;e--){let a=t[e];if(!s){s=n(a)===r;continue}c.push(a),o--,o<=0&&(yield c,c=[],o=i)}}c.length>0&&(yield c)}0&&(t.exports={QueryBuilder,createJoiner,createMatcher,createPaginator,createProjector,createSorter})}))(),o=class e{static dbResources=new Map;static eventBusMap=new Map;static createDatabaseFactory(e){return async()=>{let{database:t,enableTelemetry:r=!1,collection:i=`stores`}=e,a=await(0,n.DatabaseConnection)({database:t,enableTelemetry:r},n.createIndexedDbStore);try{await a.createCollection({name:i,version:`1.0.0`,nestedSchemas:{},fields:{store:{name:`store`,type:`string`,required:!0},data:{name:`data`,type:`object`,required:!0},version:{name:`version`,type:`string`,required:!0},app:{name:`app`,type:`string`,required:!0}}})}catch(e){if(e instanceof n.DatabaseError&&e.type!==`SCHEMA_ALREADY_EXISTS`)throw e}return a}}static createDatabaseCleanup(t){return n=>{n?.close();let r=[];e.eventBusMap.forEach((e,n)=>{n.startsWith(`${t}_store_`)&&(e.clear(),r.push(n))}),r.forEach(t=>e.eventBusMap.delete(t))}}static async acquireDatabase(t){let n=t.database;if(!e.dbResources.has(n)){let i=new r.SharedResource(e.createDatabaseFactory(t),e.createDatabaseCleanup(n),{gracePeriod:`microtask`});e.dbResources.set(n,i)}return e.dbResources.get(n).acquire()}static releaseDatabase(t){let n=e.dbResources.get(t);n&&n.release()}static async getCollection(t){let n=await e.acquireDatabase(t),r=t.collection??`stores`;return n.collection(r)}static getEventBus(n,r){let i=`${n}_store_${r}`;return e.eventBusMap.has(i)||e.eventBusMap.set(i,(0,t.createEventBus)({errorHandler:e=>console.error(`Event bus error for ${n}:${r}:`,e),broadcast:{channel:i}})),e.eventBusMap.get(i)}},s=class{collection=null;collectionPromise;config;eventBus;initialized=!1;_initializing=!1;initializationCallbacks=[];doc=null;getStoreName(){return`_${this.config.app}_${this.config.store}_`}constructor(e){this.config=e,this.collectionPromise=o.getCollection(this.config),this.collectionPromise.then(e=>{this.collection=e,this.initialize()}).catch(e=>{console.error(`Failed to initialize collection for store ${this.getStoreName()}:`,e)}),this.eventBus=o.getEventBus(this.config.database,this.getStoreName()),this.eventBus.subscribe(`store:updated`,({storageKey:e})=>{e===this.getStoreName()&&(this.doc=null)})}async initialize(){this._initializing=!0;try{let e=await this._get();if(e&&e.version!==this.config.version&&this.config.onUpgrade){let{state:t}=await this.config.onUpgrade({data:e.data,version:e.version,app:e.app});await this.set(`migration`,t)}this.initialized=!0,this.initializationCallbacks.forEach(e=>e()),this.initializationCallbacks=[]}catch(e){console.error(`Failed to initialize and upgrade store ${this.getStoreName()}:`,e)}finally{this._initializing=!1}}_onInitialized(e){this.initialized?e():this.initializationCallbacks.push(e)}async getCollection(){return this.collection??this.collectionPromise}async set(e,t){!this.initialized&&!this._initializing&&await new Promise(e=>this._onInitialized(e));try{let n=await this.getCollection(),r=await this._read(),i={store:this.getStoreName(),data:t,version:this.config.version,app:this.config.app},a;return r?a=await r.update(i):(this.doc=await n.create(i),a=!0),a&&this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.getStoreName(),instanceId:e,state:t,version:this.config.version,app:this.config.app}}),a}catch(t){return console.error(`Failed to set state for store ${this.getStoreName()} in database ${this.config.database} by instance ${e}:`,t),!1}}async _read(){if(this.doc)return this.doc;let e=await this.getCollection(),t=new a.QueryBuilder().where({field:`store`,operator:`eq`,value:this.getStoreName()}).build();return this.doc=await e.find(t.filters),this.doc}async _get(){try{let e=await this._read();return e?e.read().then(()=>e):null}catch(e){return console.error(`Failed to get state for store ${this.getStoreName()} in database ${this.config.database}:`,e),null}}async get(){this.initialized||await new Promise(e=>this._onInitialized(e));let e=await this._get();return e?e.data:null}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.getStoreName()&&r!==e&&t(i)})}async clear(){this.initialized||await new Promise(e=>this._onInitialized(e));try{let e=await this._read();return e&&await e.delete()?(this.doc=null,this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.getStoreName(),instanceId:``,state:null,version:this.config.version,app:this.config.app}}),!0):!0}catch(e){return console.error(`Failed to clear state for store ${this.getStoreName()} in database ${this.config.database}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}async close(){o.releaseDatabase(this.config.database)}},c=class{eventBus;inMemoryState=null;config;constructor(e){this.config=e,this.eventBus=this.initializeEventBus(),this.setupLwwSynchronizationListener()}initializeEventBus(){let e={errorHandler:e=>console.error(`Event bus error for ${this.config.storageKey} (Ephemeral LWW):`,e)};return(typeof process>`u`||process.env.NODE_ENV!==`test`)&&(e.broadcast={channel:`ephemeral_lww_${this.config.storageKey}`}),(0,t.createEventBus)(e)}setupLwwSynchronizationListener(){this.eventBus.subscribe(`store:updated`,({storageKey:e,instanceId:t,state:n,timestamp:r,version:i})=>{e===this.config.storageKey&&(!this.inMemoryState||r&&r>this.inMemoryState.timestamp)&&(this.inMemoryState={data:n,timestamp:r,instanceId:t,version:i})})}set(e,t){try{let n=Date.now();return this.inMemoryState={data:structuredClone(t),timestamp:n,instanceId:e,version:this.config.version},this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:e,state:t,timestamp:n,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to set state in EphemeralPersistence (LWW) for ${this.config.storageKey}:`,e),!1}}get(){return this.inMemoryState?.data??null}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.config.storageKey&&r!==e&&t(i)})}clear(){try{let e=Date.now(),t=`clear-initiator`;return this.inMemoryState={data:null,timestamp:e,instanceId:t,version:this.config.version},this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:t,state:null,timestamp:e,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to clear state in EphemeralPersistence (LWW) for ${this.config.storageKey}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}};exports.EphemeralPersistence=c,exports.IndexedDBPersistence=s,exports.WebStoragePersistence=i;
|
package/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createEventBus as e}from"@asaidimu/utils-events";import{DatabaseConnection as t,DatabaseError as n,createIndexedDbStore as r}from"@asaidimu/utils-database";import{SystemError as i}from"@asaidimu/utils-error";var a=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),o=class{eventBus;storage;config;initialized=!1;initializationCallbacks=[];constructor(e){this.config=e,this.storage=e.session?sessionStorage:localStorage,this.eventBus=this.initializeEventBus(),this.initialize(),e.session||this.setupStorageEventListener()}async initialize(){try{let e=this.storage.getItem(this.getStoreName());if(e){let t=JSON.parse(e);if(t.version!==this.config.version&&this.config.onUpgrade){let{state:e}=await this.config.onUpgrade({data:t.state,version:t.version,app:t.app});this.set(`migration`,e)}}this.initialized=!0,this.initializationCallbacks.forEach(e=>e()),this.initializationCallbacks=[]}catch(e){console.error(`Failed to initialize WebStoragePersistence for ${this.config.storageKey}:`,e)}}_onInitialized(e){this.initialized?e():this.initializationCallbacks.push(e)}initializeEventBus(){let t={batch:{size:0,delay:0},errorHandler:e=>console.error(`Event bus error for ${this.config.storageKey}:`,e)};return(typeof process>`u`||process.env.NODE_ENV!==`test`)&&(t.broadcast={channel:`storage_${this.getStoreName()}`}),e(t)}getStoreName(){return`_${this.config.app}_${this.config.storageKey}_`}setupStorageEventListener(){window.addEventListener(`storage`,e=>{if(!(e.key!==this.getStoreName()||!e.newValue))try{let t=JSON.parse(e.newValue);t&&this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:`external`,state:t.state,version:t.version,app:t.app}})}catch(e){console.error(`Failed to parse storage event data:`,e)}})}set(e,t){try{let n={state:structuredClone(t),version:this.config.version,app:this.config.app},r=JSON.stringify(n);return this.storage.setItem(this.getStoreName(),r),this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:e,state:t,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to persist state to web storage for ${this.config.storageKey}:`,e),!1}}_get(){try{let e=this.storage.getItem(this.getStoreName());return e?JSON.parse(e).state:null}catch(e){return console.error(`Failed to retrieve state from web storage for ${this.config.storageKey}:`,e),null}}async get(){return this.initialized||await new Promise(e=>{this._onInitialized(e)}),this._get()}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.config.storageKey&&r!==e&&t(i)})}clear(){try{return this.storage.removeItem(this.getStoreName()),this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:`clear-initiator`,state:null,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to clear persisted state for ${this.config.storageKey}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}},s=a(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},c=e=>s(n({},`__esModule`,{value:!0}),e),l={};o(l,{QueryBuilder:()=>u,createJoiner:()=>j,createMatcher:()=>v,createPaginator:()=>z,createProjector:()=>L,createSorter:()=>I}),t.exports=c(l);var u=class{query;constructor(){this.query={}}where(e){return this.query.filters=e,this}orderBy(e,t){return this.query.sort||(this.query.sort=[]),this.query.sort.push({field:e,direction:t}),this}offset(e,t){return this.query.pagination={type:`offset`,offset:e,limit:t},this}cursor(e,t,n){return this.query.pagination={type:`cursor`,cursor:e,limit:t,direction:n},this}include(e){return this.query.projection||(this.query.projection={}),this.query.projection.include=e,this}exclude(e){return this.query.projection||(this.query.projection={}),this.query.projection.exclude=e,this}computed(e,t){return this.query.projection||(this.query.projection={}),this.query.projection.computed||(this.query.projection.computed=[]),this.query.projection.computed.push({type:`computed`,expression:e,alias:t}),this}case(e,t,n){return this.query.projection||(this.query.projection={}),this.query.projection.computed||(this.query.projection.computed=[]),this.query.projection.computed.push({type:`case`,conditions:e,else:t,alias:n}),this}join(e,t,n){return this.query.joins||(this.query.joins=[]),this.query.joins.push({relation:e,alias:n,query:t}),this}aggregate(e,t){return this.query.aggregations={groupBy:e,metrics:t},this}window(e){return this.query.window||(this.query.window=[]),this.query.window.push(e),this}hint(e){return this.query.hints||(this.query.hints=[]),this.query.hints.push(e),this}build(){return this.query}};function d(e){function t(e,t){return f(t)?e[t.field]:p(t)?t.value:g(t)?i(t,e):m(t)?i(t.expression,e):h(t)?a(e,t):t}let n=new Map([[`and`,(e,t)=>t.every(t=>o(e,t))],[`or`,(e,t)=>t.some(t=>o(e,t))],[`not`,(e,t)=>!t.every(t=>o(e,t))],[`nor`,(e,t)=>!t.some(t=>o(e,t))],[`xor`,(e,t)=>t.filter(t=>o(e,t)).length===1]]);function r(e,t){let{operator:r,conditions:i}=t,a=n.get(r);if(a)return a(e,i);throw Error(`Unsupported logical operator: ${r}`)}function i(n,r){let i=n.arguments.map(e=>t(r,e));if(e[n.function])return e[n.function](...i);throw Error(`Function ${n.function} not found!`)}function a(e,t){for(let n of t.conditions)if(o(e,n.when))return n.then;return t.else}function o(n,i){if(_(i))return r(n,i);if(!i||!i.field)return!1;let{field:a,operator:o,value:s}=i,c=n[a],l=t(n,s),u=new Map([[`eq`,(e,t)=>e===t],[`neq`,(e,t)=>e!==t],[`lt`,(e,t)=>e<t],[`lte`,(e,t)=>e<=t],[`gt`,(e,t)=>e>t],[`gte`,(e,t)=>e>=t],[`in`,(e,t)=>Array.isArray(t)&&t.includes(e)],[`nin`,(e,t)=>Array.isArray(t)&&!t.includes(e)],[`contains`,(e,t)=>typeof e==`string`?e.includes(t):Array.isArray(e)?e.includes(s):!1],[`ncontains`,(e,t)=>typeof e==`string`&&!e.includes(t)],[`startswith`,(e,t)=>typeof e==`string`&&e.startsWith(t)],[`endswith`,(e,t)=>typeof e==`string`&&e.endsWith(t)],[`exists`,e=>e!=null],[`nexists`,e=>e==null]]),d=e[o]||u.get(o);if(d)return d(c,l);throw Error(`Unsupported comparison operator: ${o}`)}return{resolve:t,evaluate:o}}function f(e){return e?.type===`field`}function p(e){return e?.type===`value`}function m(e){return e?.type===`computed`}function h(e){return e?.type===`case`}function g(e){return e?.type===`function`}function _(e){return e?e.conditions!==void 0:!1}function v(e){let{evaluate:t}=d(e),n=new WeakMap;function r(e,r){let i=n.get(e);i||(i=new Map,n.set(e,i));let a=JSON.stringify(r);if(i.has(a))return i.get(a);let o=t(e,r);return i.set(a,o),o}return{matcher:r,match:r}}var y=class extends Error{constructor(e,t){super(e),this.code=t,this.name=`JoinError`}},b=e=>e&&`field`in e&&`operator`in e&&`value`in e,x=e=>`operator`in e&&`conditions`in e,S=e=>typeof e==`object`&&!!e&&`type`in e&&e.type===`field`,C=(e,t)=>{if(!e.relation)throw new y(`Join configuration must specify a relation`,`INVALID_CONFIG`);if(!t[e.relation])throw new y(`Collection "${e.relation}" not found in database`,`COLLECTION_NOT_FOUND`);if(e.alias&&typeof e.alias!=`string`)throw new y(`Join alias must be a string`,`INVALID_ALIAS`)},w=(e,t)=>t.split(`.`).reduce((e,t)=>e?.[t],e),T=(e,t)=>{if(e){if(b(e)&&e){let n=S(e.value)?w(t,e.value.field):e.value;return{...e,value:n}}if(x(e)){let n={...e};return e.conditions&&(n.conditions=e.conditions.map(e=>T(e,t))),n}return e}},E=(e,t)=>{if(!e)return{};let n=T(e.filters,t);return{...e,filters:n}},D=(e,t,n)=>{let r=n.alias||n.relation;return[{...e,[r]:t}]},O=(e,t)=>{let n=new Map;return e.forEach(e=>{let r=e[t];n.has(r)||n.set(r,[]),n.get(r).push(e)}),n},k=async(e,t,n,r)=>{try{if(C(n,e),!Array.isArray(t))throw new y(`Source data must be an array`,`INVALID_SOURCE_DATA`);let i=e[n.relation],a;return n.query?.filters&&b(n.query.filters)&&(a=O(i,n.query.filters.field)),(await Promise.all(t.map(async e=>{let t=E(n.query,e),o;return o=t.filters&&b(t.filters)&&a?.has(t.filters.value)?a.get(t.filters.value)||[]:i.filter(e=>r.matcher(e,t.filters)),D(e,await[e=>t.sort?r.sorter(e,t.sort):e,e=>t.projection?r.projector(e,t.projection):e,async e=>t.pagination?await r.paginator(e,t.pagination):e].reduce(async(e,t)=>t(await e),Promise.resolve(o)),n)}))).flat()}catch(e){throw e instanceof y?e:new y(`Join operation failed: ${e.message}`,`JOIN_EXECUTION_ERROR`)}},A=async(e,t,n,r)=>n.reduce(async(t,n)=>k(e,await t,n,r),Promise.resolve(t));function j(){return{join:A}}var M=class extends Error{constructor(e,t){super(`Unsupported comparison between ${typeof e} and ${typeof t}`),this.name=`UnsupportedComparisonError`}},N=class extends Error{constructor(e){super(`Unsupported sort direction: ${e}`),this.name=`InvalidSortDirectionError`}};function P(e,t,n){if(e===t)return 0;if(e==null||t==null)return e==null?n===`asc`?-1:1:n===`asc`?1:-1;if(typeof e==`string`&&typeof t==`string`)return n===`asc`?e.localeCompare(t):t.localeCompare(e);if(typeof e==`number`&&typeof t==`number`)return n===`asc`?e-t:t-e;throw new M(e,t)}function F(e,t){let n=Array.from(e);return t.length===0?n:n.sort((e,n)=>{for(let r of t){let{field:t,direction:i}=r,a=e[t],o=n[t];try{if(i!==`asc`&&i!==`desc`)throw new N(i);let e=P(a,o,i);if(e!==0)return e}catch(e){throw e instanceof M||e instanceof N?e:Error(`Error comparing field '${t}': ${e.message}`)}}return 0})}function I(){return{sort:F}}function L(e){let{resolve:t}=d(e);function n(e,t,n){for(let r of t){if(typeof r==`string`){let t=r;n[t]=e[t];continue}for(let[t,i]of Object.entries(r))Object.prototype.hasOwnProperty.call(e,t)&&(n[t]=a(e[t],i))}}function r(e,t,n){Object.keys(n).length===0&&Object.assign(n,e);for(let e of t){if(typeof e==`string`){let t=e;delete n[t];continue}for(let[t,r]of Object.entries(e)){if(!Object.prototype.hasOwnProperty.call(n,t))continue;let e=n[t];e&&typeof e==`object`?n[t]=a(e,r):delete n[t]}}}function i(e,n,r){for(let i of n)r[i.alias]=t(e,i)}function a(e,t){let a={};return t.include?.length&&n(e,t.include,a),t.exclude?.length&&r(e,t.exclude,a),t.computed?.length&&i(e,t.computed,a),a}return{project:a}}async function*R(e,t,n=e=>String(e)){t.type===`offset`?yield*B(e,t):yield*V(e,t,n)}function z(){return{paginate:R}}async function*B(e,t){let{offset:n,limit:r}=t,i=0,a=r,o=[];for await(let t of e){if(a<=0&&(yield o,o=[],a=r),i<n){i++;continue}o.push(t),a--}o.length>0&&(yield o)}async function*V(e,t,n){let{cursor:r,limit:i,direction:a}=t,o=i,s=r===void 0,c=[];if(a===`forward`)for await(let t of e){if(o<=0&&(yield c,c=[],o=i),!s){s=n(t)===r;continue}c.push(t),o--}else{let t=[];for await(let n of e)t.push(n);for(let e=t.length-1;e>=0;e--){let a=t[e];if(!s){s=n(a)===r;continue}c.push(a),o--,o<=0&&(yield c,c=[],o=i)}}c.length>0&&(yield c)}0&&(t.exports={QueryBuilder,createJoiner,createMatcher,createPaginator,createProjector,createSorter})}))(),c=class e extends i{constructor(t,n){super({code:`SYNC_ERROR`,message:t,cause:n}),this.name=`SyncError`,Object.setPrototypeOf(this,e.prototype)}},l=class e extends c{constructor(t){super(`[ArtifactContainer] Operation timed out: ${t}`),this.name=`TimeoutError`,Object.setPrototypeOf(this,e.prototype)}},u=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){this._locked=!0;return}if(this.waiters.length>=this._capacity)throw Error(`Mutex queue is full (capacity: ${this._capacity})`);let t,n=new Promise(e=>t=e);if(this.waiters.push(t),e==null){await n;return}let r;await Promise.race([n.then(()=>clearTimeout(r)),new Promise((n,i)=>{r=setTimeout(()=>{let e=this.waiters.indexOf(t);e!==-1&&this.waiters.splice(e,1),i(new l(`Mutex lock timed out`))},e)})])}tryLock(){return this._locked?!1:(this._locked=!0,!0)}unlock(){if(!this._locked)throw Error(`Mutex is not locked`);let e=this.waiters.shift();e?this._yieldMode===`microtask`?queueMicrotask(e):setTimeout(e,0):this._locked=!1}locked(){return this._locked}pending(){return this.waiters.length}},d=class{mutex=new u({yieldMode:`microtask`});promise=null;_value=null;_error;_done=!1;retry;throws;constructor({retry:e,throws:t}={}){this.retry=!!e,this.throws=!!t}resolve(e){if(this._done)throw Error(`Cannot resolve: operation is already completed.`);if(this.running())throw 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){let e=Error(`Cannot execute doSync while an async operation is pending.`);if(this.throws)throw e;return{value:null,error:e}}if(!this.mutex.tryLock()){let e=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()}let e=Error(`Cannot execute doSync while an async operation is pending.`);if(this.throws)throw e;return{value:null,error:e}}try{let 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 this.promise!==null&&!this.done()}peek(){return{value:this._value,error:this._error}}get(){if(!this._done)throw Error(`Once operation is not yet complete`);if(this._error)throw this._error;return this._value}reset(){if(this.running())throw 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,n=`Operation timed out`){if(t==null)return e;let r;return Promise.race([e.then(e=>(clearTimeout(r),e)),new Promise((e,i)=>{r=setTimeout(()=>i(new l(n)),t)})])}},f=class{factory;onCleanup;options;_count=0;init=new d({retry:!1,throws:!1});pendingMicrotask=!1;cleanupTimer;constructor(e,t,n={}){this.factory=e,this.onCleanup=t,this.options=n}get subscribers(){return this._count}async acquire(){this.cancelPendingCleanup(),this._count++;let e=await this.init.do(this.factory);if(e.error)throw e.error;return e.value}release(){if(this._count<=0){console.warn(`SharedResource.release() called, but count is already 0.`);return}this._count--,this._count===0&&this.scheduleCleanup()}peek(){let e=this.init.peek();return e.error?null:e.value}forceCleanup(){this.cancelPendingCleanup(),this._count=0,this.executeCleanup()}cancelPendingCleanup(){this.pendingMicrotask=!1,this.cleanupTimer!==void 0&&(clearTimeout(this.cleanupTimer),this.cleanupTimer=void 0)}scheduleCleanup(){let e=this.options.gracePeriod??`microtask`;if(e===`sync`){this.executeCleanup();return}if(e===`microtask`){this.pendingMicrotask=!0,queueMicrotask(()=>{!this.pendingMicrotask||this._count>0||(this.pendingMicrotask=!1,this.executeCleanup())});return}this.cleanupTimer=setTimeout(()=>{this.cleanupTimer=void 0,!(this._count>0)&&this.executeCleanup()},e)}executeCleanup(){let 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()}},p=class i{static dbResources=new Map;static eventBusMap=new Map;static createDatabaseFactory(e){return async()=>{let{database:i,enableTelemetry:a=!1,collection:o=`stores`}=e,s=await t({database:i,enableTelemetry:a},r);try{await s.createCollection({name:o,version:`1.0.0`,nestedSchemas:{},fields:{store:{name:`store`,type:`string`,required:!0},data:{name:`data`,type:`object`,required:!0},version:{name:`version`,type:`string`,required:!0},app:{name:`app`,type:`string`,required:!0}}})}catch(e){if(e instanceof n&&e.type!==`SCHEMA_ALREADY_EXISTS`)throw e}return s}}static createDatabaseCleanup(e){return t=>{t?.close();let n=[];i.eventBusMap.forEach((t,r)=>{r.startsWith(`${e}_store_`)&&(t.clear(),n.push(r))}),n.forEach(e=>i.eventBusMap.delete(e))}}static async acquireDatabase(e){let t=e.database;if(!i.dbResources.has(t)){let n=new f(i.createDatabaseFactory(e),i.createDatabaseCleanup(t),{gracePeriod:`microtask`});i.dbResources.set(t,n)}return i.dbResources.get(t).acquire()}static releaseDatabase(e){let t=i.dbResources.get(e);t&&t.release()}static async getCollection(e){let t=await i.acquireDatabase(e),n=e.collection??`stores`;return t.collection(n)}static getEventBus(t,n){let r=`${t}_store_${n}`;return i.eventBusMap.has(r)||i.eventBusMap.set(r,e({errorHandler:e=>console.error(`Event bus error for ${t}:${n}:`,e),broadcast:{channel:r}})),i.eventBusMap.get(r)}},m=class{collection=null;collectionPromise;config;eventBus;initialized=!1;_initializing=!1;initializationCallbacks=[];doc=null;getStoreName(){return`_${this.config.app}_${this.config.store}_`}constructor(e){this.config=e,this.collectionPromise=p.getCollection(this.config),this.collectionPromise.then(e=>{this.collection=e,this.initialize()}).catch(e=>{console.error(`Failed to initialize collection for store ${this.getStoreName()}:`,e)}),this.eventBus=p.getEventBus(this.config.database,this.getStoreName()),this.eventBus.subscribe(`store:updated`,({storageKey:e})=>{e===this.getStoreName()&&(this.doc=null)})}async initialize(){this._initializing=!0;try{let e=await this._get();if(e&&e.version!==this.config.version&&this.config.onUpgrade){let{state:t}=await this.config.onUpgrade({data:e.data,version:e.version,app:e.app});await this.set(`migration`,t)}this.initialized=!0,this.initializationCallbacks.forEach(e=>e()),this.initializationCallbacks=[]}catch(e){console.error(`Failed to initialize and upgrade store ${this.getStoreName()}:`,e)}finally{this._initializing=!1}}_onInitialized(e){this.initialized?e():this.initializationCallbacks.push(e)}async getCollection(){return this.collection??this.collectionPromise}async set(e,t){!this.initialized&&!this._initializing&&await new Promise(e=>this._onInitialized(e));try{let n=await this.getCollection(),r=await this._read(),i={store:this.getStoreName(),data:t,version:this.config.version,app:this.config.app},a;return r?a=await r.update(i):(this.doc=await n.create(i),a=!0),a&&this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.getStoreName(),instanceId:e,state:t,version:this.config.version,app:this.config.app}}),a}catch(t){return console.error(`Failed to set state for store ${this.getStoreName()} in database ${this.config.database} by instance ${e}:`,t),!1}}async _read(){if(this.doc)return this.doc;let e=await this.getCollection(),t=new s.QueryBuilder().where({field:`store`,operator:`eq`,value:this.getStoreName()}).build();return this.doc=await e.find(t.filters),this.doc}async _get(){try{let e=await this._read();return e?e.read().then(()=>e):null}catch(e){return console.error(`Failed to get state for store ${this.getStoreName()} in database ${this.config.database}:`,e),null}}async get(){this.initialized||await new Promise(e=>this._onInitialized(e));let e=await this._get();return e?e.data:null}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.getStoreName()&&r!==e&&t(i)})}async clear(){this.initialized||await new Promise(e=>this._onInitialized(e));try{let e=await this._read();return e&&await e.delete()?(this.doc=null,this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.getStoreName(),instanceId:``,state:null,version:this.config.version,app:this.config.app}}),!0):!0}catch(e){return console.error(`Failed to clear state for store ${this.getStoreName()} in database ${this.config.database}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}async close(){p.releaseDatabase(this.config.database)}},h=class{eventBus;inMemoryState=null;config;constructor(e){this.config=e,this.eventBus=this.initializeEventBus(),this.setupLwwSynchronizationListener()}initializeEventBus(){let t={errorHandler:e=>console.error(`Event bus error for ${this.config.storageKey} (Ephemeral LWW):`,e)};return(typeof process>`u`||process.env.NODE_ENV!==`test`)&&(t.broadcast={channel:`ephemeral_lww_${this.config.storageKey}`}),e(t)}setupLwwSynchronizationListener(){this.eventBus.subscribe(`store:updated`,({storageKey:e,instanceId:t,state:n,timestamp:r,version:i})=>{e===this.config.storageKey&&(!this.inMemoryState||r&&r>this.inMemoryState.timestamp)&&(this.inMemoryState={data:n,timestamp:r,instanceId:t,version:i})})}set(e,t){try{let n=Date.now();return this.inMemoryState={data:structuredClone(t),timestamp:n,instanceId:e,version:this.config.version},this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:e,state:t,timestamp:n,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to set state in EphemeralPersistence (LWW) for ${this.config.storageKey}:`,e),!1}}get(){return this.inMemoryState?.data??null}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.config.storageKey&&r!==e&&t(i)})}clear(){try{let e=Date.now(),t=`clear-initiator`;return this.inMemoryState={data:null,timestamp:e,instanceId:t,version:this.config.version},this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:t,state:null,timestamp:e,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to clear state in EphemeralPersistence (LWW) for ${this.config.storageKey}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}};export{h as EphemeralPersistence,m as IndexedDBPersistence,o as WebStoragePersistence};
|
|
1
|
+
import{createEventBus as e}from"@asaidimu/utils-events";import{DatabaseConnection as t,DatabaseError as n,createIndexedDbStore as r}from"@asaidimu/utils-database";import{SharedResource as i}from"@asaidimu/utils-sync";var a=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),o=class{eventBus;storage;config;initialized=!1;initializationCallbacks=[];constructor(e){this.config=e,this.storage=e.session?sessionStorage:localStorage,this.eventBus=this.initializeEventBus(),this.initialize(),e.session||this.setupStorageEventListener()}async initialize(){try{let e=this.storage.getItem(this.getStoreName());if(e){let t=JSON.parse(e);if(t.version!==this.config.version&&this.config.onUpgrade){let{state:e}=await this.config.onUpgrade({data:t.state,version:t.version,app:t.app});this.set(`migration`,e)}}this.initialized=!0,this.initializationCallbacks.forEach(e=>e()),this.initializationCallbacks=[]}catch(e){console.error(`Failed to initialize WebStoragePersistence for ${this.config.storageKey}:`,e)}}_onInitialized(e){this.initialized?e():this.initializationCallbacks.push(e)}initializeEventBus(){let t={batch:{size:0,delay:0},errorHandler:e=>console.error(`Event bus error for ${this.config.storageKey}:`,e)};return(typeof process>`u`||process.env.NODE_ENV!==`test`)&&(t.broadcast={channel:`storage_${this.getStoreName()}`}),e(t)}getStoreName(){return`_${this.config.app}_${this.config.storageKey}_`}setupStorageEventListener(){window.addEventListener(`storage`,e=>{if(!(e.key!==this.getStoreName()||!e.newValue))try{let t=JSON.parse(e.newValue);t&&this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:`external`,state:t.state,version:t.version,app:t.app}})}catch(e){console.error(`Failed to parse storage event data:`,e)}})}set(e,t){try{let n={state:structuredClone(t),version:this.config.version,app:this.config.app},r=JSON.stringify(n);return this.storage.setItem(this.getStoreName(),r),this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:e,state:t,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to persist state to web storage for ${this.config.storageKey}:`,e),!1}}_get(){try{let e=this.storage.getItem(this.getStoreName());return e?JSON.parse(e).state:null}catch(e){return console.error(`Failed to retrieve state from web storage for ${this.config.storageKey}:`,e),null}}async get(){return this.initialized||await new Promise(e=>{this._onInitialized(e)}),this._get()}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.config.storageKey&&r!==e&&t(i)})}clear(){try{return this.storage.removeItem(this.getStoreName()),this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:`clear-initiator`,state:null,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to clear persisted state for ${this.config.storageKey}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}},s=a(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},c=e=>s(n({},`__esModule`,{value:!0}),e),l={};o(l,{QueryBuilder:()=>u,createJoiner:()=>j,createMatcher:()=>v,createPaginator:()=>z,createProjector:()=>L,createSorter:()=>I}),t.exports=c(l);var u=class{query;constructor(){this.query={}}where(e){return this.query.filters=e,this}orderBy(e,t){return this.query.sort||(this.query.sort=[]),this.query.sort.push({field:e,direction:t}),this}offset(e,t){return this.query.pagination={type:`offset`,offset:e,limit:t},this}cursor(e,t,n){return this.query.pagination={type:`cursor`,cursor:e,limit:t,direction:n},this}include(e){return this.query.projection||(this.query.projection={}),this.query.projection.include=e,this}exclude(e){return this.query.projection||(this.query.projection={}),this.query.projection.exclude=e,this}computed(e,t){return this.query.projection||(this.query.projection={}),this.query.projection.computed||(this.query.projection.computed=[]),this.query.projection.computed.push({type:`computed`,expression:e,alias:t}),this}case(e,t,n){return this.query.projection||(this.query.projection={}),this.query.projection.computed||(this.query.projection.computed=[]),this.query.projection.computed.push({type:`case`,conditions:e,else:t,alias:n}),this}join(e,t,n){return this.query.joins||(this.query.joins=[]),this.query.joins.push({relation:e,alias:n,query:t}),this}aggregate(e,t){return this.query.aggregations={groupBy:e,metrics:t},this}window(e){return this.query.window||(this.query.window=[]),this.query.window.push(e),this}hint(e){return this.query.hints||(this.query.hints=[]),this.query.hints.push(e),this}build(){return this.query}};function d(e){function t(e,t){return f(t)?e[t.field]:p(t)?t.value:g(t)?i(t,e):m(t)?i(t.expression,e):h(t)?a(e,t):t}let n=new Map([[`and`,(e,t)=>t.every(t=>o(e,t))],[`or`,(e,t)=>t.some(t=>o(e,t))],[`not`,(e,t)=>!t.every(t=>o(e,t))],[`nor`,(e,t)=>!t.some(t=>o(e,t))],[`xor`,(e,t)=>t.filter(t=>o(e,t)).length===1]]);function r(e,t){let{operator:r,conditions:i}=t,a=n.get(r);if(a)return a(e,i);throw Error(`Unsupported logical operator: ${r}`)}function i(n,r){let i=n.arguments.map(e=>t(r,e));if(e[n.function])return e[n.function](...i);throw Error(`Function ${n.function} not found!`)}function a(e,t){for(let n of t.conditions)if(o(e,n.when))return n.then;return t.else}function o(n,i){if(_(i))return r(n,i);if(!i||!i.field)return!1;let{field:a,operator:o,value:s}=i,c=n[a],l=t(n,s),u=new Map([[`eq`,(e,t)=>e===t],[`neq`,(e,t)=>e!==t],[`lt`,(e,t)=>e<t],[`lte`,(e,t)=>e<=t],[`gt`,(e,t)=>e>t],[`gte`,(e,t)=>e>=t],[`in`,(e,t)=>Array.isArray(t)&&t.includes(e)],[`nin`,(e,t)=>Array.isArray(t)&&!t.includes(e)],[`contains`,(e,t)=>typeof e==`string`?e.includes(t):Array.isArray(e)?e.includes(s):!1],[`ncontains`,(e,t)=>typeof e==`string`&&!e.includes(t)],[`startswith`,(e,t)=>typeof e==`string`&&e.startsWith(t)],[`endswith`,(e,t)=>typeof e==`string`&&e.endsWith(t)],[`exists`,e=>e!=null],[`nexists`,e=>e==null]]),d=e[o]||u.get(o);if(d)return d(c,l);throw Error(`Unsupported comparison operator: ${o}`)}return{resolve:t,evaluate:o}}function f(e){return e?.type===`field`}function p(e){return e?.type===`value`}function m(e){return e?.type===`computed`}function h(e){return e?.type===`case`}function g(e){return e?.type===`function`}function _(e){return e?e.conditions!==void 0:!1}function v(e){let{evaluate:t}=d(e),n=new WeakMap;function r(e,r){let i=n.get(e);i||(i=new Map,n.set(e,i));let a=JSON.stringify(r);if(i.has(a))return i.get(a);let o=t(e,r);return i.set(a,o),o}return{matcher:r,match:r}}var y=class extends Error{constructor(e,t){super(e),this.code=t,this.name=`JoinError`}},b=e=>e&&`field`in e&&`operator`in e&&`value`in e,x=e=>`operator`in e&&`conditions`in e,S=e=>typeof e==`object`&&!!e&&`type`in e&&e.type===`field`,C=(e,t)=>{if(!e.relation)throw new y(`Join configuration must specify a relation`,`INVALID_CONFIG`);if(!t[e.relation])throw new y(`Collection "${e.relation}" not found in database`,`COLLECTION_NOT_FOUND`);if(e.alias&&typeof e.alias!=`string`)throw new y(`Join alias must be a string`,`INVALID_ALIAS`)},w=(e,t)=>t.split(`.`).reduce((e,t)=>e?.[t],e),T=(e,t)=>{if(e){if(b(e)&&e){let n=S(e.value)?w(t,e.value.field):e.value;return{...e,value:n}}if(x(e)){let n={...e};return e.conditions&&(n.conditions=e.conditions.map(e=>T(e,t))),n}return e}},E=(e,t)=>{if(!e)return{};let n=T(e.filters,t);return{...e,filters:n}},D=(e,t,n)=>{let r=n.alias||n.relation;return[{...e,[r]:t}]},O=(e,t)=>{let n=new Map;return e.forEach(e=>{let r=e[t];n.has(r)||n.set(r,[]),n.get(r).push(e)}),n},k=async(e,t,n,r)=>{try{if(C(n,e),!Array.isArray(t))throw new y(`Source data must be an array`,`INVALID_SOURCE_DATA`);let i=e[n.relation],a;return n.query?.filters&&b(n.query.filters)&&(a=O(i,n.query.filters.field)),(await Promise.all(t.map(async e=>{let t=E(n.query,e),o;return o=t.filters&&b(t.filters)&&a?.has(t.filters.value)?a.get(t.filters.value)||[]:i.filter(e=>r.matcher(e,t.filters)),D(e,await[e=>t.sort?r.sorter(e,t.sort):e,e=>t.projection?r.projector(e,t.projection):e,async e=>t.pagination?await r.paginator(e,t.pagination):e].reduce(async(e,t)=>t(await e),Promise.resolve(o)),n)}))).flat()}catch(e){throw e instanceof y?e:new y(`Join operation failed: ${e.message}`,`JOIN_EXECUTION_ERROR`)}},A=async(e,t,n,r)=>n.reduce(async(t,n)=>k(e,await t,n,r),Promise.resolve(t));function j(){return{join:A}}var M=class extends Error{constructor(e,t){super(`Unsupported comparison between ${typeof e} and ${typeof t}`),this.name=`UnsupportedComparisonError`}},N=class extends Error{constructor(e){super(`Unsupported sort direction: ${e}`),this.name=`InvalidSortDirectionError`}};function P(e,t,n){if(e===t)return 0;if(e==null||t==null)return e==null?n===`asc`?-1:1:n===`asc`?1:-1;if(typeof e==`string`&&typeof t==`string`)return n===`asc`?e.localeCompare(t):t.localeCompare(e);if(typeof e==`number`&&typeof t==`number`)return n===`asc`?e-t:t-e;throw new M(e,t)}function F(e,t){let n=Array.from(e);return t.length===0?n:n.sort((e,n)=>{for(let r of t){let{field:t,direction:i}=r,a=e[t],o=n[t];try{if(i!==`asc`&&i!==`desc`)throw new N(i);let e=P(a,o,i);if(e!==0)return e}catch(e){throw e instanceof M||e instanceof N?e:Error(`Error comparing field '${t}': ${e.message}`)}}return 0})}function I(){return{sort:F}}function L(e){let{resolve:t}=d(e);function n(e,t,n){for(let r of t){if(typeof r==`string`){let t=r;n[t]=e[t];continue}for(let[t,i]of Object.entries(r))Object.prototype.hasOwnProperty.call(e,t)&&(n[t]=a(e[t],i))}}function r(e,t,n){Object.keys(n).length===0&&Object.assign(n,e);for(let e of t){if(typeof e==`string`){let t=e;delete n[t];continue}for(let[t,r]of Object.entries(e)){if(!Object.prototype.hasOwnProperty.call(n,t))continue;let e=n[t];e&&typeof e==`object`?n[t]=a(e,r):delete n[t]}}}function i(e,n,r){for(let i of n)r[i.alias]=t(e,i)}function a(e,t){let a={};return t.include?.length&&n(e,t.include,a),t.exclude?.length&&r(e,t.exclude,a),t.computed?.length&&i(e,t.computed,a),a}return{project:a}}async function*R(e,t,n=e=>String(e)){t.type===`offset`?yield*B(e,t):yield*V(e,t,n)}function z(){return{paginate:R}}async function*B(e,t){let{offset:n,limit:r}=t,i=0,a=r,o=[];for await(let t of e){if(a<=0&&(yield o,o=[],a=r),i<n){i++;continue}o.push(t),a--}o.length>0&&(yield o)}async function*V(e,t,n){let{cursor:r,limit:i,direction:a}=t,o=i,s=r===void 0,c=[];if(a===`forward`)for await(let t of e){if(o<=0&&(yield c,c=[],o=i),!s){s=n(t)===r;continue}c.push(t),o--}else{let t=[];for await(let n of e)t.push(n);for(let e=t.length-1;e>=0;e--){let a=t[e];if(!s){s=n(a)===r;continue}c.push(a),o--,o<=0&&(yield c,c=[],o=i)}}c.length>0&&(yield c)}0&&(t.exports={QueryBuilder,createJoiner,createMatcher,createPaginator,createProjector,createSorter})}))(),c=class a{static dbResources=new Map;static eventBusMap=new Map;static createDatabaseFactory(e){return async()=>{let{database:i,enableTelemetry:a=!1,collection:o=`stores`}=e,s=await t({database:i,enableTelemetry:a},r);try{await s.createCollection({name:o,version:`1.0.0`,nestedSchemas:{},fields:{store:{name:`store`,type:`string`,required:!0},data:{name:`data`,type:`object`,required:!0},version:{name:`version`,type:`string`,required:!0},app:{name:`app`,type:`string`,required:!0}}})}catch(e){if(e instanceof n&&e.type!==`SCHEMA_ALREADY_EXISTS`)throw e}return s}}static createDatabaseCleanup(e){return t=>{t?.close();let n=[];a.eventBusMap.forEach((t,r)=>{r.startsWith(`${e}_store_`)&&(t.clear(),n.push(r))}),n.forEach(e=>a.eventBusMap.delete(e))}}static async acquireDatabase(e){let t=e.database;if(!a.dbResources.has(t)){let n=new i(a.createDatabaseFactory(e),a.createDatabaseCleanup(t),{gracePeriod:`microtask`});a.dbResources.set(t,n)}return a.dbResources.get(t).acquire()}static releaseDatabase(e){let t=a.dbResources.get(e);t&&t.release()}static async getCollection(e){let t=await a.acquireDatabase(e),n=e.collection??`stores`;return t.collection(n)}static getEventBus(t,n){let r=`${t}_store_${n}`;return a.eventBusMap.has(r)||a.eventBusMap.set(r,e({errorHandler:e=>console.error(`Event bus error for ${t}:${n}:`,e),broadcast:{channel:r}})),a.eventBusMap.get(r)}},l=class{collection=null;collectionPromise;config;eventBus;initialized=!1;_initializing=!1;initializationCallbacks=[];doc=null;getStoreName(){return`_${this.config.app}_${this.config.store}_`}constructor(e){this.config=e,this.collectionPromise=c.getCollection(this.config),this.collectionPromise.then(e=>{this.collection=e,this.initialize()}).catch(e=>{console.error(`Failed to initialize collection for store ${this.getStoreName()}:`,e)}),this.eventBus=c.getEventBus(this.config.database,this.getStoreName()),this.eventBus.subscribe(`store:updated`,({storageKey:e})=>{e===this.getStoreName()&&(this.doc=null)})}async initialize(){this._initializing=!0;try{let e=await this._get();if(e&&e.version!==this.config.version&&this.config.onUpgrade){let{state:t}=await this.config.onUpgrade({data:e.data,version:e.version,app:e.app});await this.set(`migration`,t)}this.initialized=!0,this.initializationCallbacks.forEach(e=>e()),this.initializationCallbacks=[]}catch(e){console.error(`Failed to initialize and upgrade store ${this.getStoreName()}:`,e)}finally{this._initializing=!1}}_onInitialized(e){this.initialized?e():this.initializationCallbacks.push(e)}async getCollection(){return this.collection??this.collectionPromise}async set(e,t){!this.initialized&&!this._initializing&&await new Promise(e=>this._onInitialized(e));try{let n=await this.getCollection(),r=await this._read(),i={store:this.getStoreName(),data:t,version:this.config.version,app:this.config.app},a;return r?a=await r.update(i):(this.doc=await n.create(i),a=!0),a&&this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.getStoreName(),instanceId:e,state:t,version:this.config.version,app:this.config.app}}),a}catch(t){return console.error(`Failed to set state for store ${this.getStoreName()} in database ${this.config.database} by instance ${e}:`,t),!1}}async _read(){if(this.doc)return this.doc;let e=await this.getCollection(),t=new s.QueryBuilder().where({field:`store`,operator:`eq`,value:this.getStoreName()}).build();return this.doc=await e.find(t.filters),this.doc}async _get(){try{let e=await this._read();return e?e.read().then(()=>e):null}catch(e){return console.error(`Failed to get state for store ${this.getStoreName()} in database ${this.config.database}:`,e),null}}async get(){this.initialized||await new Promise(e=>this._onInitialized(e));let e=await this._get();return e?e.data:null}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.getStoreName()&&r!==e&&t(i)})}async clear(){this.initialized||await new Promise(e=>this._onInitialized(e));try{let e=await this._read();return e&&await e.delete()?(this.doc=null,this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.getStoreName(),instanceId:``,state:null,version:this.config.version,app:this.config.app}}),!0):!0}catch(e){return console.error(`Failed to clear state for store ${this.getStoreName()} in database ${this.config.database}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}async close(){c.releaseDatabase(this.config.database)}},u=class{eventBus;inMemoryState=null;config;constructor(e){this.config=e,this.eventBus=this.initializeEventBus(),this.setupLwwSynchronizationListener()}initializeEventBus(){let t={errorHandler:e=>console.error(`Event bus error for ${this.config.storageKey} (Ephemeral LWW):`,e)};return(typeof process>`u`||process.env.NODE_ENV!==`test`)&&(t.broadcast={channel:`ephemeral_lww_${this.config.storageKey}`}),e(t)}setupLwwSynchronizationListener(){this.eventBus.subscribe(`store:updated`,({storageKey:e,instanceId:t,state:n,timestamp:r,version:i})=>{e===this.config.storageKey&&(!this.inMemoryState||r&&r>this.inMemoryState.timestamp)&&(this.inMemoryState={data:n,timestamp:r,instanceId:t,version:i})})}set(e,t){try{let n=Date.now();return this.inMemoryState={data:structuredClone(t),timestamp:n,instanceId:e,version:this.config.version},this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:e,state:t,timestamp:n,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to set state in EphemeralPersistence (LWW) for ${this.config.storageKey}:`,e),!1}}get(){return this.inMemoryState?.data??null}subscribe(e,t){return this.eventBus.subscribe(`store:updated`,({storageKey:n,instanceId:r,state:i})=>{n===this.config.storageKey&&r!==e&&t(i)})}clear(){try{let e=Date.now(),t=`clear-initiator`;return this.inMemoryState={data:null,timestamp:e,instanceId:t,version:this.config.version},this.eventBus.emit({name:`store:updated`,payload:{storageKey:this.config.storageKey,instanceId:t,state:null,timestamp:e,version:this.config.version,app:this.config.app}}),!0}catch(e){return console.error(`Failed to clear state in EphemeralPersistence (LWW) for ${this.config.storageKey}:`,e),!1}}stats(){return{version:this.config.version,id:this.config.app}}};export{u as EphemeralPersistence,l as IndexedDBPersistence,o as WebStoragePersistence};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asaidimu/utils-persistence",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.14",
|
|
4
4
|
"description": "Persistence utilities.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -31,9 +31,10 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@asaidimu/indexed": "^3.0.1",
|
|
33
33
|
"@asaidimu/query": "^1.1.2",
|
|
34
|
-
"@asaidimu/utils-database": "^3.1.
|
|
35
|
-
"@asaidimu/utils-events": "^1.
|
|
36
|
-
"uuid": "^14.0.0"
|
|
34
|
+
"@asaidimu/utils-database": "^3.1.11",
|
|
35
|
+
"@asaidimu/utils-events": "^1.2.3",
|
|
36
|
+
"uuid": "^14.0.0",
|
|
37
|
+
"@asaidimu/utils-sync": "^2.3.2"
|
|
37
38
|
},
|
|
38
39
|
"exports": {
|
|
39
40
|
".": {
|
|
@@ -66,5 +67,6 @@
|
|
|
66
67
|
}
|
|
67
68
|
]
|
|
68
69
|
]
|
|
69
|
-
}
|
|
70
|
+
},
|
|
71
|
+
"peerDependencies": {}
|
|
70
72
|
}
|