@blac/core
Advanced tools
| "use strict";var m=Object.defineProperty;var D=(c,t,e)=>t in c?m(c,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):c[t]=e;var n=(c,t,e)=>D(c,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var g=(c=>(c.BLOC_DISPOSED="BLOC_DISPOSED",c.BLOC_CREATED="BLOC_CREATED",c.LISTENER_REMOVED="LISTENER_REMOVED",c.LISTENER_ADDED="LISTENER_ADDED",c.STATE_CHANGED="STATE_CHANGED",c.BLOC_CONSUMER_REMOVED="BLOC_CONSUMER_REMOVED",c.BLOC_CONSUMER_ADDED="BLOC_CONSUMER_ADDED",c))(g||{});const r=class r{constructor(t={}){n(this,"createdAt",Date.now());n(this,"blocInstanceMap",new Map);n(this,"isolatedBlocMap",new Map);n(this,"pluginList",[]);n(this,"postChangesToDocument",!1);n(this,"log",(...t)=>{r.enableLog&&console.warn(`☢️ [Blac ${this.createdAt.toString()}]`,...t)});n(this,"warn",(t,...e)=>{r.enableLog&&console.warn(`🚨 [Blac ${String(r.instance.createdAt)}]`,t,...e)});n(this,"error",(t,...e)=>{r.enableLog&&console.error(`🚨 [Blac ${String(r.instance.createdAt)}]`,t,...e)});n(this,"resetInstance",()=>{this.log("Reset Blac instance");const t=new Map(this.blocInstanceMap),e=new Map(this.isolatedBlocMap);t.forEach(s=>{s._dispose()}),e.forEach(s=>{s.forEach(i=>{i._dispose()})}),this.blocInstanceMap.clear(),this.isolatedBlocMap.clear(),r.instance=new r({__unsafe_ignore_singleton:!0})});n(this,"addPlugin",t=>{this.pluginList.findIndex(s=>s.name===t.name)===-1&&(this.log("Add plugin",t.name),this.pluginList.push(t))});n(this,"dispatchEventToPlugins",(t,e,s)=>{this.pluginList.forEach(i=>{i.onEvent(t,e,s)})});n(this,"dispatchEvent",(t,e,s)=>{switch(this.log(t,e,s),t){case"BLOC_DISPOSED":this.disposeBloc(e);break;case"BLOC_CONSUMER_REMOVED":case"LISTENER_REMOVED":this.log(`[${e._name}:${String(e._id)}] Listener/Consumer removed. Listeners: ${String(e._observer.size)}, Consumers: ${String(e._consumers.size)}, KeepAlive: ${String(e._keepAlive)}`),e._observer.size===0&&e._consumers.size===0&&!e._keepAlive&&(this.log(`[${e._name}:${String(e._id)}] No listeners or consumers left and not keepAlive. Disposing.`),e._dispose());break}this.dispatchEventToPlugins(t,e,s)});n(this,"disposeBloc",t=>{const e=t.constructor,s=this.createBlocInstanceMapKey(t._name,t._id);this.log(`[${t._name}:${String(t._id)}] disposeBloc called. Isolated: ${String(e.isolated)}`),e.isolated?(this.unregisterIsolatedBlocInstance(t),this.blocInstanceMap.delete(s)):this.unregisterBlocInstance(t),this.dispatchEventToPlugins("BLOC_DISPOSED",t)});n(this,"getBloc",(t,e={})=>{const{id:s}=e,i=t,a=s??t.name;if(this.log(`[${t.name}:${String(a)}] getBloc called. Options:`,e),i.isolated){const o=this.findIsolatedBlocInstance(t,a);if(o)return this.log(`[${t.name}:${String(a)}] Found existing isolated instance.`),o}if(!i.isolated){const o=this.findRegisteredBlocInstance(t,a);if(o)return this.log(`[${t.name}:${String(a)}] Found existing registered instance.`),o}return this.log(`[${t.name}:${String(a)}] No existing instance found. Creating new one.`),this.createNewBlocInstance(t,a,e)});n(this,"getBlocOrThrow",(t,e={})=>{const s=t.isolated,i=e.id||t.name,a=s?this.findIsolatedBlocInstance(t,i):this.findRegisteredBlocInstance(t,i);if(a)return a;throw new Error(`Bloc ${t.name} not found`)});n(this,"getAllBlocs",(t,e={})=>{const s=[];if(this.blocInstanceMap.forEach(i=>{i.constructor===t&&s.push(i)}),e.searchIsolated!==!1){const i=this.isolatedBlocMap.get(t);i&&s.push(...i.map(a=>a))}return s});const{__unsafe_ignore_singleton:e=!1}=t;if(!e)return r.instance;r.instance=this}static getInstance(){return r.instance}createBlocInstanceMapKey(t,e){return`${t}:${String(e)}`}unregisterBlocInstance(t){const e=this.createBlocInstanceMapKey(t._name,t._id);this.blocInstanceMap.delete(e)}registerBlocInstance(t){const e=this.createBlocInstanceMapKey(t._name,t._id);this.blocInstanceMap.set(e,t)}findRegisteredBlocInstance(t,e){if(t.isolated)return;const i=this.createBlocInstanceMapKey(t.name,e),a=this.blocInstanceMap.get(i);return a&&this.log(`[${t.name}:${String(e)}] Found registered instance. Returning.`),a}registerIsolatedBlocInstance(t){const e=t.constructor,s=this.isolatedBlocMap.get(e);s?s.push(t):this.isolatedBlocMap.set(e,[t])}unregisterIsolatedBlocInstance(t){const e=t.constructor,s=this.isolatedBlocMap.get(e);if(s){const i=s.findIndex(a=>a._id===t._id);i!==-1&&s.splice(i,1),s.length===0&&this.isolatedBlocMap.delete(e)}}findIsolatedBlocInstance(t,e){if(!t.isolated)return;const i=this.isolatedBlocMap.get(t);if(!i)return;const a=i.find(o=>o._id===e);return a&&this.log(`[${t.name}:${String(e)}] Found isolated instance. Returning.`),a}createNewBlocInstance(t,e,s={}){const{props:i,instanceRef:a}=s,o=new t(i);return o._instanceRef=a,o.props=i||null,o._updateId(e),o.isIsolated?(this.registerIsolatedBlocInstance(o),o):(this.registerBlocInstance(o),o)}};n(r,"instance",new r),n(r,"getAllBlocs",r.instance.getAllBlocs),n(r,"addPlugin",r.instance.addPlugin),n(r,"enableLog",!1),n(r,"log",r.instance.log),n(r,"warn",r.instance.warn),n(r,"error",r.instance.error),n(r,"resetInstance",r.instance.resetInstance),n(r,"getBloc",r.instance.getBloc),n(r,"getBlocOrThrow",r.instance.getBlocOrThrow);let h=r;class B{constructor(t){n(this,"bloc");n(this,"_observers",new Set);this.bloc=t}get size(){return this._observers.size}get observers(){return this._observers}subscribe(t){return this._observers.add(t),h.instance.dispatchEvent(g.LISTENER_ADDED,this.bloc,{listenerId:t.id}),t.lastState||(t.lastState=t.dependencyArray?t.dependencyArray(this.bloc.state,this.bloc.state):[]),()=>{this.unsubscribe(t)}}unsubscribe(t){this._observers.delete(t),h.instance.dispatchEvent(g.LISTENER_REMOVED,this.bloc,{listenerId:t.id})}notify(t,e,s){this._observers.forEach(i=>{let a=!1;if(i.dependencyArray){const o=i.lastState||[],p=i.dependencyArray(t,e);for(let _=0;_<p.length;_++){const S=p[_],d=o[_]||[];for(let l=0;l<S.length;l++)if(!Object.is(S[l],d[l])){a=!0;break}}i.lastState=p}else a=!0;a&&i.fn(t,e,s)})}dispose(){this._observers.forEach(t=>{var e;this.unsubscribe(t),(e=t.dispose)==null||e.call(t)}),this._observers.clear()}}class E{constructor(t){n(this,"defaultDependencySelector");n(this,"_addons");n(this,"_isolated",!1);n(this,"_observer");n(this,"_blac",h.getInstance());n(this,"_id");n(this,"_instanceRef");n(this,"_keepAlive",!1);n(this,"_createdAt",Date.now());n(this,"_state");n(this,"_oldState");n(this,"props",null);n(this,"_updateId",t=>{const e=this._id;!t||t===e||(this._id=t)});n(this,"onDispose");n(this,"_consumers",new Set);n(this,"_addConsumer",t=>{this._consumers.add(t),this._blac.dispatchEvent(g.BLOC_CONSUMER_ADDED,this,{consumerId:t})});n(this,"_removeConsumer",t=>{this._blac.log(`[${this._name}:${String(this._id??"default_id")}] Removing consumer: ${t}`),this._consumers.delete(t),this._blac.dispatchEvent(g.BLOC_CONSUMER_REMOVED,this,{consumerId:t})});n(this,"_connectAddons",()=>{const{_addons:t}=this;t&&t.forEach(e=>{var s;(s=e.onInit)==null||s.call(e,this)})});n(this,"lastUpdate",Date.now());n(this,"_pushState",(t,e,s)=>{this._state=t,this._observer.notify(t,e,s),this.lastUpdate=Date.now()});this._state=t,this._observer=new B(this),this._blac.dispatchEvent(g.BLOC_CREATED,this),this._id=this.constructor.name;const e=this.constructor;this._keepAlive=e.keepAlive,this._isolated=e.isolated,this._addons=e.addons,this._connectAddons()}get isIsolated(){return this._isolated}get isKeepAlive(){return this._keepAlive}get state(){return this._state}get _name(){return this.constructor.name}_dispose(){var t;this._observer.dispose(),this._blac.dispatchEvent(g.BLOC_DISPOSED,this),(t=this.onDispose)==null||t.call(this)}}n(E,"isolated",!1),n(E,"keepAlive",!1);class O extends E{emit(t){if(Object.is(t,this.state))return;const e=this.state,s=t;this._pushState(s,e)}patch(t,e=!1){if(typeof this.state!="object"||this.state===null){h.warn("Cubit.patch: was called on a cubit where the state is not an object. This is a no-op.");return}let s=!1;if(e)s=!0;else for(const i in t)if(Object.prototype.hasOwnProperty.call(t,i)){const o=this.state[i];if(!Object.is(t[i],o)){s=!0;break}}s&&this.emit({...this.state,...t})}}class M extends E{constructor(){super(...arguments);n(this,"eventHandlers",new Map);n(this,"add",async e=>{const s=e.constructor,i=this.eventHandlers.get(s);if(i){const a=o=>{const p=this.state;this._pushState(o,p,e)};try{await i(e,a)}catch(o){h.error(`[Bloc ${this._name}:${String(this._id)}] Error in event handler for '${s.name}':`,o,"Action:",e)}}else{const a=e.constructor.name||"UnnamedConstructor";h.warn(`[Bloc ${this._name}:${String(this._id)}] No handler registered for action type: '${a}'. Action was:`,e)}})}on(e,s){this.eventHandlers.has(e)&&h.warn(`[Bloc ${this._name}:${String(this._id)}] Handler for event '${e.name}' already registered. It will be overwritten.`),this.eventHandlers.set(e,s)}}function I(c){switch(c){case"localStorage":return localStorage;case"sessionStorage":return sessionStorage;default:return localStorage}}function w(c={}){const{keyPrefix:t="blac",keyName:e,defaultValue:s,storageType:i="localStorage"}=c,a=d=>`${t}:${String(d)}`,o=d=>{var l;try{const u=I(i).getItem(a(d));if(typeof u!="string")return s;const f=JSON.parse(u);return typeof f.v<"u"?f.v:s}catch(u){return(l=c.onError)==null||l.call(c,u),s}},p=d=>{const l=e??d._id,u=o(l);d._pushState(u,null)};let _="";return{name:"Persist",onInit:p,onEmit:({newState:d,cubit:l})=>{const u=e??l._id,f=JSON.stringify({v:d});f!==_&&(I(i).setItem(a(u),f),_=f)}}}exports.Blac=h;exports.BlacLifecycleEvent=g;exports.BlacObservable=B;exports.Bloc=M;exports.BlocBase=E;exports.Cubit=O;exports.Persist=w; | ||
| //# sourceMappingURL=index.cjs.js.map |
| {"version":3,"file":"index.cjs.js","sources":["../src/Blac.ts","../src/BlacObserver.ts","../src/BlocBase.ts","../src/Cubit.ts","../src/Bloc.ts","../src/addons/Persist.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { BlacPlugin } from \"./BlacPlugin\";\nimport { BlocBase, BlocInstanceId } from \"./BlocBase\";\nimport {\n BlocBaseAbstract,\n BlocConstructor,\n BlocHookDependencyArrayFn,\n BlocState,\n InferPropsFromGeneric\n} from \"./types\";\n\n/**\n * Configuration options for the Blac instance\n */\nexport interface BlacConfig {\n /** Whether to expose the Blac instance globally */\n exposeBlacInstance?: boolean;\n}\n\nexport interface GetBlocOptions<B extends BlocBase<any>> {\n id?: string;\n dependencySelector?: BlocHookDependencyArrayFn<BlocState<B>>;\n props?: InferPropsFromGeneric<B>;\n onMount?: (bloc: B) => void;\n instanceRef?: string;\n}\n\n/**\n * Enum representing different lifecycle events that can occur in the Blac system.\n * These events are used to track the lifecycle of blocs and their consumers.\n */\nexport enum BlacLifecycleEvent {\n BLOC_DISPOSED = \"BLOC_DISPOSED\",\n BLOC_CREATED = \"BLOC_CREATED\",\n LISTENER_REMOVED = \"LISTENER_REMOVED\",\n LISTENER_ADDED = \"LISTENER_ADDED\",\n STATE_CHANGED = \"STATE_CHANGED\",\n BLOC_CONSUMER_REMOVED = \"BLOC_CONSUMER_REMOVED\",\n BLOC_CONSUMER_ADDED = \"BLOC_CONSUMER_ADDED\",\n}\n\n/**\n * Main Blac class that manages the state management system.\n * Implements a singleton pattern to ensure only one instance exists.\n * Handles bloc lifecycle, plugin management, and instance tracking.\n * \n * Key responsibilities:\n * - Managing bloc instances (creation, disposal, lookup)\n * - Handling isolated and non-isolated blocs\n * - Managing plugins and lifecycle events\n * - Providing logging and debugging capabilities\n */\nexport class Blac {\n /** The singleton instance of Blac */\n static instance: Blac = new Blac();\n /** Timestamp when the instance was created */\n createdAt = Date.now();\n static getAllBlocs = Blac.instance.getAllBlocs;\n static addPlugin = Blac.instance.addPlugin;\n /** Map storing all registered bloc instances by their class name and ID */\n blocInstanceMap: Map<string, BlocBase<any>> = new Map();\n /** Map storing isolated bloc instances grouped by their constructor */\n isolatedBlocMap: Map<BlocConstructor<any>, BlocBase<any>[]> = new Map();\n pluginList: BlacPlugin[] = [];\n /** Flag to control whether changes should be posted to document */\n postChangesToDocument = false;\n\n /**\n * Creates a new Blac instance.\n * @param options - Configuration options including singleton control\n */\n constructor(options: { __unsafe_ignore_singleton?: boolean } = {}) {\n const { __unsafe_ignore_singleton = false } = options;\n if (!__unsafe_ignore_singleton) {\n return Blac.instance;\n }\n Blac.instance = this;\n }\n\n /** Flag to enable/disable logging */\n static enableLog = false;\n\n /**\n * Logs messages to console when logging is enabled\n * @param args - Arguments to log\n */\n log = (...args: unknown[]) => {\n if (Blac.enableLog) console.warn(`☢️ [Blac ${this.createdAt.toString()}]`, ...args);\n };\n static log = Blac.instance.log;\n\n /**\n * Gets the singleton instance of Blac\n * @returns The Blac instance\n */\n static getInstance(): Blac {\n return Blac.instance;\n }\n\n\n /**\n * Logs a warning message\n * @param message - Warning message\n * @param args - Additional arguments\n */\n warn = (message: string, ...args: unknown[]) => {\n if (Blac.enableLog) {\n console.warn(`🚨 [Blac ${String(Blac.instance.createdAt)}]`, message, ...args);\n }\n };\n static warn = Blac.instance.warn;\n /**\n * Logs an error message\n * @param message - Error message\n * @param args - Additional arguments\n */\n error = (message: string, ...args: unknown[]) => {\n if (Blac.enableLog) {\n console.error(`🚨 [Blac ${String(Blac.instance.createdAt)}]`, message, ...args);\n }\n };\n static error = Blac.instance.error;\n\n /**\n * Resets the Blac instance to a new one, disposing non-keepAlive blocs\n * from the old instance.\n */\n resetInstance = (): void => {\n this.log(\"Reset Blac instance\");\n\n // Dispose non-keepAlive blocs from the current instance\n const oldBlocInstanceMap = new Map(this.blocInstanceMap);\n const oldIsolatedBlocMap = new Map(this.isolatedBlocMap);\n\n oldBlocInstanceMap.forEach((bloc) => {\n bloc._dispose();\n });\n\n oldIsolatedBlocMap.forEach((blocArray) => {\n blocArray.forEach((bloc) => {\n bloc._dispose();\n });\n });\n\n this.blocInstanceMap.clear();\n this.isolatedBlocMap.clear();\n\n // Create and assign the new instance\n Blac.instance = new Blac({\n __unsafe_ignore_singleton: true,\n });\n }\n static resetInstance = Blac.instance.resetInstance;\n\n /**\n * Adds a plugin to the Blac instance\n * @param plugin - The plugin to add\n */\n addPlugin = (plugin: BlacPlugin): void => {\n // check if already added\n const index = this.pluginList.findIndex((p) => p.name === plugin.name);\n if (index !== -1) return;\n this.log(\"Add plugin\", plugin.name);\n this.pluginList.push(plugin);\n };\n\n /**\n * Dispatches a lifecycle event to all registered plugins\n * @param event - The lifecycle event to dispatch\n * @param bloc - The bloc instance involved in the event\n * @param params - Additional parameters for the event\n */\n dispatchEventToPlugins = (\n event: BlacLifecycleEvent,\n bloc: BlocBase<any>,\n params?: unknown,\n ) => {\n this.pluginList.forEach((plugin) => {\n plugin.onEvent(event, bloc, params);\n });\n };\n\n /**\n * Dispatches a lifecycle event and handles related cleanup actions.\n * This method is responsible for:\n * - Logging the event\n * - Handling bloc disposal when needed\n * - Managing bloc consumer cleanup\n * - Forwarding the event to plugins\n * \n * @param event - The lifecycle event to dispatch\n * @param bloc - The bloc instance involved in the event\n * @param params - Additional parameters for the event\n */\n dispatchEvent = (\n event: BlacLifecycleEvent,\n bloc: BlocBase<any>,\n params?: unknown,\n ) => {\n this.log(event, bloc, params);\n\n switch (event) {\n case BlacLifecycleEvent.BLOC_DISPOSED:\n this.disposeBloc(bloc);\n break;\n case BlacLifecycleEvent.BLOC_CONSUMER_REMOVED:\n case BlacLifecycleEvent.LISTENER_REMOVED:\n this.log(`[${bloc._name}:${String(bloc._id)}] Listener/Consumer removed. Listeners: ${String(bloc._observer.size)}, Consumers: ${String(bloc._consumers.size)}, KeepAlive: ${String(bloc._keepAlive)}`);\n if (\n bloc._observer.size === 0 &&\n bloc._consumers.size === 0 &&\n !bloc._keepAlive\n ) {\n this.log(`[${bloc._name}:${String(bloc._id)}] No listeners or consumers left and not keepAlive. Disposing.`);\n bloc._dispose();\n }\n break;\n }\n\n this.dispatchEventToPlugins(event, bloc, params);\n };\n\n /**\n * Disposes of a bloc instance by removing it from the appropriate registry\n * @param bloc - The bloc instance to dispose\n */\n disposeBloc = (bloc: BlocBase<any>): void => {\n const base = bloc.constructor as unknown as BlocBaseAbstract;\n const key = this.createBlocInstanceMapKey(bloc._name, bloc._id);\n this.log(`[${bloc._name}:${String(bloc._id)}] disposeBloc called. Isolated: ${String(base.isolated)}`);\n\n if (base.isolated) {\n this.unregisterIsolatedBlocInstance(bloc);\n this.blocInstanceMap.delete(key);\n } else {\n this.unregisterBlocInstance(bloc);\n }\n this.dispatchEventToPlugins(BlacLifecycleEvent.BLOC_DISPOSED, bloc);\n };\n\n /**\n * Creates a unique key for a bloc instance in the map based on the bloc class name and instance ID\n * @param blocClassName - The name of the bloc class\n * @param id - The instance ID\n * @returns A unique key string in the format \"className:id\"\n */\n createBlocInstanceMapKey(blocClassName: string, id: BlocInstanceId): string {\n return `${blocClassName}:${String(id)}`;\n }\n\n /**\n * Unregister a bloc instance from the main registry\n * @param bloc - The bloc instance to unregister\n */\n unregisterBlocInstance(bloc: BlocBase<any>): void {\n const key = this.createBlocInstanceMapKey(bloc._name, bloc._id);\n this.blocInstanceMap.delete(key);\n }\n\n /**\n * Registers a bloc instance in the main registry\n * @param bloc - The bloc instance to register\n */\n registerBlocInstance(bloc: BlocBase<any>): void {\n const key = this.createBlocInstanceMapKey(bloc._name, bloc._id);\n this.blocInstanceMap.set(key, bloc);\n }\n\n /**\n * Finds a registered bloc instance by its class and ID\n * @param blocClass - The bloc class to search for\n * @param id - The instance ID\n * @returns The found bloc instance or undefined if not found\n */\n findRegisteredBlocInstance<B extends BlocConstructor<unknown>>(\n blocClass: B,\n id: BlocInstanceId,\n ): InstanceType<B> | undefined {\n const base = blocClass as unknown as BlocBaseAbstract;\n if (base.isolated) return undefined;\n\n const key = this.createBlocInstanceMapKey(blocClass.name, id);\n const found = this.blocInstanceMap.get(key) as InstanceType<B> | undefined;\n if (found) {\n this.log(`[${blocClass.name}:${String(id)}] Found registered instance. Returning.`);\n }\n return found \n }\n\n /**\n * Registers an isolated bloc instance in the isolated registry\n * @param bloc - The isolated bloc instance to register\n */\n registerIsolatedBlocInstance(bloc: BlocBase<any>): void {\n const blocClass = bloc.constructor as BlocConstructor<unknown>;\n const blocs = this.isolatedBlocMap.get(blocClass);\n if (blocs) {\n blocs.push(bloc);\n } else {\n this.isolatedBlocMap.set(blocClass, [bloc]);\n }\n }\n\n /**\n * Unregister an isolated bloc instance from the isolated registry\n * @param bloc - The isolated bloc instance to unregister\n */\n unregisterIsolatedBlocInstance(bloc: BlocBase<any>): void {\n const blocClass = bloc.constructor;\n const blocs = this.isolatedBlocMap.get(blocClass as BlocConstructor<unknown>);\n if (blocs) {\n const index = blocs.findIndex((b) => b._id === bloc._id);\n if (index !== -1) {\n blocs.splice(index, 1);\n }\n\n if (blocs.length === 0) {\n this.isolatedBlocMap.delete(blocClass as BlocConstructor<unknown>);\n }\n }\n }\n\n /**\n * Finds an isolated bloc instance by its class and ID\n */\n findIsolatedBlocInstance<B extends BlocConstructor<unknown>>(\n blocClass: B,\n id: BlocInstanceId,\n ): InstanceType<B> | undefined {\n const base = blocClass as unknown as BlocBaseAbstract;\n if (!base.isolated) return undefined;\n\n const blocs = this.isolatedBlocMap.get(blocClass);\n if (!blocs) return undefined;\n\n // Fix: Find the specific bloc by ID within the isolated array\n const found = blocs.find((b) => b._id === id) as InstanceType<B> | undefined;\n\n if (found) {\n this.log(`[${blocClass.name}:${String(id)}] Found isolated instance. Returning.`);\n }\n\n return found;\n }\n\n /**\n * Creates a new bloc instance and registers it in the appropriate registry\n * @param blocClass - The bloc class to instantiate\n * @param id - The instance ID\n * @param props - Properties to pass to the bloc constructor\n * @param instanceRef - Optional reference string for the instance\n * @returns The newly created bloc instance\n */\n createNewBlocInstance<B extends BlocConstructor<BlocBase<any>>>(\n blocClass: B,\n id: BlocInstanceId,\n options: GetBlocOptions<InstanceType<B>> = {},\n ): InstanceType<B> {\n const { props, instanceRef } = options;\n const newBloc = new blocClass(props as never) as InstanceType<BlocConstructor<BlocBase<unknown>>>;\n newBloc._instanceRef = instanceRef;\n newBloc.props = props || null;\n newBloc._updateId(id);\n\n if (newBloc.isIsolated) {\n this.registerIsolatedBlocInstance(newBloc);\n return newBloc as InstanceType<B>;\n }\n\n this.registerBlocInstance(newBloc);\n return newBloc as InstanceType<B>;\n }\n\n /**\n * Gets or creates a bloc instance based on the provided class and options.\n * If a bloc with the given ID exists, it will be returned. Otherwise, a new instance will be created.\n * \n * @param blocClass - The bloc class to get or create\n * @param options - Options including:\n * - id: The instance ID (defaults to class name if not provided)\n * - props: Properties to pass to the bloc constructor\n * - instanceRef: Optional reference string for the instance\n * @returns The bloc instance\n */\n getBloc = <B extends BlocConstructor<BlocBase<any>>>(\n blocClass: B,\n options: GetBlocOptions<InstanceType<B>> = {},\n ): InstanceType<B> => {\n const { id } = options;\n const base = blocClass as unknown as BlocBaseAbstract;\n const blocId = id ?? blocClass.name;\n\n this.log(`[${blocClass.name}:${String(blocId)}] getBloc called. Options:`, options);\n\n if (base.isolated) {\n const isolatedBloc = this.findIsolatedBlocInstance<B>(blocClass, blocId)\n if (isolatedBloc) {\n this.log(`[${blocClass.name}:${String(blocId)}] Found existing isolated instance.`);\n return isolatedBloc;\n }\n }\n\n if (!base.isolated) {\n const registeredBloc = this.findRegisteredBlocInstance(blocClass, blocId)\n if (registeredBloc) {\n this.log(`[${blocClass.name}:${String(blocId)}] Found existing registered instance.`);\n return registeredBloc\n }\n }\n\n this.log(`[${blocClass.name}:${String(blocId)}] No existing instance found. Creating new one.`);\n return this.createNewBlocInstance(\n blocClass,\n blocId,\n options,\n );\n };\n static getBloc = Blac.instance.getBloc;\n\n /**\n * Gets a bloc instance or throws an error if it doesn't exist\n * @param blocClass - The bloc class to get\n * @param options - Options including:\n * - id: The instance ID (defaults to class name if not provided)\n * - props: Properties to pass to the bloc constructor\n * - instanceRef: Optional reference string for the instance\n */\n getBlocOrThrow = <B extends BlocConstructor<unknown>>(\n blocClass: B,\n options: {\n id?: BlocInstanceId;\n props?: InferPropsFromGeneric<B>;\n instanceRef?: string;\n } = {},\n ): InstanceType<B> => {\n const isIsolated = (blocClass as unknown as BlocBaseAbstract).isolated;\n const id = options.id || blocClass.name;\n\n const registered = isIsolated\n ? this.findIsolatedBlocInstance(blocClass, id)\n : this.findRegisteredBlocInstance(blocClass, id);\n\n if (registered) {\n return registered;\n }\n throw new Error(`Bloc ${blocClass.name} not found`);\n };\n static getBlocOrThrow = Blac.instance.getBlocOrThrow;\n\n /**\n * Gets all instances of a specific bloc class\n * @param blocClass - The bloc class to search for\n * @param options - Options including:\n * - searchIsolated: Whether to search in isolated blocs (defaults to bloc's isolated property)\n * @returns Array of matching bloc instances\n */\n getAllBlocs = <B extends BlocConstructor<unknown>>(\n blocClass: B,\n options: {\n searchIsolated?: boolean;\n } = {},\n ): InstanceType<B>[] => {\n const results: InstanceType<B>[] = [];\n // const blocClassName = (blocClass as any).name; // Temporarily removed for debugging\n\n // Search non-isolated blocs\n this.blocInstanceMap.forEach((blocInstance) => {\n if (blocInstance.constructor === blocClass) { // Strict constructor check\n results.push(blocInstance as InstanceType<B>);\n }\n });\n\n // Optionally search isolated blocs\n if (options.searchIsolated !== false) {\n const isolatedBlocs = this.isolatedBlocMap.get(blocClass);\n if (isolatedBlocs) {\n results.push(...isolatedBlocs.map(bloc => bloc as InstanceType<B>));\n }\n }\n\n return results;\n };\n}\n","import { Blac, BlacLifecycleEvent } from './Blac';\nimport { BlocBase } from './BlocBase';\nimport { BlocHookDependencyArrayFn } from './types';\n\n/**\n * Represents an observer that can subscribe to state changes in a Bloc\n * @template S - The type of state being observed\n */\nexport type BlacObserver<S> = {\n /** Function to be called when state changes */\n fn: (newState: S, oldState: S, action?: unknown) => void | Promise<void>;\n /** Optional function to determine if the observer should be notified of state changes */\n dependencyArray?: BlocHookDependencyArrayFn<S>;\n /** Dispose function for the observer */\n dispose?: () => void;\n /** Cached state values used for dependency comparison */\n lastState?: unknown[][];\n /** Unique identifier for the observer */\n id: string;\n};\n\n/**\n * A class that manages observers for a Bloc's state changes\n * @template S - The type of state being observed\n */\nexport class BlacObservable<S = unknown> {\n /** The Bloc instance this observable is associated with */\n bloc: BlocBase<S>;\n\n /**\n * Creates a new BlacObservable instance\n * @param bloc - The Bloc instance to observe\n */\n constructor(bloc: BlocBase<S>) {\n this.bloc = bloc;\n }\n\n private _observers = new Set<BlacObserver<S>>();\n\n /**\n * Gets the number of active observers\n * @returns The number of observers currently subscribed\n */\n get size(): number {\n return this._observers.size;\n }\n\n /**\n * Gets the set of all observers\n * @returns The Set of all BlacObserver instances\n */\n get observers() {\n return this._observers;\n }\n\n /**\n * Subscribes an observer to state changes\n * @param observer - The observer to subscribe\n * @returns A function that can be called to unsubscribe the observer\n */\n subscribe(observer: BlacObserver<S>): () => void {\n this._observers.add(observer);\n Blac.instance.dispatchEvent(BlacLifecycleEvent.LISTENER_ADDED, this.bloc, { listenerId: observer.id });\n if (!observer.lastState) {\n observer.lastState = observer.dependencyArray\n ? observer.dependencyArray(this.bloc.state, this.bloc.state)\n : [];\n }\n return () => {\n this.unsubscribe(observer);\n }\n }\n\n /**\n * Unsubscribes an observer from state changes\n * @param observer - The observer to unsubscribe\n */\n unsubscribe(observer: BlacObserver<S>) {\n this._observers.delete(observer);\n Blac.instance.dispatchEvent(BlacLifecycleEvent.LISTENER_REMOVED, this.bloc, { listenerId: observer.id });\n }\n\n /**\n * Notifies all observers of a state change\n * @param newState - The new state value\n * @param oldState - The previous state value\n * @param action - Optional action that triggered the state change\n */\n notify(newState: S, oldState: S, action?: unknown) {\n this._observers.forEach((observer) => {\n let shouldUpdate = false;\n\n if (observer.dependencyArray) {\n const lastDependencyCheck = observer.lastState || [];\n const newDependencyCheck = observer.dependencyArray(newState, oldState);\n\n for (let o = 0; o < newDependencyCheck.length; o++) {\n const partNew = newDependencyCheck[o];\n const partOld = lastDependencyCheck[o] || [];\n for (let i = 0; i < partNew.length; i++) {\n if (!Object.is(partNew[i], partOld[i])) {\n shouldUpdate = true;\n break;\n }\n }\n }\n\n observer.lastState = newDependencyCheck;\n } else {\n shouldUpdate = true;\n }\n\n if (shouldUpdate) {\n void observer.fn(newState, oldState, action);\n }\n });\n }\n\n /**\n * Disposes of all observers and clears the observer set\n */\n dispose() {\n this._observers.forEach((observer) => {\n this.unsubscribe(observer);\n observer.dispose?.();\n });\n this._observers.clear();\n }\n}\n","import { Blac, BlacLifecycleEvent } from './Blac';\nimport { BlacObservable } from './BlacObserver';\nimport BlacAddon from './addons/BlacAddon';\nimport { BlocConstructor } from './types';\n\nexport type BlocInstanceId = string | number | undefined;\ntype DependencySelector<S> = (newState: S, oldState?: S) => unknown[][];\n\n// Define an interface for the static properties expected on a Bloc/Cubit constructor\ninterface BlocStaticProperties {\n isolated: boolean;\n keepAlive: boolean;\n addons?: BlacAddon[];\n}\n\n/**\n * Base class for both Blocs and Cubits that provides core state management functionality.\n * Handles state transitions, observer notifications, lifecycle management, and addon integration.\n * \n * @abstract This class should be extended, not instantiated directly\n * @template S The type of state managed by this Bloc\n * @template P The type of props that can be passed during instance creation (optional)\n */\nexport abstract class BlocBase<\n S,\n P = unknown\n> {\n /**\n * When true, every consumer will receive its own unique instance of this Bloc.\n * Use this when state should not be shared between components.\n * @default false\n */\n static isolated = false;\n get isIsolated() {\n return this._isolated;\n }\n \n /**\n * When true, the Bloc instance persists even when there are no active consumers.\n * Useful for maintaining state between component unmount/remount cycles.\n * @default false\n */\n static keepAlive = false;\n get isKeepAlive() {\n return this._keepAlive;\n }\n \n /**\n * Defines how dependencies are selected from the state for efficient updates.\n * When provided, observers will only be notified when selected dependencies change.\n */\n defaultDependencySelector: DependencySelector<S> | undefined;\n\n /**\n * @internal\n * Optional array of addons to extend the functionality of this Bloc.\n */\n public _addons?: BlacAddon[];\n \n /**\n * @internal\n * Indicates if this specific Bloc instance is isolated from others of the same type.\n */\n public _isolated = false;\n \n /**\n * @internal\n * Observable responsible for managing state listeners and notifying consumers.\n */\n public _observer: BlacObservable<S>;\n \n /**\n * @internal\n * Reference to the global Blac manager instance.\n */\n public _blac = Blac.getInstance();\n \n /**\n * The unique identifier for this Bloc instance.\n * Defaults to the class name, but can be customized.\n */\n public _id: BlocInstanceId;\n \n /**\n * @internal\n * Reference string used internally for tracking and debugging.\n */\n public _instanceRef?: string;\n \n /**\n * @internal\n * Indicates if this specific Bloc instance should be kept alive when no consumers are present.\n */\n public _keepAlive = false;\n \n /**\n * @readonly\n * Timestamp when this Bloc instance was created, useful for debugging and performance tracking.\n */\n public readonly _createdAt = Date.now();\n\n /**\n * @internal\n * The current state of the Bloc.\n */\n public _state: S;\n \n /**\n * @internal\n * The previous state of the Bloc, maintained for comparison and history.\n */\n public _oldState: S | undefined;\n \n /**\n * Props passed during Bloc instance creation.\n * Can be used to configure or parameterize the Bloc's behavior.\n */\n public props: P | null = null;\n\n /**\n * Creates a new BlocBase instance with the given initial state.\n * Sets up the observer, registers with the Blac manager, and initializes addons.\n * \n * @param initialState The initial state value for this Bloc\n */\n constructor(initialState: S) {\n this._state = initialState;\n this._observer = new BlacObservable(this);\n this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_CREATED, this);\n this._id = this.constructor.name;\n\n // Use a type assertion for the constructor to access static properties safely\n const constructorWithStaticProps = this.constructor as BlocConstructor<this> & BlocStaticProperties;\n\n this._keepAlive = constructorWithStaticProps.keepAlive;\n this._isolated = constructorWithStaticProps.isolated;\n this._addons = constructorWithStaticProps.addons;\n\n this._connectAddons();\n }\n\n /**\n * Returns the current state of the Bloc.\n * Use this getter to access the state in a read-only manner.\n */\n get state(): S {\n return this._state;\n }\n\n /**\n * @internal\n * Returns the name of the Bloc class for identification and debugging.\n */\n get _name() {\n return this.constructor.name;\n }\n\n /**\n * @internal\n * Updates the Bloc instance's ID to a new value.\n * Only updates if the new ID is defined and different from the current one.\n * \n * @param id The new ID to assign to this Bloc instance\n */\n _updateId = (id?: BlocInstanceId) => {\n const originalId = this._id;\n if (!id || id === originalId) return;\n this._id = id;\n };\n\n /**\n * @internal\n * Cleans up resources and removes this Bloc from the system.\n * Notifies the Blac manager and clears all observers.\n */\n _dispose() {\n this._observer.dispose();\n this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_DISPOSED, this);\n this.onDispose?.();\n }\n\n /**\n * @internal\n * Optional function to be called when the Bloc is disposed.\n */\n onDispose?: () => void;\n\n /**\n * @internal\n * Set of consumer IDs currently listening to this Bloc's state changes.\n */\n _consumers = new Set<string>();\n\n /**\n * @internal\n * Registers a new consumer to this Bloc instance.\n * Notifies the Blac manager that a consumer has been added.\n * \n * @param consumerId The unique ID of the consumer being added\n */\n _addConsumer = (consumerId: string) => {\n this._consumers.add(consumerId);\n this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_CONSUMER_ADDED, this, { consumerId });\n };\n\n /**\n * @internal\n * Unregisters a consumer from this Bloc instance.\n * Notifies the Blac manager that a consumer has been removed.\n * \n * @param consumerId The unique ID of the consumer being removed\n */\n _removeConsumer = (consumerId: string) => {\n this._blac.log(`[${this._name}:${String(this._id ?? 'default_id')}] Removing consumer: ${consumerId}`);\n this._consumers.delete(consumerId);\n this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_CONSUMER_REMOVED, this, { consumerId });\n };\n\n /**\n * @internal\n * Initializes all registered addons for this Bloc instance.\n * Calls the onInit lifecycle method on each addon if defined.\n */\n _connectAddons = () => {\n const { _addons: addons } = this;\n if (addons) {\n addons.forEach(addon => {\n addon.onInit?.(this);\n });\n }\n };\n\n lastUpdate = Date.now();\n\n /**\n * @internal\n * Updates the state and notifies all observers of the change.\n * \n * @param newState The new state to be set\n * @param oldState The previous state for comparison\n * @param action Optional metadata about what caused the state change\n */\n _pushState = (newState: S, oldState: S, action?: unknown): void => {\n this._state = newState;\n this._observer.notify(newState, oldState, action);\n this.lastUpdate = Date.now();\n };\n}\n","import { Blac } from './Blac';\nimport { BlocBase } from './BlocBase';\n\n/**\n * A Cubit is a simpler version of a Bloc that doesn't handle events.\n * It manages state and provides methods to update it.\n * @template S - The type of state this Cubit manages\n * @template P - The type of parameters (optional, defaults to null)\n */\nexport abstract class Cubit<S, P = null> extends BlocBase<S, P> {\n /**\n * Updates the current state and notifies all observers of the change.\n * If the new state is identical to the current state (using Object.is),\n * no update will occur.\n * @param state - The new state to set\n */\n emit(state: S): void {\n if (Object.is(state, this.state)) {\n return;\n }\n\n const oldState = this.state;\n const newState = state;\n this._pushState(newState, oldState);\n }\n\n /**\n * Partially updates the current state by merging it with the provided state patch.\n * This method is only applicable when the state is an object type.\n * \n * @param statePatch - A partial state object containing only the properties to update\n * @param ignoreChangeCheck - If true, skips checking if the state has actually changed\n * @throws {TypeError} If the state is not an object type\n */\n patch(\n statePatch: S extends object ? Partial<S> : S,\n ignoreChangeCheck = false,\n ): void {\n if (typeof this.state !== 'object' || this.state === null) {\n Blac.warn(\n 'Cubit.patch: was called on a cubit where the state is not an object. This is a no-op.',\n );\n return;\n }\n\n let changes = false;\n if (!ignoreChangeCheck) {\n for (const key in statePatch) {\n if (Object.prototype.hasOwnProperty.call(statePatch, key)) {\n const s = this.state;\n const current = s[key as keyof S];\n if (!Object.is(statePatch[key as keyof S], current)) {\n changes = true;\n break;\n }\n }\n }\n } else {\n changes = true;\n }\n\n if (changes) {\n this.emit({\n ...this.state,\n ...(statePatch as Partial<S>),\n } as S);\n }\n }\n}\n","import { Blac } from './Blac';\nimport { BlocBase } from './BlocBase';\n\n// A should be the base type for all events this Bloc handles and must be an object type\n// to access action.constructor. Events are typically class instances.\n// P is for props, changed from any to unknown.\nexport abstract class Bloc<\n S, // State type\n A extends object, // Base Action/Event type, constrained to object\n P = unknown // Props type\n> extends BlocBase<S, P> {\n // Stores handlers: Map<EventConstructor (subtype of A), HandlerFunction>\n // The handler's event parameter will be correctly typed to the specific EventConstructor\n // by the 'on' method's signature.\n readonly eventHandlers: Map<\n // Key: Constructor of a specific event E (where E extends A)\n // Using 'any[]' for constructor arguments for broader compatibility.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (...args: any[]) => A,\n // Value: Handler function. 'event: A' is used here for the stored function type.\n // The 'on' method ensures the specific handler (event: E) is correctly typed.\n (event: A, emit: (newState: S) => void) => void | Promise<void>\n > = new Map();\n\n /**\n * Registers an event handler for a specific event type.\n * This method is typically called in the constructor of a derived Bloc class.\n * @param eventConstructor The constructor of the event to handle (e.g., LoadDataEvent).\n * @param handler A function that processes the event and can emit new states.\n * The 'event' parameter in the handler will be typed to the specific eventConstructor.\n */\n protected on<E extends A>(\n // Using 'any[]' for constructor arguments for broader compatibility.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n eventConstructor: new (...args: any[]) => E, \n handler: (event: E, emit: (newState: S) => void) => void | Promise<void>\n ): void {\n if (this.eventHandlers.has(eventConstructor)) {\n // Using Blac.warn or a similar logging mechanism from BlocBase if available,\n // otherwise console.warn. Assuming this._name and this._id are available from BlocBase.\n Blac.warn(\n `[Bloc ${this._name}:${String(this._id)}] Handler for event '${eventConstructor.name}' already registered. It will be overwritten.`\n );\n }\n // Cast the specific handler (event: E) to a more general (event: A) for storage.\n // This is safe because E extends A. When the handler is called with an 'action' of type A,\n // if it was originally registered for type E, 'action' must be an instance of E.\n this.eventHandlers.set(\n eventConstructor,\n handler as (event: A, emit: (newState: S) => void) => void | Promise<void>\n );\n }\n\n /**\n * Dispatches an action/event to the Bloc.\n * If a handler is registered for this specific event type (via 'on'), it will be invoked.\n * Asynchronous handlers are awaited.\n * @param action The action/event instance to be processed.\n */\n public add = async (action: A): Promise<void> => {\n // Using 'any[]' for constructor arguments for broader compatibility.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const eventConstructor = action.constructor as new (...args: any[]) => A;\n const handler = this.eventHandlers.get(eventConstructor);\n\n if (handler) {\n // Define the 'emit' function that handlers will use to update state.\n // It captures the current state ('this.state') right before each emission\n // to provide the correct 'previousState' to _pushState.\n const emit = (newState: S): void => {\n const previousState = this.state; // State just before this specific emission\n // The 'action' passed to _pushState is the original action that triggered the handler,\n // providing context for the state change (e.g., for logging or plugins).\n this._pushState(newState, previousState, action);\n };\n\n try {\n // Await the handler in case it's an async function (e.g., performs API calls).\n // The 'action' is passed to the handler, and due to the way 'on' is typed,\n // the 'event' parameter within the handler function will be correctly\n // typed to its specific class (e.g., LoadMyFeatureData).\n await handler(action, emit);\n } catch (error) {\n // It's good practice to handle errors occurring within event handlers.\n Blac.error(\n `[Bloc ${this._name}:${String(this._id)}] Error in event handler for '${eventConstructor.name}':`,\n error,\n \"Action:\", action\n );\n // Depending on the desired error handling strategy, you might:\n // 1. Emit a specific error state: this.emit(new MyErrorState(error));\n // 2. Re-throw the error: throw error;\n // 3. Log and ignore (as done here by default).\n // This should be decided based on application requirements.\n }\n } else {\n // action.constructor.name should be safe due to 'A extends object' and common JS practice for constructors.\n // If linting still complains, it might be overly strict for this common pattern.\n const constructorName = (action.constructor as { name?: string }).name || 'UnnamedConstructor';\n Blac.warn(\n `[Bloc ${this._name}:${String(this._id)}] No handler registered for action type: '${constructorName}'. Action was:`,\n action\n );\n // If no handler is found, the action is effectively ignored.\n // Consider if this is the desired behavior or if an error should be thrown\n // or a default handler should be invoked.\n }\n };\n}\n","import { BlocBase, BlocInstanceId } from '../BlocBase';\nimport BlacAddon, { BlacAddonEmit, BlacAddonInit } from './BlacAddon';\n\ntype StorageType = 'localStorage' | 'sessionStorage';\n\nfunction getStorage(type: StorageType): Storage {\n switch (type) {\n case 'localStorage':\n return localStorage;\n case 'sessionStorage':\n return sessionStorage;\n default:\n return localStorage;\n }\n}\n\n/**\n * Persist addon\n *\n * @param options\n * @returns BlacAddon\n */\nexport function Persist(\n options: {\n /**\n * @default 'blac'\n */\n keyPrefix?: string;\n /**\n * @default the bloc's id\n */\n keyName?: string;\n /**\n * Used when the value is not found in storage\n */\n defaultValue?: unknown;\n\n /**\n * @default 'localStorage'\n * @see StorageType\n */\n storageType?: StorageType;\n\n /**\n * @default false\n */\n onError?: (e: unknown) => void;\n } = {},\n): BlacAddon {\n const {\n keyPrefix = 'blac',\n keyName,\n defaultValue,\n storageType = 'localStorage',\n } = options;\n\n const fullKey = (id: string | BlocInstanceId) => `${keyPrefix}:${String(id)}`;\n\n const getFromLocalStorage = (id: string | BlocInstanceId): unknown => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n const value = getStorage(storageType).getItem(fullKey(id));\n if (typeof value !== 'string') {\n return defaultValue;\n }\n\n const p = JSON.parse(value) as { v: unknown };\n if (typeof p.v !== 'undefined') {\n return p.v;\n } else {\n return defaultValue;\n }\n } catch (e) {\n options.onError?.(e);\n return defaultValue;\n }\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const onInit: BlacAddonInit = (e: BlocBase<any>) => {\n const id = keyName ?? e._id;\n\n const value = getFromLocalStorage(id);\n e._pushState(value, null);\n };\n\n let currentCachedValue = '';\n const onEmit: BlacAddonEmit = ({ newState, cubit }) => {\n const id = keyName ?? cubit._id;\n\n const newValue = JSON.stringify({ v: newState });\n\n if (newValue !== currentCachedValue) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access\n getStorage(storageType).setItem(fullKey(id), newValue);\n currentCachedValue = newValue;\n }\n };\n\n return {\n name: 'Persist',\n onInit,\n onEmit,\n };\n}\n"],"names":["BlacLifecycleEvent","_Blac","options","__publicField","args","message","oldBlocInstanceMap","oldIsolatedBlocMap","bloc","blocArray","plugin","p","event","params","base","key","blocClass","id","blocId","isolatedBloc","registeredBloc","isIsolated","registered","results","blocInstance","isolatedBlocs","__unsafe_ignore_singleton","blocClassName","found","blocs","index","b","props","instanceRef","newBloc","Blac","BlacObservable","observer","newState","oldState","action","shouldUpdate","lastDependencyCheck","newDependencyCheck","o","partNew","partOld","i","_a","BlocBase","initialState","originalId","consumerId","addons","addon","constructorWithStaticProps","Cubit","state","statePatch","ignoreChangeCheck","changes","current","Bloc","eventConstructor","handler","emit","previousState","error","constructorName","getStorage","type","Persist","keyPrefix","keyName","defaultValue","storageType","fullKey","getFromLocalStorage","value","e","onInit","currentCachedValue","cubit","newValue"],"mappings":"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"} |
+505
| /** | ||
| * Main Blac class that manages the state management system. | ||
| * Implements a singleton pattern to ensure only one instance exists. | ||
| * Handles bloc lifecycle, plugin management, and instance tracking. | ||
| * | ||
| * Key responsibilities: | ||
| * - Managing bloc instances (creation, disposal, lookup) | ||
| * - Handling isolated and non-isolated blocs | ||
| * - Managing plugins and lifecycle events | ||
| * - Providing logging and debugging capabilities | ||
| */ | ||
| export declare class Blac { | ||
| /** The singleton instance of Blac */ | ||
| static instance: Blac; | ||
| /** Timestamp when the instance was created */ | ||
| createdAt: number; | ||
| static getAllBlocs: <B extends BlocConstructor<unknown>>(blocClass: B, options?: { | ||
| searchIsolated?: boolean; | ||
| }) => InstanceType<B>[]; | ||
| static addPlugin: (plugin: BlacPlugin) => void; | ||
| /** Map storing all registered bloc instances by their class name and ID */ | ||
| blocInstanceMap: Map<string, BlocBase<any>>; | ||
| /** Map storing isolated bloc instances grouped by their constructor */ | ||
| isolatedBlocMap: Map<BlocConstructor<any>, BlocBase<any>[]>; | ||
| pluginList: BlacPlugin[]; | ||
| /** Flag to control whether changes should be posted to document */ | ||
| postChangesToDocument: boolean; | ||
| /** | ||
| * Creates a new Blac instance. | ||
| * @param options - Configuration options including singleton control | ||
| */ | ||
| constructor(options?: { | ||
| __unsafe_ignore_singleton?: boolean; | ||
| }); | ||
| /** Flag to enable/disable logging */ | ||
| static enableLog: boolean; | ||
| /** | ||
| * Logs messages to console when logging is enabled | ||
| * @param args - Arguments to log | ||
| */ | ||
| log: (...args: unknown[]) => void; | ||
| static log: (...args: unknown[]) => void; | ||
| /** | ||
| * Gets the singleton instance of Blac | ||
| * @returns The Blac instance | ||
| */ | ||
| static getInstance(): Blac; | ||
| /** | ||
| * Logs a warning message | ||
| * @param message - Warning message | ||
| * @param args - Additional arguments | ||
| */ | ||
| warn: (message: string, ...args: unknown[]) => void; | ||
| static warn: (message: string, ...args: unknown[]) => void; | ||
| /** | ||
| * Logs an error message | ||
| * @param message - Error message | ||
| * @param args - Additional arguments | ||
| */ | ||
| error: (message: string, ...args: unknown[]) => void; | ||
| static error: (message: string, ...args: unknown[]) => void; | ||
| /** | ||
| * Resets the Blac instance to a new one, disposing non-keepAlive blocs | ||
| * from the old instance. | ||
| */ | ||
| resetInstance: () => void; | ||
| static resetInstance: () => void; | ||
| /** | ||
| * Adds a plugin to the Blac instance | ||
| * @param plugin - The plugin to add | ||
| */ | ||
| addPlugin: (plugin: BlacPlugin) => void; | ||
| /** | ||
| * Dispatches a lifecycle event to all registered plugins | ||
| * @param event - The lifecycle event to dispatch | ||
| * @param bloc - The bloc instance involved in the event | ||
| * @param params - Additional parameters for the event | ||
| */ | ||
| dispatchEventToPlugins: (event: BlacLifecycleEvent, bloc: BlocBase<any>, params?: unknown) => void; | ||
| /** | ||
| * Dispatches a lifecycle event and handles related cleanup actions. | ||
| * This method is responsible for: | ||
| * - Logging the event | ||
| * - Handling bloc disposal when needed | ||
| * - Managing bloc consumer cleanup | ||
| * - Forwarding the event to plugins | ||
| * | ||
| * @param event - The lifecycle event to dispatch | ||
| * @param bloc - The bloc instance involved in the event | ||
| * @param params - Additional parameters for the event | ||
| */ | ||
| dispatchEvent: (event: BlacLifecycleEvent, bloc: BlocBase<any>, params?: unknown) => void; | ||
| /** | ||
| * Disposes of a bloc instance by removing it from the appropriate registry | ||
| * @param bloc - The bloc instance to dispose | ||
| */ | ||
| disposeBloc: (bloc: BlocBase<any>) => void; | ||
| /** | ||
| * Creates a unique key for a bloc instance in the map based on the bloc class name and instance ID | ||
| * @param blocClassName - The name of the bloc class | ||
| * @param id - The instance ID | ||
| * @returns A unique key string in the format "className:id" | ||
| */ | ||
| createBlocInstanceMapKey(blocClassName: string, id: BlocInstanceId): string; | ||
| /** | ||
| * Unregister a bloc instance from the main registry | ||
| * @param bloc - The bloc instance to unregister | ||
| */ | ||
| unregisterBlocInstance(bloc: BlocBase<any>): void; | ||
| /** | ||
| * Registers a bloc instance in the main registry | ||
| * @param bloc - The bloc instance to register | ||
| */ | ||
| registerBlocInstance(bloc: BlocBase<any>): void; | ||
| /** | ||
| * Finds a registered bloc instance by its class and ID | ||
| * @param blocClass - The bloc class to search for | ||
| * @param id - The instance ID | ||
| * @returns The found bloc instance or undefined if not found | ||
| */ | ||
| findRegisteredBlocInstance<B extends BlocConstructor<unknown>>(blocClass: B, id: BlocInstanceId): InstanceType<B> | undefined; | ||
| /** | ||
| * Registers an isolated bloc instance in the isolated registry | ||
| * @param bloc - The isolated bloc instance to register | ||
| */ | ||
| registerIsolatedBlocInstance(bloc: BlocBase<any>): void; | ||
| /** | ||
| * Unregister an isolated bloc instance from the isolated registry | ||
| * @param bloc - The isolated bloc instance to unregister | ||
| */ | ||
| unregisterIsolatedBlocInstance(bloc: BlocBase<any>): void; | ||
| /** | ||
| * Finds an isolated bloc instance by its class and ID | ||
| */ | ||
| findIsolatedBlocInstance<B extends BlocConstructor<unknown>>(blocClass: B, id: BlocInstanceId): InstanceType<B> | undefined; | ||
| /** | ||
| * Creates a new bloc instance and registers it in the appropriate registry | ||
| * @param blocClass - The bloc class to instantiate | ||
| * @param id - The instance ID | ||
| * @param props - Properties to pass to the bloc constructor | ||
| * @param instanceRef - Optional reference string for the instance | ||
| * @returns The newly created bloc instance | ||
| */ | ||
| createNewBlocInstance<B extends BlocConstructor<BlocBase<any>>>(blocClass: B, id: BlocInstanceId, options?: GetBlocOptions<InstanceType<B>>): InstanceType<B>; | ||
| /** | ||
| * Gets or creates a bloc instance based on the provided class and options. | ||
| * If a bloc with the given ID exists, it will be returned. Otherwise, a new instance will be created. | ||
| * | ||
| * @param blocClass - The bloc class to get or create | ||
| * @param options - Options including: | ||
| * - id: The instance ID (defaults to class name if not provided) | ||
| * - props: Properties to pass to the bloc constructor | ||
| * - instanceRef: Optional reference string for the instance | ||
| * @returns The bloc instance | ||
| */ | ||
| getBloc: <B extends BlocConstructor<BlocBase<any>>>(blocClass: B, options?: GetBlocOptions<InstanceType<B>>) => InstanceType<B>; | ||
| static getBloc: <B extends BlocConstructor<BlocBase<any>>>(blocClass: B, options?: GetBlocOptions<InstanceType<B>>) => InstanceType<B>; | ||
| /** | ||
| * Gets a bloc instance or throws an error if it doesn't exist | ||
| * @param blocClass - The bloc class to get | ||
| * @param options - Options including: | ||
| * - id: The instance ID (defaults to class name if not provided) | ||
| * - props: Properties to pass to the bloc constructor | ||
| * - instanceRef: Optional reference string for the instance | ||
| */ | ||
| getBlocOrThrow: <B extends BlocConstructor<unknown>>(blocClass: B, options?: { | ||
| id?: BlocInstanceId; | ||
| props?: InferPropsFromGeneric<B>; | ||
| instanceRef?: string; | ||
| }) => InstanceType<B>; | ||
| static getBlocOrThrow: <B extends BlocConstructor<unknown>>(blocClass: B, options?: { | ||
| id?: BlocInstanceId; | ||
| props?: InferPropsFromGeneric<B> | undefined; | ||
| instanceRef?: string; | ||
| }) => InstanceType<B>; | ||
| /** | ||
| * Gets all instances of a specific bloc class | ||
| * @param blocClass - The bloc class to search for | ||
| * @param options - Options including: | ||
| * - searchIsolated: Whether to search in isolated blocs (defaults to bloc's isolated property) | ||
| * @returns Array of matching bloc instances | ||
| */ | ||
| getAllBlocs: <B extends BlocConstructor<unknown>>(blocClass: B, options?: { | ||
| searchIsolated?: boolean; | ||
| }) => InstanceType<B>[]; | ||
| } | ||
| declare type BlacAddon = { | ||
| name: string; | ||
| onInit?: BlacAddonInit; | ||
| onEmit?: BlacAddonEmit; | ||
| }; | ||
| declare type BlacAddonEmit = (params: { | ||
| oldState: unknown; | ||
| newState: unknown; | ||
| cubit: BlocBase<any>; | ||
| }) => void; | ||
| declare type BlacAddonInit = (bloc: BlocBase<any>) => void; | ||
| /** | ||
| * Configuration options for the Blac instance | ||
| */ | ||
| export declare interface BlacConfig { | ||
| /** Whether to expose the Blac instance globally */ | ||
| exposeBlacInstance?: boolean; | ||
| } | ||
| /** | ||
| * Enum representing different lifecycle events that can occur in the Blac system. | ||
| * These events are used to track the lifecycle of blocs and their consumers. | ||
| */ | ||
| export declare enum BlacLifecycleEvent { | ||
| BLOC_DISPOSED = "BLOC_DISPOSED", | ||
| BLOC_CREATED = "BLOC_CREATED", | ||
| LISTENER_REMOVED = "LISTENER_REMOVED", | ||
| LISTENER_ADDED = "LISTENER_ADDED", | ||
| STATE_CHANGED = "STATE_CHANGED", | ||
| BLOC_CONSUMER_REMOVED = "BLOC_CONSUMER_REMOVED", | ||
| BLOC_CONSUMER_ADDED = "BLOC_CONSUMER_ADDED" | ||
| } | ||
| /** | ||
| * A class that manages observers for a Bloc's state changes | ||
| * @template S - The type of state being observed | ||
| */ | ||
| export declare class BlacObservable<S = unknown> { | ||
| /** The Bloc instance this observable is associated with */ | ||
| bloc: BlocBase<S>; | ||
| /** | ||
| * Creates a new BlacObservable instance | ||
| * @param bloc - The Bloc instance to observe | ||
| */ | ||
| constructor(bloc: BlocBase<S>); | ||
| private _observers; | ||
| /** | ||
| * Gets the number of active observers | ||
| * @returns The number of observers currently subscribed | ||
| */ | ||
| get size(): number; | ||
| /** | ||
| * Gets the set of all observers | ||
| * @returns The Set of all BlacObserver instances | ||
| */ | ||
| get observers(): Set<BlacObserver<S>>; | ||
| /** | ||
| * Subscribes an observer to state changes | ||
| * @param observer - The observer to subscribe | ||
| * @returns A function that can be called to unsubscribe the observer | ||
| */ | ||
| subscribe(observer: BlacObserver<S>): () => void; | ||
| /** | ||
| * Unsubscribes an observer from state changes | ||
| * @param observer - The observer to unsubscribe | ||
| */ | ||
| unsubscribe(observer: BlacObserver<S>): void; | ||
| /** | ||
| * Notifies all observers of a state change | ||
| * @param newState - The new state value | ||
| * @param oldState - The previous state value | ||
| * @param action - Optional action that triggered the state change | ||
| */ | ||
| notify(newState: S, oldState: S, action?: unknown): void; | ||
| /** | ||
| * Disposes of all observers and clears the observer set | ||
| */ | ||
| dispose(): void; | ||
| } | ||
| /** | ||
| * Represents an observer that can subscribe to state changes in a Bloc | ||
| * @template S - The type of state being observed | ||
| */ | ||
| export declare type BlacObserver<S> = { | ||
| /** Function to be called when state changes */ | ||
| fn: (newState: S, oldState: S, action?: unknown) => void | Promise<void>; | ||
| /** Optional function to determine if the observer should be notified of state changes */ | ||
| dependencyArray?: BlocHookDependencyArrayFn<S>; | ||
| /** Dispose function for the observer */ | ||
| dispose?: () => void; | ||
| /** Cached state values used for dependency comparison */ | ||
| lastState?: unknown[][]; | ||
| /** Unique identifier for the observer */ | ||
| id: string; | ||
| }; | ||
| export declare interface BlacPlugin { | ||
| name: string; | ||
| onEvent(event: BlacLifecycleEvent, bloc: BlocBase<any>, params?: unknown): void; | ||
| } | ||
| export declare abstract class Bloc<S, // State type | ||
| A extends object, // Base Action/Event type, constrained to object | ||
| P = unknown> extends BlocBase<S, P> { | ||
| readonly eventHandlers: Map<new (...args: any[]) => A, (event: A, emit: (newState: S) => void) => void | Promise<void>>; | ||
| /** | ||
| * Registers an event handler for a specific event type. | ||
| * This method is typically called in the constructor of a derived Bloc class. | ||
| * @param eventConstructor The constructor of the event to handle (e.g., LoadDataEvent). | ||
| * @param handler A function that processes the event and can emit new states. | ||
| * The 'event' parameter in the handler will be typed to the specific eventConstructor. | ||
| */ | ||
| protected on<E extends A>(eventConstructor: new (...args: any[]) => E, handler: (event: E, emit: (newState: S) => void) => void | Promise<void>): void; | ||
| /** | ||
| * Dispatches an action/event to the Bloc. | ||
| * If a handler is registered for this specific event type (via 'on'), it will be invoked. | ||
| * Asynchronous handlers are awaited. | ||
| * @param action The action/event instance to be processed. | ||
| */ | ||
| add: (action: A) => Promise<void>; | ||
| } | ||
| /** | ||
| * Base class for both Blocs and Cubits that provides core state management functionality. | ||
| * Handles state transitions, observer notifications, lifecycle management, and addon integration. | ||
| * | ||
| * @abstract This class should be extended, not instantiated directly | ||
| * @template S The type of state managed by this Bloc | ||
| * @template P The type of props that can be passed during instance creation (optional) | ||
| */ | ||
| export declare abstract class BlocBase<S, P = unknown> { | ||
| /** | ||
| * When true, every consumer will receive its own unique instance of this Bloc. | ||
| * Use this when state should not be shared between components. | ||
| * @default false | ||
| */ | ||
| static isolated: boolean; | ||
| get isIsolated(): boolean; | ||
| /** | ||
| * When true, the Bloc instance persists even when there are no active consumers. | ||
| * Useful for maintaining state between component unmount/remount cycles. | ||
| * @default false | ||
| */ | ||
| static keepAlive: boolean; | ||
| get isKeepAlive(): boolean; | ||
| /** | ||
| * Defines how dependencies are selected from the state for efficient updates. | ||
| * When provided, observers will only be notified when selected dependencies change. | ||
| */ | ||
| defaultDependencySelector: DependencySelector<S> | undefined; | ||
| /* Excluded from this release type: _addons */ | ||
| /* Excluded from this release type: _isolated */ | ||
| /* Excluded from this release type: _observer */ | ||
| /* Excluded from this release type: _blac */ | ||
| /** | ||
| * The unique identifier for this Bloc instance. | ||
| * Defaults to the class name, but can be customized. | ||
| */ | ||
| _id: BlocInstanceId; | ||
| /* Excluded from this release type: _instanceRef */ | ||
| /* Excluded from this release type: _keepAlive */ | ||
| /** | ||
| * @readonly | ||
| * Timestamp when this Bloc instance was created, useful for debugging and performance tracking. | ||
| */ | ||
| readonly _createdAt: number; | ||
| /* Excluded from this release type: _state */ | ||
| /* Excluded from this release type: _oldState */ | ||
| /** | ||
| * Props passed during Bloc instance creation. | ||
| * Can be used to configure or parameterize the Bloc's behavior. | ||
| */ | ||
| props: P | null; | ||
| /** | ||
| * Creates a new BlocBase instance with the given initial state. | ||
| * Sets up the observer, registers with the Blac manager, and initializes addons. | ||
| * | ||
| * @param initialState The initial state value for this Bloc | ||
| */ | ||
| constructor(initialState: S); | ||
| /** | ||
| * Returns the current state of the Bloc. | ||
| * Use this getter to access the state in a read-only manner. | ||
| */ | ||
| get state(): S; | ||
| /* Excluded from this release type: _name */ | ||
| /* Excluded from this release type: _updateId */ | ||
| /* Excluded from this release type: _dispose */ | ||
| /* Excluded from this release type: onDispose */ | ||
| /* Excluded from this release type: _consumers */ | ||
| /* Excluded from this release type: _addConsumer */ | ||
| /* Excluded from this release type: _removeConsumer */ | ||
| /* Excluded from this release type: _connectAddons */ | ||
| lastUpdate: number; | ||
| /* Excluded from this release type: _pushState */ | ||
| } | ||
| /** | ||
| * Represents the abstract base types for Bloc and Cubit | ||
| */ | ||
| export declare type BlocBaseAbstract = typeof Bloc<any, any, any> | typeof Cubit<any, any>; | ||
| /** | ||
| * Represents a constructor type for a Bloc that takes no parameters | ||
| * @template B - The type of the Bloc instance | ||
| */ | ||
| export declare type BlocClassNoParams<B> = new (args: never[]) => B; | ||
| /** | ||
| * Represents a constructor type for a Bloc that can take any parameters | ||
| * @template B - The type of the Bloc instance | ||
| */ | ||
| export declare type BlocConstructor<B> = new (...args: any) => B; | ||
| /** | ||
| * Extracts the constructor parameters type from a BlocBase | ||
| * @template B - The BlocBase type to extract the constructor parameters from | ||
| */ | ||
| export declare type BlocConstructorParameters<B extends BlocBase<any>> = BlocConstructor<B> extends new (...args: infer P) => B ? P : never; | ||
| /** | ||
| * Represents a function type for determining hook dependencies based on state changes | ||
| * @template B - The BlocGeneric type | ||
| */ | ||
| export declare type BlocHookDependencyArrayFn<S> = (newState: S, oldState: S) => unknown[][]; | ||
| export declare type BlocInstanceId = string | number | undefined; | ||
| /** | ||
| * Extracts the state type from either a Bloc or Cubit | ||
| * @template T - The Bloc or Cubit type to extract the state from | ||
| */ | ||
| export declare type BlocState<T> = T extends BlocBase<infer S> ? S : never; | ||
| /** | ||
| * A Cubit is a simpler version of a Bloc that doesn't handle events. | ||
| * It manages state and provides methods to update it. | ||
| * @template S - The type of state this Cubit manages | ||
| * @template P - The type of parameters (optional, defaults to null) | ||
| */ | ||
| export declare abstract class Cubit<S, P = null> extends BlocBase<S, P> { | ||
| /** | ||
| * Updates the current state and notifies all observers of the change. | ||
| * If the new state is identical to the current state (using Object.is), | ||
| * no update will occur. | ||
| * @param state - The new state to set | ||
| */ | ||
| emit(state: S): void; | ||
| /** | ||
| * Partially updates the current state by merging it with the provided state patch. | ||
| * This method is only applicable when the state is an object type. | ||
| * | ||
| * @param statePatch - A partial state object containing only the properties to update | ||
| * @param ignoreChangeCheck - If true, skips checking if the state has actually changed | ||
| * @throws {TypeError} If the state is not an object type | ||
| */ | ||
| patch(statePatch: S extends object ? Partial<S> : S, ignoreChangeCheck?: boolean): void; | ||
| } | ||
| declare type DependencySelector<S> = (newState: S, oldState?: S) => unknown[][]; | ||
| export declare interface GetBlocOptions<B extends BlocBase<any>> { | ||
| id?: string; | ||
| dependencySelector?: BlocHookDependencyArrayFn<BlocState<B>>; | ||
| props?: InferPropsFromGeneric<B>; | ||
| onMount?: (bloc: B) => void; | ||
| instanceRef?: string; | ||
| } | ||
| /** | ||
| * Extracts the props type from either a Bloc or Cubit | ||
| * @template T - The Bloc or Cubit type to extract the props from | ||
| */ | ||
| export declare type InferPropsFromGeneric<T> = T extends Bloc<any, any, infer P> ? P : T extends Cubit<any, infer P> ? P : never; | ||
| /** | ||
| * Persist addon | ||
| * | ||
| * @param options | ||
| * @returns BlacAddon | ||
| */ | ||
| export declare function Persist(options?: { | ||
| /** | ||
| * @default 'blac' | ||
| */ | ||
| keyPrefix?: string; | ||
| /** | ||
| * @default the bloc's id | ||
| */ | ||
| keyName?: string; | ||
| /** | ||
| * Used when the value is not found in storage | ||
| */ | ||
| defaultValue?: unknown; | ||
| /** | ||
| * @default 'localStorage' | ||
| * @see StorageType | ||
| */ | ||
| storageType?: StorageType; | ||
| /** | ||
| * @default false | ||
| */ | ||
| onError?: (e: unknown) => void; | ||
| }): BlacAddon; | ||
| declare type StorageType = 'localStorage' | 'sessionStorage'; | ||
| /** | ||
| * Extracts the state type from a BlocBase instance | ||
| * @template B - The BlocBase type to extract the state from | ||
| */ | ||
| export declare type ValueType<B extends BlocBase<any>> = B extends BlocBase<infer U> ? U : never; | ||
| export { } |
+670
| var B = Object.defineProperty; | ||
| var m = (c, t, e) => t in c ? B(c, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[t] = e; | ||
| var n = (c, t, e) => m(c, typeof t != "symbol" ? t + "" : t, e); | ||
| var _ = /* @__PURE__ */ ((c) => (c.BLOC_DISPOSED = "BLOC_DISPOSED", c.BLOC_CREATED = "BLOC_CREATED", c.LISTENER_REMOVED = "LISTENER_REMOVED", c.LISTENER_ADDED = "LISTENER_ADDED", c.STATE_CHANGED = "STATE_CHANGED", c.BLOC_CONSUMER_REMOVED = "BLOC_CONSUMER_REMOVED", c.BLOC_CONSUMER_ADDED = "BLOC_CONSUMER_ADDED", c))(_ || {}); | ||
| const r = class r { | ||
| /** | ||
| * Creates a new Blac instance. | ||
| * @param options - Configuration options including singleton control | ||
| */ | ||
| constructor(t = {}) { | ||
| /** Timestamp when the instance was created */ | ||
| n(this, "createdAt", Date.now()); | ||
| /** Map storing all registered bloc instances by their class name and ID */ | ||
| n(this, "blocInstanceMap", /* @__PURE__ */ new Map()); | ||
| /** Map storing isolated bloc instances grouped by their constructor */ | ||
| n(this, "isolatedBlocMap", /* @__PURE__ */ new Map()); | ||
| n(this, "pluginList", []); | ||
| /** Flag to control whether changes should be posted to document */ | ||
| n(this, "postChangesToDocument", !1); | ||
| /** | ||
| * Logs messages to console when logging is enabled | ||
| * @param args - Arguments to log | ||
| */ | ||
| n(this, "log", (...t) => { | ||
| r.enableLog && console.warn(`☢️ [Blac ${this.createdAt.toString()}]`, ...t); | ||
| }); | ||
| /** | ||
| * Logs a warning message | ||
| * @param message - Warning message | ||
| * @param args - Additional arguments | ||
| */ | ||
| n(this, "warn", (t, ...e) => { | ||
| r.enableLog && console.warn(`🚨 [Blac ${String(r.instance.createdAt)}]`, t, ...e); | ||
| }); | ||
| /** | ||
| * Logs an error message | ||
| * @param message - Error message | ||
| * @param args - Additional arguments | ||
| */ | ||
| n(this, "error", (t, ...e) => { | ||
| r.enableLog && console.error(`🚨 [Blac ${String(r.instance.createdAt)}]`, t, ...e); | ||
| }); | ||
| /** | ||
| * Resets the Blac instance to a new one, disposing non-keepAlive blocs | ||
| * from the old instance. | ||
| */ | ||
| n(this, "resetInstance", () => { | ||
| this.log("Reset Blac instance"); | ||
| const t = new Map(this.blocInstanceMap), e = new Map(this.isolatedBlocMap); | ||
| t.forEach((s) => { | ||
| s._dispose(); | ||
| }), e.forEach((s) => { | ||
| s.forEach((i) => { | ||
| i._dispose(); | ||
| }); | ||
| }), this.blocInstanceMap.clear(), this.isolatedBlocMap.clear(), r.instance = new r({ | ||
| __unsafe_ignore_singleton: !0 | ||
| }); | ||
| }); | ||
| /** | ||
| * Adds a plugin to the Blac instance | ||
| * @param plugin - The plugin to add | ||
| */ | ||
| n(this, "addPlugin", (t) => { | ||
| this.pluginList.findIndex((s) => s.name === t.name) === -1 && (this.log("Add plugin", t.name), this.pluginList.push(t)); | ||
| }); | ||
| /** | ||
| * Dispatches a lifecycle event to all registered plugins | ||
| * @param event - The lifecycle event to dispatch | ||
| * @param bloc - The bloc instance involved in the event | ||
| * @param params - Additional parameters for the event | ||
| */ | ||
| n(this, "dispatchEventToPlugins", (t, e, s) => { | ||
| this.pluginList.forEach((i) => { | ||
| i.onEvent(t, e, s); | ||
| }); | ||
| }); | ||
| /** | ||
| * Dispatches a lifecycle event and handles related cleanup actions. | ||
| * This method is responsible for: | ||
| * - Logging the event | ||
| * - Handling bloc disposal when needed | ||
| * - Managing bloc consumer cleanup | ||
| * - Forwarding the event to plugins | ||
| * | ||
| * @param event - The lifecycle event to dispatch | ||
| * @param bloc - The bloc instance involved in the event | ||
| * @param params - Additional parameters for the event | ||
| */ | ||
| n(this, "dispatchEvent", (t, e, s) => { | ||
| switch (this.log(t, e, s), t) { | ||
| case "BLOC_DISPOSED": | ||
| this.disposeBloc(e); | ||
| break; | ||
| case "BLOC_CONSUMER_REMOVED": | ||
| case "LISTENER_REMOVED": | ||
| this.log(`[${e._name}:${String(e._id)}] Listener/Consumer removed. Listeners: ${String(e._observer.size)}, Consumers: ${String(e._consumers.size)}, KeepAlive: ${String(e._keepAlive)}`), e._observer.size === 0 && e._consumers.size === 0 && !e._keepAlive && (this.log(`[${e._name}:${String(e._id)}] No listeners or consumers left and not keepAlive. Disposing.`), e._dispose()); | ||
| break; | ||
| } | ||
| this.dispatchEventToPlugins(t, e, s); | ||
| }); | ||
| /** | ||
| * Disposes of a bloc instance by removing it from the appropriate registry | ||
| * @param bloc - The bloc instance to dispose | ||
| */ | ||
| n(this, "disposeBloc", (t) => { | ||
| const e = t.constructor, s = this.createBlocInstanceMapKey(t._name, t._id); | ||
| this.log(`[${t._name}:${String(t._id)}] disposeBloc called. Isolated: ${String(e.isolated)}`), e.isolated ? (this.unregisterIsolatedBlocInstance(t), this.blocInstanceMap.delete(s)) : this.unregisterBlocInstance(t), this.dispatchEventToPlugins("BLOC_DISPOSED", t); | ||
| }); | ||
| /** | ||
| * Gets or creates a bloc instance based on the provided class and options. | ||
| * If a bloc with the given ID exists, it will be returned. Otherwise, a new instance will be created. | ||
| * | ||
| * @param blocClass - The bloc class to get or create | ||
| * @param options - Options including: | ||
| * - id: The instance ID (defaults to class name if not provided) | ||
| * - props: Properties to pass to the bloc constructor | ||
| * - instanceRef: Optional reference string for the instance | ||
| * @returns The bloc instance | ||
| */ | ||
| n(this, "getBloc", (t, e = {}) => { | ||
| const { id: s } = e, i = t, a = s ?? t.name; | ||
| if (this.log(`[${t.name}:${String(a)}] getBloc called. Options:`, e), i.isolated) { | ||
| const o = this.findIsolatedBlocInstance(t, a); | ||
| if (o) | ||
| return this.log(`[${t.name}:${String(a)}] Found existing isolated instance.`), o; | ||
| } | ||
| if (!i.isolated) { | ||
| const o = this.findRegisteredBlocInstance(t, a); | ||
| if (o) | ||
| return this.log(`[${t.name}:${String(a)}] Found existing registered instance.`), o; | ||
| } | ||
| return this.log(`[${t.name}:${String(a)}] No existing instance found. Creating new one.`), this.createNewBlocInstance( | ||
| t, | ||
| a, | ||
| e | ||
| ); | ||
| }); | ||
| /** | ||
| * Gets a bloc instance or throws an error if it doesn't exist | ||
| * @param blocClass - The bloc class to get | ||
| * @param options - Options including: | ||
| * - id: The instance ID (defaults to class name if not provided) | ||
| * - props: Properties to pass to the bloc constructor | ||
| * - instanceRef: Optional reference string for the instance | ||
| */ | ||
| n(this, "getBlocOrThrow", (t, e = {}) => { | ||
| const s = t.isolated, i = e.id || t.name, a = s ? this.findIsolatedBlocInstance(t, i) : this.findRegisteredBlocInstance(t, i); | ||
| if (a) | ||
| return a; | ||
| throw new Error(`Bloc ${t.name} not found`); | ||
| }); | ||
| /** | ||
| * Gets all instances of a specific bloc class | ||
| * @param blocClass - The bloc class to search for | ||
| * @param options - Options including: | ||
| * - searchIsolated: Whether to search in isolated blocs (defaults to bloc's isolated property) | ||
| * @returns Array of matching bloc instances | ||
| */ | ||
| n(this, "getAllBlocs", (t, e = {}) => { | ||
| const s = []; | ||
| if (this.blocInstanceMap.forEach((i) => { | ||
| i.constructor === t && s.push(i); | ||
| }), e.searchIsolated !== !1) { | ||
| const i = this.isolatedBlocMap.get(t); | ||
| i && s.push(...i.map((a) => a)); | ||
| } | ||
| return s; | ||
| }); | ||
| const { __unsafe_ignore_singleton: e = !1 } = t; | ||
| if (!e) | ||
| return r.instance; | ||
| r.instance = this; | ||
| } | ||
| /** | ||
| * Gets the singleton instance of Blac | ||
| * @returns The Blac instance | ||
| */ | ||
| static getInstance() { | ||
| return r.instance; | ||
| } | ||
| /** | ||
| * Creates a unique key for a bloc instance in the map based on the bloc class name and instance ID | ||
| * @param blocClassName - The name of the bloc class | ||
| * @param id - The instance ID | ||
| * @returns A unique key string in the format "className:id" | ||
| */ | ||
| createBlocInstanceMapKey(t, e) { | ||
| return `${t}:${String(e)}`; | ||
| } | ||
| /** | ||
| * Unregister a bloc instance from the main registry | ||
| * @param bloc - The bloc instance to unregister | ||
| */ | ||
| unregisterBlocInstance(t) { | ||
| const e = this.createBlocInstanceMapKey(t._name, t._id); | ||
| this.blocInstanceMap.delete(e); | ||
| } | ||
| /** | ||
| * Registers a bloc instance in the main registry | ||
| * @param bloc - The bloc instance to register | ||
| */ | ||
| registerBlocInstance(t) { | ||
| const e = this.createBlocInstanceMapKey(t._name, t._id); | ||
| this.blocInstanceMap.set(e, t); | ||
| } | ||
| /** | ||
| * Finds a registered bloc instance by its class and ID | ||
| * @param blocClass - The bloc class to search for | ||
| * @param id - The instance ID | ||
| * @returns The found bloc instance or undefined if not found | ||
| */ | ||
| findRegisteredBlocInstance(t, e) { | ||
| if (t.isolated) return; | ||
| const i = this.createBlocInstanceMapKey(t.name, e), a = this.blocInstanceMap.get(i); | ||
| return a && this.log(`[${t.name}:${String(e)}] Found registered instance. Returning.`), a; | ||
| } | ||
| /** | ||
| * Registers an isolated bloc instance in the isolated registry | ||
| * @param bloc - The isolated bloc instance to register | ||
| */ | ||
| registerIsolatedBlocInstance(t) { | ||
| const e = t.constructor, s = this.isolatedBlocMap.get(e); | ||
| s ? s.push(t) : this.isolatedBlocMap.set(e, [t]); | ||
| } | ||
| /** | ||
| * Unregister an isolated bloc instance from the isolated registry | ||
| * @param bloc - The isolated bloc instance to unregister | ||
| */ | ||
| unregisterIsolatedBlocInstance(t) { | ||
| const e = t.constructor, s = this.isolatedBlocMap.get(e); | ||
| if (s) { | ||
| const i = s.findIndex((a) => a._id === t._id); | ||
| i !== -1 && s.splice(i, 1), s.length === 0 && this.isolatedBlocMap.delete(e); | ||
| } | ||
| } | ||
| /** | ||
| * Finds an isolated bloc instance by its class and ID | ||
| */ | ||
| findIsolatedBlocInstance(t, e) { | ||
| if (!t.isolated) return; | ||
| const i = this.isolatedBlocMap.get(t); | ||
| if (!i) return; | ||
| const a = i.find((o) => o._id === e); | ||
| return a && this.log(`[${t.name}:${String(e)}] Found isolated instance. Returning.`), a; | ||
| } | ||
| /** | ||
| * Creates a new bloc instance and registers it in the appropriate registry | ||
| * @param blocClass - The bloc class to instantiate | ||
| * @param id - The instance ID | ||
| * @param props - Properties to pass to the bloc constructor | ||
| * @param instanceRef - Optional reference string for the instance | ||
| * @returns The newly created bloc instance | ||
| */ | ||
| createNewBlocInstance(t, e, s = {}) { | ||
| const { props: i, instanceRef: a } = s, o = new t(i); | ||
| return o._instanceRef = a, o.props = i || null, o._updateId(e), o.isIsolated ? (this.registerIsolatedBlocInstance(o), o) : (this.registerBlocInstance(o), o); | ||
| } | ||
| }; | ||
| /** The singleton instance of Blac */ | ||
| n(r, "instance", new r()), n(r, "getAllBlocs", r.instance.getAllBlocs), n(r, "addPlugin", r.instance.addPlugin), /** Flag to enable/disable logging */ | ||
| n(r, "enableLog", !1), n(r, "log", r.instance.log), n(r, "warn", r.instance.warn), n(r, "error", r.instance.error), n(r, "resetInstance", r.instance.resetInstance), n(r, "getBloc", r.instance.getBloc), n(r, "getBlocOrThrow", r.instance.getBlocOrThrow); | ||
| let h = r; | ||
| class D { | ||
| /** | ||
| * Creates a new BlacObservable instance | ||
| * @param bloc - The Bloc instance to observe | ||
| */ | ||
| constructor(t) { | ||
| /** The Bloc instance this observable is associated with */ | ||
| n(this, "bloc"); | ||
| n(this, "_observers", /* @__PURE__ */ new Set()); | ||
| this.bloc = t; | ||
| } | ||
| /** | ||
| * Gets the number of active observers | ||
| * @returns The number of observers currently subscribed | ||
| */ | ||
| get size() { | ||
| return this._observers.size; | ||
| } | ||
| /** | ||
| * Gets the set of all observers | ||
| * @returns The Set of all BlacObserver instances | ||
| */ | ||
| get observers() { | ||
| return this._observers; | ||
| } | ||
| /** | ||
| * Subscribes an observer to state changes | ||
| * @param observer - The observer to subscribe | ||
| * @returns A function that can be called to unsubscribe the observer | ||
| */ | ||
| subscribe(t) { | ||
| return this._observers.add(t), h.instance.dispatchEvent(_.LISTENER_ADDED, this.bloc, { listenerId: t.id }), t.lastState || (t.lastState = t.dependencyArray ? t.dependencyArray(this.bloc.state, this.bloc.state) : []), () => { | ||
| this.unsubscribe(t); | ||
| }; | ||
| } | ||
| /** | ||
| * Unsubscribes an observer from state changes | ||
| * @param observer - The observer to unsubscribe | ||
| */ | ||
| unsubscribe(t) { | ||
| this._observers.delete(t), h.instance.dispatchEvent(_.LISTENER_REMOVED, this.bloc, { listenerId: t.id }); | ||
| } | ||
| /** | ||
| * Notifies all observers of a state change | ||
| * @param newState - The new state value | ||
| * @param oldState - The previous state value | ||
| * @param action - Optional action that triggered the state change | ||
| */ | ||
| notify(t, e, s) { | ||
| this._observers.forEach((i) => { | ||
| let a = !1; | ||
| if (i.dependencyArray) { | ||
| const o = i.lastState || [], g = i.dependencyArray(t, e); | ||
| for (let p = 0; p < g.length; p++) { | ||
| const S = g[p], d = o[p] || []; | ||
| for (let l = 0; l < S.length; l++) | ||
| if (!Object.is(S[l], d[l])) { | ||
| a = !0; | ||
| break; | ||
| } | ||
| } | ||
| i.lastState = g; | ||
| } else | ||
| a = !0; | ||
| a && i.fn(t, e, s); | ||
| }); | ||
| } | ||
| /** | ||
| * Disposes of all observers and clears the observer set | ||
| */ | ||
| dispose() { | ||
| this._observers.forEach((t) => { | ||
| var e; | ||
| this.unsubscribe(t), (e = t.dispose) == null || e.call(t); | ||
| }), this._observers.clear(); | ||
| } | ||
| } | ||
| class E { | ||
| /** | ||
| * Creates a new BlocBase instance with the given initial state. | ||
| * Sets up the observer, registers with the Blac manager, and initializes addons. | ||
| * | ||
| * @param initialState The initial state value for this Bloc | ||
| */ | ||
| constructor(t) { | ||
| /** | ||
| * Defines how dependencies are selected from the state for efficient updates. | ||
| * When provided, observers will only be notified when selected dependencies change. | ||
| */ | ||
| n(this, "defaultDependencySelector"); | ||
| /** | ||
| * @internal | ||
| * Optional array of addons to extend the functionality of this Bloc. | ||
| */ | ||
| n(this, "_addons"); | ||
| /** | ||
| * @internal | ||
| * Indicates if this specific Bloc instance is isolated from others of the same type. | ||
| */ | ||
| n(this, "_isolated", !1); | ||
| /** | ||
| * @internal | ||
| * Observable responsible for managing state listeners and notifying consumers. | ||
| */ | ||
| n(this, "_observer"); | ||
| /** | ||
| * @internal | ||
| * Reference to the global Blac manager instance. | ||
| */ | ||
| n(this, "_blac", h.getInstance()); | ||
| /** | ||
| * The unique identifier for this Bloc instance. | ||
| * Defaults to the class name, but can be customized. | ||
| */ | ||
| n(this, "_id"); | ||
| /** | ||
| * @internal | ||
| * Reference string used internally for tracking and debugging. | ||
| */ | ||
| n(this, "_instanceRef"); | ||
| /** | ||
| * @internal | ||
| * Indicates if this specific Bloc instance should be kept alive when no consumers are present. | ||
| */ | ||
| n(this, "_keepAlive", !1); | ||
| /** | ||
| * @readonly | ||
| * Timestamp when this Bloc instance was created, useful for debugging and performance tracking. | ||
| */ | ||
| n(this, "_createdAt", Date.now()); | ||
| /** | ||
| * @internal | ||
| * The current state of the Bloc. | ||
| */ | ||
| n(this, "_state"); | ||
| /** | ||
| * @internal | ||
| * The previous state of the Bloc, maintained for comparison and history. | ||
| */ | ||
| n(this, "_oldState"); | ||
| /** | ||
| * Props passed during Bloc instance creation. | ||
| * Can be used to configure or parameterize the Bloc's behavior. | ||
| */ | ||
| n(this, "props", null); | ||
| /** | ||
| * @internal | ||
| * Updates the Bloc instance's ID to a new value. | ||
| * Only updates if the new ID is defined and different from the current one. | ||
| * | ||
| * @param id The new ID to assign to this Bloc instance | ||
| */ | ||
| n(this, "_updateId", (t) => { | ||
| const e = this._id; | ||
| !t || t === e || (this._id = t); | ||
| }); | ||
| /** | ||
| * @internal | ||
| * Optional function to be called when the Bloc is disposed. | ||
| */ | ||
| n(this, "onDispose"); | ||
| /** | ||
| * @internal | ||
| * Set of consumer IDs currently listening to this Bloc's state changes. | ||
| */ | ||
| n(this, "_consumers", /* @__PURE__ */ new Set()); | ||
| /** | ||
| * @internal | ||
| * Registers a new consumer to this Bloc instance. | ||
| * Notifies the Blac manager that a consumer has been added. | ||
| * | ||
| * @param consumerId The unique ID of the consumer being added | ||
| */ | ||
| n(this, "_addConsumer", (t) => { | ||
| this._consumers.add(t), this._blac.dispatchEvent(_.BLOC_CONSUMER_ADDED, this, { consumerId: t }); | ||
| }); | ||
| /** | ||
| * @internal | ||
| * Unregisters a consumer from this Bloc instance. | ||
| * Notifies the Blac manager that a consumer has been removed. | ||
| * | ||
| * @param consumerId The unique ID of the consumer being removed | ||
| */ | ||
| n(this, "_removeConsumer", (t) => { | ||
| this._blac.log(`[${this._name}:${String(this._id ?? "default_id")}] Removing consumer: ${t}`), this._consumers.delete(t), this._blac.dispatchEvent(_.BLOC_CONSUMER_REMOVED, this, { consumerId: t }); | ||
| }); | ||
| /** | ||
| * @internal | ||
| * Initializes all registered addons for this Bloc instance. | ||
| * Calls the onInit lifecycle method on each addon if defined. | ||
| */ | ||
| n(this, "_connectAddons", () => { | ||
| const { _addons: t } = this; | ||
| t && t.forEach((e) => { | ||
| var s; | ||
| (s = e.onInit) == null || s.call(e, this); | ||
| }); | ||
| }); | ||
| n(this, "lastUpdate", Date.now()); | ||
| /** | ||
| * @internal | ||
| * Updates the state and notifies all observers of the change. | ||
| * | ||
| * @param newState The new state to be set | ||
| * @param oldState The previous state for comparison | ||
| * @param action Optional metadata about what caused the state change | ||
| */ | ||
| n(this, "_pushState", (t, e, s) => { | ||
| this._state = t, this._observer.notify(t, e, s), this.lastUpdate = Date.now(); | ||
| }); | ||
| this._state = t, this._observer = new D(this), this._blac.dispatchEvent(_.BLOC_CREATED, this), this._id = this.constructor.name; | ||
| const e = this.constructor; | ||
| this._keepAlive = e.keepAlive, this._isolated = e.isolated, this._addons = e.addons, this._connectAddons(); | ||
| } | ||
| get isIsolated() { | ||
| return this._isolated; | ||
| } | ||
| get isKeepAlive() { | ||
| return this._keepAlive; | ||
| } | ||
| /** | ||
| * Returns the current state of the Bloc. | ||
| * Use this getter to access the state in a read-only manner. | ||
| */ | ||
| get state() { | ||
| return this._state; | ||
| } | ||
| /** | ||
| * @internal | ||
| * Returns the name of the Bloc class for identification and debugging. | ||
| */ | ||
| get _name() { | ||
| return this.constructor.name; | ||
| } | ||
| /** | ||
| * @internal | ||
| * Cleans up resources and removes this Bloc from the system. | ||
| * Notifies the Blac manager and clears all observers. | ||
| */ | ||
| _dispose() { | ||
| var t; | ||
| this._observer.dispose(), this._blac.dispatchEvent(_.BLOC_DISPOSED, this), (t = this.onDispose) == null || t.call(this); | ||
| } | ||
| } | ||
| /** | ||
| * When true, every consumer will receive its own unique instance of this Bloc. | ||
| * Use this when state should not be shared between components. | ||
| * @default false | ||
| */ | ||
| n(E, "isolated", !1), /** | ||
| * When true, the Bloc instance persists even when there are no active consumers. | ||
| * Useful for maintaining state between component unmount/remount cycles. | ||
| * @default false | ||
| */ | ||
| n(E, "keepAlive", !1); | ||
| class M extends E { | ||
| /** | ||
| * Updates the current state and notifies all observers of the change. | ||
| * If the new state is identical to the current state (using Object.is), | ||
| * no update will occur. | ||
| * @param state - The new state to set | ||
| */ | ||
| emit(t) { | ||
| if (Object.is(t, this.state)) | ||
| return; | ||
| const e = this.state, s = t; | ||
| this._pushState(s, e); | ||
| } | ||
| /** | ||
| * Partially updates the current state by merging it with the provided state patch. | ||
| * This method is only applicable when the state is an object type. | ||
| * | ||
| * @param statePatch - A partial state object containing only the properties to update | ||
| * @param ignoreChangeCheck - If true, skips checking if the state has actually changed | ||
| * @throws {TypeError} If the state is not an object type | ||
| */ | ||
| patch(t, e = !1) { | ||
| if (typeof this.state != "object" || this.state === null) { | ||
| h.warn( | ||
| "Cubit.patch: was called on a cubit where the state is not an object. This is a no-op." | ||
| ); | ||
| return; | ||
| } | ||
| let s = !1; | ||
| if (e) | ||
| s = !0; | ||
| else | ||
| for (const i in t) | ||
| if (Object.prototype.hasOwnProperty.call(t, i)) { | ||
| const o = this.state[i]; | ||
| if (!Object.is(t[i], o)) { | ||
| s = !0; | ||
| break; | ||
| } | ||
| } | ||
| s && this.emit({ | ||
| ...this.state, | ||
| ...t | ||
| }); | ||
| } | ||
| } | ||
| class w extends E { | ||
| constructor() { | ||
| super(...arguments); | ||
| // Stores handlers: Map<EventConstructor (subtype of A), HandlerFunction> | ||
| // The handler's event parameter will be correctly typed to the specific EventConstructor | ||
| // by the 'on' method's signature. | ||
| n(this, "eventHandlers", /* @__PURE__ */ new Map()); | ||
| /** | ||
| * Dispatches an action/event to the Bloc. | ||
| * If a handler is registered for this specific event type (via 'on'), it will be invoked. | ||
| * Asynchronous handlers are awaited. | ||
| * @param action The action/event instance to be processed. | ||
| */ | ||
| n(this, "add", async (e) => { | ||
| const s = e.constructor, i = this.eventHandlers.get(s); | ||
| if (i) { | ||
| const a = (o) => { | ||
| const g = this.state; | ||
| this._pushState(o, g, e); | ||
| }; | ||
| try { | ||
| await i(e, a); | ||
| } catch (o) { | ||
| h.error( | ||
| `[Bloc ${this._name}:${String(this._id)}] Error in event handler for '${s.name}':`, | ||
| o, | ||
| "Action:", | ||
| e | ||
| ); | ||
| } | ||
| } else { | ||
| const a = e.constructor.name || "UnnamedConstructor"; | ||
| h.warn( | ||
| `[Bloc ${this._name}:${String(this._id)}] No handler registered for action type: '${a}'. Action was:`, | ||
| e | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * Registers an event handler for a specific event type. | ||
| * This method is typically called in the constructor of a derived Bloc class. | ||
| * @param eventConstructor The constructor of the event to handle (e.g., LoadDataEvent). | ||
| * @param handler A function that processes the event and can emit new states. | ||
| * The 'event' parameter in the handler will be typed to the specific eventConstructor. | ||
| */ | ||
| on(e, s) { | ||
| this.eventHandlers.has(e) && h.warn( | ||
| `[Bloc ${this._name}:${String(this._id)}] Handler for event '${e.name}' already registered. It will be overwritten.` | ||
| ), this.eventHandlers.set( | ||
| e, | ||
| s | ||
| ); | ||
| } | ||
| } | ||
| function I(c) { | ||
| switch (c) { | ||
| case "localStorage": | ||
| return localStorage; | ||
| case "sessionStorage": | ||
| return sessionStorage; | ||
| default: | ||
| return localStorage; | ||
| } | ||
| } | ||
| function $(c = {}) { | ||
| const { | ||
| keyPrefix: t = "blac", | ||
| keyName: e, | ||
| defaultValue: s, | ||
| storageType: i = "localStorage" | ||
| } = c, a = (d) => `${t}:${String(d)}`, o = (d) => { | ||
| var l; | ||
| try { | ||
| const u = I(i).getItem(a(d)); | ||
| if (typeof u != "string") | ||
| return s; | ||
| const f = JSON.parse(u); | ||
| return typeof f.v < "u" ? f.v : s; | ||
| } catch (u) { | ||
| return (l = c.onError) == null || l.call(c, u), s; | ||
| } | ||
| }, g = (d) => { | ||
| const l = e ?? d._id, u = o(l); | ||
| d._pushState(u, null); | ||
| }; | ||
| let p = ""; | ||
| return { | ||
| name: "Persist", | ||
| onInit: g, | ||
| onEmit: ({ newState: d, cubit: l }) => { | ||
| const u = e ?? l._id, f = JSON.stringify({ v: d }); | ||
| f !== p && (I(i).setItem(a(u), f), p = f); | ||
| } | ||
| }; | ||
| } | ||
| export { | ||
| h as Blac, | ||
| _ as BlacLifecycleEvent, | ||
| D as BlacObservable, | ||
| w as Bloc, | ||
| E as BlocBase, | ||
| M as Cubit, | ||
| $ as Persist | ||
| }; | ||
| //# sourceMappingURL=index.es.js.map |
| {"version":3,"file":"index.es.js","sources":["../src/Blac.ts","../src/BlacObserver.ts","../src/BlocBase.ts","../src/Cubit.ts","../src/Bloc.ts","../src/addons/Persist.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { BlacPlugin } from \"./BlacPlugin\";\nimport { BlocBase, BlocInstanceId } from \"./BlocBase\";\nimport {\n BlocBaseAbstract,\n BlocConstructor,\n BlocHookDependencyArrayFn,\n BlocState,\n InferPropsFromGeneric\n} from \"./types\";\n\n/**\n * Configuration options for the Blac instance\n */\nexport interface BlacConfig {\n /** Whether to expose the Blac instance globally */\n exposeBlacInstance?: boolean;\n}\n\nexport interface GetBlocOptions<B extends BlocBase<any>> {\n id?: string;\n dependencySelector?: BlocHookDependencyArrayFn<BlocState<B>>;\n props?: InferPropsFromGeneric<B>;\n onMount?: (bloc: B) => void;\n instanceRef?: string;\n}\n\n/**\n * Enum representing different lifecycle events that can occur in the Blac system.\n * These events are used to track the lifecycle of blocs and their consumers.\n */\nexport enum BlacLifecycleEvent {\n BLOC_DISPOSED = \"BLOC_DISPOSED\",\n BLOC_CREATED = \"BLOC_CREATED\",\n LISTENER_REMOVED = \"LISTENER_REMOVED\",\n LISTENER_ADDED = \"LISTENER_ADDED\",\n STATE_CHANGED = \"STATE_CHANGED\",\n BLOC_CONSUMER_REMOVED = \"BLOC_CONSUMER_REMOVED\",\n BLOC_CONSUMER_ADDED = \"BLOC_CONSUMER_ADDED\",\n}\n\n/**\n * Main Blac class that manages the state management system.\n * Implements a singleton pattern to ensure only one instance exists.\n * Handles bloc lifecycle, plugin management, and instance tracking.\n * \n * Key responsibilities:\n * - Managing bloc instances (creation, disposal, lookup)\n * - Handling isolated and non-isolated blocs\n * - Managing plugins and lifecycle events\n * - Providing logging and debugging capabilities\n */\nexport class Blac {\n /** The singleton instance of Blac */\n static instance: Blac = new Blac();\n /** Timestamp when the instance was created */\n createdAt = Date.now();\n static getAllBlocs = Blac.instance.getAllBlocs;\n static addPlugin = Blac.instance.addPlugin;\n /** Map storing all registered bloc instances by their class name and ID */\n blocInstanceMap: Map<string, BlocBase<any>> = new Map();\n /** Map storing isolated bloc instances grouped by their constructor */\n isolatedBlocMap: Map<BlocConstructor<any>, BlocBase<any>[]> = new Map();\n pluginList: BlacPlugin[] = [];\n /** Flag to control whether changes should be posted to document */\n postChangesToDocument = false;\n\n /**\n * Creates a new Blac instance.\n * @param options - Configuration options including singleton control\n */\n constructor(options: { __unsafe_ignore_singleton?: boolean } = {}) {\n const { __unsafe_ignore_singleton = false } = options;\n if (!__unsafe_ignore_singleton) {\n return Blac.instance;\n }\n Blac.instance = this;\n }\n\n /** Flag to enable/disable logging */\n static enableLog = false;\n\n /**\n * Logs messages to console when logging is enabled\n * @param args - Arguments to log\n */\n log = (...args: unknown[]) => {\n if (Blac.enableLog) console.warn(`☢️ [Blac ${this.createdAt.toString()}]`, ...args);\n };\n static log = Blac.instance.log;\n\n /**\n * Gets the singleton instance of Blac\n * @returns The Blac instance\n */\n static getInstance(): Blac {\n return Blac.instance;\n }\n\n\n /**\n * Logs a warning message\n * @param message - Warning message\n * @param args - Additional arguments\n */\n warn = (message: string, ...args: unknown[]) => {\n if (Blac.enableLog) {\n console.warn(`🚨 [Blac ${String(Blac.instance.createdAt)}]`, message, ...args);\n }\n };\n static warn = Blac.instance.warn;\n /**\n * Logs an error message\n * @param message - Error message\n * @param args - Additional arguments\n */\n error = (message: string, ...args: unknown[]) => {\n if (Blac.enableLog) {\n console.error(`🚨 [Blac ${String(Blac.instance.createdAt)}]`, message, ...args);\n }\n };\n static error = Blac.instance.error;\n\n /**\n * Resets the Blac instance to a new one, disposing non-keepAlive blocs\n * from the old instance.\n */\n resetInstance = (): void => {\n this.log(\"Reset Blac instance\");\n\n // Dispose non-keepAlive blocs from the current instance\n const oldBlocInstanceMap = new Map(this.blocInstanceMap);\n const oldIsolatedBlocMap = new Map(this.isolatedBlocMap);\n\n oldBlocInstanceMap.forEach((bloc) => {\n bloc._dispose();\n });\n\n oldIsolatedBlocMap.forEach((blocArray) => {\n blocArray.forEach((bloc) => {\n bloc._dispose();\n });\n });\n\n this.blocInstanceMap.clear();\n this.isolatedBlocMap.clear();\n\n // Create and assign the new instance\n Blac.instance = new Blac({\n __unsafe_ignore_singleton: true,\n });\n }\n static resetInstance = Blac.instance.resetInstance;\n\n /**\n * Adds a plugin to the Blac instance\n * @param plugin - The plugin to add\n */\n addPlugin = (plugin: BlacPlugin): void => {\n // check if already added\n const index = this.pluginList.findIndex((p) => p.name === plugin.name);\n if (index !== -1) return;\n this.log(\"Add plugin\", plugin.name);\n this.pluginList.push(plugin);\n };\n\n /**\n * Dispatches a lifecycle event to all registered plugins\n * @param event - The lifecycle event to dispatch\n * @param bloc - The bloc instance involved in the event\n * @param params - Additional parameters for the event\n */\n dispatchEventToPlugins = (\n event: BlacLifecycleEvent,\n bloc: BlocBase<any>,\n params?: unknown,\n ) => {\n this.pluginList.forEach((plugin) => {\n plugin.onEvent(event, bloc, params);\n });\n };\n\n /**\n * Dispatches a lifecycle event and handles related cleanup actions.\n * This method is responsible for:\n * - Logging the event\n * - Handling bloc disposal when needed\n * - Managing bloc consumer cleanup\n * - Forwarding the event to plugins\n * \n * @param event - The lifecycle event to dispatch\n * @param bloc - The bloc instance involved in the event\n * @param params - Additional parameters for the event\n */\n dispatchEvent = (\n event: BlacLifecycleEvent,\n bloc: BlocBase<any>,\n params?: unknown,\n ) => {\n this.log(event, bloc, params);\n\n switch (event) {\n case BlacLifecycleEvent.BLOC_DISPOSED:\n this.disposeBloc(bloc);\n break;\n case BlacLifecycleEvent.BLOC_CONSUMER_REMOVED:\n case BlacLifecycleEvent.LISTENER_REMOVED:\n this.log(`[${bloc._name}:${String(bloc._id)}] Listener/Consumer removed. Listeners: ${String(bloc._observer.size)}, Consumers: ${String(bloc._consumers.size)}, KeepAlive: ${String(bloc._keepAlive)}`);\n if (\n bloc._observer.size === 0 &&\n bloc._consumers.size === 0 &&\n !bloc._keepAlive\n ) {\n this.log(`[${bloc._name}:${String(bloc._id)}] No listeners or consumers left and not keepAlive. Disposing.`);\n bloc._dispose();\n }\n break;\n }\n\n this.dispatchEventToPlugins(event, bloc, params);\n };\n\n /**\n * Disposes of a bloc instance by removing it from the appropriate registry\n * @param bloc - The bloc instance to dispose\n */\n disposeBloc = (bloc: BlocBase<any>): void => {\n const base = bloc.constructor as unknown as BlocBaseAbstract;\n const key = this.createBlocInstanceMapKey(bloc._name, bloc._id);\n this.log(`[${bloc._name}:${String(bloc._id)}] disposeBloc called. Isolated: ${String(base.isolated)}`);\n\n if (base.isolated) {\n this.unregisterIsolatedBlocInstance(bloc);\n this.blocInstanceMap.delete(key);\n } else {\n this.unregisterBlocInstance(bloc);\n }\n this.dispatchEventToPlugins(BlacLifecycleEvent.BLOC_DISPOSED, bloc);\n };\n\n /**\n * Creates a unique key for a bloc instance in the map based on the bloc class name and instance ID\n * @param blocClassName - The name of the bloc class\n * @param id - The instance ID\n * @returns A unique key string in the format \"className:id\"\n */\n createBlocInstanceMapKey(blocClassName: string, id: BlocInstanceId): string {\n return `${blocClassName}:${String(id)}`;\n }\n\n /**\n * Unregister a bloc instance from the main registry\n * @param bloc - The bloc instance to unregister\n */\n unregisterBlocInstance(bloc: BlocBase<any>): void {\n const key = this.createBlocInstanceMapKey(bloc._name, bloc._id);\n this.blocInstanceMap.delete(key);\n }\n\n /**\n * Registers a bloc instance in the main registry\n * @param bloc - The bloc instance to register\n */\n registerBlocInstance(bloc: BlocBase<any>): void {\n const key = this.createBlocInstanceMapKey(bloc._name, bloc._id);\n this.blocInstanceMap.set(key, bloc);\n }\n\n /**\n * Finds a registered bloc instance by its class and ID\n * @param blocClass - The bloc class to search for\n * @param id - The instance ID\n * @returns The found bloc instance or undefined if not found\n */\n findRegisteredBlocInstance<B extends BlocConstructor<unknown>>(\n blocClass: B,\n id: BlocInstanceId,\n ): InstanceType<B> | undefined {\n const base = blocClass as unknown as BlocBaseAbstract;\n if (base.isolated) return undefined;\n\n const key = this.createBlocInstanceMapKey(blocClass.name, id);\n const found = this.blocInstanceMap.get(key) as InstanceType<B> | undefined;\n if (found) {\n this.log(`[${blocClass.name}:${String(id)}] Found registered instance. Returning.`);\n }\n return found \n }\n\n /**\n * Registers an isolated bloc instance in the isolated registry\n * @param bloc - The isolated bloc instance to register\n */\n registerIsolatedBlocInstance(bloc: BlocBase<any>): void {\n const blocClass = bloc.constructor as BlocConstructor<unknown>;\n const blocs = this.isolatedBlocMap.get(blocClass);\n if (blocs) {\n blocs.push(bloc);\n } else {\n this.isolatedBlocMap.set(blocClass, [bloc]);\n }\n }\n\n /**\n * Unregister an isolated bloc instance from the isolated registry\n * @param bloc - The isolated bloc instance to unregister\n */\n unregisterIsolatedBlocInstance(bloc: BlocBase<any>): void {\n const blocClass = bloc.constructor;\n const blocs = this.isolatedBlocMap.get(blocClass as BlocConstructor<unknown>);\n if (blocs) {\n const index = blocs.findIndex((b) => b._id === bloc._id);\n if (index !== -1) {\n blocs.splice(index, 1);\n }\n\n if (blocs.length === 0) {\n this.isolatedBlocMap.delete(blocClass as BlocConstructor<unknown>);\n }\n }\n }\n\n /**\n * Finds an isolated bloc instance by its class and ID\n */\n findIsolatedBlocInstance<B extends BlocConstructor<unknown>>(\n blocClass: B,\n id: BlocInstanceId,\n ): InstanceType<B> | undefined {\n const base = blocClass as unknown as BlocBaseAbstract;\n if (!base.isolated) return undefined;\n\n const blocs = this.isolatedBlocMap.get(blocClass);\n if (!blocs) return undefined;\n\n // Fix: Find the specific bloc by ID within the isolated array\n const found = blocs.find((b) => b._id === id) as InstanceType<B> | undefined;\n\n if (found) {\n this.log(`[${blocClass.name}:${String(id)}] Found isolated instance. Returning.`);\n }\n\n return found;\n }\n\n /**\n * Creates a new bloc instance and registers it in the appropriate registry\n * @param blocClass - The bloc class to instantiate\n * @param id - The instance ID\n * @param props - Properties to pass to the bloc constructor\n * @param instanceRef - Optional reference string for the instance\n * @returns The newly created bloc instance\n */\n createNewBlocInstance<B extends BlocConstructor<BlocBase<any>>>(\n blocClass: B,\n id: BlocInstanceId,\n options: GetBlocOptions<InstanceType<B>> = {},\n ): InstanceType<B> {\n const { props, instanceRef } = options;\n const newBloc = new blocClass(props as never) as InstanceType<BlocConstructor<BlocBase<unknown>>>;\n newBloc._instanceRef = instanceRef;\n newBloc.props = props || null;\n newBloc._updateId(id);\n\n if (newBloc.isIsolated) {\n this.registerIsolatedBlocInstance(newBloc);\n return newBloc as InstanceType<B>;\n }\n\n this.registerBlocInstance(newBloc);\n return newBloc as InstanceType<B>;\n }\n\n /**\n * Gets or creates a bloc instance based on the provided class and options.\n * If a bloc with the given ID exists, it will be returned. Otherwise, a new instance will be created.\n * \n * @param blocClass - The bloc class to get or create\n * @param options - Options including:\n * - id: The instance ID (defaults to class name if not provided)\n * - props: Properties to pass to the bloc constructor\n * - instanceRef: Optional reference string for the instance\n * @returns The bloc instance\n */\n getBloc = <B extends BlocConstructor<BlocBase<any>>>(\n blocClass: B,\n options: GetBlocOptions<InstanceType<B>> = {},\n ): InstanceType<B> => {\n const { id } = options;\n const base = blocClass as unknown as BlocBaseAbstract;\n const blocId = id ?? blocClass.name;\n\n this.log(`[${blocClass.name}:${String(blocId)}] getBloc called. Options:`, options);\n\n if (base.isolated) {\n const isolatedBloc = this.findIsolatedBlocInstance<B>(blocClass, blocId)\n if (isolatedBloc) {\n this.log(`[${blocClass.name}:${String(blocId)}] Found existing isolated instance.`);\n return isolatedBloc;\n }\n }\n\n if (!base.isolated) {\n const registeredBloc = this.findRegisteredBlocInstance(blocClass, blocId)\n if (registeredBloc) {\n this.log(`[${blocClass.name}:${String(blocId)}] Found existing registered instance.`);\n return registeredBloc\n }\n }\n\n this.log(`[${blocClass.name}:${String(blocId)}] No existing instance found. Creating new one.`);\n return this.createNewBlocInstance(\n blocClass,\n blocId,\n options,\n );\n };\n static getBloc = Blac.instance.getBloc;\n\n /**\n * Gets a bloc instance or throws an error if it doesn't exist\n * @param blocClass - The bloc class to get\n * @param options - Options including:\n * - id: The instance ID (defaults to class name if not provided)\n * - props: Properties to pass to the bloc constructor\n * - instanceRef: Optional reference string for the instance\n */\n getBlocOrThrow = <B extends BlocConstructor<unknown>>(\n blocClass: B,\n options: {\n id?: BlocInstanceId;\n props?: InferPropsFromGeneric<B>;\n instanceRef?: string;\n } = {},\n ): InstanceType<B> => {\n const isIsolated = (blocClass as unknown as BlocBaseAbstract).isolated;\n const id = options.id || blocClass.name;\n\n const registered = isIsolated\n ? this.findIsolatedBlocInstance(blocClass, id)\n : this.findRegisteredBlocInstance(blocClass, id);\n\n if (registered) {\n return registered;\n }\n throw new Error(`Bloc ${blocClass.name} not found`);\n };\n static getBlocOrThrow = Blac.instance.getBlocOrThrow;\n\n /**\n * Gets all instances of a specific bloc class\n * @param blocClass - The bloc class to search for\n * @param options - Options including:\n * - searchIsolated: Whether to search in isolated blocs (defaults to bloc's isolated property)\n * @returns Array of matching bloc instances\n */\n getAllBlocs = <B extends BlocConstructor<unknown>>(\n blocClass: B,\n options: {\n searchIsolated?: boolean;\n } = {},\n ): InstanceType<B>[] => {\n const results: InstanceType<B>[] = [];\n // const blocClassName = (blocClass as any).name; // Temporarily removed for debugging\n\n // Search non-isolated blocs\n this.blocInstanceMap.forEach((blocInstance) => {\n if (blocInstance.constructor === blocClass) { // Strict constructor check\n results.push(blocInstance as InstanceType<B>);\n }\n });\n\n // Optionally search isolated blocs\n if (options.searchIsolated !== false) {\n const isolatedBlocs = this.isolatedBlocMap.get(blocClass);\n if (isolatedBlocs) {\n results.push(...isolatedBlocs.map(bloc => bloc as InstanceType<B>));\n }\n }\n\n return results;\n };\n}\n","import { Blac, BlacLifecycleEvent } from './Blac';\nimport { BlocBase } from './BlocBase';\nimport { BlocHookDependencyArrayFn } from './types';\n\n/**\n * Represents an observer that can subscribe to state changes in a Bloc\n * @template S - The type of state being observed\n */\nexport type BlacObserver<S> = {\n /** Function to be called when state changes */\n fn: (newState: S, oldState: S, action?: unknown) => void | Promise<void>;\n /** Optional function to determine if the observer should be notified of state changes */\n dependencyArray?: BlocHookDependencyArrayFn<S>;\n /** Dispose function for the observer */\n dispose?: () => void;\n /** Cached state values used for dependency comparison */\n lastState?: unknown[][];\n /** Unique identifier for the observer */\n id: string;\n};\n\n/**\n * A class that manages observers for a Bloc's state changes\n * @template S - The type of state being observed\n */\nexport class BlacObservable<S = unknown> {\n /** The Bloc instance this observable is associated with */\n bloc: BlocBase<S>;\n\n /**\n * Creates a new BlacObservable instance\n * @param bloc - The Bloc instance to observe\n */\n constructor(bloc: BlocBase<S>) {\n this.bloc = bloc;\n }\n\n private _observers = new Set<BlacObserver<S>>();\n\n /**\n * Gets the number of active observers\n * @returns The number of observers currently subscribed\n */\n get size(): number {\n return this._observers.size;\n }\n\n /**\n * Gets the set of all observers\n * @returns The Set of all BlacObserver instances\n */\n get observers() {\n return this._observers;\n }\n\n /**\n * Subscribes an observer to state changes\n * @param observer - The observer to subscribe\n * @returns A function that can be called to unsubscribe the observer\n */\n subscribe(observer: BlacObserver<S>): () => void {\n this._observers.add(observer);\n Blac.instance.dispatchEvent(BlacLifecycleEvent.LISTENER_ADDED, this.bloc, { listenerId: observer.id });\n if (!observer.lastState) {\n observer.lastState = observer.dependencyArray\n ? observer.dependencyArray(this.bloc.state, this.bloc.state)\n : [];\n }\n return () => {\n this.unsubscribe(observer);\n }\n }\n\n /**\n * Unsubscribes an observer from state changes\n * @param observer - The observer to unsubscribe\n */\n unsubscribe(observer: BlacObserver<S>) {\n this._observers.delete(observer);\n Blac.instance.dispatchEvent(BlacLifecycleEvent.LISTENER_REMOVED, this.bloc, { listenerId: observer.id });\n }\n\n /**\n * Notifies all observers of a state change\n * @param newState - The new state value\n * @param oldState - The previous state value\n * @param action - Optional action that triggered the state change\n */\n notify(newState: S, oldState: S, action?: unknown) {\n this._observers.forEach((observer) => {\n let shouldUpdate = false;\n\n if (observer.dependencyArray) {\n const lastDependencyCheck = observer.lastState || [];\n const newDependencyCheck = observer.dependencyArray(newState, oldState);\n\n for (let o = 0; o < newDependencyCheck.length; o++) {\n const partNew = newDependencyCheck[o];\n const partOld = lastDependencyCheck[o] || [];\n for (let i = 0; i < partNew.length; i++) {\n if (!Object.is(partNew[i], partOld[i])) {\n shouldUpdate = true;\n break;\n }\n }\n }\n\n observer.lastState = newDependencyCheck;\n } else {\n shouldUpdate = true;\n }\n\n if (shouldUpdate) {\n void observer.fn(newState, oldState, action);\n }\n });\n }\n\n /**\n * Disposes of all observers and clears the observer set\n */\n dispose() {\n this._observers.forEach((observer) => {\n this.unsubscribe(observer);\n observer.dispose?.();\n });\n this._observers.clear();\n }\n}\n","import { Blac, BlacLifecycleEvent } from './Blac';\nimport { BlacObservable } from './BlacObserver';\nimport BlacAddon from './addons/BlacAddon';\nimport { BlocConstructor } from './types';\n\nexport type BlocInstanceId = string | number | undefined;\ntype DependencySelector<S> = (newState: S, oldState?: S) => unknown[][];\n\n// Define an interface for the static properties expected on a Bloc/Cubit constructor\ninterface BlocStaticProperties {\n isolated: boolean;\n keepAlive: boolean;\n addons?: BlacAddon[];\n}\n\n/**\n * Base class for both Blocs and Cubits that provides core state management functionality.\n * Handles state transitions, observer notifications, lifecycle management, and addon integration.\n * \n * @abstract This class should be extended, not instantiated directly\n * @template S The type of state managed by this Bloc\n * @template P The type of props that can be passed during instance creation (optional)\n */\nexport abstract class BlocBase<\n S,\n P = unknown\n> {\n /**\n * When true, every consumer will receive its own unique instance of this Bloc.\n * Use this when state should not be shared between components.\n * @default false\n */\n static isolated = false;\n get isIsolated() {\n return this._isolated;\n }\n \n /**\n * When true, the Bloc instance persists even when there are no active consumers.\n * Useful for maintaining state between component unmount/remount cycles.\n * @default false\n */\n static keepAlive = false;\n get isKeepAlive() {\n return this._keepAlive;\n }\n \n /**\n * Defines how dependencies are selected from the state for efficient updates.\n * When provided, observers will only be notified when selected dependencies change.\n */\n defaultDependencySelector: DependencySelector<S> | undefined;\n\n /**\n * @internal\n * Optional array of addons to extend the functionality of this Bloc.\n */\n public _addons?: BlacAddon[];\n \n /**\n * @internal\n * Indicates if this specific Bloc instance is isolated from others of the same type.\n */\n public _isolated = false;\n \n /**\n * @internal\n * Observable responsible for managing state listeners and notifying consumers.\n */\n public _observer: BlacObservable<S>;\n \n /**\n * @internal\n * Reference to the global Blac manager instance.\n */\n public _blac = Blac.getInstance();\n \n /**\n * The unique identifier for this Bloc instance.\n * Defaults to the class name, but can be customized.\n */\n public _id: BlocInstanceId;\n \n /**\n * @internal\n * Reference string used internally for tracking and debugging.\n */\n public _instanceRef?: string;\n \n /**\n * @internal\n * Indicates if this specific Bloc instance should be kept alive when no consumers are present.\n */\n public _keepAlive = false;\n \n /**\n * @readonly\n * Timestamp when this Bloc instance was created, useful for debugging and performance tracking.\n */\n public readonly _createdAt = Date.now();\n\n /**\n * @internal\n * The current state of the Bloc.\n */\n public _state: S;\n \n /**\n * @internal\n * The previous state of the Bloc, maintained for comparison and history.\n */\n public _oldState: S | undefined;\n \n /**\n * Props passed during Bloc instance creation.\n * Can be used to configure or parameterize the Bloc's behavior.\n */\n public props: P | null = null;\n\n /**\n * Creates a new BlocBase instance with the given initial state.\n * Sets up the observer, registers with the Blac manager, and initializes addons.\n * \n * @param initialState The initial state value for this Bloc\n */\n constructor(initialState: S) {\n this._state = initialState;\n this._observer = new BlacObservable(this);\n this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_CREATED, this);\n this._id = this.constructor.name;\n\n // Use a type assertion for the constructor to access static properties safely\n const constructorWithStaticProps = this.constructor as BlocConstructor<this> & BlocStaticProperties;\n\n this._keepAlive = constructorWithStaticProps.keepAlive;\n this._isolated = constructorWithStaticProps.isolated;\n this._addons = constructorWithStaticProps.addons;\n\n this._connectAddons();\n }\n\n /**\n * Returns the current state of the Bloc.\n * Use this getter to access the state in a read-only manner.\n */\n get state(): S {\n return this._state;\n }\n\n /**\n * @internal\n * Returns the name of the Bloc class for identification and debugging.\n */\n get _name() {\n return this.constructor.name;\n }\n\n /**\n * @internal\n * Updates the Bloc instance's ID to a new value.\n * Only updates if the new ID is defined and different from the current one.\n * \n * @param id The new ID to assign to this Bloc instance\n */\n _updateId = (id?: BlocInstanceId) => {\n const originalId = this._id;\n if (!id || id === originalId) return;\n this._id = id;\n };\n\n /**\n * @internal\n * Cleans up resources and removes this Bloc from the system.\n * Notifies the Blac manager and clears all observers.\n */\n _dispose() {\n this._observer.dispose();\n this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_DISPOSED, this);\n this.onDispose?.();\n }\n\n /**\n * @internal\n * Optional function to be called when the Bloc is disposed.\n */\n onDispose?: () => void;\n\n /**\n * @internal\n * Set of consumer IDs currently listening to this Bloc's state changes.\n */\n _consumers = new Set<string>();\n\n /**\n * @internal\n * Registers a new consumer to this Bloc instance.\n * Notifies the Blac manager that a consumer has been added.\n * \n * @param consumerId The unique ID of the consumer being added\n */\n _addConsumer = (consumerId: string) => {\n this._consumers.add(consumerId);\n this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_CONSUMER_ADDED, this, { consumerId });\n };\n\n /**\n * @internal\n * Unregisters a consumer from this Bloc instance.\n * Notifies the Blac manager that a consumer has been removed.\n * \n * @param consumerId The unique ID of the consumer being removed\n */\n _removeConsumer = (consumerId: string) => {\n this._blac.log(`[${this._name}:${String(this._id ?? 'default_id')}] Removing consumer: ${consumerId}`);\n this._consumers.delete(consumerId);\n this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_CONSUMER_REMOVED, this, { consumerId });\n };\n\n /**\n * @internal\n * Initializes all registered addons for this Bloc instance.\n * Calls the onInit lifecycle method on each addon if defined.\n */\n _connectAddons = () => {\n const { _addons: addons } = this;\n if (addons) {\n addons.forEach(addon => {\n addon.onInit?.(this);\n });\n }\n };\n\n lastUpdate = Date.now();\n\n /**\n * @internal\n * Updates the state and notifies all observers of the change.\n * \n * @param newState The new state to be set\n * @param oldState The previous state for comparison\n * @param action Optional metadata about what caused the state change\n */\n _pushState = (newState: S, oldState: S, action?: unknown): void => {\n this._state = newState;\n this._observer.notify(newState, oldState, action);\n this.lastUpdate = Date.now();\n };\n}\n","import { Blac } from './Blac';\nimport { BlocBase } from './BlocBase';\n\n/**\n * A Cubit is a simpler version of a Bloc that doesn't handle events.\n * It manages state and provides methods to update it.\n * @template S - The type of state this Cubit manages\n * @template P - The type of parameters (optional, defaults to null)\n */\nexport abstract class Cubit<S, P = null> extends BlocBase<S, P> {\n /**\n * Updates the current state and notifies all observers of the change.\n * If the new state is identical to the current state (using Object.is),\n * no update will occur.\n * @param state - The new state to set\n */\n emit(state: S): void {\n if (Object.is(state, this.state)) {\n return;\n }\n\n const oldState = this.state;\n const newState = state;\n this._pushState(newState, oldState);\n }\n\n /**\n * Partially updates the current state by merging it with the provided state patch.\n * This method is only applicable when the state is an object type.\n * \n * @param statePatch - A partial state object containing only the properties to update\n * @param ignoreChangeCheck - If true, skips checking if the state has actually changed\n * @throws {TypeError} If the state is not an object type\n */\n patch(\n statePatch: S extends object ? Partial<S> : S,\n ignoreChangeCheck = false,\n ): void {\n if (typeof this.state !== 'object' || this.state === null) {\n Blac.warn(\n 'Cubit.patch: was called on a cubit where the state is not an object. This is a no-op.',\n );\n return;\n }\n\n let changes = false;\n if (!ignoreChangeCheck) {\n for (const key in statePatch) {\n if (Object.prototype.hasOwnProperty.call(statePatch, key)) {\n const s = this.state;\n const current = s[key as keyof S];\n if (!Object.is(statePatch[key as keyof S], current)) {\n changes = true;\n break;\n }\n }\n }\n } else {\n changes = true;\n }\n\n if (changes) {\n this.emit({\n ...this.state,\n ...(statePatch as Partial<S>),\n } as S);\n }\n }\n}\n","import { Blac } from './Blac';\nimport { BlocBase } from './BlocBase';\n\n// A should be the base type for all events this Bloc handles and must be an object type\n// to access action.constructor. Events are typically class instances.\n// P is for props, changed from any to unknown.\nexport abstract class Bloc<\n S, // State type\n A extends object, // Base Action/Event type, constrained to object\n P = unknown // Props type\n> extends BlocBase<S, P> {\n // Stores handlers: Map<EventConstructor (subtype of A), HandlerFunction>\n // The handler's event parameter will be correctly typed to the specific EventConstructor\n // by the 'on' method's signature.\n readonly eventHandlers: Map<\n // Key: Constructor of a specific event E (where E extends A)\n // Using 'any[]' for constructor arguments for broader compatibility.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (...args: any[]) => A,\n // Value: Handler function. 'event: A' is used here for the stored function type.\n // The 'on' method ensures the specific handler (event: E) is correctly typed.\n (event: A, emit: (newState: S) => void) => void | Promise<void>\n > = new Map();\n\n /**\n * Registers an event handler for a specific event type.\n * This method is typically called in the constructor of a derived Bloc class.\n * @param eventConstructor The constructor of the event to handle (e.g., LoadDataEvent).\n * @param handler A function that processes the event and can emit new states.\n * The 'event' parameter in the handler will be typed to the specific eventConstructor.\n */\n protected on<E extends A>(\n // Using 'any[]' for constructor arguments for broader compatibility.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n eventConstructor: new (...args: any[]) => E, \n handler: (event: E, emit: (newState: S) => void) => void | Promise<void>\n ): void {\n if (this.eventHandlers.has(eventConstructor)) {\n // Using Blac.warn or a similar logging mechanism from BlocBase if available,\n // otherwise console.warn. Assuming this._name and this._id are available from BlocBase.\n Blac.warn(\n `[Bloc ${this._name}:${String(this._id)}] Handler for event '${eventConstructor.name}' already registered. It will be overwritten.`\n );\n }\n // Cast the specific handler (event: E) to a more general (event: A) for storage.\n // This is safe because E extends A. When the handler is called with an 'action' of type A,\n // if it was originally registered for type E, 'action' must be an instance of E.\n this.eventHandlers.set(\n eventConstructor,\n handler as (event: A, emit: (newState: S) => void) => void | Promise<void>\n );\n }\n\n /**\n * Dispatches an action/event to the Bloc.\n * If a handler is registered for this specific event type (via 'on'), it will be invoked.\n * Asynchronous handlers are awaited.\n * @param action The action/event instance to be processed.\n */\n public add = async (action: A): Promise<void> => {\n // Using 'any[]' for constructor arguments for broader compatibility.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const eventConstructor = action.constructor as new (...args: any[]) => A;\n const handler = this.eventHandlers.get(eventConstructor);\n\n if (handler) {\n // Define the 'emit' function that handlers will use to update state.\n // It captures the current state ('this.state') right before each emission\n // to provide the correct 'previousState' to _pushState.\n const emit = (newState: S): void => {\n const previousState = this.state; // State just before this specific emission\n // The 'action' passed to _pushState is the original action that triggered the handler,\n // providing context for the state change (e.g., for logging or plugins).\n this._pushState(newState, previousState, action);\n };\n\n try {\n // Await the handler in case it's an async function (e.g., performs API calls).\n // The 'action' is passed to the handler, and due to the way 'on' is typed,\n // the 'event' parameter within the handler function will be correctly\n // typed to its specific class (e.g., LoadMyFeatureData).\n await handler(action, emit);\n } catch (error) {\n // It's good practice to handle errors occurring within event handlers.\n Blac.error(\n `[Bloc ${this._name}:${String(this._id)}] Error in event handler for '${eventConstructor.name}':`,\n error,\n \"Action:\", action\n );\n // Depending on the desired error handling strategy, you might:\n // 1. Emit a specific error state: this.emit(new MyErrorState(error));\n // 2. Re-throw the error: throw error;\n // 3. Log and ignore (as done here by default).\n // This should be decided based on application requirements.\n }\n } else {\n // action.constructor.name should be safe due to 'A extends object' and common JS practice for constructors.\n // If linting still complains, it might be overly strict for this common pattern.\n const constructorName = (action.constructor as { name?: string }).name || 'UnnamedConstructor';\n Blac.warn(\n `[Bloc ${this._name}:${String(this._id)}] No handler registered for action type: '${constructorName}'. Action was:`,\n action\n );\n // If no handler is found, the action is effectively ignored.\n // Consider if this is the desired behavior or if an error should be thrown\n // or a default handler should be invoked.\n }\n };\n}\n","import { BlocBase, BlocInstanceId } from '../BlocBase';\nimport BlacAddon, { BlacAddonEmit, BlacAddonInit } from './BlacAddon';\n\ntype StorageType = 'localStorage' | 'sessionStorage';\n\nfunction getStorage(type: StorageType): Storage {\n switch (type) {\n case 'localStorage':\n return localStorage;\n case 'sessionStorage':\n return sessionStorage;\n default:\n return localStorage;\n }\n}\n\n/**\n * Persist addon\n *\n * @param options\n * @returns BlacAddon\n */\nexport function Persist(\n options: {\n /**\n * @default 'blac'\n */\n keyPrefix?: string;\n /**\n * @default the bloc's id\n */\n keyName?: string;\n /**\n * Used when the value is not found in storage\n */\n defaultValue?: unknown;\n\n /**\n * @default 'localStorage'\n * @see StorageType\n */\n storageType?: StorageType;\n\n /**\n * @default false\n */\n onError?: (e: unknown) => void;\n } = {},\n): BlacAddon {\n const {\n keyPrefix = 'blac',\n keyName,\n defaultValue,\n storageType = 'localStorage',\n } = options;\n\n const fullKey = (id: string | BlocInstanceId) => `${keyPrefix}:${String(id)}`;\n\n const getFromLocalStorage = (id: string | BlocInstanceId): unknown => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n const value = getStorage(storageType).getItem(fullKey(id));\n if (typeof value !== 'string') {\n return defaultValue;\n }\n\n const p = JSON.parse(value) as { v: unknown };\n if (typeof p.v !== 'undefined') {\n return p.v;\n } else {\n return defaultValue;\n }\n } catch (e) {\n options.onError?.(e);\n return defaultValue;\n }\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const onInit: BlacAddonInit = (e: BlocBase<any>) => {\n const id = keyName ?? e._id;\n\n const value = getFromLocalStorage(id);\n e._pushState(value, null);\n };\n\n let currentCachedValue = '';\n const onEmit: BlacAddonEmit = ({ newState, cubit }) => {\n const id = keyName ?? cubit._id;\n\n const newValue = JSON.stringify({ v: newState });\n\n if (newValue !== currentCachedValue) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access\n getStorage(storageType).setItem(fullKey(id), newValue);\n currentCachedValue = newValue;\n }\n };\n\n return {\n name: 'Persist',\n onInit,\n onEmit,\n };\n}\n"],"names":["BlacLifecycleEvent","_Blac","options","__publicField","args","message","oldBlocInstanceMap","oldIsolatedBlocMap","bloc","blocArray","plugin","p","event","params","base","key","blocClass","id","blocId","isolatedBloc","registeredBloc","isIsolated","registered","results","blocInstance","isolatedBlocs","__unsafe_ignore_singleton","blocClassName","found","blocs","index","b","props","instanceRef","newBloc","Blac","BlacObservable","observer","newState","oldState","action","shouldUpdate","lastDependencyCheck","newDependencyCheck","o","partNew","partOld","i","_a","BlocBase","initialState","originalId","consumerId","addons","addon","constructorWithStaticProps","Cubit","state","statePatch","ignoreChangeCheck","changes","current","Bloc","eventConstructor","handler","emit","previousState","error","constructorName","getStorage","type","Persist","keyPrefix","keyName","defaultValue","storageType","fullKey","getFromLocalStorage","value","e","onInit","currentCachedValue","cubit","newValue"],"mappings":"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"} |
+21
| MIT License | ||
| Copyright (c) Facebook, Inc. and its affiliates. | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
+252
| # @blac/core | ||
| A lightweight, flexible state management library for JavaScript/TypeScript applications focusing on predictable state transitions. | ||
| ## Features | ||
| - 🔄 Predictable unidirectional data flow | ||
| - 🧩 Modular architecture with Blocs and Cubits | ||
| - 🧪 Unit test friendly | ||
| - 🔒 Isolated state instances when needed | ||
| - 🔌 Plugin system for extensibility | ||
| ## Installation | ||
| Install `@blac/core` using your favorite package manager: | ||
| ```bash | ||
| # pnpm | ||
| pnpm add @blac/core | ||
| # yarn | ||
| yarn add @blac/core | ||
| # npm | ||
| npm install @blac/core | ||
| ``` | ||
| ## Core Concepts | ||
| ### Blocs and Cubits | ||
| **Cubit**: A simple state container with methods to emit new states. | ||
| ```typescript | ||
| class CounterCubit extends Cubit<number> { | ||
| constructor() { | ||
| super(0); // Initial state | ||
| } | ||
| increment = () => { | ||
| this.emit(this.state + 1); | ||
| } | ||
| decrement = () => { | ||
| this.emit(this.state - 1); | ||
| } | ||
| } | ||
| ``` | ||
| **Bloc**: More powerful state container with a reducer pattern for action-based state transitions. | ||
| ```typescript | ||
| // Define actions | ||
| type CounterAction = | ||
| | { type: 'increment', amount: number } | ||
| | { type: 'decrement', amount: number }; | ||
| class CounterBloc extends Bloc<number, CounterAction> { | ||
| constructor() { | ||
| super(0); // Initial state | ||
| } | ||
| reducer = (action: CounterAction, state: number): number => { | ||
| switch (action.type) { | ||
| case 'increment': | ||
| return state + action.amount; | ||
| case 'decrement': | ||
| return state - action.amount; | ||
| } | ||
| } | ||
| increment = (amount = 1) => { | ||
| this.add({ type: 'increment', amount }); | ||
| } | ||
| decrement = (amount = 1) => { | ||
| this.add({ type: 'decrement', amount }); | ||
| } | ||
| } | ||
| ``` | ||
| ### Important: Arrow Functions Required | ||
| All methods in Bloc or Cubit classes must use arrow function syntax (`method = () => {}`) instead of the traditional method syntax (`method() {}`). This is because arrow functions automatically bind `this` to the class instance. Without this binding, methods called from React components would lose their context and could not access instance properties like `this.state` or `this.emit()`. | ||
| ### State Management Patterns | ||
| #### Shared State (Default) | ||
| By default, bloc instances are shared across all consumers: | ||
| ```typescript | ||
| class GlobalCounterCubit extends Cubit<number> { | ||
| constructor() { | ||
| super(0); | ||
| } | ||
| increment = () => { | ||
| this.emit(this.state + 1); | ||
| } | ||
| } | ||
| ``` | ||
| #### Isolated State | ||
| When each consumer needs its own state instance: | ||
| ```typescript | ||
| class LocalCounterCubit extends Cubit<number> { | ||
| static isolated = true; // Each consumer gets its own instance | ||
| constructor() { | ||
| super(0); | ||
| } | ||
| increment = () => { | ||
| this.emit(this.state + 1); | ||
| } | ||
| } | ||
| ``` | ||
| #### Persistent State | ||
| Keep state alive even when no consumers are using it: | ||
| ```typescript | ||
| class PersistentCounterCubit extends Cubit<number> { | ||
| static keepAlive = true; // State persists even when no consumers | ||
| constructor() { | ||
| super(0); | ||
| } | ||
| increment = () => { | ||
| this.emit(this.state + 1); | ||
| } | ||
| } | ||
| ``` | ||
| ## Advanced Usage | ||
| ### Custom Plugins | ||
| Create plugins to add functionality like logging, persistence, or analytics: | ||
| ```typescript | ||
| import { BlacPlugin, BlacLifecycleEvent, BlocBase } from '@blac/core'; | ||
| class LoggerPlugin implements BlacPlugin { | ||
| name = 'LoggerPlugin'; | ||
| onEvent(event: BlacLifecycleEvent, bloc: BlocBase, params?: any) { | ||
| if (event === BlacLifecycleEvent.STATE_CHANGED) { | ||
| console.log(`[${bloc._name}] State changed:`, bloc.state); | ||
| } | ||
| } | ||
| } | ||
| // Add the plugin to Blac | ||
| import { Blac } from '@blac/core'; | ||
| Blac.addPlugin(new LoggerPlugin()); | ||
| ``` | ||
| ### Using Props with Blocs | ||
| Blocs can be designed to accept properties through their constructor, allowing for configurable instances. Here's an example of a `UserProfileBloc` that takes a `userId` prop: | ||
| ```typescript | ||
| import { Bloc } from '@blac/core'; // Or your specific import path | ||
| // Define props interface (optional, but good practice) | ||
| interface UserProfileProps { | ||
| userId: string; | ||
| } | ||
| // Define state interface | ||
| interface UserProfileState { | ||
| loading: boolean; | ||
| userData: { id: string; name: string; bio?: string } | null; | ||
| error: string | null; | ||
| } | ||
| // Define actions (if any, for this example we'll focus on constructor and an async method) | ||
| type UserProfileAction = { type: 'dataLoaded', data: any } | { type: 'error', error: string }; | ||
| class UserProfileBloc extends Bloc<UserProfileState, UserProfileAction> { | ||
| private userId: string; | ||
| // The Blac library or its React bindings (like @blac/react) | ||
| // might provide a way to pass these props during instantiation. | ||
| // For example, `useBloc(UserProfileBloc, { props: { userId: '123' } })` | ||
| constructor(props: UserProfileProps) { | ||
| super({ loading: true, userData: null, error: null }); // Initial state | ||
| this.userId = props.userId; | ||
| // Optional: Set a dynamic name for easier debugging with multiple instances | ||
| this._name = `UserProfileBloc_${this.userId}`; | ||
| this.fetchUserProfile(); | ||
| } | ||
| // Example reducer | ||
| reducer = (action: UserProfileAction, state: UserProfileState): UserProfileState => { | ||
| switch (action.type) { | ||
| case 'dataLoaded': | ||
| return { ...state, loading: false, userData: action.data, error: null }; | ||
| case 'error': | ||
| return { ...state, loading: false, error: action.error }; | ||
| default: | ||
| return state; | ||
| } | ||
| } | ||
| fetchUserProfile = async ()_ => { | ||
| this.emit({ ...this.state, loading: true }); // Set loading true before fetch | ||
| try { | ||
| // Simulate an API call | ||
| await new Promise(resolve => setTimeout(resolve, 1000)); | ||
| const mockUserData = { id: this.userId, name: `User ${this.userId}`, bio: 'Loves Blac states!' }; | ||
| // Dispatch an action or directly emit a new state | ||
| this.add({ type: 'dataLoaded', data: mockUserData }); | ||
| } catch (e:any) { | ||
| this.add({ type: 'error', error: e.message || 'Failed to fetch user profile' }); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| ## API Reference | ||
| ### Core Classes | ||
| - `BlocBase<S, P>`: Base class for state containers | ||
| - `Cubit<S, P>`: Simple state container with `emit()` and `patch()` | ||
| - `Bloc<S, A, P>`: Action-based state container with a reducer pattern | ||
| - `Blac`: Singleton manager for all Bloc instances | ||
| ### React Hooks | ||
| - `useBloc<B>(BlocClass, options?)`: Connect a component to a Bloc | ||
| ### Lifecycle Events | ||
| - `BLOC_CREATED`: When a new Bloc is instantiated | ||
| - `BLOC_DISPOSED`: When a Bloc is disposed | ||
| - `LISTENER_ADDED`: When a state listener is added | ||
| - `LISTENER_REMOVED`: When a state listener is removed | ||
| - `STATE_CHANGED`: When state is updated | ||
| - `BLOC_CONSUMER_ADDED`: When a new consumer starts using a Bloc | ||
| - `BLOC_CONSUMER_REMOVED`: When a consumer stops using a Bloc | ||
| ## License | ||
| This project is licensed under the MIT License - see the [LICENSE](../../LICENSE) file for details. |
| import { BlocBase } from '../BlocBase'; | ||
| export type BlacAddonInit = (bloc: BlocBase<any>) => void; | ||
| export type BlacAddonEmit = (params: { | ||
| oldState: unknown; | ||
| newState: unknown; | ||
| cubit: BlocBase<any>; | ||
| }) => void; | ||
| type BlacAddon = { | ||
| name: string; | ||
| onInit?: BlacAddonInit; | ||
| onEmit?: BlacAddonEmit; | ||
| }; | ||
| export default BlacAddon; |
| export * from './Persist'; |
| import { BlocBase, BlocInstanceId } from '../BlocBase'; | ||
| import BlacAddon, { BlacAddonEmit, BlacAddonInit } from './BlacAddon'; | ||
| type StorageType = 'localStorage' | 'sessionStorage'; | ||
| function getStorage(type: StorageType): Storage { | ||
| switch (type) { | ||
| case 'localStorage': | ||
| return localStorage; | ||
| case 'sessionStorage': | ||
| return sessionStorage; | ||
| default: | ||
| return localStorage; | ||
| } | ||
| } | ||
| /** | ||
| * Persist addon | ||
| * | ||
| * @param options | ||
| * @returns BlacAddon | ||
| */ | ||
| export function Persist( | ||
| options: { | ||
| /** | ||
| * @default 'blac' | ||
| */ | ||
| keyPrefix?: string; | ||
| /** | ||
| * @default the bloc's id | ||
| */ | ||
| keyName?: string; | ||
| /** | ||
| * Used when the value is not found in storage | ||
| */ | ||
| defaultValue?: unknown; | ||
| /** | ||
| * @default 'localStorage' | ||
| * @see StorageType | ||
| */ | ||
| storageType?: StorageType; | ||
| /** | ||
| * @default false | ||
| */ | ||
| onError?: (e: unknown) => void; | ||
| } = {}, | ||
| ): BlacAddon { | ||
| const { | ||
| keyPrefix = 'blac', | ||
| keyName, | ||
| defaultValue, | ||
| storageType = 'localStorage', | ||
| } = options; | ||
| const fullKey = (id: string | BlocInstanceId) => `${keyPrefix}:${String(id)}`; | ||
| const getFromLocalStorage = (id: string | BlocInstanceId): unknown => { | ||
| try { | ||
| // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access | ||
| const value = getStorage(storageType).getItem(fullKey(id)); | ||
| if (typeof value !== 'string') { | ||
| return defaultValue; | ||
| } | ||
| const p = JSON.parse(value) as { v: unknown }; | ||
| if (typeof p.v !== 'undefined') { | ||
| return p.v; | ||
| } else { | ||
| return defaultValue; | ||
| } | ||
| } catch (e) { | ||
| options.onError?.(e); | ||
| return defaultValue; | ||
| } | ||
| }; | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| const onInit: BlacAddonInit = (e: BlocBase<any>) => { | ||
| const id = keyName ?? e._id; | ||
| const value = getFromLocalStorage(id); | ||
| e._pushState(value, null); | ||
| }; | ||
| let currentCachedValue = ''; | ||
| const onEmit: BlacAddonEmit = ({ newState, cubit }) => { | ||
| const id = keyName ?? cubit._id; | ||
| const newValue = JSON.stringify({ v: newState }); | ||
| if (newValue !== currentCachedValue) { | ||
| // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access | ||
| getStorage(storageType).setItem(fullKey(id), newValue); | ||
| currentCachedValue = newValue; | ||
| } | ||
| }; | ||
| return { | ||
| name: 'Persist', | ||
| onInit, | ||
| onEmit, | ||
| }; | ||
| } |
+483
| /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
| import { BlacPlugin } from "./BlacPlugin"; | ||
| import { BlocBase, BlocInstanceId } from "./BlocBase"; | ||
| import { | ||
| BlocBaseAbstract, | ||
| BlocConstructor, | ||
| BlocHookDependencyArrayFn, | ||
| BlocState, | ||
| InferPropsFromGeneric | ||
| } from "./types"; | ||
| /** | ||
| * Configuration options for the Blac instance | ||
| */ | ||
| export interface BlacConfig { | ||
| /** Whether to expose the Blac instance globally */ | ||
| exposeBlacInstance?: boolean; | ||
| } | ||
| export interface GetBlocOptions<B extends BlocBase<any>> { | ||
| id?: string; | ||
| dependencySelector?: BlocHookDependencyArrayFn<BlocState<B>>; | ||
| props?: InferPropsFromGeneric<B>; | ||
| onMount?: (bloc: B) => void; | ||
| instanceRef?: string; | ||
| } | ||
| /** | ||
| * Enum representing different lifecycle events that can occur in the Blac system. | ||
| * These events are used to track the lifecycle of blocs and their consumers. | ||
| */ | ||
| export enum BlacLifecycleEvent { | ||
| BLOC_DISPOSED = "BLOC_DISPOSED", | ||
| BLOC_CREATED = "BLOC_CREATED", | ||
| LISTENER_REMOVED = "LISTENER_REMOVED", | ||
| LISTENER_ADDED = "LISTENER_ADDED", | ||
| STATE_CHANGED = "STATE_CHANGED", | ||
| BLOC_CONSUMER_REMOVED = "BLOC_CONSUMER_REMOVED", | ||
| BLOC_CONSUMER_ADDED = "BLOC_CONSUMER_ADDED", | ||
| } | ||
| /** | ||
| * Main Blac class that manages the state management system. | ||
| * Implements a singleton pattern to ensure only one instance exists. | ||
| * Handles bloc lifecycle, plugin management, and instance tracking. | ||
| * | ||
| * Key responsibilities: | ||
| * - Managing bloc instances (creation, disposal, lookup) | ||
| * - Handling isolated and non-isolated blocs | ||
| * - Managing plugins and lifecycle events | ||
| * - Providing logging and debugging capabilities | ||
| */ | ||
| export class Blac { | ||
| /** The singleton instance of Blac */ | ||
| static instance: Blac = new Blac(); | ||
| /** Timestamp when the instance was created */ | ||
| createdAt = Date.now(); | ||
| static getAllBlocs = Blac.instance.getAllBlocs; | ||
| static addPlugin = Blac.instance.addPlugin; | ||
| /** Map storing all registered bloc instances by their class name and ID */ | ||
| blocInstanceMap: Map<string, BlocBase<any>> = new Map(); | ||
| /** Map storing isolated bloc instances grouped by their constructor */ | ||
| isolatedBlocMap: Map<BlocConstructor<any>, BlocBase<any>[]> = new Map(); | ||
| pluginList: BlacPlugin[] = []; | ||
| /** Flag to control whether changes should be posted to document */ | ||
| postChangesToDocument = false; | ||
| /** | ||
| * Creates a new Blac instance. | ||
| * @param options - Configuration options including singleton control | ||
| */ | ||
| constructor(options: { __unsafe_ignore_singleton?: boolean } = {}) { | ||
| const { __unsafe_ignore_singleton = false } = options; | ||
| if (!__unsafe_ignore_singleton) { | ||
| return Blac.instance; | ||
| } | ||
| Blac.instance = this; | ||
| } | ||
| /** Flag to enable/disable logging */ | ||
| static enableLog = false; | ||
| /** | ||
| * Logs messages to console when logging is enabled | ||
| * @param args - Arguments to log | ||
| */ | ||
| log = (...args: unknown[]) => { | ||
| if (Blac.enableLog) console.warn(`☢️ [Blac ${this.createdAt.toString()}]`, ...args); | ||
| }; | ||
| static log = Blac.instance.log; | ||
| /** | ||
| * Gets the singleton instance of Blac | ||
| * @returns The Blac instance | ||
| */ | ||
| static getInstance(): Blac { | ||
| return Blac.instance; | ||
| } | ||
| /** | ||
| * Logs a warning message | ||
| * @param message - Warning message | ||
| * @param args - Additional arguments | ||
| */ | ||
| warn = (message: string, ...args: unknown[]) => { | ||
| if (Blac.enableLog) { | ||
| console.warn(`🚨 [Blac ${String(Blac.instance.createdAt)}]`, message, ...args); | ||
| } | ||
| }; | ||
| static warn = Blac.instance.warn; | ||
| /** | ||
| * Logs an error message | ||
| * @param message - Error message | ||
| * @param args - Additional arguments | ||
| */ | ||
| error = (message: string, ...args: unknown[]) => { | ||
| if (Blac.enableLog) { | ||
| console.error(`🚨 [Blac ${String(Blac.instance.createdAt)}]`, message, ...args); | ||
| } | ||
| }; | ||
| static error = Blac.instance.error; | ||
| /** | ||
| * Resets the Blac instance to a new one, disposing non-keepAlive blocs | ||
| * from the old instance. | ||
| */ | ||
| resetInstance = (): void => { | ||
| this.log("Reset Blac instance"); | ||
| // Dispose non-keepAlive blocs from the current instance | ||
| const oldBlocInstanceMap = new Map(this.blocInstanceMap); | ||
| const oldIsolatedBlocMap = new Map(this.isolatedBlocMap); | ||
| oldBlocInstanceMap.forEach((bloc) => { | ||
| bloc._dispose(); | ||
| }); | ||
| oldIsolatedBlocMap.forEach((blocArray) => { | ||
| blocArray.forEach((bloc) => { | ||
| bloc._dispose(); | ||
| }); | ||
| }); | ||
| this.blocInstanceMap.clear(); | ||
| this.isolatedBlocMap.clear(); | ||
| // Create and assign the new instance | ||
| Blac.instance = new Blac({ | ||
| __unsafe_ignore_singleton: true, | ||
| }); | ||
| } | ||
| static resetInstance = Blac.instance.resetInstance; | ||
| /** | ||
| * Adds a plugin to the Blac instance | ||
| * @param plugin - The plugin to add | ||
| */ | ||
| addPlugin = (plugin: BlacPlugin): void => { | ||
| // check if already added | ||
| const index = this.pluginList.findIndex((p) => p.name === plugin.name); | ||
| if (index !== -1) return; | ||
| this.log("Add plugin", plugin.name); | ||
| this.pluginList.push(plugin); | ||
| }; | ||
| /** | ||
| * Dispatches a lifecycle event to all registered plugins | ||
| * @param event - The lifecycle event to dispatch | ||
| * @param bloc - The bloc instance involved in the event | ||
| * @param params - Additional parameters for the event | ||
| */ | ||
| dispatchEventToPlugins = ( | ||
| event: BlacLifecycleEvent, | ||
| bloc: BlocBase<any>, | ||
| params?: unknown, | ||
| ) => { | ||
| this.pluginList.forEach((plugin) => { | ||
| plugin.onEvent(event, bloc, params); | ||
| }); | ||
| }; | ||
| /** | ||
| * Dispatches a lifecycle event and handles related cleanup actions. | ||
| * This method is responsible for: | ||
| * - Logging the event | ||
| * - Handling bloc disposal when needed | ||
| * - Managing bloc consumer cleanup | ||
| * - Forwarding the event to plugins | ||
| * | ||
| * @param event - The lifecycle event to dispatch | ||
| * @param bloc - The bloc instance involved in the event | ||
| * @param params - Additional parameters for the event | ||
| */ | ||
| dispatchEvent = ( | ||
| event: BlacLifecycleEvent, | ||
| bloc: BlocBase<any>, | ||
| params?: unknown, | ||
| ) => { | ||
| this.log(event, bloc, params); | ||
| switch (event) { | ||
| case BlacLifecycleEvent.BLOC_DISPOSED: | ||
| this.disposeBloc(bloc); | ||
| break; | ||
| case BlacLifecycleEvent.BLOC_CONSUMER_REMOVED: | ||
| case BlacLifecycleEvent.LISTENER_REMOVED: | ||
| this.log(`[${bloc._name}:${String(bloc._id)}] Listener/Consumer removed. Listeners: ${String(bloc._observer.size)}, Consumers: ${String(bloc._consumers.size)}, KeepAlive: ${String(bloc._keepAlive)}`); | ||
| if ( | ||
| bloc._observer.size === 0 && | ||
| bloc._consumers.size === 0 && | ||
| !bloc._keepAlive | ||
| ) { | ||
| this.log(`[${bloc._name}:${String(bloc._id)}] No listeners or consumers left and not keepAlive. Disposing.`); | ||
| bloc._dispose(); | ||
| } | ||
| break; | ||
| } | ||
| this.dispatchEventToPlugins(event, bloc, params); | ||
| }; | ||
| /** | ||
| * Disposes of a bloc instance by removing it from the appropriate registry | ||
| * @param bloc - The bloc instance to dispose | ||
| */ | ||
| disposeBloc = (bloc: BlocBase<any>): void => { | ||
| const base = bloc.constructor as unknown as BlocBaseAbstract; | ||
| const key = this.createBlocInstanceMapKey(bloc._name, bloc._id); | ||
| this.log(`[${bloc._name}:${String(bloc._id)}] disposeBloc called. Isolated: ${String(base.isolated)}`); | ||
| if (base.isolated) { | ||
| this.unregisterIsolatedBlocInstance(bloc); | ||
| this.blocInstanceMap.delete(key); | ||
| } else { | ||
| this.unregisterBlocInstance(bloc); | ||
| } | ||
| this.dispatchEventToPlugins(BlacLifecycleEvent.BLOC_DISPOSED, bloc); | ||
| }; | ||
| /** | ||
| * Creates a unique key for a bloc instance in the map based on the bloc class name and instance ID | ||
| * @param blocClassName - The name of the bloc class | ||
| * @param id - The instance ID | ||
| * @returns A unique key string in the format "className:id" | ||
| */ | ||
| createBlocInstanceMapKey(blocClassName: string, id: BlocInstanceId): string { | ||
| return `${blocClassName}:${String(id)}`; | ||
| } | ||
| /** | ||
| * Unregister a bloc instance from the main registry | ||
| * @param bloc - The bloc instance to unregister | ||
| */ | ||
| unregisterBlocInstance(bloc: BlocBase<any>): void { | ||
| const key = this.createBlocInstanceMapKey(bloc._name, bloc._id); | ||
| this.blocInstanceMap.delete(key); | ||
| } | ||
| /** | ||
| * Registers a bloc instance in the main registry | ||
| * @param bloc - The bloc instance to register | ||
| */ | ||
| registerBlocInstance(bloc: BlocBase<any>): void { | ||
| const key = this.createBlocInstanceMapKey(bloc._name, bloc._id); | ||
| this.blocInstanceMap.set(key, bloc); | ||
| } | ||
| /** | ||
| * Finds a registered bloc instance by its class and ID | ||
| * @param blocClass - The bloc class to search for | ||
| * @param id - The instance ID | ||
| * @returns The found bloc instance or undefined if not found | ||
| */ | ||
| findRegisteredBlocInstance<B extends BlocConstructor<unknown>>( | ||
| blocClass: B, | ||
| id: BlocInstanceId, | ||
| ): InstanceType<B> | undefined { | ||
| const base = blocClass as unknown as BlocBaseAbstract; | ||
| if (base.isolated) return undefined; | ||
| const key = this.createBlocInstanceMapKey(blocClass.name, id); | ||
| const found = this.blocInstanceMap.get(key) as InstanceType<B> | undefined; | ||
| if (found) { | ||
| this.log(`[${blocClass.name}:${String(id)}] Found registered instance. Returning.`); | ||
| } | ||
| return found | ||
| } | ||
| /** | ||
| * Registers an isolated bloc instance in the isolated registry | ||
| * @param bloc - The isolated bloc instance to register | ||
| */ | ||
| registerIsolatedBlocInstance(bloc: BlocBase<any>): void { | ||
| const blocClass = bloc.constructor as BlocConstructor<unknown>; | ||
| const blocs = this.isolatedBlocMap.get(blocClass); | ||
| if (blocs) { | ||
| blocs.push(bloc); | ||
| } else { | ||
| this.isolatedBlocMap.set(blocClass, [bloc]); | ||
| } | ||
| } | ||
| /** | ||
| * Unregister an isolated bloc instance from the isolated registry | ||
| * @param bloc - The isolated bloc instance to unregister | ||
| */ | ||
| unregisterIsolatedBlocInstance(bloc: BlocBase<any>): void { | ||
| const blocClass = bloc.constructor; | ||
| const blocs = this.isolatedBlocMap.get(blocClass as BlocConstructor<unknown>); | ||
| if (blocs) { | ||
| const index = blocs.findIndex((b) => b._id === bloc._id); | ||
| if (index !== -1) { | ||
| blocs.splice(index, 1); | ||
| } | ||
| if (blocs.length === 0) { | ||
| this.isolatedBlocMap.delete(blocClass as BlocConstructor<unknown>); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Finds an isolated bloc instance by its class and ID | ||
| */ | ||
| findIsolatedBlocInstance<B extends BlocConstructor<unknown>>( | ||
| blocClass: B, | ||
| id: BlocInstanceId, | ||
| ): InstanceType<B> | undefined { | ||
| const base = blocClass as unknown as BlocBaseAbstract; | ||
| if (!base.isolated) return undefined; | ||
| const blocs = this.isolatedBlocMap.get(blocClass); | ||
| if (!blocs) return undefined; | ||
| // Fix: Find the specific bloc by ID within the isolated array | ||
| const found = blocs.find((b) => b._id === id) as InstanceType<B> | undefined; | ||
| if (found) { | ||
| this.log(`[${blocClass.name}:${String(id)}] Found isolated instance. Returning.`); | ||
| } | ||
| return found; | ||
| } | ||
| /** | ||
| * Creates a new bloc instance and registers it in the appropriate registry | ||
| * @param blocClass - The bloc class to instantiate | ||
| * @param id - The instance ID | ||
| * @param props - Properties to pass to the bloc constructor | ||
| * @param instanceRef - Optional reference string for the instance | ||
| * @returns The newly created bloc instance | ||
| */ | ||
| createNewBlocInstance<B extends BlocConstructor<BlocBase<any>>>( | ||
| blocClass: B, | ||
| id: BlocInstanceId, | ||
| options: GetBlocOptions<InstanceType<B>> = {}, | ||
| ): InstanceType<B> { | ||
| const { props, instanceRef } = options; | ||
| const newBloc = new blocClass(props as never) as InstanceType<BlocConstructor<BlocBase<unknown>>>; | ||
| newBloc._instanceRef = instanceRef; | ||
| newBloc.props = props || null; | ||
| newBloc._updateId(id); | ||
| if (newBloc.isIsolated) { | ||
| this.registerIsolatedBlocInstance(newBloc); | ||
| return newBloc as InstanceType<B>; | ||
| } | ||
| this.registerBlocInstance(newBloc); | ||
| return newBloc as InstanceType<B>; | ||
| } | ||
| /** | ||
| * Gets or creates a bloc instance based on the provided class and options. | ||
| * If a bloc with the given ID exists, it will be returned. Otherwise, a new instance will be created. | ||
| * | ||
| * @param blocClass - The bloc class to get or create | ||
| * @param options - Options including: | ||
| * - id: The instance ID (defaults to class name if not provided) | ||
| * - props: Properties to pass to the bloc constructor | ||
| * - instanceRef: Optional reference string for the instance | ||
| * @returns The bloc instance | ||
| */ | ||
| getBloc = <B extends BlocConstructor<BlocBase<any>>>( | ||
| blocClass: B, | ||
| options: GetBlocOptions<InstanceType<B>> = {}, | ||
| ): InstanceType<B> => { | ||
| const { id } = options; | ||
| const base = blocClass as unknown as BlocBaseAbstract; | ||
| const blocId = id ?? blocClass.name; | ||
| this.log(`[${blocClass.name}:${String(blocId)}] getBloc called. Options:`, options); | ||
| if (base.isolated) { | ||
| const isolatedBloc = this.findIsolatedBlocInstance<B>(blocClass, blocId) | ||
| if (isolatedBloc) { | ||
| this.log(`[${blocClass.name}:${String(blocId)}] Found existing isolated instance.`); | ||
| return isolatedBloc; | ||
| } | ||
| } | ||
| if (!base.isolated) { | ||
| const registeredBloc = this.findRegisteredBlocInstance(blocClass, blocId) | ||
| if (registeredBloc) { | ||
| this.log(`[${blocClass.name}:${String(blocId)}] Found existing registered instance.`); | ||
| return registeredBloc | ||
| } | ||
| } | ||
| this.log(`[${blocClass.name}:${String(blocId)}] No existing instance found. Creating new one.`); | ||
| return this.createNewBlocInstance( | ||
| blocClass, | ||
| blocId, | ||
| options, | ||
| ); | ||
| }; | ||
| static getBloc = Blac.instance.getBloc; | ||
| /** | ||
| * Gets a bloc instance or throws an error if it doesn't exist | ||
| * @param blocClass - The bloc class to get | ||
| * @param options - Options including: | ||
| * - id: The instance ID (defaults to class name if not provided) | ||
| * - props: Properties to pass to the bloc constructor | ||
| * - instanceRef: Optional reference string for the instance | ||
| */ | ||
| getBlocOrThrow = <B extends BlocConstructor<unknown>>( | ||
| blocClass: B, | ||
| options: { | ||
| id?: BlocInstanceId; | ||
| props?: InferPropsFromGeneric<B>; | ||
| instanceRef?: string; | ||
| } = {}, | ||
| ): InstanceType<B> => { | ||
| const isIsolated = (blocClass as unknown as BlocBaseAbstract).isolated; | ||
| const id = options.id || blocClass.name; | ||
| const registered = isIsolated | ||
| ? this.findIsolatedBlocInstance(blocClass, id) | ||
| : this.findRegisteredBlocInstance(blocClass, id); | ||
| if (registered) { | ||
| return registered; | ||
| } | ||
| throw new Error(`Bloc ${blocClass.name} not found`); | ||
| }; | ||
| static getBlocOrThrow = Blac.instance.getBlocOrThrow; | ||
| /** | ||
| * Gets all instances of a specific bloc class | ||
| * @param blocClass - The bloc class to search for | ||
| * @param options - Options including: | ||
| * - searchIsolated: Whether to search in isolated blocs (defaults to bloc's isolated property) | ||
| * @returns Array of matching bloc instances | ||
| */ | ||
| getAllBlocs = <B extends BlocConstructor<unknown>>( | ||
| blocClass: B, | ||
| options: { | ||
| searchIsolated?: boolean; | ||
| } = {}, | ||
| ): InstanceType<B>[] => { | ||
| const results: InstanceType<B>[] = []; | ||
| // const blocClassName = (blocClass as any).name; // Temporarily removed for debugging | ||
| // Search non-isolated blocs | ||
| this.blocInstanceMap.forEach((blocInstance) => { | ||
| if (blocInstance.constructor === blocClass) { // Strict constructor check | ||
| results.push(blocInstance as InstanceType<B>); | ||
| } | ||
| }); | ||
| // Optionally search isolated blocs | ||
| if (options.searchIsolated !== false) { | ||
| const isolatedBlocs = this.isolatedBlocMap.get(blocClass); | ||
| if (isolatedBlocs) { | ||
| results.push(...isolatedBlocs.map(bloc => bloc as InstanceType<B>)); | ||
| } | ||
| } | ||
| return results; | ||
| }; | ||
| } |
| export default class BlacEvent<T = any> extends CustomEvent<T> { | ||
| constructor(type: string, eventInitDict?: CustomEventInit<T> | undefined) { | ||
| super(type, eventInitDict); | ||
| } | ||
| } |
| import { Blac, BlacLifecycleEvent } from './Blac'; | ||
| import { BlocBase } from './BlocBase'; | ||
| import { BlocHookDependencyArrayFn } from './types'; | ||
| /** | ||
| * Represents an observer that can subscribe to state changes in a Bloc | ||
| * @template S - The type of state being observed | ||
| */ | ||
| export type BlacObserver<S> = { | ||
| /** Function to be called when state changes */ | ||
| fn: (newState: S, oldState: S, action?: unknown) => void | Promise<void>; | ||
| /** Optional function to determine if the observer should be notified of state changes */ | ||
| dependencyArray?: BlocHookDependencyArrayFn<S>; | ||
| /** Cached state values used for dependency comparison */ | ||
| lastState?: unknown[][]; | ||
| /** Unique identifier for the observer */ | ||
| id: string; | ||
| }; | ||
| /** | ||
| * A class that manages observers for a Bloc's state changes | ||
| * @template S - The type of state being observed | ||
| */ | ||
| export class BlacObservable<S = unknown> { | ||
| /** The Bloc instance this observable is associated with */ | ||
| bloc: BlocBase<S>; | ||
| /** | ||
| * Creates a new BlacObservable instance | ||
| * @param bloc - The Bloc instance to observe | ||
| */ | ||
| constructor(bloc: BlocBase<S>) { | ||
| this.bloc = bloc; | ||
| } | ||
| private _observers = new Set<BlacObserver<S>>(); | ||
| /** | ||
| * Gets the number of active observers | ||
| * @returns The number of observers currently subscribed | ||
| */ | ||
| get size(): number { | ||
| return this._observers.size; | ||
| } | ||
| /** | ||
| * Gets the set of all observers | ||
| * @returns The Set of all BlacObserver instances | ||
| */ | ||
| get observers() { | ||
| return this._observers; | ||
| } | ||
| /** | ||
| * Subscribes an observer to state changes | ||
| * @param observer - The observer to subscribe | ||
| * @returns A function that can be called to unsubscribe the observer | ||
| */ | ||
| subscribe(observer: BlacObserver<S>): () => void { | ||
| this._observers.add(observer); | ||
| Blac.instance.dispatchEvent(BlacLifecycleEvent.LISTENER_ADDED, this.bloc, { listenerId: observer.id }); | ||
| // Immediately notify the new observer with the current state | ||
| // Pass current state as both newState and oldState for initial notification context | ||
| void observer.fn(this.bloc.state, this.bloc.state, { initialSubscription: true }); | ||
| if (!observer.lastState) { | ||
| observer.lastState = observer.dependencyArray | ||
| ? observer.dependencyArray(this.bloc.state, this.bloc.state) | ||
| : []; | ||
| } | ||
| return () => { | ||
| this.unsubscribe(observer); | ||
| } | ||
| } | ||
| /** | ||
| * Unsubscribes an observer from state changes | ||
| * @param observer - The observer to unsubscribe | ||
| */ | ||
| unsubscribe(observer: BlacObserver<S>) { | ||
| this._observers.delete(observer); | ||
| Blac.instance.dispatchEvent(BlacLifecycleEvent.LISTENER_REMOVED, this.bloc, { listenerId: observer.id }); | ||
| } | ||
| } | ||
| /** | ||
| import { Blac, BlacLifecycleEvent } from './Blac'; | ||
| import { BlocBase } from './BlocBase'; | ||
| import { BlocHookDependencyArrayFn } from './types'; | ||
| /** | ||
| * Represents an observer that can subscribe to state changes in a Bloc | ||
| * @template S - The type of state being observed | ||
| */ | ||
| export type BlacObserver<S> = { | ||
| /** Function to be called when state changes */ | ||
| fn: (newState: S, oldState: S, action?: unknown) => void | Promise<void>; | ||
| /** Optional function to determine if the observer should be notified of state changes */ | ||
| dependencyArray?: BlocHookDependencyArrayFn<S>; | ||
| /** Dispose function for the observer */ | ||
| dispose?: () => void; | ||
| /** Cached state values used for dependency comparison */ | ||
| lastState?: unknown[][]; | ||
| /** Unique identifier for the observer */ | ||
| id: string; | ||
| }; | ||
| /** | ||
| * A class that manages observers for a Bloc's state changes | ||
| * @template S - The type of state being observed | ||
| */ | ||
| export class BlacObservable<S = unknown> { | ||
| /** The Bloc instance this observable is associated with */ | ||
| bloc: BlocBase<S>; | ||
| /** | ||
| * Creates a new BlacObservable instance | ||
| * @param bloc - The Bloc instance to observe | ||
| */ | ||
| constructor(bloc: BlocBase<S>) { | ||
| this.bloc = bloc; | ||
| } | ||
| private _observers = new Set<BlacObserver<S>>(); | ||
| /** | ||
| * Gets the number of active observers | ||
| * @returns The number of observers currently subscribed | ||
| */ | ||
| get size(): number { | ||
| return this._observers.size; | ||
| } | ||
| /** | ||
| * Gets the set of all observers | ||
| * @returns The Set of all BlacObserver instances | ||
| */ | ||
| get observers() { | ||
| return this._observers; | ||
| } | ||
| /** | ||
| * Subscribes an observer to state changes | ||
| * @param observer - The observer to subscribe | ||
| * @returns A function that can be called to unsubscribe the observer | ||
| */ | ||
| subscribe(observer: BlacObserver<S>): () => void { | ||
| this._observers.add(observer); | ||
| Blac.instance.dispatchEvent(BlacLifecycleEvent.LISTENER_ADDED, this.bloc, { listenerId: observer.id }); | ||
| if (!observer.lastState) { | ||
| observer.lastState = observer.dependencyArray | ||
| ? observer.dependencyArray(this.bloc.state, this.bloc.state) | ||
| : []; | ||
| } | ||
| return () => { | ||
| this.unsubscribe(observer); | ||
| } | ||
| } | ||
| /** | ||
| * Unsubscribes an observer from state changes | ||
| * @param observer - The observer to unsubscribe | ||
| */ | ||
| unsubscribe(observer: BlacObserver<S>) { | ||
| this._observers.delete(observer); | ||
| Blac.instance.dispatchEvent(BlacLifecycleEvent.LISTENER_REMOVED, this.bloc, { listenerId: observer.id }); | ||
| } | ||
| /** | ||
| * Notifies all observers of a state change | ||
| * @param newState - The new state value | ||
| * @param oldState - The previous state value | ||
| * @param action - Optional action that triggered the state change | ||
| */ | ||
| notify(newState: S, oldState: S, action?: unknown) { | ||
| this._observers.forEach((observer) => { | ||
| let shouldUpdate = false; | ||
| if (observer.dependencyArray) { | ||
| const lastDependencyCheck = observer.lastState || []; | ||
| const newDependencyCheck = observer.dependencyArray(newState, oldState); | ||
| for (let o = 0; o < newDependencyCheck.length; o++) { | ||
| const partNew = newDependencyCheck[o]; | ||
| const partOld = lastDependencyCheck[o] || []; | ||
| for (let i = 0; i < partNew.length; i++) { | ||
| if (!Object.is(partNew[i], partOld[i])) { | ||
| shouldUpdate = true; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| observer.lastState = newDependencyCheck; | ||
| } else { | ||
| shouldUpdate = true; | ||
| } | ||
| if (shouldUpdate) { | ||
| void observer.fn(newState, oldState, action); | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * Disposes of all observers and clears the observer set | ||
| */ | ||
| dispose() { | ||
| this._observers.forEach((observer) => { | ||
| this.unsubscribe(observer); | ||
| observer.dispose?.(); | ||
| }); | ||
| this._observers.clear(); | ||
| } | ||
| } |
| import { BlacLifecycleEvent } from './Blac'; | ||
| import { BlocBase } from './BlocBase'; | ||
| export interface BlacPlugin { | ||
| name: string; | ||
| onEvent( | ||
| event: BlacLifecycleEvent, | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| bloc: BlocBase<any>, | ||
| params?: unknown, | ||
| ): void; | ||
| } |
+109
| import { Blac } from './Blac'; | ||
| import { BlocBase } from './BlocBase'; | ||
| // A should be the base type for all events this Bloc handles and must be an object type | ||
| // to access action.constructor. Events are typically class instances. | ||
| // P is for props, changed from any to unknown. | ||
| export abstract class Bloc< | ||
| S, // State type | ||
| A extends object, // Base Action/Event type, constrained to object | ||
| P = unknown // Props type | ||
| > extends BlocBase<S, P> { | ||
| // Stores handlers: Map<EventConstructor (subtype of A), HandlerFunction> | ||
| // The handler's event parameter will be correctly typed to the specific EventConstructor | ||
| // by the 'on' method's signature. | ||
| readonly eventHandlers: Map< | ||
| // Key: Constructor of a specific event E (where E extends A) | ||
| // Using 'any[]' for constructor arguments for broader compatibility. | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| new (...args: any[]) => A, | ||
| // Value: Handler function. 'event: A' is used here for the stored function type. | ||
| // The 'on' method ensures the specific handler (event: E) is correctly typed. | ||
| (event: A, emit: (newState: S) => void) => void | Promise<void> | ||
| > = new Map(); | ||
| /** | ||
| * Registers an event handler for a specific event type. | ||
| * This method is typically called in the constructor of a derived Bloc class. | ||
| * @param eventConstructor The constructor of the event to handle (e.g., LoadDataEvent). | ||
| * @param handler A function that processes the event and can emit new states. | ||
| * The 'event' parameter in the handler will be typed to the specific eventConstructor. | ||
| */ | ||
| protected on<E extends A>( | ||
| // Using 'any[]' for constructor arguments for broader compatibility. | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| eventConstructor: new (...args: any[]) => E, | ||
| handler: (event: E, emit: (newState: S) => void) => void | Promise<void> | ||
| ): void { | ||
| if (this.eventHandlers.has(eventConstructor)) { | ||
| // Using Blac.warn or a similar logging mechanism from BlocBase if available, | ||
| // otherwise console.warn. Assuming this._name and this._id are available from BlocBase. | ||
| Blac.warn( | ||
| `[Bloc ${this._name}:${String(this._id)}] Handler for event '${eventConstructor.name}' already registered. It will be overwritten.` | ||
| ); | ||
| } | ||
| // Cast the specific handler (event: E) to a more general (event: A) for storage. | ||
| // This is safe because E extends A. When the handler is called with an 'action' of type A, | ||
| // if it was originally registered for type E, 'action' must be an instance of E. | ||
| this.eventHandlers.set( | ||
| eventConstructor, | ||
| handler as (event: A, emit: (newState: S) => void) => void | Promise<void> | ||
| ); | ||
| } | ||
| /** | ||
| * Dispatches an action/event to the Bloc. | ||
| * If a handler is registered for this specific event type (via 'on'), it will be invoked. | ||
| * Asynchronous handlers are awaited. | ||
| * @param action The action/event instance to be processed. | ||
| */ | ||
| public add = async (action: A): Promise<void> => { | ||
| // Using 'any[]' for constructor arguments for broader compatibility. | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| const eventConstructor = action.constructor as new (...args: any[]) => A; | ||
| const handler = this.eventHandlers.get(eventConstructor); | ||
| if (handler) { | ||
| // Define the 'emit' function that handlers will use to update state. | ||
| // It captures the current state ('this.state') right before each emission | ||
| // to provide the correct 'previousState' to _pushState. | ||
| const emit = (newState: S): void => { | ||
| const previousState = this.state; // State just before this specific emission | ||
| // The 'action' passed to _pushState is the original action that triggered the handler, | ||
| // providing context for the state change (e.g., for logging or plugins). | ||
| this._pushState(newState, previousState, action); | ||
| }; | ||
| try { | ||
| // Await the handler in case it's an async function (e.g., performs API calls). | ||
| // The 'action' is passed to the handler, and due to the way 'on' is typed, | ||
| // the 'event' parameter within the handler function will be correctly | ||
| // typed to its specific class (e.g., LoadMyFeatureData). | ||
| await handler(action, emit); | ||
| } catch (error) { | ||
| // It's good practice to handle errors occurring within event handlers. | ||
| Blac.error( | ||
| `[Bloc ${this._name}:${String(this._id)}] Error in event handler for '${eventConstructor.name}':`, | ||
| error, | ||
| "Action:", action | ||
| ); | ||
| // Depending on the desired error handling strategy, you might: | ||
| // 1. Emit a specific error state: this.emit(new MyErrorState(error)); | ||
| // 2. Re-throw the error: throw error; | ||
| // 3. Log and ignore (as done here by default). | ||
| // This should be decided based on application requirements. | ||
| } | ||
| } else { | ||
| // action.constructor.name should be safe due to 'A extends object' and common JS practice for constructors. | ||
| // If linting still complains, it might be overly strict for this common pattern. | ||
| const constructorName = (action.constructor as { name?: string }).name || 'UnnamedConstructor'; | ||
| Blac.warn( | ||
| `[Bloc ${this._name}:${String(this._id)}] No handler registered for action type: '${constructorName}'. Action was:`, | ||
| action | ||
| ); | ||
| // If no handler is found, the action is effectively ignored. | ||
| // Consider if this is the desired behavior or if an error should be thrown | ||
| // or a default handler should be invoked. | ||
| } | ||
| }; | ||
| } |
+248
| import { Blac, BlacLifecycleEvent } from './Blac'; | ||
| import { BlacObservable } from './BlacObserver'; | ||
| import BlacAddon from './addons/BlacAddon'; | ||
| import { BlocConstructor } from './types'; | ||
| export type BlocInstanceId = string | number | undefined; | ||
| type DependencySelector<S> = (newState: S, oldState?: S) => unknown[][]; | ||
| // Define an interface for the static properties expected on a Bloc/Cubit constructor | ||
| interface BlocStaticProperties { | ||
| isolated: boolean; | ||
| keepAlive: boolean; | ||
| addons?: BlacAddon[]; | ||
| } | ||
| /** | ||
| * Base class for both Blocs and Cubits that provides core state management functionality. | ||
| * Handles state transitions, observer notifications, lifecycle management, and addon integration. | ||
| * | ||
| * @abstract This class should be extended, not instantiated directly | ||
| * @template S The type of state managed by this Bloc | ||
| * @template P The type of props that can be passed during instance creation (optional) | ||
| */ | ||
| export abstract class BlocBase< | ||
| S, | ||
| P = unknown | ||
| > { | ||
| /** | ||
| * When true, every consumer will receive its own unique instance of this Bloc. | ||
| * Use this when state should not be shared between components. | ||
| * @default false | ||
| */ | ||
| static isolated = false; | ||
| get isIsolated() { | ||
| return this._isolated; | ||
| } | ||
| /** | ||
| * When true, the Bloc instance persists even when there are no active consumers. | ||
| * Useful for maintaining state between component unmount/remount cycles. | ||
| * @default false | ||
| */ | ||
| static keepAlive = false; | ||
| get isKeepAlive() { | ||
| return this._keepAlive; | ||
| } | ||
| /** | ||
| * Defines how dependencies are selected from the state for efficient updates. | ||
| * When provided, observers will only be notified when selected dependencies change. | ||
| */ | ||
| defaultDependencySelector: DependencySelector<S> | undefined; | ||
| /** | ||
| * @internal | ||
| * Optional array of addons to extend the functionality of this Bloc. | ||
| */ | ||
| public _addons?: BlacAddon[]; | ||
| /** | ||
| * @internal | ||
| * Indicates if this specific Bloc instance is isolated from others of the same type. | ||
| */ | ||
| public _isolated = false; | ||
| /** | ||
| * @internal | ||
| * Observable responsible for managing state listeners and notifying consumers. | ||
| */ | ||
| public _observer: BlacObservable<S>; | ||
| /** | ||
| * @internal | ||
| * Reference to the global Blac manager instance. | ||
| */ | ||
| public _blac = Blac.getInstance(); | ||
| /** | ||
| * The unique identifier for this Bloc instance. | ||
| * Defaults to the class name, but can be customized. | ||
| */ | ||
| public _id: BlocInstanceId; | ||
| /** | ||
| * @internal | ||
| * Reference string used internally for tracking and debugging. | ||
| */ | ||
| public _instanceRef?: string; | ||
| /** | ||
| * @internal | ||
| * Indicates if this specific Bloc instance should be kept alive when no consumers are present. | ||
| */ | ||
| public _keepAlive = false; | ||
| /** | ||
| * @readonly | ||
| * Timestamp when this Bloc instance was created, useful for debugging and performance tracking. | ||
| */ | ||
| public readonly _createdAt = Date.now(); | ||
| /** | ||
| * @internal | ||
| * The current state of the Bloc. | ||
| */ | ||
| public _state: S; | ||
| /** | ||
| * @internal | ||
| * The previous state of the Bloc, maintained for comparison and history. | ||
| */ | ||
| public _oldState: S | undefined; | ||
| /** | ||
| * Props passed during Bloc instance creation. | ||
| * Can be used to configure or parameterize the Bloc's behavior. | ||
| */ | ||
| public props: P | null = null; | ||
| /** | ||
| * Creates a new BlocBase instance with the given initial state. | ||
| * Sets up the observer, registers with the Blac manager, and initializes addons. | ||
| * | ||
| * @param initialState The initial state value for this Bloc | ||
| */ | ||
| constructor(initialState: S) { | ||
| this._state = initialState; | ||
| this._observer = new BlacObservable(this); | ||
| this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_CREATED, this); | ||
| this._id = this.constructor.name; | ||
| // Use a type assertion for the constructor to access static properties safely | ||
| const constructorWithStaticProps = this.constructor as BlocConstructor<this> & BlocStaticProperties; | ||
| this._keepAlive = constructorWithStaticProps.keepAlive; | ||
| this._isolated = constructorWithStaticProps.isolated; | ||
| this._addons = constructorWithStaticProps.addons; | ||
| this._connectAddons(); | ||
| } | ||
| /** | ||
| * Returns the current state of the Bloc. | ||
| * Use this getter to access the state in a read-only manner. | ||
| */ | ||
| get state(): S { | ||
| return this._state; | ||
| } | ||
| /** | ||
| * @internal | ||
| * Returns the name of the Bloc class for identification and debugging. | ||
| */ | ||
| get _name() { | ||
| return this.constructor.name; | ||
| } | ||
| /** | ||
| * @internal | ||
| * Updates the Bloc instance's ID to a new value. | ||
| * Only updates if the new ID is defined and different from the current one. | ||
| * | ||
| * @param id The new ID to assign to this Bloc instance | ||
| */ | ||
| _updateId = (id?: BlocInstanceId) => { | ||
| const originalId = this._id; | ||
| if (!id || id === originalId) return; | ||
| this._id = id; | ||
| }; | ||
| /** | ||
| * @internal | ||
| * Cleans up resources and removes this Bloc from the system. | ||
| * Notifies the Blac manager and clears all observers. | ||
| */ | ||
| _dispose() { | ||
| this._observer.dispose(); | ||
| this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_DISPOSED, this); | ||
| this.onDispose?.(); | ||
| } | ||
| /** | ||
| * @internal | ||
| * Optional function to be called when the Bloc is disposed. | ||
| */ | ||
| onDispose?: () => void; | ||
| /** | ||
| * @internal | ||
| * Set of consumer IDs currently listening to this Bloc's state changes. | ||
| */ | ||
| _consumers = new Set<string>(); | ||
| /** | ||
| * @internal | ||
| * Registers a new consumer to this Bloc instance. | ||
| * Notifies the Blac manager that a consumer has been added. | ||
| * | ||
| * @param consumerId The unique ID of the consumer being added | ||
| */ | ||
| _addConsumer = (consumerId: string) => { | ||
| this._consumers.add(consumerId); | ||
| this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_CONSUMER_ADDED, this, { consumerId }); | ||
| }; | ||
| /** | ||
| * @internal | ||
| * Unregisters a consumer from this Bloc instance. | ||
| * Notifies the Blac manager that a consumer has been removed. | ||
| * | ||
| * @param consumerId The unique ID of the consumer being removed | ||
| */ | ||
| _removeConsumer = (consumerId: string) => { | ||
| this._blac.log(`[${this._name}:${String(this._id ?? 'default_id')}] Removing consumer: ${consumerId}`); | ||
| this._consumers.delete(consumerId); | ||
| this._blac.dispatchEvent(BlacLifecycleEvent.BLOC_CONSUMER_REMOVED, this, { consumerId }); | ||
| }; | ||
| /** | ||
| * @internal | ||
| * Initializes all registered addons for this Bloc instance. | ||
| * Calls the onInit lifecycle method on each addon if defined. | ||
| */ | ||
| _connectAddons = () => { | ||
| const { _addons: addons } = this; | ||
| if (addons) { | ||
| addons.forEach(addon => { | ||
| addon.onInit?.(this); | ||
| }); | ||
| } | ||
| }; | ||
| lastUpdate = Date.now(); | ||
| /** | ||
| * @internal | ||
| * Updates the state and notifies all observers of the change. | ||
| * | ||
| * @param newState The new state to be set | ||
| * @param oldState The previous state for comparison | ||
| * @param action Optional metadata about what caused the state change | ||
| */ | ||
| _pushState = (newState: S, oldState: S, action?: unknown): void => { | ||
| this._state = newState; | ||
| this._observer.notify(newState, oldState, action); | ||
| this.lastUpdate = Date.now(); | ||
| }; | ||
| } |
+69
| import { Blac } from './Blac'; | ||
| import { BlocBase } from './BlocBase'; | ||
| /** | ||
| * A Cubit is a simpler version of a Bloc that doesn't handle events. | ||
| * It manages state and provides methods to update it. | ||
| * @template S - The type of state this Cubit manages | ||
| * @template P - The type of parameters (optional, defaults to null) | ||
| */ | ||
| export abstract class Cubit<S, P = null> extends BlocBase<S, P> { | ||
| /** | ||
| * Updates the current state and notifies all observers of the change. | ||
| * If the new state is identical to the current state (using Object.is), | ||
| * no update will occur. | ||
| * @param state - The new state to set | ||
| */ | ||
| emit(state: S): void { | ||
| if (Object.is(state, this.state)) { | ||
| return; | ||
| } | ||
| const oldState = this.state; | ||
| const newState = state; | ||
| this._pushState(newState, oldState); | ||
| } | ||
| /** | ||
| * Partially updates the current state by merging it with the provided state patch. | ||
| * This method is only applicable when the state is an object type. | ||
| * | ||
| * @param statePatch - A partial state object containing only the properties to update | ||
| * @param ignoreChangeCheck - If true, skips checking if the state has actually changed | ||
| * @throws {TypeError} If the state is not an object type | ||
| */ | ||
| patch( | ||
| statePatch: S extends object ? Partial<S> : S, | ||
| ignoreChangeCheck = false, | ||
| ): void { | ||
| if (typeof this.state !== 'object' || this.state === null) { | ||
| Blac.warn( | ||
| 'Cubit.patch: was called on a cubit where the state is not an object. This is a no-op.', | ||
| ); | ||
| return; | ||
| } | ||
| let changes = false; | ||
| if (!ignoreChangeCheck) { | ||
| for (const key in statePatch) { | ||
| if (Object.prototype.hasOwnProperty.call(statePatch, key)) { | ||
| const s = this.state; | ||
| const current = s[key as keyof S]; | ||
| if (!Object.is(statePatch[key as keyof S], current)) { | ||
| changes = true; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } else { | ||
| changes = true; | ||
| } | ||
| if (changes) { | ||
| this.emit({ | ||
| ...this.state, | ||
| ...(statePatch as Partial<S>), | ||
| } as S); | ||
| } | ||
| } | ||
| } |
| export * from './Blac'; | ||
| export * from './BlacObserver'; | ||
| export * from './BlocBase'; | ||
| export * from './Cubit'; | ||
| export * from './Bloc'; | ||
| export * from './types'; | ||
| export * from './BlacPlugin'; | ||
| export * from './addons'; |
+63
| /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
| import { Bloc } from './Bloc'; | ||
| import { BlocBase } from './BlocBase'; | ||
| import { Cubit } from './Cubit'; | ||
| /** | ||
| * Represents a constructor type for a Bloc that takes no parameters | ||
| * @template B - The type of the Bloc instance | ||
| */ | ||
| export type BlocClassNoParams<B> = new (args: never[]) => B; | ||
| /** | ||
| * Represents the abstract base types for Bloc and Cubit | ||
| */ | ||
| export type BlocBaseAbstract = | ||
| | typeof Bloc<any, any, any> | ||
| | typeof Cubit<any, any>; | ||
| /** | ||
| * Represents a constructor type for a Bloc that can take any parameters | ||
| * @template B - The type of the Bloc instance | ||
| */ | ||
| export type BlocConstructor<B> = new (...args: any) => B; | ||
| /** | ||
| * Extracts the state type from a BlocBase instance | ||
| * @template B - The BlocBase type to extract the state from | ||
| */ | ||
| export type ValueType<B extends BlocBase<any>> = | ||
| B extends BlocBase<infer U> ? U : never; | ||
| /** | ||
| * Extracts the state type from either a Bloc or Cubit | ||
| * @template T - The Bloc or Cubit type to extract the state from | ||
| */ | ||
| export type BlocState<T> = T extends BlocBase<infer S> ? S : never; | ||
| /** | ||
| * Extracts the props type from either a Bloc or Cubit | ||
| * @template T - The Bloc or Cubit type to extract the props from | ||
| */ | ||
| export type InferPropsFromGeneric<T> = | ||
| T extends Bloc<any, any, infer P> | ||
| ? P | ||
| : T extends Cubit<any, infer P> | ||
| ? P | ||
| : never; | ||
| /** | ||
| * Extracts the constructor parameters type from a BlocBase | ||
| * @template B - The BlocBase type to extract the constructor parameters from | ||
| */ | ||
| export type BlocConstructorParameters<B extends BlocBase<any>> = | ||
| BlocConstructor<B> extends new (...args: infer P) => B ? P : never; | ||
| /** | ||
| * Represents a function type for determining hook dependencies based on state changes | ||
| * @template B - The BlocGeneric type | ||
| */ | ||
| export type BlocHookDependencyArrayFn<S> = ( | ||
| newState: S, | ||
| oldState: S, | ||
| ) => unknown[][]; |
+38
-18
| { | ||
| "name": "@blac/core", | ||
| "version": "0.1.0", | ||
| "version": "2.0.0", | ||
| "license": "MIT", | ||
| "author": "Brendan Mullins <jsnanigans@gmail.com>", | ||
| "main": "dist/index.cjs.js", | ||
| "module": "dist/index.es.js", | ||
| "typings": "dist/index.d.ts", | ||
| "types": "dist/index.d.ts", | ||
| "files": [ | ||
| "dist" | ||
| "dist", | ||
| "src", | ||
| "README.md", | ||
| "LICENSE" | ||
| ], | ||
| "main": "./dist/blac.umd.js", | ||
| "module": "./dist/blac.mjs", | ||
| "exports": { | ||
| ".": { | ||
| "import": "./dist/blac.mjs", | ||
| "require": "./dist/blac.umd.js" | ||
| } | ||
| "sideEffects": false, | ||
| "keywords": [ | ||
| "typescript", | ||
| "rxjs", | ||
| "state-management", | ||
| "observer-pattern", | ||
| "bloc", | ||
| "bloc-pattern" | ||
| ], | ||
| "dependencies": {}, | ||
| "devDependencies": { | ||
| "@testing-library/jest-dom": "^6.6.3", | ||
| "@testing-library/user-event": "^14.6.1", | ||
| "@vitest/browser": "^3.1.3", | ||
| "jsdom": "^24.1.1", | ||
| "prettier": "^3.5.3", | ||
| "typescript": "^5.8.3", | ||
| "vite-plugin-dts": "^4.5.3", | ||
| "vitest": "^1.6.0" | ||
| }, | ||
| "scripts": { | ||
| "build": "vite build" | ||
| }, | ||
| "keywords": [], | ||
| "author": "", | ||
| "license": "ISC", | ||
| "description": "", | ||
| "devDependencies": { | ||
| "vite": "^6.0.5" | ||
| "prettier": "prettier --write ./src", | ||
| "test": "vitest run", | ||
| "test:watch": "vitest --watch", | ||
| "coverage": "vitest run --coverage", | ||
| "typecheck": "tsc --noEmit", | ||
| "build": "vite build", | ||
| "deploy": "pnpm run build && pnpm publish --access public" | ||
| } | ||
| } | ||
| } |
-279
| var _ = /* @__PURE__ */ ((o) => (o.BLOC_DISPOSED = "BLOC_DISPOSED", o.BLOC_CREATED = "BLOC_CREATED", o.LISTENER_REMOVED = "LISTENER_REMOVED", o.LISTENER_ADDED = "LISTENER_ADDED", o.STATE_CHANGED = "STATE_CHANGED", o.BLOC_CONSUMER_REMOVED = "BLOC_CONSUMER_REMOVED", o.BLOC_CONSUMER_ADDED = "BLOC_CONSUMER_ADDED", o))(_ || {}); | ||
| const a = class a { | ||
| constructor(s = {}) { | ||
| this.createdAt = Date.now(), this.blocInstanceMap = /* @__PURE__ */ new Map(), this.isolatedBlocMap = /* @__PURE__ */ new Map(), this.pluginList = [], this.postChangesToDocument = !1, this.dispatchEvent = (t) => { | ||
| this.log("Broadcast signal", t), Array.from(this.blocInstanceMap.values()).forEach((i) => { | ||
| var r; | ||
| (r = i._onEvent) == null || r.call(i, t); | ||
| }); | ||
| }, this.log = (...t) => { | ||
| a.enableLog && console.warn(`☢️ [Blac ${this.createdAt}]`, ...t); | ||
| }, this.addPlugin = (t) => { | ||
| this.pluginList.findIndex((i) => i.name === t.name) === -1 && (this.log("Add plugin", t.name), this.pluginList.push(t)); | ||
| }, this.reportToPlugins = (t, n, i) => { | ||
| this.pluginList.forEach((r) => { | ||
| r.onEvent(t, n, i); | ||
| }); | ||
| }, this.report = (t, n, i) => { | ||
| switch (this.log(t, n, i), t) { | ||
| case "BLOC_DISPOSED": | ||
| this.disposeBloc(n); | ||
| break; | ||
| case "BLOC_CONSUMER_REMOVED": | ||
| n._consumers.size === 0 && n._observer.size === 0 && !n._keepAlive && n._dispose(); | ||
| break; | ||
| } | ||
| this.reportToPlugins(t, n, i); | ||
| }, this.disposeBloc = (t) => { | ||
| const n = t.constructor; | ||
| t._isBlacLive = !1, n.isolated ? this.unregisterIsolatedBlocInstance(t) : this.unregisterBlocInstance(t); | ||
| }, this.getBloc = (t, n = {}) => { | ||
| const i = t.isolated, r = n.id || t.name, c = i ? this.findIsolatedBlocInstance(t, r) : this.findRegisteredBlocInstance(t, r); | ||
| return c || this.createNewBlocInstance( | ||
| t, | ||
| r, | ||
| n.props, | ||
| n.instanceRef | ||
| ); | ||
| }, this.getAllBlocs = (t, n = {}) => { | ||
| const i = t, { searchIsolated: r = i.isolated } = n; | ||
| if (r) { | ||
| const c = this.isolatedBlocMap.get(t); | ||
| if (c) return c; | ||
| } else | ||
| return Array.from(this.blocInstanceMap.values()).filter((d) => d instanceof t); | ||
| return []; | ||
| }; | ||
| const { __unsafe_ignore_singleton: e = !1 } = s; | ||
| if (a.instance && !e) | ||
| return a.instance; | ||
| a.instance = this; | ||
| } | ||
| static getInstance() { | ||
| return a.instance; | ||
| } | ||
| resetInstance() { | ||
| this.log("Reset Blac instance"), a.instance = new a({ | ||
| __unsafe_ignore_singleton: !0 | ||
| }); | ||
| } | ||
| createBlocInstanceMapKey(s, e) { | ||
| return `${s}:${e}`; | ||
| } | ||
| unregisterBlocInstance(s) { | ||
| const e = this.createBlocInstanceMapKey(s._name, s._id); | ||
| this.blocInstanceMap.delete(e); | ||
| } | ||
| registerBlocInstance(s) { | ||
| const e = this.createBlocInstanceMapKey(s._name, s._id); | ||
| this.blocInstanceMap.set(e, s); | ||
| } | ||
| findRegisteredBlocInstance(s, e) { | ||
| if (s.isolated) return; | ||
| const n = this.createBlocInstanceMapKey(s.name, e); | ||
| return this.blocInstanceMap.get(n); | ||
| } | ||
| registerIsolatedBlocInstance(s) { | ||
| const e = s.constructor, t = this.isolatedBlocMap.get(e); | ||
| t ? t.push(s) : this.isolatedBlocMap.set(e, [s]); | ||
| } | ||
| unregisterIsolatedBlocInstance(s) { | ||
| const e = s.constructor, t = this.isolatedBlocMap.get(e); | ||
| if (t) { | ||
| const n = t.findIndex((i) => i._id === s._id); | ||
| t.splice(n, 1), t.length === 0 && this.isolatedBlocMap.delete(e); | ||
| } | ||
| } | ||
| findIsolatedBlocInstance(s, e) { | ||
| const t = this.isolatedBlocMap.get(s); | ||
| if (t) | ||
| return t.find((n) => n._id === e); | ||
| } | ||
| createNewBlocInstance(s, e, t, n) { | ||
| const i = s, r = new s(t); | ||
| return r._instanceRef = n, r.props = t || null, r._updateId(e), i.isolated ? (this.registerIsolatedBlocInstance(r), r) : (this.registerBlocInstance(r), r); | ||
| } | ||
| }; | ||
| a.instance = new a(), a.getAllBlocs = a.instance.getAllBlocs, a.addPlugin = a.instance.addPlugin, a.dispatchEvent = a.instance.dispatchEvent, a.enableLog = !1, a.warn = (s, ...e) => { | ||
| console.warn(`🚨 [Blac ${a.instance.createdAt}]`, s, ...e); | ||
| }, a.error = (s, ...e) => { | ||
| console.error(`🚨 [Blac ${a.instance.createdAt}]`, s, ...e); | ||
| }, a.getBloc = a.instance.getBloc; | ||
| let f = a; | ||
| class S { | ||
| constructor(s) { | ||
| this._observers = /* @__PURE__ */ new Set(), this.bloc = s; | ||
| } | ||
| get size() { | ||
| return this._observers.size; | ||
| } | ||
| get observers() { | ||
| return this._observers; | ||
| } | ||
| subscribe(s) { | ||
| return this._observers.add(s), f.instance.report(_.LISTENER_ADDED, this.bloc), s.lastState || (s.lastState = s.dependencyArray ? s.dependencyArray(this.bloc.state, this.bloc.state) : []), () => this.unsubscribe(s); | ||
| } | ||
| unsubscribe(s) { | ||
| this._observers.delete(s), f.instance.report(_.LISTENER_REMOVED, this.bloc); | ||
| } | ||
| notify(s, e, t) { | ||
| this._observers.forEach((n) => { | ||
| let i = !1; | ||
| if (n.dependencyArray) { | ||
| let r = n.lastState || []; | ||
| const c = n.dependencyArray(s, e); | ||
| for (let d = 0; d < c.length; d++) { | ||
| const E = c[d], l = r[d] || []; | ||
| for (let h = 0; h < E.length; h++) | ||
| if (E[h] !== l[h]) { | ||
| i = !0; | ||
| break; | ||
| } | ||
| } | ||
| n.lastState = c; | ||
| } else | ||
| i = !0; | ||
| if (i) | ||
| return n.fn(s, e, t); | ||
| }); | ||
| } | ||
| dispose() { | ||
| this._observers.clear(); | ||
| } | ||
| } | ||
| const p = class p { | ||
| constructor(s) { | ||
| this._isolated = !1, this._isBlacLive = !0, this._blac = f.getInstance(), this._keepAlive = !1, this._createdAt = Date.now(), this.props = null, this._updateId = (e) => { | ||
| const t = this._id; | ||
| !e || e === t || (this._id = e); | ||
| }, this._consumers = /* @__PURE__ */ new Set(), this._addConsumer = (e) => { | ||
| this._consumers.add(e), this._blac.report(_.BLOC_CONSUMER_ADDED, this, { | ||
| consumerId: e | ||
| }); | ||
| }, this._removeConsumer = (e) => { | ||
| this._consumers.delete(e), this._blac.report(_.BLOC_CONSUMER_REMOVED, this, { | ||
| consumerId: e | ||
| }); | ||
| }, this._connectAddons = () => { | ||
| const { _addons: e } = this; | ||
| if (e) | ||
| for (const t of e) | ||
| t.onEmit && this._observer.subscribe({ | ||
| fn: (n, i) => { | ||
| var r; | ||
| (r = t.onEmit) == null || r.call(t, { | ||
| newState: n, | ||
| oldState: i, | ||
| cubit: this | ||
| }); | ||
| }, | ||
| passive: !0, | ||
| id: t.name | ||
| }), t.onInit && t.onInit(this); | ||
| }, this._pushState = (e, t, n) => { | ||
| this._state = e, this._oldState = t, this._observer.notify(e, t, n), this._blac.report(_.STATE_CHANGED, this, { | ||
| newState: e, | ||
| oldState: t | ||
| }); | ||
| }, this._state = s, this._observer = new S(this), this._blac.report(_.BLOC_CREATED, this), this._id = this.constructor.name, this._isolated = this.constructor.isolated, this._addons = this.constructor.addons, this._keepAlive = this.constructor.keepAlive, this._connectAddons(); | ||
| } | ||
| get state() { | ||
| return this._state; | ||
| } | ||
| get _name() { | ||
| return this.constructor.name; | ||
| } | ||
| _dispose() { | ||
| this._blac.report(_.BLOC_DISPOSED, this), this._observer.dispose(); | ||
| } | ||
| _onEvent(s) { | ||
| } | ||
| }; | ||
| p.isolated = !1, p.keepAlive = !1, p.isBlacClass = !0; | ||
| let g = p; | ||
| class O extends g { | ||
| /** | ||
| * Update the state then will notify all observers | ||
| * @param state: new state | ||
| **/ | ||
| emit(s) { | ||
| if (Object.is(s, this.state)) | ||
| return; | ||
| const e = this.state, t = s; | ||
| this._pushState(t, e); | ||
| } | ||
| /** | ||
| * Merges current state object with the parameter "state" and emits the new state | ||
| * Warning: only works when the state is an object. | ||
| * @param state: Partial state that should change | ||
| **/ | ||
| patch(s, e = !1) { | ||
| let t = !1; | ||
| if (!e) | ||
| for (const n in s) { | ||
| const i = this.state[n]; | ||
| if (!Object.is(s[n], i)) { | ||
| t = !0; | ||
| break; | ||
| } | ||
| } | ||
| t && this.emit({ ...this.state, ...s }); | ||
| } | ||
| } | ||
| class A extends g { | ||
| constructor() { | ||
| super(...arguments), this.add = (s) => { | ||
| const e = this.state, t = this.reducer(s, this.state); | ||
| this._pushState(t, e, s); | ||
| }; | ||
| } | ||
| } | ||
| function D(o) { | ||
| switch (o) { | ||
| case "localStorage": | ||
| return localStorage; | ||
| case "sessionStorage": | ||
| return sessionStorage; | ||
| default: | ||
| return localStorage; | ||
| } | ||
| } | ||
| function B(o = {}) { | ||
| const { | ||
| keyPrefix: s = "blac", | ||
| keyName: e, | ||
| defaultValue: t, | ||
| storageType: n = "localStorage" | ||
| } = o, i = (l) => `${s}:${l}`, r = (l) => { | ||
| const h = D(n).getItem(i(l)); | ||
| if (typeof h != "string") | ||
| return t; | ||
| try { | ||
| const u = JSON.parse(h); | ||
| return typeof u.v < "u" ? u.v : t; | ||
| } catch { | ||
| return t; | ||
| } | ||
| }, c = (l) => { | ||
| const h = e ?? l._id, u = r(h); | ||
| typeof u !== void 0 && l._pushState(u, null); | ||
| }; | ||
| let d = ""; | ||
| return { | ||
| name: "Persist", | ||
| onInit: c, | ||
| onEmit: ({ newState: l, cubit: h }) => { | ||
| const u = e ?? h._id, I = JSON.stringify({ v: l }); | ||
| I !== d && (D(n).setItem(i(u), I), d = I); | ||
| } | ||
| }; | ||
| } | ||
| export { | ||
| f as Blac, | ||
| _ as BlacLifecycleEvent, | ||
| S as BlacObservable, | ||
| A as Bloc, | ||
| g as BlocBase, | ||
| O as Cubit, | ||
| B as Persist | ||
| }; |
| (function(l,c){typeof exports=="object"&&typeof module<"u"?c(exports):typeof define=="function"&&define.amd?define(["exports"],c):(l=typeof globalThis<"u"?globalThis:l||self,c(l.BlacCore={}))})(this,function(l){"use strict";var c=(r=>(r.BLOC_DISPOSED="BLOC_DISPOSED",r.BLOC_CREATED="BLOC_CREATED",r.LISTENER_REMOVED="LISTENER_REMOVED",r.LISTENER_ADDED="LISTENER_ADDED",r.STATE_CHANGED="STATE_CHANGED",r.BLOC_CONSUMER_REMOVED="BLOC_CONSUMER_REMOVED",r.BLOC_CONSUMER_ADDED="BLOC_CONSUMER_ADDED",r))(c||{});const o=class o{constructor(e={}){this.createdAt=Date.now(),this.blocInstanceMap=new Map,this.isolatedBlocMap=new Map,this.pluginList=[],this.postChangesToDocument=!1,this.dispatchEvent=t=>{this.log("Broadcast signal",t),Array.from(this.blocInstanceMap.values()).forEach(i=>{var a;(a=i._onEvent)==null||a.call(i,t)})},this.log=(...t)=>{o.enableLog&&console.warn(`☢️ [Blac ${this.createdAt}]`,...t)},this.addPlugin=t=>{this.pluginList.findIndex(i=>i.name===t.name)===-1&&(this.log("Add plugin",t.name),this.pluginList.push(t))},this.reportToPlugins=(t,n,i)=>{this.pluginList.forEach(a=>{a.onEvent(t,n,i)})},this.report=(t,n,i)=>{switch(this.log(t,n,i),t){case"BLOC_DISPOSED":this.disposeBloc(n);break;case"BLOC_CONSUMER_REMOVED":n._consumers.size===0&&n._observer.size===0&&!n._keepAlive&&n._dispose();break}this.reportToPlugins(t,n,i)},this.disposeBloc=t=>{const n=t.constructor;t._isBlacLive=!1,n.isolated?this.unregisterIsolatedBlocInstance(t):this.unregisterBlocInstance(t)},this.getBloc=(t,n={})=>{const i=t.isolated,a=n.id||t.name,h=i?this.findIsolatedBlocInstance(t,a):this.findRegisteredBlocInstance(t,a);return h||this.createNewBlocInstance(t,a,n.props,n.instanceRef)},this.getAllBlocs=(t,n={})=>{const i=t,{searchIsolated:a=i.isolated}=n;if(a){const h=this.isolatedBlocMap.get(t);if(h)return h}else return Array.from(this.blocInstanceMap.values()).filter(f=>f instanceof t);return[]};const{__unsafe_ignore_singleton:s=!1}=e;if(o.instance&&!s)return o.instance;o.instance=this}static getInstance(){return o.instance}resetInstance(){this.log("Reset Blac instance"),o.instance=new o({__unsafe_ignore_singleton:!0})}createBlocInstanceMapKey(e,s){return`${e}:${s}`}unregisterBlocInstance(e){const s=this.createBlocInstanceMapKey(e._name,e._id);this.blocInstanceMap.delete(s)}registerBlocInstance(e){const s=this.createBlocInstanceMapKey(e._name,e._id);this.blocInstanceMap.set(s,e)}findRegisteredBlocInstance(e,s){if(e.isolated)return;const n=this.createBlocInstanceMapKey(e.name,s);return this.blocInstanceMap.get(n)}registerIsolatedBlocInstance(e){const s=e.constructor,t=this.isolatedBlocMap.get(s);t?t.push(e):this.isolatedBlocMap.set(s,[e])}unregisterIsolatedBlocInstance(e){const s=e.constructor,t=this.isolatedBlocMap.get(s);if(t){const n=t.findIndex(i=>i._id===e._id);t.splice(n,1),t.length===0&&this.isolatedBlocMap.delete(s)}}findIsolatedBlocInstance(e,s){const t=this.isolatedBlocMap.get(e);if(t)return t.find(n=>n._id===s)}createNewBlocInstance(e,s,t,n){const i=e,a=new e(t);return a._instanceRef=n,a.props=t||null,a._updateId(s),i.isolated?(this.registerIsolatedBlocInstance(a),a):(this.registerBlocInstance(a),a)}};o.instance=new o,o.getAllBlocs=o.instance.getAllBlocs,o.addPlugin=o.instance.addPlugin,o.dispatchEvent=o.instance.dispatchEvent,o.enableLog=!1,o.warn=(e,...s)=>{console.warn(`🚨 [Blac ${o.instance.createdAt}]`,e,...s)},o.error=(e,...s)=>{console.error(`🚨 [Blac ${o.instance.createdAt}]`,e,...s)},o.getBloc=o.instance.getBloc;let p=o;class B{constructor(e){this._observers=new Set,this.bloc=e}get size(){return this._observers.size}get observers(){return this._observers}subscribe(e){return this._observers.add(e),p.instance.report(c.LISTENER_ADDED,this.bloc),e.lastState||(e.lastState=e.dependencyArray?e.dependencyArray(this.bloc.state,this.bloc.state):[]),()=>this.unsubscribe(e)}unsubscribe(e){this._observers.delete(e),p.instance.report(c.LISTENER_REMOVED,this.bloc)}notify(e,s,t){this._observers.forEach(n=>{let i=!1;if(n.dependencyArray){let a=n.lastState||[];const h=n.dependencyArray(e,s);for(let f=0;f<h.length;f++){const I=h[f],u=a[f]||[];for(let d=0;d<I.length;d++)if(I[d]!==u[d]){i=!0;break}}n.lastState=h}else i=!0;if(i)return n.fn(e,s,t)})}dispose(){this._observers.clear()}}const E=class E{constructor(e){this._isolated=!1,this._isBlacLive=!0,this._blac=p.getInstance(),this._keepAlive=!1,this._createdAt=Date.now(),this.props=null,this._updateId=s=>{const t=this._id;!s||s===t||(this._id=s)},this._consumers=new Set,this._addConsumer=s=>{this._consumers.add(s),this._blac.report(c.BLOC_CONSUMER_ADDED,this,{consumerId:s})},this._removeConsumer=s=>{this._consumers.delete(s),this._blac.report(c.BLOC_CONSUMER_REMOVED,this,{consumerId:s})},this._connectAddons=()=>{const{_addons:s}=this;if(s)for(const t of s)t.onEmit&&this._observer.subscribe({fn:(n,i)=>{var a;(a=t.onEmit)==null||a.call(t,{newState:n,oldState:i,cubit:this})},passive:!0,id:t.name}),t.onInit&&t.onInit(this)},this._pushState=(s,t,n)=>{this._state=s,this._oldState=t,this._observer.notify(s,t,n),this._blac.report(c.STATE_CHANGED,this,{newState:s,oldState:t})},this._state=e,this._observer=new B(this),this._blac.report(c.BLOC_CREATED,this),this._id=this.constructor.name,this._isolated=this.constructor.isolated,this._addons=this.constructor.addons,this._keepAlive=this.constructor.keepAlive,this._connectAddons()}get state(){return this._state}get _name(){return this.constructor.name}_dispose(){this._blac.report(c.BLOC_DISPOSED,this),this._observer.dispose()}_onEvent(e){}};E.isolated=!1,E.keepAlive=!1,E.isBlacClass=!0;let g=E;class O extends g{emit(e){if(Object.is(e,this.state))return;const s=this.state,t=e;this._pushState(t,s)}patch(e,s=!1){let t=!1;if(!s)for(const n in e){const i=this.state[n];if(!Object.is(e[n],i)){t=!0;break}}t&&this.emit({...this.state,...e})}}class A extends g{constructor(){super(...arguments),this.add=e=>{const s=this.state,t=this.reducer(e,this.state);this._pushState(t,s,e)}}}function D(r){switch(r){case"localStorage":return localStorage;case"sessionStorage":return sessionStorage;default:return localStorage}}function b(r={}){const{keyPrefix:e="blac",keyName:s,defaultValue:t,storageType:n="localStorage"}=r,i=u=>`${e}:${u}`,a=u=>{const d=D(n).getItem(i(u));if(typeof d!="string")return t;try{const _=JSON.parse(d);return typeof _.v<"u"?_.v:t}catch{return t}},h=u=>{const d=s??u._id,_=a(d);typeof _!==void 0&&u._pushState(_,null)};let f="";return{name:"Persist",onInit:h,onEmit:({newState:u,cubit:d})=>{const _=s??d._id,S=JSON.stringify({v:u});S!==f&&(D(n).setItem(i(_),S),f=S)}}}l.Blac=p,l.BlacLifecycleEvent=c,l.BlacObservable=B,l.Bloc=A,l.BlocBase=g,l.Cubit=O,l.Persist=b,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
207544
1176.64%21
600%2382
678.43%1
-50%2
-33.33%1
-50%252
Infinity%8
700%8
700%1
Infinity%