Work on sync stuff

This commit is contained in:
Zef Hemel
2022-04-05 17:02:17 +02:00
parent 1b0048cdcf
commit 38faf50ab8
51 changed files with 704 additions and 257 deletions
+6 -2
View File
@@ -1,8 +1,12 @@
import { Editor } from "./editor";
import { Space } from "./space";
import { safeRun } from "./util";
import { IndexedDBSpace } from "./spaces/indexeddb_space";
let editor = new Editor(new Space(""), document.getElementById("root")!);
let editor = new Editor(
// new HttpRestSpace(""),
new IndexedDBSpace("pages"),
document.getElementById("root")!
);
safeRun(async () => {
await editor.init();
+2 -11
View File
@@ -2,18 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="panel.scss" rel="stylesheet" />
<link href="panel.scss" rel="stylesheet"/>
<base target="_top">
<script type="module">
window.addEventListener("message", (message) => {
const data = message.data;
switch(data.type) {
case "html":
document.body.innerHTML = data.html;
break;
}
})
</script>
<script src="panel_page.ts"/>
</head>
<body>
Send me HTML
+17 -1
View File
@@ -1,4 +1,4 @@
import {useEffect, useRef} from "react";
import { useEffect, useRef } from "react";
// @ts-ignore
import iframeHtml from "bundle-text:./panel.html";
@@ -23,6 +23,22 @@ export function Panel({ html, flex }: { html: string; flex: number }) {
iframe.onload = null;
};
}, [html]);
useEffect(() => {
let messageListener = (evt: any) => {
if (evt.source !== iFrameRef.current!.contentWindow) {
return;
}
let data = evt.data;
if (!data) return;
console.log("Got message from panel", data);
};
window.addEventListener("message", messageListener);
return () => {
window.removeEventListener("message", messageListener);
};
}, []);
return (
<div className="panel" style={{ flex }}>
<iframe srcDoc={iframeHtml} ref={iFrameRef} />
+22
View File
@@ -0,0 +1,22 @@
window.addEventListener("message", (message) => {
const data = message.data;
switch (data.type) {
case "html":
document.body.innerHTML = data.html;
break;
}
});
function sendEvent(data: any) {
window.parent.postMessage(
{
type: "event",
data: data,
},
"*"
);
}
//
// setInterval(() => {
// self.sendEvent("testing");
// }, 2000);
+1 -1
View File
@@ -1,4 +1,4 @@
import {EditorView} from "@codemirror/view";
import { EditorView } from "@codemirror/view";
import * as util from "../util";
export function StatusBar({ editorView }: { editorView?: EditorView }) {
+3 -3
View File
@@ -1,6 +1,6 @@
import {Notification} from "../types";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faFileLines} from "@fortawesome/free-solid-svg-icons";
import { Notification } from "../types";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faFileLines } from "@fortawesome/free-solid-svg-icons";
function prettyName(s: string | undefined): string {
if (!s) {
+61 -40
View File
@@ -1,10 +1,10 @@
import {autocompletion, completionKeymap} from "@codemirror/autocomplete";
import {closeBrackets, closeBracketsKeymap} from "@codemirror/closebrackets";
import {indentWithTab, standardKeymap} from "@codemirror/commands";
import {history, historyKeymap} from "@codemirror/history";
import {bracketMatching} from "@codemirror/matchbrackets";
import {searchKeymap} from "@codemirror/search";
import {EditorSelection, EditorState} from "@codemirror/state";
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
import { closeBrackets, closeBracketsKeymap } from "@codemirror/closebrackets";
import { indentWithTab, standardKeymap } from "@codemirror/commands";
import { history, historyKeymap } from "@codemirror/history";
import { bracketMatching } from "@codemirror/matchbrackets";
import { searchKeymap } from "@codemirror/search";
import { EditorSelection, EditorState } from "@codemirror/state";
import {
drawSelection,
dropCursor,
@@ -13,41 +13,41 @@ import {
KeyBinding,
keymap,
ViewPlugin,
ViewUpdate,
ViewUpdate
} from "@codemirror/view";
import React, {useEffect, useReducer} from "react";
import React, { useEffect, useReducer } from "react";
import ReactDOM from "react-dom";
import {createSandbox as createIFrameSandbox} from "../plugos/environments/webworker_sandbox";
import {AppEvent, AppEventDispatcher, ClickEvent} from "./app_event";
import { createSandbox as createIFrameSandbox } from "../plugos/environments/webworker_sandbox";
import { AppEvent, AppEventDispatcher, ClickEvent } from "./app_event";
import * as commands from "./commands";
import {CommandPalette} from "./components/command_palette";
import {PageNavigator} from "./components/page_navigator";
import {TopBar} from "./components/top_bar";
import {lineWrapper} from "./line_wrapper";
import {markdown} from "./markdown";
import {PathPageNavigator} from "./navigator";
import { CommandPalette } from "./components/command_palette";
import { PageNavigator } from "./components/page_navigator";
import { TopBar } from "./components/top_bar";
import { lineWrapper } from "./line_wrapper";
import { markdown } from "./markdown";
import { PathPageNavigator } from "./navigator";
import customMarkDown from "./parser";
import reducer from "./reducer";
import {smartQuoteKeymap} from "./smart_quotes";
import {Space} from "./space";
import { smartQuoteKeymap } from "./smart_quotes";
import { Space } from "./spaces/space";
import customMarkdownStyle from "./style";
import {editorSyscalls} from "./syscalls/editor";
import {indexerSyscalls} from "./syscalls/indexer";
import {spaceSyscalls} from "./syscalls/space";
import {Action, AppViewState, initialViewState} from "./types";
import {SilverBulletHooks} from "../common/manifest";
import {safeRun, throttle} from "./util";
import {System} from "../plugos/system";
import {EventHook} from "../plugos/hooks/event";
import {systemSyscalls} from "./syscalls/system";
import {Panel} from "./components/panel";
import {CommandHook} from "./hooks/command";
import {SlashCommandHook} from "./hooks/slash_command";
import {CompleterHook} from "./hooks/completer";
import {pasteLinkExtension} from "./editor_paste";
import {markdownSyscalls} from "../common/syscalls/markdown";
import {clientStoreSyscalls} from "./syscalls/clientStore";
import {StatusBar} from "./components/status_bar";
import { editorSyscalls } from "./syscalls/editor";
import { indexerSyscalls } from "./syscalls/indexer";
import { spaceSyscalls } from "./syscalls/space";
import { Action, AppViewState, initialViewState } from "./types";
import { SilverBulletHooks } from "../common/manifest";
import { safeRun, throttle } from "./util";
import { System } from "../plugos/system";
import { EventHook } from "../plugos/hooks/event";
import { systemSyscalls } from "./syscalls/system";
import { Panel } from "./components/panel";
import { CommandHook } from "./hooks/command";
import { SlashCommandHook } from "./hooks/slash_command";
import { CompleterHook } from "./hooks/completer";
import { pasteLinkExtension } from "./editor_paste";
import { markdownSyscalls } from "../common/syscalls/markdown";
import { clientStoreSyscalls } from "./syscalls/clientStore";
import { StatusBar } from "./components/status_bar";
class PageState {
scrollTop: number;
@@ -341,6 +341,23 @@ export class Editor implements AppEventDispatcher {
return true;
},
},
{
key: "Ctrl-l",
mac: "Cmd-l",
run: (): boolean => {
this.editorView?.dispatch({
effects: [
EditorView.scrollIntoView(
this.editorView.state.selection.main.anchor,
{
y: "center",
}
),
],
});
return true;
},
},
]),
EditorView.domEventHandlers({
@@ -409,10 +426,13 @@ export class Editor implements AppEventDispatcher {
// Persist current page state and nicely close page
if (this.currentPage) {
let pageState = this.openPages.get(this.currentPage)!;
let pageState = this.openPages.get(this.currentPage);
if (pageState) {
pageState.selection = this.editorView!.state.selection;
pageState.scrollTop = this.editorView!.scrollDOM.scrollTop;
pageState.scrollTop =
this.editorView!.scrollDOM.parentElement!.parentElement!.scrollTop;
// pageState.scrollTop = this.editorView!.scrollDOM.scrollTop;
// console.log("Saved pageState", this.currentPage, pageState);
}
this.space.unwatchPage(this.currentPage);
await this.save(true);
@@ -431,11 +451,12 @@ export class Editor implements AppEventDispatcher {
});
} else {
// Restore state
console.log("Restoring selection state", pageState.selection);
// console.log("Restoring selection state", pageState);
editorView.dispatch({
selection: pageState.selection,
});
editorView.scrollDOM.scrollTop = pageState!.scrollTop;
editorView.scrollDOM.parentElement!.parentElement!.scrollTop =
pageState!.scrollTop;
}
this.space.watchPage(pageName);
+1 -1
View File
@@ -1,4 +1,4 @@
import {safeRun} from "./util";
import { safeRun } from "./util";
function encodePageUrl(name: string): string {
return name.replaceAll(" ", "_");
+4 -4
View File
@@ -1,8 +1,8 @@
import {styleTags, tags as t} from "@codemirror/highlight";
import {BlockContext, LeafBlock, LeafBlockParser, MarkdownConfig, TaskList,} from "@lezer/markdown";
import {commonmark, mkLang} from "./markdown/markdown";
import { styleTags, tags as t } from "@codemirror/highlight";
import { BlockContext, LeafBlock, LeafBlockParser, MarkdownConfig, TaskList } from "@lezer/markdown";
import { commonmark, mkLang } from "./markdown/markdown";
import * as ct from "./customtags";
import {pageLinkRegex} from "./constant";
import { pageLinkRegex } from "./constant";
const pageLinkRegexPrefix = new RegExp(
"^" + pageLinkRegex.toString().slice(1, -1)
+1 -1
View File
@@ -1,4 +1,4 @@
import {Action, AppViewState} from "./types";
import { Action, AppViewState } from "./types";
export default function reducer(
state: AppViewState,
@@ -1,27 +1,14 @@
import { EventEmitter } from "../common/event";
import { Manifest } from "../common/manifest";
import { safeRun } from "./util";
import { Plug } from "../plugos/plug";
import { PageMeta } from "../common/types";
export type SpaceEvents = {
pageCreated: (meta: PageMeta) => void;
pageChanged: (meta: PageMeta) => void;
pageDeleted: (name: string) => void;
pageListUpdated: (pages: Set<PageMeta>) => void;
plugLoaded: (plugName: string, plug: Manifest) => void;
plugUnloaded: (plugName: string) => void;
};
type PlugMeta = {
name: string;
version: number;
};
import { EventEmitter } from "../../common/event";
import { PageMeta } from "../../common/types";
import { safeRun } from "../util";
import { Plug } from "../../plugos/plug";
import { Manifest } from "../../common/manifest";
import { PlugMeta, Space, SpaceEvents } from "./space";
const pageWatchInterval = 2000;
const plugWatchInterval = 5000;
export class Space extends EventEmitter<SpaceEvents> {
export class HttpRestSpace extends EventEmitter<SpaceEvents> implements Space {
pageUrl: string;
pageMetaCache = new Map<string, PageMeta>();
plugMetaCache = new Map<string, PlugMeta>();
@@ -29,7 +16,6 @@ export class Space extends EventEmitter<SpaceEvents> {
saving = false;
private plugUrl: string;
private initialPageListLoad = true;
private initialPlugListLoad = true;
constructor(url: string) {
super();
@@ -40,11 +26,11 @@ export class Space extends EventEmitter<SpaceEvents> {
this.updatePageListAsync();
}
public watchPage(pageName: string) {
watchPage(pageName: string) {
this.watchedPages.add(pageName);
}
public unwatchPage(pageName: string) {
unwatchPage(pageName: string) {
this.watchedPages.delete(pageName);
}
@@ -114,23 +100,11 @@ export class Space extends EventEmitter<SpaceEvents> {
});
}
public async listPages(): Promise<Set<PageMeta>> {
// this.updatePageListAsync();
async listPages(): Promise<Set<PageMeta>> {
return new Set([...this.pageMetaCache.values()]);
}
private responseToMetaCacher(name: string, res: Response): PageMeta {
const meta = {
name,
lastModified: +(res.headers.get("Last-Modified") || "0"),
};
this.pageMetaCache.set(name, meta);
return meta;
}
public async readPage(
name: string
): Promise<{ text: string; meta: PageMeta }> {
async readPage(name: string): Promise<{ text: string; meta: PageMeta }> {
let res = await fetch(`${this.pageUrl}/${name}`, {
method: "GET",
});
@@ -140,11 +114,13 @@ export class Space extends EventEmitter<SpaceEvents> {
};
}
public async writePage(
async writePage(
name: string,
text: string,
selfUpdate?: boolean
selfUpdate?: boolean,
withMeta?: PageMeta
): Promise<PageMeta> {
// TODO: withMeta ignored for now
try {
this.saving = true;
let res = await fetch(`${this.pageUrl}/${name}`, {
@@ -161,7 +137,7 @@ export class Space extends EventEmitter<SpaceEvents> {
}
}
public async deletePage(name: string): Promise<void> {
async deletePage(name: string): Promise<void> {
let req = await fetch(`${this.pageUrl}/${name}`, {
method: "DELETE",
});
@@ -173,18 +149,7 @@ export class Space extends EventEmitter<SpaceEvents> {
this.emit("pageListUpdated", new Set([...this.pageMetaCache.values()]));
}
private async getPageMeta(name: string): Promise<PageMeta> {
let res = await fetch(`${this.pageUrl}/${name}`, {
method: "OPTIONS",
});
return this.responseToMetaCacher(name, res);
}
async remoteSyscall(
plug: Plug<any>,
name: string,
args: any[]
): Promise<any> {
async proxySyscall(plug: Plug<any>, name: string, args: any[]): Promise<any> {
let req = await fetch(`${this.plugUrl}/${plug.name}/syscall/${name}`, {
method: "POST",
headers: {
@@ -202,7 +167,17 @@ export class Space extends EventEmitter<SpaceEvents> {
return await req.json();
}
async remoteInvoke(plug: Plug<any>, name: string, args: any[]): Promise<any> {
async invokeFunction(
plug: Plug<any>,
env: string,
name: string,
args: any[]
): Promise<any> {
// Invoke locally
if (!env || env === "client") {
return plug.invoke(name, args);
}
// Or dispatch to server
let req = await fetch(`${this.plugUrl}/${plug.name}/function/${name}`, {
method: "POST",
headers: {
@@ -220,8 +195,38 @@ export class Space extends EventEmitter<SpaceEvents> {
return await req.json();
}
async getPageMeta(name: string): Promise<PageMeta> {
let res = await fetch(`${this.pageUrl}/${name}`, {
method: "OPTIONS",
});
return this.responseToMetaCacher(name, res);
}
public async listPlugs(): Promise<PlugMeta[]> {
let res = await fetch(`${this.plugUrl}`, {
method: "GET",
});
return (await res.json()) as PlugMeta[];
}
public async loadPlug(name: string): Promise<Manifest> {
let res = await fetch(`${this.plugUrl}/${name}`, {
method: "GET",
});
return (await res.json()) as Manifest;
}
private responseToMetaCacher(name: string, res: Response): PageMeta {
const meta = {
name,
lastModified: +(res.headers.get("Last-Modified") || "0"),
};
this.pageMetaCache.set(name, meta);
return meta;
}
private async pollPlugs(): Promise<void> {
const newPlugs = await this.loadPlugs();
const newPlugs = await this.listPlugs();
let deletedPlugs = new Set<string>(this.plugMetaCache.keys());
for (const newPlugMeta of newPlugs) {
const oldPlugMeta = this.plugMetaCache.get(newPlugMeta.name);
@@ -247,18 +252,4 @@ export class Space extends EventEmitter<SpaceEvents> {
this.emit("plugUnloaded", deletedPlug);
}
}
private async loadPlugs(): Promise<PlugMeta[]> {
let res = await fetch(`${this.plugUrl}`, {
method: "GET",
});
return (await res.json()) as PlugMeta[];
}
private async loadPlug(name: string): Promise<Manifest> {
let res = await fetch(`${this.plugUrl}/${name}`, {
method: "GET",
});
return (await res.json()) as Manifest;
}
}
+130
View File
@@ -0,0 +1,130 @@
import { PlugMeta, Space, SpaceEvents } from "./space";
import { EventEmitter } from "../../common/event";
import { PageMeta } from "../../common/types";
import Dexie, { Table } from "dexie";
import { Plug } from "../../plugos/plug";
import { Manifest } from "../../common/manifest";
type Page = {
name: string;
text: string;
meta: PageMeta;
};
type PlugManifest = {
name: string;
manifest: Manifest;
};
export class IndexedDBSpace extends EventEmitter<SpaceEvents> implements Space {
private pageTable: Table<Page, string>;
private plugMetaTable: Table<PlugMeta, string>;
private plugManifestTable: Table<PlugManifest, string>;
constructor(dbName: string) {
super();
const db = new Dexie(dbName);
db.version(1).stores({
page: "name",
plugMeta: "name",
plugManifest: "name",
});
this.pageTable = db.table("page");
this.plugMetaTable = db.table("plugMeta");
this.plugManifestTable = db.table("plugManifest");
}
async deletePage(name: string): Promise<void> {
this.emit("pageDeleted", name);
return this.pageTable.delete(name);
}
async getPageMeta(name: string): Promise<PageMeta> {
let entry = await this.pageTable.get(name);
if (entry) {
return entry.meta;
} else {
throw Error(`Page not found ${name}`);
}
}
invokeFunction(
plug: Plug<any>,
env: string,
name: string,
args: any[]
): Promise<any> {
return plug.invoke(name, args);
}
async listPages(): Promise<Set<PageMeta>> {
let allPages = await this.pageTable.toArray();
let set = new Set(allPages.map((p) => p.meta));
this.emit("pageListUpdated", set);
return set;
}
proxySyscall(plug: Plug<any>, name: string, args: any[]): Promise<any> {
return plug.syscall(name, args);
}
async readPage(name: string): Promise<{ text: string; meta: PageMeta }> {
let page = await this.pageTable.get(name);
if (page) {
return page!;
} else {
return {
text: "",
meta: {
name,
lastModified: 0,
},
};
}
}
async writePage(
name: string,
text: string,
selfUpdate?: boolean,
withMeta?: PageMeta
): Promise<PageMeta> {
let meta = withMeta
? withMeta
: {
name,
lastModified: new Date().getTime(),
};
await this.pageTable.put({
name,
text,
meta,
});
if (!selfUpdate) {
this.emit("pageChanged", meta);
}
// TODO: add pageCreated
return meta;
}
unwatchPage(pageName: string): void {}
updatePageListAsync(): void {
this.listPages();
}
watchPage(pageName: string): void {}
async listPlugs(): Promise<PlugMeta[]> {
return this.plugMetaTable.toArray();
}
async loadPlug(name: string): Promise<Manifest> {
let plugManifest = await this.plugManifestTable.get(name);
if (plugManifest) {
return plugManifest.manifest;
} else {
throw Error(`Plug not found ${name}`);
}
}
}
+52
View File
@@ -0,0 +1,52 @@
import { Manifest } from "../../common/manifest";
import { Plug } from "../../plugos/plug";
import { PageMeta } from "../../common/types";
export type SpaceEvents = {
pageCreated: (meta: PageMeta) => void;
pageChanged: (meta: PageMeta) => void;
pageDeleted: (name: string) => void;
pageListUpdated: (pages: Set<PageMeta>) => void;
plugLoaded: (plugName: string, plug: Manifest) => void;
plugUnloaded: (plugName: string) => void;
};
export type PlugMeta = {
name: string;
version: number;
};
export interface Space {
// Pages
watchPage(pageName: string): void;
unwatchPage(pageName: string): void;
listPages(): Promise<Set<PageMeta>>;
readPage(name: string): Promise<{ text: string; meta: PageMeta }>;
getPageMeta(name: string): Promise<PageMeta>;
writePage(
name: string,
text: string,
selfUpdate?: boolean,
withMeta?: PageMeta
): Promise<PageMeta>;
deletePage(name: string): Promise<void>;
// Plugs
listPlugs(): Promise<PlugMeta[]>;
loadPlug(name: string): Promise<Manifest>;
proxySyscall(plug: Plug<any>, name: string, args: any[]): Promise<any>;
invokeFunction(
plug: Plug<any>,
env: string,
name: string,
args: any[]
): Promise<any>;
// Events
on(handlers: Partial<SpaceEvents>): void;
off(handlers: Partial<SpaceEvents>): void;
emit(eventName: keyof SpaceEvents, ...args: any[]): void;
// TODO: Get rid of this
updatePageListAsync(): void;
}
+62
View File
@@ -0,0 +1,62 @@
import { expect, test } from "@jest/globals";
import { IndexedDBSpace } from "./indexeddb_space";
import { SpaceSync } from "./sync";
// For testing in node.js
require("fake-indexeddb/auto");
test("Test store", async () => {
let primary = new IndexedDBSpace("primary");
let secondary = new IndexedDBSpace("secondary");
let sync = new SpaceSync(primary, secondary, 0);
// Write one page to primary
await primary.writePage("start", "Hello");
expect((await secondary.listPages()).size).toBe(0);
await sync.syncPages();
expect((await secondary.listPages()).size).toBe(1);
expect((await secondary.readPage("start")).text).toBe("Hello");
let lastSync = sync.lastSync;
// Should be a no-op
await sync.syncPages();
expect(sync.lastSync).toBe(lastSync);
// Now let's make a change on the secondary
await secondary.writePage("start", "Hello!!");
await secondary.writePage("test", "Test page");
// And sync it
await sync.syncPages();
expect((await primary.listPages()).size).toBe(2);
expect((await secondary.listPages()).size).toBe(2);
expect((await primary.readPage("start")).text).toBe("Hello!!");
// Let's make some random edits on both ends
await primary.writePage("start", "1");
await primary.writePage("start2", "2");
await secondary.writePage("start3", "3");
await secondary.writePage("start4", "4");
await sync.syncPages();
expect((await primary.listPages()).size).toBe(5);
expect((await secondary.listPages()).size).toBe(5);
console.log("Should be no op");
await sync.syncPages();
console.log("Done");
// Cause a conflict
await primary.writePage("start", "Hello 1");
await secondary.writePage("start", "Hello 2");
try {
await sync.syncPages();
// This should throw a sync conflict, so cannot be here
expect(false).toBe(true);
} catch {}
});
+99
View File
@@ -0,0 +1,99 @@
import { Space } from "./space";
export class SpaceSync {
lastSync: number;
constructor(
private primary: Space,
private secondary: Space,
lastSync: number
) {
this.lastSync = lastSync;
}
async syncPages() {
let allPagesPrimary = new Map(
[...(await this.primary.listPages())].map((p) => [p.name, p])
);
let allPagesSecondary = new Map(
[...(await this.secondary.listPages())].map((p) => [p.name, p])
);
let createdPagesOnSecondary = new Set<string>();
// Iterate over all pages on the primary first
for (let [name, pageMetaPrimary] of allPagesPrimary.entries()) {
let pageMetaSecondary = allPagesSecondary.get(pageMetaPrimary.name);
if (!pageMetaSecondary) {
// New page on primary
// Push from primary to secondary
console.log("New page on primary", name, "syncing to secondary");
let pageData = await this.primary.readPage(name);
await this.secondary.writePage(
name,
pageData.text,
true,
pageData.meta
);
createdPagesOnSecondary.add(name);
} else {
// Existing page
if (pageMetaPrimary.lastModified > this.lastSync) {
// Primary updated since last sync
if (pageMetaSecondary.lastModified > this.lastSync) {
// Secondary also updated! CONFLICT
throw Error(`Sync conflict for ${name}`);
} else {
// Ok, not changed on secondary, push it secondary
console.log(
"Changed page on primary",
name,
"syncing to secondary"
);
let pageData = await this.primary.readPage(name);
await this.secondary.writePage(
name,
pageData.text,
true,
pageData.meta
);
}
} else if (pageMetaSecondary.lastModified > this.lastSync) {
// Secondary updated, but not primary (checked above)
// Push from secondary to primary
console.log("Changed page on secondary", name, "syncing to primary");
let pageData = await this.secondary.readPage(name);
await this.primary.writePage(
name,
pageData.text,
true,
pageData.meta
);
} else {
// Neither updated, no-op
}
}
}
// Now do a simplified version in reverse, only detecting new pages
// Finally, let's go over all pages on the secondary and see if the primary has them
for (let [name, pageMetaSecondary] of allPagesSecondary.entries()) {
if (!allPagesPrimary.has(pageMetaSecondary.name)) {
// New page on secondary
// Push from secondary to primary
console.log("New page on secondary", name, "pushing to primary");
let pageData = await this.secondary.readPage(name);
await this.primary.writePage(name, pageData.text, true, pageData.meta);
}
}
// Find the latest timestamp on the primary and set it as lastSync
allPagesPrimary.forEach((pageMeta) => {
this.lastSync = Math.max(this.lastSync, pageMeta.lastModified);
});
allPagesSecondary.forEach((pageMeta) => {
this.lastSync = Math.max(this.lastSync, pageMeta.lastModified);
});
}
}
+4 -4
View File
@@ -1,10 +1,10 @@
import {transportSyscalls} from "../../plugos/syscalls/transport";
import {SysCallMapping} from "../../plugos/system";
import {storeSyscalls} from "../../plugos/syscalls/store.dexie_browser";
import { proxySyscalls } from "../../plugos/syscalls/transport";
import { SysCallMapping } from "../../plugos/system";
import { storeSyscalls } from "../../plugos/syscalls/store.dexie_browser";
export function clientStoreSyscalls(): SysCallMapping {
const storeCalls = storeSyscalls("local", "localData");
return transportSyscalls(
return proxySyscalls(
["clientStore.get", "clientStore.set", "clientStore.delete"],
(ctx, name, ...args) => {
return storeCalls[name.replace("clientStore.", "store.")](ctx, ...args);
+3 -3
View File
@@ -1,6 +1,6 @@
import {Editor} from "../editor";
import {Transaction} from "@codemirror/state";
import {SysCallMapping} from "../../plugos/system";
import { Editor } from "../editor";
import { Transaction } from "@codemirror/state";
import { SysCallMapping } from "../../plugos/system";
type SyntaxNode = {
name: string;
+5 -5
View File
@@ -1,9 +1,9 @@
import {Space} from "../space";
import {SysCallMapping} from "../../plugos/system";
import {transportSyscalls} from "../../plugos/syscalls/transport";
import { Space } from "../spaces/space";
import { SysCallMapping } from "../../plugos/system";
import { proxySyscalls } from "../../plugos/syscalls/transport";
export function indexerSyscalls(space: Space): SysCallMapping {
return transportSyscalls(
return proxySyscalls(
[
"index.scanPrefixForPage",
"index.scanPrefixGlobal",
@@ -12,6 +12,6 @@ export function indexerSyscalls(space: Space): SysCallMapping {
"index.batchSet",
"index.delete",
],
(ctx, name, ...args) => space.remoteSyscall(ctx.plug, name, args)
(ctx, name, ...args) => space.proxySyscall(ctx.plug, name, args)
);
}
+3 -3
View File
@@ -1,6 +1,6 @@
import {Editor} from "../editor";
import {SysCallMapping} from "../../plugos/system";
import {PageMeta} from "../../common/types";
import { Editor } from "../editor";
import { SysCallMapping } from "../../plugos/system";
import { PageMeta } from "../../common/types";
export function spaceSyscalls(editor: Editor): SysCallMapping {
return {
+6 -4
View File
@@ -1,17 +1,19 @@
import {SysCallMapping} from "../../plugos/system";
import {Space} from "../space";
import { SysCallMapping } from "../../plugos/system";
import { Space } from "../spaces/space";
export function systemSyscalls(space: Space): SysCallMapping {
return {
"system.invokeFunctionOnServer": async (
"system.invokeFunction": async (
ctx,
env: string,
name: string,
...args: any[]
) => {
if (!ctx.plug) {
throw Error("No plug associated with context");
}
return space.remoteInvoke(ctx.plug, name, args);
return space.invokeFunction(ctx.plug, env, name, args);
},
};
}
+2 -2
View File
@@ -1,5 +1,5 @@
import {AppCommand} from "./hooks/command";
import {PageMeta} from "../common/types";
import { AppCommand } from "./hooks/command";
import { PageMeta } from "../common/types";
export const slashCommandRegexp = /\/[\w\-]*/;