{
  "version": 3,
  "sources": ["../../../src/lib/core/transactions.ts"],
  "sourcesContent": ["import { _Atom } from './Atom'\nimport { GLOBAL_START_EPOCH } from './constants'\nimport { EffectScheduler } from './EffectScheduler'\nimport { singleton } from './helpers'\nimport { Child, Signal } from './types'\n\nclass Transaction {\n\tconstructor(public readonly parent: Transaction | null) {}\n\tinitialAtomValues = new Map<_Atom, any>()\n\n\t/**\n\t * Get whether this transaction is a root (no parents).\n\t *\n\t * @public\n\t */\n\t// eslint-disable-next-line no-restricted-syntax\n\tget isRoot() {\n\t\treturn this.parent === null\n\t}\n\n\t/**\n\t * Commit the transaction's changes.\n\t *\n\t * @public\n\t */\n\tcommit() {\n\t\tif (this.isRoot) {\n\t\t\t// For root transactions, flush changes to each of the atom's initial values.\n\t\t\tconst atoms = this.initialAtomValues\n\t\t\tthis.initialAtomValues = new Map()\n\t\t\tflushChanges(atoms.keys())\n\t\t} else {\n\t\t\t// For transaction's with parents, add the transaction's initial values to the parent's.\n\t\t\tthis.initialAtomValues.forEach((value, atom) => {\n\t\t\t\tif (!this.parent!.initialAtomValues.has(atom)) {\n\t\t\t\t\tthis.parent!.initialAtomValues.set(atom, value)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Abort the transaction.\n\t *\n\t * @public\n\t */\n\tabort() {\n\t\tinst.globalEpoch++\n\n\t\t// Reset each of the transaction's atoms to its initial value.\n\t\tthis.initialAtomValues.forEach((value, atom) => {\n\t\t\tatom.set(value)\n\t\t\tatom.historyBuffer?.clear()\n\t\t})\n\n\t\t// Commit the changes.\n\t\tthis.commit()\n\t}\n}\n\nconst inst = singleton('transactions', () => ({\n\t// The current epoch (global to all atoms).\n\tglobalEpoch: GLOBAL_START_EPOCH + 1,\n\t// Whether any transaction is reacting.\n\tglobalIsReacting: false,\n\tcurrentTransaction: null as Transaction | null,\n}))\n\nexport function getGlobalEpoch() {\n\treturn inst.globalEpoch\n}\n\n/**\n * Collect all of the reactors that need to run for an atom and run them.\n *\n * @param atom The atom to flush changes for.\n */\nfunction flushChanges(atoms: Iterable<_Atom>) {\n\tif (inst.globalIsReacting) {\n\t\tthrow new Error('cannot change atoms during reaction cycle')\n\t}\n\n\ttry {\n\t\tinst.globalIsReacting = true\n\n\t\t// Collect all of the visited reactors.\n\t\tconst reactors = new Set<EffectScheduler<unknown>>()\n\n\t\t// Visit each descendant of the atom, collecting reactors.\n\t\tconst traverse = (node: Child) => {\n\t\t\tif (node.lastTraversedEpoch === inst.globalEpoch) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tnode.lastTraversedEpoch = inst.globalEpoch\n\n\t\t\tif (node instanceof EffectScheduler) {\n\t\t\t\treactors.add(node)\n\t\t\t} else {\n\t\t\t\t;(node as any as Signal<any>).children.visit(traverse)\n\t\t\t}\n\t\t}\n\n\t\tfor (const atom of atoms) {\n\t\t\tatom.children.visit(traverse)\n\t\t}\n\n\t\t// Run each reactor.\n\t\tfor (const r of reactors) {\n\t\t\tr.maybeScheduleEffect()\n\t\t}\n\t} finally {\n\t\tinst.globalIsReacting = false\n\t}\n}\n\n/**\n * Handle a change to an atom.\n *\n * @param atom The atom that changed.\n * @param previousValue The atom's previous value.\n *\n * @internal\n */\nexport function atomDidChange(atom: _Atom, previousValue: any) {\n\tif (!inst.currentTransaction) {\n\t\tflushChanges([atom])\n\t} else if (!inst.currentTransaction.initialAtomValues.has(atom)) {\n\t\tinst.currentTransaction.initialAtomValues.set(atom, previousValue)\n\t}\n}\n\nexport function advanceGlobalEpoch() {\n\tinst.globalEpoch++\n}\n\n/**\n * Batches state updates, deferring side effects until after the transaction completes.\n *\n * @example\n * ```ts\n * const firstName = atom('John')\n * const lastName = atom('Doe')\n *\n * react('greet', () => {\n *   print(`Hello, ${firstName.get()} ${lastName.get()}!`)\n * })\n *\n * // Logs \"Hello, John Doe!\"\n *\n * transaction(() => {\n *  firstName.set('Jane')\n *  lastName.set('Smith')\n * })\n *\n * // Logs \"Hello, Jane Smith!\"\n * ```\n *\n * If the function throws, the transaction is aborted and any signals that were updated during the transaction revert to their state before the transaction began.\n *\n * @example\n * ```ts\n * const firstName = atom('John')\n * const lastName = atom('Doe')\n *\n * react('greet', () => {\n *   print(`Hello, ${firstName.get()} ${lastName.get()}!`)\n * })\n *\n * // Logs \"Hello, John Doe!\"\n *\n * transaction(() => {\n *  firstName.set('Jane')\n *  throw new Error('oops')\n * })\n *\n * // Does not log\n * // firstName.get() === 'John'\n * ```\n *\n * A `rollback` callback is passed into the function.\n * Calling this will prevent the transaction from committing and will revert any signals that were updated during the transaction to their state before the transaction began.\n *\n *  * @example\n * ```ts\n * const firstName = atom('John')\n * const lastName = atom('Doe')\n *\n * react('greet', () => {\n *   print(`Hello, ${firstName.get()} ${lastName.get()}!`)\n * })\n *\n * // Logs \"Hello, John Doe!\"\n *\n * transaction((rollback) => {\n *  firstName.set('Jane')\n *  lastName.set('Smith')\n *  rollback()\n * })\n *\n * // Does not log\n * // firstName.get() === 'John'\n * // lastName.get() === 'Doe'\n * ```\n *\n * @param fn - The function to run in a transaction, called with a function to roll back the change.\n * @public\n */\nexport function transaction<T>(fn: (rollback: () => void) => T) {\n\tconst txn = new Transaction(inst.currentTransaction)\n\n\t// Set the current transaction to the transaction\n\tinst.currentTransaction = txn\n\n\ttry {\n\t\tlet rollback = false\n\n\t\t// Run the function.\n\t\tconst result = fn(() => (rollback = true))\n\n\t\tif (rollback) {\n\t\t\t// If the rollback was triggered, abort the transaction.\n\t\t\ttxn.abort()\n\t\t} else {\n\t\t\t// Otherwise, commit the transaction.\n\t\t\ttxn.commit()\n\t\t}\n\n\t\treturn result\n\t} catch (e) {\n\t\t// Abort the transaction if the function throws.\n\t\ttxn.abort()\n\t\tthrow e\n\t} finally {\n\t\t// Set the current transaction to the transaction's parent.\n\t\tinst.currentTransaction = inst.currentTransaction.parent\n\t}\n}\n\n/**\n * Like [transaction](#transaction), but does not create a new transaction if there is already one in progress.\n *\n * @param fn - The function to run in a transaction.\n * @public\n */\nexport function transact<T>(fn: () => T): T {\n\tif (inst.currentTransaction) {\n\t\treturn fn()\n\t}\n\treturn transaction(fn)\n}\n"],
  "mappings": "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",
  "names": []
}
