@oobleck/fluid-backend
Version:
Fluid Framework backend for nteract RTC
132 lines (114 loc) • 4.15 kB
text/typescript
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
}