{
  "version": 3,
  "sources": ["../../src/transactions.ts"],
  "sourcesContent": ["import { _Atom } from './Atom.js'\nimport { GLOBAL_START_EPOCH } from './constants.js'\nimport { EffectScheduler } from './EffectScheduler.js'\nimport { Child, Signal } from './types.js'\n\n// The current epoch (global to all atoms).\nexport let globalEpoch = GLOBAL_START_EPOCH + 1\n\n// Whether any transaction is reacting.\nlet globalIsReacting = false\n\nexport function advanceGlobalEpoch() {\n\tglobalEpoch++\n}\n\nclass Transaction {\n\tconstructor(public readonly parent: Transaction | null) {}\n\tinitialAtomValues = new Map<_Atom<any>, any>()\n\n\t/**\n\t * Get whether this transaction is a root (no parents).\n\t *\n\t * @public\n\t */\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\tglobalEpoch++\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\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<any>>) {\n\tif (globalIsReacting) {\n\t\tthrow new Error('cannot change atoms during reaction cycle')\n\t}\n\n\ttry {\n\t\tglobalIsReacting = 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 === globalEpoch) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tnode.lastTraversedEpoch = globalEpoch\n\n\t\t\tif ('maybeScheduleEffect' in node) {\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\tglobalIsReacting = 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<any>, previousValue: any) {\n\tif (!currentTransaction) {\n\t\tflushChanges([atom])\n\t} else if (!currentTransaction.initialAtomValues.has(atom)) {\n\t\tcurrentTransaction.initialAtomValues.set(atom, previousValue)\n\t}\n}\n\n/**\n * The current transaction, if there is one.\n *\n * @global\n * @public\n */\nexport let currentTransaction = null as Transaction | null\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 *   console.log(`Hello, ${firstName.value} ${lastName.value}!`)\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 *   console.log(`Hello, ${firstName.value} ${lastName.value}!`)\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.value === '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 *   console.log(`Hello, ${firstName.value} ${lastName.value}!`)\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.value === 'John'\n * // lastName.value === '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(currentTransaction)\n\n\t// Set the current transaction to the transaction\n\tcurrentTransaction = 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\tcurrentTransaction = 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\n * @public\n */\nexport function transact<T>(fn: () => T): T {\n\tif (currentTransaction) {\n\t\treturn fn()\n\t}\n\treturn transaction(fn)\n}\n"],
  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,uBAAmC;AAK5B,IAAI,cAAc,sCAAqB;AAG9C,IAAI,mBAAmB;AAEhB,SAAS,qBAAqB;AACpC;AACD;AAEA,IAAM,cAAN,MAAkB;AAAA,EACjB,YAA4B,QAA4B;AAA5B;AAAA,EAA6B;AAAA,EACzD,oBAAoB,oBAAI,IAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO7C,IAAI,SAAS;AACZ,WAAO,KAAK,WAAW;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS;AACR,QAAI,KAAK,QAAQ;AAEhB,YAAM,QAAQ,KAAK;AACnB,WAAK,oBAAoB,oBAAI,IAAI;AACjC,mBAAa,MAAM,KAAK,CAAC;AAAA,IAC1B,OAAO;AAEN,WAAK,kBAAkB,QAAQ,CAAC,OAAO,SAAS;AAC/C,YAAI,CAAC,KAAK,OAAQ,kBAAkB,IAAI,IAAI,GAAG;AAC9C,eAAK,OAAQ,kBAAkB,IAAI,MAAM,KAAK;AAAA,QAC/C;AAAA,MACD,CAAC;AAAA,IACF;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ;AACP;AAGA,SAAK,kBAAkB,QAAQ,CAAC,OAAO,SAAS;AAC/C,WAAK,IAAI,KAAK;AACd,WAAK,eAAe,MAAM;AAAA,IAC3B,CAAC;AAGD,SAAK,OAAO;AAAA,EACb;AACD;AAOA,SAAS,aAAa,OAA6B;AAClD,MAAI,kBAAkB;AACrB,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC5D;AAEA,MAAI;AACH,uBAAmB;AAGnB,UAAM,WAAW,oBAAI,IAA8B;AAGnD,UAAM,WAAW,CAAC,SAAgB;AACjC,UAAI,KAAK,uBAAuB,aAAa;AAC5C;AAAA,MACD;AAEA,WAAK,qBAAqB;AAE1B,UAAI,yBAAyB,MAAM;AAClC,iBAAS,IAAI,IAAI;AAAA,MAClB,OAAO;AACN;AAAC,QAAC,KAA4B,SAAS,MAAM,QAAQ;AAAA,MACtD;AAAA,IACD;AAEA,eAAW,QAAQ,OAAO;AACzB,WAAK,SAAS,MAAM,QAAQ;AAAA,IAC7B;AAGA,eAAW,KAAK,UAAU;AACzB,QAAE,oBAAoB;AAAA,IACvB;AAAA,EACD,UAAE;AACD,uBAAmB;AAAA,EACpB;AACD;AAUO,SAAS,cAAc,MAAkB,eAAoB;AACnE,MAAI,CAAC,oBAAoB;AACxB,iBAAa,CAAC,IAAI,CAAC;AAAA,EACpB,WAAW,CAAC,mBAAmB,kBAAkB,IAAI,IAAI,GAAG;AAC3D,uBAAmB,kBAAkB,IAAI,MAAM,aAAa;AAAA,EAC7D;AACD;AAQO,IAAI,qBAAqB;AA0EzB,SAAS,YAAe,IAAiC;AAC/D,QAAM,MAAM,IAAI,YAAY,kBAAkB;AAG9C,uBAAqB;AAErB,MAAI;AACH,QAAI,WAAW;AAGf,UAAM,SAAS,GAAG,MAAO,WAAW,IAAK;AAEzC,QAAI,UAAU;AAEb,UAAI,MAAM;AAAA,IACX,OAAO;AAEN,UAAI,OAAO;AAAA,IACZ;AAEA,WAAO;AAAA,EACR,SAAS,GAAP;AAED,QAAI,MAAM;AACV,UAAM;AAAA,EACP,UAAE;AAED,yBAAqB,mBAAmB;AAAA,EACzC;AACD;AAQO,SAAS,SAAY,IAAgB;AAC3C,MAAI,oBAAoB;AACvB,WAAO,GAAG;AAAA,EACX;AACA,SAAO,YAAY,EAAE;AACtB;",
  "names": []
}
