// The operations the inspector and the drive surface apply (spec §7.2), // over a Y.Doc in the browser — the same vocabulary as core/src/ops.rs, // with the same result. A new op is added to both or to neither; the // vectors under vectors/ hold the two to one view. // // applyOps(ydoc, ops, origin) parses and checks the whole batch against a // model of the deck first (ops-model.mjs) and throws an OpsError — with // the core's own message, prefixed `op N:` — having written nothing; // only then does it apply every op (ops-apply.mjs) inside one // ydoc.transact with the given origin, so the undo manager and the // save queue see one change. // // Ids: the core never draws entropy and neither does this module. Every // new slide or block id arrives in the op (`id`, `new_id`, // `new_block_ids`); the browser mints them with ids.mjs before calling. import { roots } from './ydoc.mjs' import { Model, OpsError, parseOp } from './ops-model.mjs' import { applyOne } from './ops-apply.mjs' export { OpsError } from './ops-model.mjs' export { view } from './view.mjs' function at (i, e, applying = false) { const msg = e instanceof Error ? e.message : String(e) return new OpsError(`op ${i}${applying ? ' (applying)' : ''}: ${msg}`, i) } /** * Applies ops (an array of §7.2 operations) to ydoc in one transaction. * Throws OpsError, applying nothing, when any op is malformed or names * something the deck (as the batch so far leaves it) does not have. */ export function applyOps (ydoc, ops, origin = 'ops') { if (!Array.isArray(ops)) throw new OpsError('ops must be a JSON array', -1) const parsed = ops.map((raw, i) => { try { return parseOp(raw) } catch (e) { throw at(i, e) } }) const r = roots(ydoc) const model = Model.read(r) parsed.forEach((op, i) => { try { model.check(op) } catch (e) { throw at(i, e) } }) ydoc.transact(() => { parsed.forEach((op, i) => { try { applyOne(r, op) } catch (e) { throw at(i, e, true) } }) }, origin) }