{"version":3,"file":"index.modern.mjs","sources":["../src/derive.ts"],"sourcesContent":["import { getVersion, proxy, subscribe } from 'valtio/vanilla'\n\ntype DeriveGet = (proxyObject: T) => T\n\ntype Subscription = {\n s: object // \"s\"ourceObject\n d: object // \"d\"erivedObject\n k: string // derived \"k\"ey\n c: () => void // \"c\"allback\n n: boolean // \"n\"otifyInSync\n i: string[] // \"i\"goringKeys\n p?: Promise // \"p\"romise\n}\n\ntype SourceObjectEntry = [\n subscriptions: Set,\n unsubscribe: () => void,\n pendingCount: number,\n pendingCallbacks: Set<() => void>,\n]\n\ntype DerivedObjectEntry = [subscriptions: Set]\n\nconst sourceObjectMap = new WeakMap()\nconst derivedObjectMap = new WeakMap()\n\nconst markPending = (sourceObject: object, callback?: () => void) => {\n const sourceObjectEntry = sourceObjectMap.get(sourceObject)\n if (sourceObjectEntry) {\n sourceObjectEntry[0].forEach((subscription) => {\n const { d: derivedObject } = subscription\n if (sourceObject !== derivedObject) {\n markPending(derivedObject)\n }\n })\n ++sourceObjectEntry[2] // pendingCount\n if (callback) {\n sourceObjectEntry[3].add(callback) // pendingCallbacks\n }\n }\n}\n\n// has side effect (even though used in Array.map)\nconst checkPending = (sourceObject: object, callback: () => void) => {\n const sourceObjectEntry = sourceObjectMap.get(sourceObject)\n if (sourceObjectEntry?.[2]) {\n sourceObjectEntry[3].add(callback) // pendingCallbacks\n return true\n }\n return false\n}\n\nconst unmarkPending = (sourceObject: object) => {\n const sourceObjectEntry = sourceObjectMap.get(sourceObject)\n if (sourceObjectEntry) {\n --sourceObjectEntry[2] // pendingCount\n if (!sourceObjectEntry[2]) {\n sourceObjectEntry[3].forEach((callback) => callback())\n sourceObjectEntry[3].clear() // pendingCallbacks\n }\n sourceObjectEntry[0].forEach((subscription) => {\n const { d: derivedObject } = subscription\n if (sourceObject !== derivedObject) {\n unmarkPending(derivedObject)\n }\n })\n }\n}\n\nconst addSubscription = (subscription: Subscription) => {\n const { s: sourceObject, d: derivedObject } = subscription\n let derivedObjectEntry = derivedObjectMap.get(derivedObject)\n if (!derivedObjectEntry) {\n derivedObjectEntry = [new Set()]\n derivedObjectMap.set(subscription.d, derivedObjectEntry)\n }\n derivedObjectEntry[0].add(subscription)\n let sourceObjectEntry = sourceObjectMap.get(sourceObject)\n if (!sourceObjectEntry) {\n const subscriptions = new Set()\n const unsubscribe = subscribe(\n sourceObject,\n (ops) => {\n subscriptions.forEach((subscription) => {\n const {\n d: derivedObject,\n c: callback,\n n: notifyInSync,\n i: ignoreKeys,\n } = subscription\n if (\n sourceObject === derivedObject &&\n ops.every(\n (op) =>\n op[1].length === 1 && ignoreKeys.includes(op[1][0] as string)\n )\n ) {\n // only setting derived properties\n return\n }\n if (subscription.p) {\n // already scheduled\n return\n }\n markPending(sourceObject, callback)\n if (notifyInSync) {\n unmarkPending(sourceObject)\n } else {\n subscription.p = Promise.resolve().then(() => {\n delete subscription.p // promise\n unmarkPending(sourceObject)\n })\n }\n })\n },\n true\n )\n sourceObjectEntry = [subscriptions, unsubscribe, 0, new Set()]\n sourceObjectMap.set(sourceObject, sourceObjectEntry)\n }\n sourceObjectEntry[0].add(subscription)\n}\n\nconst removeSubscription = (subscription: Subscription) => {\n const { s: sourceObject, d: derivedObject } = subscription\n const derivedObjectEntry = derivedObjectMap.get(derivedObject)\n derivedObjectEntry?.[0].delete(subscription)\n if (derivedObjectEntry?.[0].size === 0) {\n derivedObjectMap.delete(derivedObject)\n }\n const sourceObjectEntry = sourceObjectMap.get(sourceObject)\n if (sourceObjectEntry) {\n const [subscriptions, unsubscribe] = sourceObjectEntry\n subscriptions.delete(subscription)\n if (!subscriptions.size) {\n unsubscribe()\n sourceObjectMap.delete(sourceObject)\n }\n }\n}\n\nconst listSubscriptions = (derivedObject: object) => {\n const derivedObjectEntry = derivedObjectMap.get(derivedObject)\n if (derivedObjectEntry) {\n return Array.from(derivedObjectEntry[0]) // NOTE do we need to copy?\n }\n return []\n}\n\n// NOTE This is experimentally exported.\n// The availability is not guaranteed, and it will be renamed,\n// changed or removed without any notice in future versions.\n// It's not expected to use this in production.\nexport const unstable_deriveSubscriptions = {\n add: addSubscription,\n remove: removeSubscription,\n list: listSubscriptions,\n}\n\n/**\n * derive\n *\n * This creates derived properties and attaches them\n * to a new proxy object or an existing proxy object.\n *\n * @example\n * import { proxy } from 'valtio'\n * import { derive } from 'valtio/utils'\n *\n * const state = proxy({\n * count: 1,\n * })\n *\n * const derivedState = derive({\n * doubled: (get) => get(state).count * 2,\n * })\n *\n * derive({\n * tripled: (get) => get(state).count * 3,\n * }, {\n * proxy: state,\n * })\n */\nexport function derive(\n derivedFns: {\n [K in keyof U]: (get: DeriveGet) => U[K]\n },\n options?: {\n proxy?: T\n sync?: boolean\n }\n) {\n const proxyObject = (options?.proxy || proxy({})) as U\n const notifyInSync = !!options?.sync\n const derivedKeys = Object.keys(derivedFns)\n derivedKeys.forEach((key) => {\n if (Object.getOwnPropertyDescriptor(proxyObject, key)) {\n throw new Error('object property already defined')\n }\n const fn = derivedFns[key as keyof U]\n type DependencyEntry = {\n v: number // \"v\"ersion\n s?: Subscription // \"s\"ubscription\n }\n let lastDependencies: Map | null = null\n const evaluate = () => {\n if (lastDependencies) {\n if (\n Array.from(lastDependencies)\n .map(([p]) => checkPending(p, evaluate))\n .some((isPending) => isPending)\n ) {\n // some dependencies are pending\n return\n }\n if (\n Array.from(lastDependencies).every(\n ([p, entry]) => getVersion(p) === entry.v\n )\n ) {\n // no dependencies are changed\n return\n }\n }\n const dependencies = new Map()\n const get =

(p: P) => {\n dependencies.set(p, { v: getVersion(p) as number })\n return p\n }\n const value = fn(get)\n const subscribeToDependencies = () => {\n dependencies.forEach((entry, p) => {\n const lastSubscription = lastDependencies?.get(p)?.s\n if (lastSubscription) {\n entry.s = lastSubscription\n } else {\n const subscription: Subscription = {\n s: p, // sourceObject\n d: proxyObject, // derivedObject\n k: key, // derived key\n c: evaluate, // callback\n n: notifyInSync,\n i: derivedKeys, // ignoringKeys\n }\n addSubscription(subscription)\n entry.s = subscription\n }\n })\n lastDependencies?.forEach((entry, p) => {\n if (!dependencies.has(p) && entry.s) {\n removeSubscription(entry.s)\n }\n })\n lastDependencies = dependencies\n }\n if ((value as unknown) instanceof Promise) {\n ;(value as Promise).finally(subscribeToDependencies)\n } else {\n subscribeToDependencies()\n }\n proxyObject[key as keyof U] = value\n }\n evaluate()\n })\n return proxyObject as T & U\n}\n\n/**\n * underive\n *\n * This stops derived properties to evaluate.\n * It will stop all (or specified by `keys` option) subscriptions.\n * If you specify `delete` option, it will delete the properties\n * and you can attach new derived properties.\n *\n * @example\n * import { proxy } from 'valtio'\n * import { derive, underive } from 'valtio/utils'\n *\n * const state = proxy({\n * count: 1,\n * })\n *\n * const derivedState = derive({\n * doubled: (get) => get(state).count * 2,\n * })\n *\n * underive(derivedState)\n */\nexport function underive(\n proxyObject: T & U,\n options?: {\n delete?: boolean\n keys?: (keyof U)[]\n }\n) {\n const keysToDelete = options?.delete ? new Set() : null\n listSubscriptions(proxyObject).forEach((subscription) => {\n const { k: key } = subscription\n if (!options?.keys || options.keys.includes(key as keyof U)) {\n removeSubscription(subscription)\n if (keysToDelete) {\n keysToDelete.add(key as keyof U)\n }\n }\n })\n if (keysToDelete) {\n keysToDelete.forEach((key) => {\n delete proxyObject[key]\n })\n }\n}\n"],"names":["sourceObjectMap","WeakMap","derivedObjectMap","markPending","sourceObject","callback","sourceObjectEntry","get","forEach","subscription","d","derivedObject","add","unmarkPending","clear","addSubscription","s","derivedObjectEntry","Set","set","subscriptions","unsubscribe","subscribe","ops","c","n","notifyInSync","i","ignoreKeys","every","op","length","includes","p","Promise","resolve","then","removeSubscription","delete","size","listSubscriptions","Array","from","unstable_deriveSubscriptions","remove","list","derive","derivedFns","options","proxyObject","proxy","sync","derivedKeys","Object","keys","key","getOwnPropertyDescriptor","Error","fn","lastDependencies","evaluate","map","checkPending","some","isPending","entry","getVersion","v","dependencies","Map","value","subscribeToDependencies","_lastDependencies2","_lastDependencies","lastSubscription","k","has","finally","underive","keysToDelete"],"mappings":"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"}