import "../_dnt.polyfills.js"; import { Run, Rune, Runner } from "./Rune.js" import { Receipt, Timeline } from "./Timeline.js" import { ValueRune } from "./ValueRune.js" export class MetaRune extends Rune, U2> { flat(): ValueRune { return ValueRune.new(RunFlat, this) } metaMap(fn: (rune: Rune) => Rune): MetaRune { return this.into(ValueRune).map(fn).into(MetaRune).pin(fn(Rune._placeholder())) } flatMap(fn: (rune: Rune) => Rune): ValueRune { return this.into(ValueRune).map(fn).into(MetaRune).pin(fn(Rune._placeholder())).flat() } asOrtho(): OrthoRune { return new OrthoRune((runner) => new RunAsOrtho(runner, this)) } pin(pinned: Rune) { return Rune.pin(this, pinned).into(MetaRune) } } export class OrthoRune extends Rune, U2> { flatSingular(): ValueRune { return ValueRune.new(RunFlatSingular, this) } orthoMap(fn: (rune: ValueRune) => ValueRune): OrthoRune { return this .into(ValueRune) .map((run) => fn(new ValueRune(() => run))) .into(MetaRune) .asOrtho() .pin(fn(Rune._placeholder().into(ValueRune))) } pin(pinned: Rune) { return Rune.pin(this, pinned).into(OrthoRune) } } class RunFlat extends Run { child constructor( runner: Runner, child: Rune, U2>, ) { super(runner) this.child = this.use(child) } lastChildReceipt = new Receipt() innerController = new AbortController() currentInner: Run = null! lastValue: T = null! first = true async _evaluate(time: number, receipt: Receipt): Promise { const rune = await this.child.evaluate(time, receipt) if (!receipt.ready) return null! if (receipt.novel) { // TODO: prime before dereferencing? if (this.currentInner) { this.currentInner.dereference() } this.currentInner = this.runner.prime(rune) this.currentInner.reference() } const _receipt = new Receipt() try { const value = await this.currentInner.evaluate(time, _receipt) if (!_receipt.ready) { if (this.first) { receipt.ready = false } return this.lastValue } this.first = false return value } finally { receipt.setFrom(_receipt) } } override cleanup(): void { if (this.currentInner) { this.dependencies.push(this.currentInner) } super.cleanup() } } class OrthoRunner extends Runner { order: number timeline: Timeline constructor(readonly parent: Runner, readonly anchorTime: number) { super() this.order = parent.order + 1 this.timeline = parent.timeline } _prime(rune: Rune): Run { const parentRun = this.parent.getPrimed(rune) if (parentRun) { if (parentRun.order === this.parent.order) { return new RunWrapOrtho(this, parentRun) } else { return parentRun } } const run = rune._prime(this) this.parent.memo.set(rune._prime, run) return run } override onCleanup(run: Run): void { super.onCleanup(run) if (run._sources.length) { this.parent.onCleanup(run) } } } class RunFlatSingular extends Run { child constructor(runner: Runner, child: Rune, U2>) { super(runner) this.child = this.use(child) } async _evaluate(time: number, receipt: Receipt) { const run = await this.child.evaluate(time, receipt) return new Rune(() => run).run(this.runner) } } class RunAsOrtho extends Run, U2> { child constructor(runner: Runner, child: Rune, U2>) { super(runner) this.child = this.use(child) } async _evaluate(time: number, receipt: Receipt) { const orthoRunner = new OrthoRunner(this.runner, time) const rune = await this.child.evaluate(time, receipt) return orthoRunner.prime(rune) } } class RunWrapOrtho extends Run { declare runner: OrthoRunner constructor(runner: OrthoRunner, readonly child: Run) { super(runner) this.useRun(child) } _evaluate(): Promise { return this.child.evaluate(this.runner.anchorTime, new Receipt()) } }