UNPKG

@oobleck/fluid-backend

Version:

Fluid Framework backend for nteract RTC

132 lines (114 loc) 4.15 kB
import { EMPTY, fromEvent, merge, Observable, of } from "rxjs"; import { DataObjectFactory, MergeTreeDeltaType, SharedMap, SharedObjectSequence, SharedString } from "@fluid-experimental/fluid-framework"; import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions"; import { CellEvent, CodeCellInput, IOutputAppendedEvent, IOutputsClearedEvent, OutputInput, } from "../schema"; import { ICellOutput, ICodeCell } from "./types"; import { ProtoCellDDS } from "./protoCell"; import { filter, mergeMap } from "rxjs/operators"; enum PropertyKey { ExecutionCount = "executionCount", Outputs = "outputs" } const convertOutput = (output: OutputInput): ICellOutput => { if ("executeResult" in output) { return { ...output.executeResult, type: "ExecuteResult" } as ICellOutput; } else if ("displayData" in output) { return { ...output.displayData, type: "DisplayData" } as ICellOutput; } else if ("stream" in output) { return { ...output.stream, type: "StreamOutput" } as ICellOutput; } else if ("error" in output) { return { ...output.error, type: "ErrorOutput" } as ICellOutput; } throw new Error("Unsupported cell output type"); } /** * Fluid DataObject */ export class CodeCellDDS extends ProtoCellDDS<CodeCellInput> implements ICodeCell { public static DataObjectName = "code-cell"; private outputSeq!: SharedObjectSequence<ICellOutput>; public static readonly Factory = new DataObjectFactory( CodeCellDDS.DataObjectName, CodeCellDDS, [SharedMap.getFactory(), SharedObjectSequence.getFactory(), SharedString.getFactory()], {} ); //#region ISolidCell get cellType(): "CodeCell" { return "CodeCell"; } get executionCount(): number | undefined { const executionCount = this.root.get<number>(PropertyKey.ExecutionCount); return executionCount; } events$!: Observable<CellEvent>; get outputs(): ICellOutput[] { return [...this.outputSeq.getItems(0)]; } appendOutput(output: OutputInput) { this.outputSeq.insert(this.outputSeq.getItemCount(), [convertOutput(output)]); } clearOutputs() { const itemCount = this.outputSeq.getItemCount(); if (itemCount !== 0) { this.outputSeq.remove(0, itemCount); } } //#endregion //#region DataObject protected async initializingFirstTime(input?: CodeCellInput): Promise<void> { await super.initializingFirstTime(input); const outputs = SharedObjectSequence.create<ICellOutput>(this.runtime); if (input) { const mappedOutputs = input.outputs?.map(convertOutput); if (mappedOutputs) { outputs.insert(0, mappedOutputs); } this.root.set(PropertyKey.ExecutionCount, input.executionCount); } this.root.set(PropertyKey.Outputs, outputs.handle); } protected async hasInitialized(): Promise<void> { await super.hasInitialized(); // cache frequently accessed properties this.outputSeq = await this.root.get(PropertyKey.Outputs).get(); const outputs$ = fromEvent<[ISequencedDocumentMessage, boolean]>(this.outputSeq, "op").pipe( filter(([, local]) => !local), mergeMap(([op]) => { const delta = op.contents; switch (delta.type) { case MergeTreeDeltaType.INSERT: if (typeof delta.pos1 === "number" && delta.seg && delta.seg.items instanceof Array) { return of<IOutputAppendedEvent>({ __typename: "OutputAppendedEvent", id: this.id, output: delta.seg.items[0] }); } break; case MergeTreeDeltaType.REMOVE: if (typeof delta.pos1 === "number" && typeof delta.pos2 === "number") { // This returns a range [pos1 ,pos2) return of<IOutputsClearedEvent>({ __typename: "OutputsClearedEvent", id: this.id }); } break; } return EMPTY; }) ); this.events$ = merge(this.metadata$, outputs$); } //#endregion }