containerized-state
Version:
Fast and minimal state container which can be used and shared across React or non-React components.
1 lines • 6.67 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/Container.ts"],"sourcesContent":["export * from \"./Container.ts\";\nexport type {\n ComputeValue,\n EqualityCheckFunction,\n Initializer,\n SubscribeCallback,\n Unsubscribe,\n} from \"./types.ts\";\n","export const Entity = {\n DEFAULT: \"default\",\n COMPUTED: \"computed\",\n} as const;\n","import { Entity } from \"./constants.ts\";\nimport type {\n CallableFunction,\n ComputedEntity,\n ComputeValue,\n ContainerEntity,\n DefaultEntity,\n EqualityCheckFunction,\n Initializer,\n SubscribeCallback,\n Unsubscribe,\n} from \"./types.ts\";\n\nexport class Container<T> {\n protected _value: T;\n protected _subscribers: Set<ContainerEntity<T>>;\n\n static create<T>(initializer: Initializer<T>): Container<T> {\n return new Container(initializer);\n }\n\n constructor(initializer: Initializer<T>) {\n const initialValue =\n typeof initializer === \"function\"\n ? (initializer as CallableFunction<[], T>)()\n : initializer;\n\n this._value = initialValue;\n this._subscribers = new Set();\n }\n\n /**\n * A snapshot of the state.\n */\n public getValue(): T {\n return this._value;\n }\n\n /**\n * Updates the value of the state and notifies the subscribers.\n */\n public async setValue(newValue: T): Promise<void> {\n const prevValue = this._value;\n\n this._value = newValue;\n\n const promises: Promise<void>[] = [];\n\n for (const entity of this._subscribers) {\n let promise: Promise<void> = Promise.resolve();\n\n if (entity.type === Entity.DEFAULT) {\n if (Object.is(prevValue, newValue)) {\n promise = Promise.resolve();\n\n continue;\n }\n\n promise = Promise.resolve(entity.cb(newValue));\n } else if (entity.type === Entity.COMPUTED) {\n const { computeValue, cb, isEqual } = entity;\n\n const computedValue = computeValue(newValue) as unknown;\n const prevComputedValue = computeValue(prevValue) as unknown;\n\n const shouldEmit = !(\n isEqual?.(prevComputedValue, computedValue) ??\n Object.is(computedValue, prevComputedValue)\n );\n\n if (!shouldEmit) {\n promise = Promise.resolve();\n\n continue;\n }\n\n promise = Promise.resolve(cb(computedValue));\n }\n\n promises.push(promise);\n }\n\n await Promise.all(promises);\n }\n\n /**\n * Subscribes to the changes of the container's state value\n * and returns the unsubscribe function.\n */\n public subscribe(\n cb: SubscribeCallback<T>,\n options?: {\n /**\n * An `AbortSignal` reference to control the unsubscibe.\n */\n signal?: AbortSignal;\n },\n ): Unsubscribe {\n const { signal } = options ?? {};\n\n const asyncCb = async (value: T): Promise<void> => {\n return cb(value);\n };\n\n const entity: DefaultEntity<T> = {\n type: Entity.DEFAULT,\n cb: asyncCb,\n };\n\n this._subscribers.add(entity);\n\n const unsubscribe: Unsubscribe = () => {\n this._subscribers.delete(entity);\n signal?.removeEventListener(\"abort\", unsubscribe);\n };\n\n signal?.addEventListener(\"abort\", unsubscribe);\n\n if (signal?.aborted) unsubscribe();\n\n return unsubscribe;\n }\n\n /**\n * Subscribes to the changes of the container's selected state values\n * and returns the unsubscribe function.\n *\n * For more control over emission changes, you may provide a custom equality function.\n */\n public computedSubscribe<P>(\n computeValue: ComputeValue<T, P>,\n cb: SubscribeCallback<P>,\n options?: {\n /**\n * An `AbortSignal` reference to control the unsubscibe.\n */\n signal?: AbortSignal;\n /**\n * A custom equality function to control emission changes.\n */\n isEqual?: EqualityCheckFunction<P>;\n },\n ): Unsubscribe {\n const { isEqual, signal } = options ?? {};\n\n const asyncCb = async (value: P): Promise<void> => {\n return cb(value);\n };\n\n const entity: ComputedEntity<T, P> = {\n type: Entity.COMPUTED,\n computeValue,\n cb: asyncCb,\n isEqual,\n };\n\n this._subscribers.add(entity);\n\n const unsubscribe: Unsubscribe = () => {\n this._subscribers.delete(entity);\n signal?.removeEventListener(\"abort\", unsubscribe);\n };\n\n signal?.addEventListener(\"abort\", unsubscribe);\n\n if (signal?.aborted) unsubscribe();\n\n return unsubscribe;\n }\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,eAAAE,IAAA,eAAAC,EAAAH,GCAO,IAAMI,EAAS,CACpB,QAAS,UACT,SAAU,UACZ,ECUO,IAAMC,EAAN,MAAMC,CAAa,CAIxB,OAAO,OAAUC,EAA2C,CAC1D,OAAO,IAAID,EAAUC,CAAW,CAClC,CAEA,YAAYA,EAA6B,CACvC,IAAMC,EACJ,OAAOD,GAAgB,WAClBA,EAAwC,EACzCA,EAEN,KAAK,OAASC,EACd,KAAK,aAAe,IAAI,GAC1B,CAKO,UAAc,CACnB,OAAO,KAAK,MACd,CAKA,MAAa,SAASC,EAA4B,CAzCpD,IAAAC,EA0CI,IAAMC,EAAY,KAAK,OAEvB,KAAK,OAASF,EAEd,IAAMG,EAA4B,CAAC,EAEnC,QAAWC,KAAU,KAAK,aAAc,CACtC,IAAIC,EAAyB,QAAQ,QAAQ,EAE7C,GAAID,EAAO,OAASE,EAAO,QAAS,CAClC,GAAI,OAAO,GAAGJ,EAAWF,CAAQ,EAAG,CAClCK,EAAU,QAAQ,QAAQ,EAE1B,QACF,CAEAA,EAAU,QAAQ,QAAQD,EAAO,GAAGJ,CAAQ,CAAC,CAC/C,SAAWI,EAAO,OAASE,EAAO,SAAU,CAC1C,GAAM,CAAE,aAAAC,EAAc,GAAAC,EAAI,QAAAC,CAAQ,EAAIL,EAEhCM,EAAgBH,EAAaP,CAAQ,EACrCW,EAAoBJ,EAAaL,CAAS,EAOhD,GAAI,CALe,GACjBD,EAAAQ,GAAA,YAAAA,EAAUE,EAAmBD,KAA7B,KAAAT,EACA,OAAO,GAAGS,EAAeC,CAAiB,GAG3B,CACfN,EAAU,QAAQ,QAAQ,EAE1B,QACF,CAEAA,EAAU,QAAQ,QAAQG,EAAGE,CAAa,CAAC,CAC7C,CAEAP,EAAS,KAAKE,CAAO,CACvB,CAEA,MAAM,QAAQ,IAAIF,CAAQ,CAC5B,CAMO,UACLK,EACAI,EAMa,CACb,GAAM,CAAE,OAAAC,CAAO,EAAID,GAAA,KAAAA,EAAW,CAAC,EAEzBE,EAAU,MAAOC,GACdP,EAAGO,CAAK,EAGXX,EAA2B,CAC/B,KAAME,EAAO,QACb,GAAIQ,CACN,EAEA,KAAK,aAAa,IAAIV,CAAM,EAE5B,IAAMY,EAA2B,IAAM,CACrC,KAAK,aAAa,OAAOZ,CAAM,EAC/BS,GAAA,MAAAA,EAAQ,oBAAoB,QAASG,EACvC,EAEA,OAAAH,GAAA,MAAAA,EAAQ,iBAAiB,QAASG,GAE9BH,GAAA,MAAAA,EAAQ,SAASG,EAAY,EAE1BA,CACT,CAQO,kBACLT,EACAC,EACAI,EAUa,CACb,GAAM,CAAE,QAAAH,EAAS,OAAAI,CAAO,EAAID,GAAA,KAAAA,EAAW,CAAC,EAElCE,EAAU,MAAOC,GACdP,EAAGO,CAAK,EAGXX,EAA+B,CACnC,KAAME,EAAO,SACb,aAAAC,EACA,GAAIO,EACJ,QAAAL,CACF,EAEA,KAAK,aAAa,IAAIL,CAAM,EAE5B,IAAMY,EAA2B,IAAM,CACrC,KAAK,aAAa,OAAOZ,CAAM,EAC/BS,GAAA,MAAAA,EAAQ,oBAAoB,QAASG,EACvC,EAEA,OAAAH,GAAA,MAAAA,EAAQ,iBAAiB,QAASG,GAE9BH,GAAA,MAAAA,EAAQ,SAASG,EAAY,EAE1BA,CACT,CACF","names":["index_exports","__export","Container","__toCommonJS","Entity","Container","_Container","initializer","initialValue","newValue","_a","prevValue","promises","entity","promise","Entity","computeValue","cb","isEqual","computedValue","prevComputedValue","options","signal","asyncCb","value","unsubscribe"]}