import type { CtxFn, Signal, StateNode, StringRecord } from "./types" import { detachedNode, getInternals, InternalStateNode, NestedMap, RunFn, } from "./internal" import { Observable, of } from "rxjs" export type InstanceCtxFn> = ( instanceKey: KT, ctxValue: (node: StateNode) => CT, ctxObservable: >( node: StateNode | Signal, key: Omit, ) => Observable, key: K, ) => Observable export type GetValueFn = (node: StateNode) => CT interface GetObservableFn { >( other: K extends CK ? StateNode | Signal : never, ): Observable >( other: StateNode | Signal, keys: Omit, ): Observable } export type InstanceKeysCtxFn> = ( ctxValue: GetValueFn, ctxObservable: GetObservableFn, key: K, ) => Observable> type InternalInstance = { isActive: boolean isLoaded?: boolean references: { byInstance: Map, Set> byKey: Map } } export const subinstance = < T, KT, KN extends string, P extends StringRecord, >( parent: StateNode, keyName: KN, getState$: InstanceCtxFn, equalityFn: (a: T, b: T) => boolean = Object.is, ): StateNode< T, { [K in keyof (P & Record)]: (P & Record)[K] } > => { const instances = new NestedMap>() const internalParent = getInternals(parent) const innerGetState$: CtxFn = (ctxValue, ctxObservable, key) => getState$(key[keyName], ctxValue, ctxObservable, key) const result = detachedNode( internalParent.keysOrder.concat(keyName), innerGetState$, equalityFn, ) const parentRun = (key: any[], isActive: boolean, isLoaded?: boolean) => { let instance: InternalInstance = instances.get(key)! if (!instance) { instance = { isActive, isLoaded, references: { byInstance: new Map(), byKey: new Map(), }, } instances.set(key, instance) } else { instance.isActive = isActive instance.isLoaded = isLoaded } instance.references.byKey.forEach(([, k]) => { result.run(k, isActive, isLoaded) }) } const idsRun = (from: InternalStateNode, key: any[], ids: KT[]) => { let instance: InternalInstance = instances.get(key)! if (!instance) { instance = { isActive: false, isLoaded: false, references: { byInstance: new Map([[from, new Set(ids)]]), byKey: new Map(), }, } ids.forEach((id) => { instance.references.byKey.set(id, [1, key.concat(id)]) }) instances.set(key, instance) return } const newSetIds = new Set(ids) if (!instance.references.byInstance.has(from)) { instance.references.byInstance.set(from, newSetIds) ids.forEach((id) => { if (instance.references.byKey.has(id)) { instance.references.byKey.get(id)![0]++ } else { const completeKey = key.concat(id) instance.references.byKey.set(id, [1, completeKey]) if (instance.isActive) result.run(completeKey, instance.isActive, instance.isLoaded) } }) return } const oldSet = instance.references.byInstance.get(from)! instance.references.byInstance.set(from, newSetIds) oldSet.forEach((oldId) => { if (newSetIds.has(oldId)) return const oldByKey = instance.references.byKey.get(oldId)! if (--oldByKey[0] === 0) { instance.references.byKey.delete(oldId) result.run(oldByKey[1], false, false) } }) newSetIds.forEach((newId) => { if (oldSet.has(newId)) return if (!instance.references.byKey.has(newId)) { const completeKey = key.concat(newId) instance.references.byKey.set(newId, [1, completeKey]) if (instance.isActive) result.run(completeKey, instance.isActive, instance.isLoaded) } else { instance.references.byKey.get(newId)![0]++ } }) } const activeInstances = >( parent: StateNode, ctxFn: InstanceKeysCtxFn, ) => { const internalInnerParent = getInternals(parent) const idsGenerator = detachedNode( internalInnerParent.keysOrder, ctxFn, () => false, ) idsGenerator.childRunners.push((key, isActive, isParentLoaded, ids) => { idsRun(internalInnerParent, key, isActive && isParentLoaded ? ids : []) }) } internalParent.childRunners.push(result.run) result.parents = internalParent return result.public } const positionNode = subinstance( {} as StateNode, "positionId", (positionId: bigint) => of({ id: positionId }), )