1
0

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

View File

@ -2,7 +2,9 @@
<module type="WEB_MODULE" version="4"> <module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" /> <component name="Go" enabled="true" />
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/dist" />
</content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>

View File

@ -35,7 +35,7 @@
"context": "node" "context": "node"
}, },
"test": { "test": {
"source": ["plugs/lib/tree.test.ts"], "source": ["plugs/lib/tree.test.ts", "webapp/spaces/sync.test.ts"],
"outputFormat": "commonjs", "outputFormat": "commonjs",
"isLibrary": true, "isLibrary": true,
"context": "node" "context": "node"
@ -64,6 +64,7 @@
"cors": "^2.8.5", "cors": "^2.8.5",
"events": "^3.3.0", "events": "^3.3.0",
"express": "^4.17.3", "express": "^4.17.3",
"dexie": "^3.2.1",
"jest": "^27.5.1", "jest": "^27.5.1",
"knex": "^1.0.4", "knex": "^1.0.4",
"node-cron": "^3.0.0", "node-cron": "^3.0.0",
@ -75,6 +76,8 @@
"supertest": "^6.2.2", "supertest": "^6.2.2",
"vm2": "^3.9.9", "vm2": "^3.9.9",
"yaml": "^1.10.2", "yaml": "^1.10.2",
"fake-indexeddb": "^3.1.7",
"yargs": "^17.3.1" "yargs": "^17.3.1"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,4 +1,4 @@
import {syscall} from "./syscall"; import { syscall } from "./syscall";
export function getCurrentPage(): Promise<string> { export function getCurrentPage(): Promise<string> {
return syscall("editor.getCurrentPage"); return syscall("editor.getCurrentPage");

View File

@ -1,5 +1,5 @@
import {syscall} from "./syscall"; import { syscall } from "./syscall";
import type {MarkdownTree} from "../common/tree"; import type { MarkdownTree } from "../common/tree";
export async function parseMarkdown(text: string): Promise<MarkdownTree> { export async function parseMarkdown(text: string): Promise<MarkdownTree> {
return syscall("markdown.parseMarkdown", text); return syscall("markdown.parseMarkdown", text);

View File

@ -1,6 +1,7 @@
import { syscall } from "./syscall"; import { syscall } from "./syscall";
export async function invokeFunctionOnServer( export async function invokeFunction(
env: string,
name: string, name: string,
...args: any[] ...args: any[]
): Promise<any> { ): Promise<any> {

View File

@ -2,18 +2,18 @@
import express from "express"; import express from "express";
import yargs from "yargs"; import yargs from "yargs";
import {hideBin} from "yargs/helpers"; import { hideBin } from "yargs/helpers";
import {DiskPlugLoader} from "../plug_loader"; import { DiskPlugLoader } from "../plug_loader";
import {CronHookT, NodeCronHook} from "../hooks/node_cron"; import { CronHookT, NodeCronHook } from "../hooks/node_cron";
import shellSyscalls from "../syscalls/shell.node"; import shellSyscalls from "../syscalls/shell.node";
import {System} from "../system"; import { System } from "../system";
import {EndpointHook, EndpointHookT} from "../hooks/endpoint"; import { EndpointHook, EndpointHookT } from "../hooks/endpoint";
import {safeRun} from "../util"; import { safeRun } from "../util";
import knex from "knex"; import knex from "knex";
import {ensureTable, storeSyscalls} from "../syscalls/store.knex_node"; import { ensureTable, storeSyscalls } from "../syscalls/store.knex_node";
import {fetchSyscalls} from "../syscalls/fetch.node"; import { fetchSyscalls } from "../syscalls/fetch.node";
import {EventHook, EventHookT} from "../hooks/event"; import { EventHook, EventHookT } from "../hooks/event";
import {eventSyscalls} from "../syscalls/event"; import { eventSyscalls } from "../syscalls/event";
let args = yargs(hideBin(process.argv)) let args = yargs(hideBin(process.argv))
.option("port", { .option("port", {

View File

@ -1,5 +1,5 @@
import {safeRun} from "../util"; import { safeRun } from "../util";
import {ControllerMessage, WorkerMessage} from "./worker"; import { ControllerMessage, WorkerMessage } from "./worker";
let loadedFunctions = new Map<string, Function>(); let loadedFunctions = new Map<string, Function>();
let pendingRequests = new Map< let pendingRequests = new Map<

View File

@ -1,6 +1,6 @@
import {Hook, Manifest} from "../types"; import { Hook, Manifest } from "../types";
import {System} from "../system"; import { System } from "../system";
import {safeRun} from "../util"; import { safeRun } from "../util";
// System events: // System events:
// - plug:load (plugName: string) // - plug:load (plugName: string)

View File

@ -1,6 +1,6 @@
import {createSandbox} from "./environments/node_sandbox"; import { createSandbox } from "./environments/node_sandbox";
import {expect, test} from "@jest/globals"; import { expect, test } from "@jest/globals";
import {System} from "./system"; import { System } from "./system";
test("Run a Node sandbox", async () => { test("Run a Node sandbox", async () => {
let system = new System("server"); let system = new System("server");

View File

@ -1,5 +1,5 @@
import {SysCallMapping} from "../system"; import { SysCallMapping } from "../system";
import {EventHook} from "../hooks/event"; import { EventHook } from "../hooks/event";
export function eventSyscalls(eventHook: EventHook): SysCallMapping { export function eventSyscalls(eventHook: EventHook): SysCallMapping {
return { return {

View File

@ -1,5 +1,5 @@
import fetch, {RequestInfo, RequestInit} from "node-fetch"; import fetch, { RequestInfo, RequestInit } from "node-fetch";
import {SysCallMapping} from "../system"; import { SysCallMapping } from "../system";
export function fetchSyscalls(): SysCallMapping { export function fetchSyscalls(): SysCallMapping {
return { return {

View File

@ -1,6 +1,6 @@
import {promisify} from "util"; import { promisify } from "util";
import {execFile} from "child_process"; import { execFile } from "child_process";
import type {SysCallMapping} from "../system"; import type { SysCallMapping } from "../system";
const execFilePromise = promisify(execFile); const execFilePromise = promisify(execFile);

View File

@ -1,7 +1,7 @@
import {createSandbox} from "../environments/node_sandbox"; import { createSandbox } from "../environments/node_sandbox";
import {expect, test} from "@jest/globals"; import { expect, test } from "@jest/globals";
import {System} from "../system"; import { System } from "../system";
import {storeSyscalls} from "./store.dexie_browser"; import { storeSyscalls } from "./store.dexie_browser";
// For testing in node.js // For testing in node.js
require("fake-indexeddb/auto"); require("fake-indexeddb/auto");

View File

@ -1,5 +1,5 @@
import Dexie from "dexie"; import Dexie from "dexie";
import {SysCallMapping} from "../system"; import { SysCallMapping } from "../system";
export type KV = { export type KV = {
key: string; key: string;

View File

@ -1,7 +1,7 @@
import {createSandbox} from "../environments/node_sandbox"; import { createSandbox } from "../environments/node_sandbox";
import {expect, test} from "@jest/globals"; import { expect, test } from "@jest/globals";
import {System} from "../system"; import { System } from "../system";
import {ensureTable, storeSyscalls} from "./store.knex_node"; import { ensureTable, storeSyscalls } from "./store.knex_node";
import knex from "knex"; import knex from "knex";
import fs from "fs/promises"; import fs from "fs/promises";

View File

@ -1,5 +1,5 @@
import {Knex} from "knex"; import { Knex } from "knex";
import {SysCallMapping} from "../system"; import { SysCallMapping } from "../system";
type Item = { type Item = {
page: string; page: string;

View File

@ -1,6 +1,6 @@
import { SyscallContext, SysCallMapping } from "../system"; import { SyscallContext, SysCallMapping } from "../system";
export function transportSyscalls( export function proxySyscalls(
names: string[], names: string[],
transportCall: ( transportCall: (
ctx: SyscallContext, ctx: SyscallContext,

View File

@ -1,7 +1,7 @@
import {Hook, Manifest, RuntimeEnvironment} from "./types"; import { Hook, Manifest, RuntimeEnvironment } from "./types";
import {EventEmitter} from "../common/event"; import { EventEmitter } from "../common/event";
import {SandboxFactory} from "./sandbox"; import { SandboxFactory } from "./sandbox";
import {Plug} from "./plug"; import { Plug } from "./plug";
export interface SysCallMapping { export interface SysCallMapping {
[key: string]: (ctx: SyscallContext, ...args: any) => Promise<any> | any; [key: string]: (ctx: SyscallContext, ...args: any) => Promise<any> | any;

View File

@ -1,9 +1,9 @@
import {IndexEvent} from "../../webapp/app_event"; import { IndexEvent } from "../../webapp/app_event";
import {whiteOutQueries} from "./materialized_queries"; import { whiteOutQueries } from "./materialized_queries";
import {batchSet} from "plugos-silverbullet-syscall/index"; import { batchSet } from "plugos-silverbullet-syscall/index";
import {parseMarkdown} from "plugos-silverbullet-syscall/markdown"; import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import {collectNodesMatching, MarkdownTree, renderMarkdown,} from "../lib/tree"; import { collectNodesMatching, MarkdownTree, renderMarkdown } from "../lib/tree";
type Item = { type Item = {
item: string; item: string;

View File

@ -1,8 +1,8 @@
import {flashNotification, getCurrentPage, reloadPage, save,} from "plugos-silverbullet-syscall/editor"; import { flashNotification, getCurrentPage, reloadPage, save } from "plugos-silverbullet-syscall/editor";
import {listPages, readPage, writePage,} from "plugos-silverbullet-syscall/space"; import { listPages, readPage, writePage } from "plugos-silverbullet-syscall/space";
import {invokeFunctionOnServer} from "plugos-silverbullet-syscall/system"; import { invokeFunction } from "plugos-silverbullet-syscall/system";
import {scanPrefixGlobal} from "plugos-silverbullet-syscall"; import { scanPrefixGlobal } from "plugos-silverbullet-syscall";
export const queryRegex = export const queryRegex =
/(<!--\s*#query\s+(?<table>\w+)\s*(filter\s+["'](?<filter>[^"']+)["'])?\s*\s*(order by\s+(?<orderBy>\w+)(?<orderDesc>\s+desc)?)?(group by\s+(?<groupBy>\w+))?\s*(limit\s+(?<limit>\d+))?\s*-->)(.+?)(<!--\s*#end\s*-->)/gs; /(<!--\s*#query\s+(?<table>\w+)\s*(filter\s+["'](?<filter>[^"']+)["'])?\s*\s*(order by\s+(?<orderBy>\w+)(?<orderDesc>\s+desc)?)?(group by\s+(?<groupBy>\w+))?\s*(limit\s+(?<limit>\d+))?\s*-->)(.+?)(<!--\s*#end\s*-->)/gs;
@ -31,7 +31,11 @@ async function replaceAsync(
export async function updateMaterializedQueriesCommand() { export async function updateMaterializedQueriesCommand() {
const currentPage = await getCurrentPage(); const currentPage = await getCurrentPage();
await save(); await save();
await invokeFunctionOnServer("updateMaterializedQueriesOnPage", currentPage); await invokeFunction(
"server",
"updateMaterializedQueriesOnPage",
currentPage
);
await reloadPage(); await reloadPage();
await flashNotification("Updated materialized queries"); await flashNotification("Updated materialized queries");
} }

View File

@ -1,9 +1,9 @@
import {ClickEvent} from "../../webapp/app_event"; import { ClickEvent } from "../../webapp/app_event";
import {updateMaterializedQueriesCommand} from "./materialized_queries"; import { updateMaterializedQueriesCommand } from "./materialized_queries";
import {getCursor, getText, navigate as navigateTo, openUrl,} from "plugos-silverbullet-syscall/editor"; import { getCursor, getText, navigate as navigateTo, openUrl } from "plugos-silverbullet-syscall/editor";
import {taskToggleAtPos} from "../tasks/task"; import { taskToggleAtPos } from "../tasks/task";
import {parseMarkdown} from "plugos-silverbullet-syscall/markdown"; import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import {MarkdownTree, nodeAtPos} from "../lib/tree"; import { MarkdownTree, nodeAtPos } from "../lib/tree";
const materializedQueryPrefix = /<!--\s*#query\s+/; const materializedQueryPrefix = /<!--\s*#query\s+/;

View File

@ -4,24 +4,13 @@ import {
batchSet, batchSet,
clearPageIndex as clearPageIndexSyscall, clearPageIndex as clearPageIndexSyscall,
clearPageIndexForPage, clearPageIndexForPage,
scanPrefixGlobal, scanPrefixGlobal
} from "plugos-silverbullet-syscall/index"; } from "plugos-silverbullet-syscall/index";
import { import { flashNotification, getCurrentPage, getText, matchBefore, navigate } from "plugos-silverbullet-syscall/editor";
flashNotification,
getCurrentPage,
getText,
matchBefore,
navigate,
} from "plugos-silverbullet-syscall/editor";
import { dispatch } from "plugos-syscall/event"; import { dispatch } from "plugos-syscall/event";
import { import { deletePage as deletePageSyscall, listPages, readPage, writePage } from "plugos-silverbullet-syscall/space";
deletePage as deletePageSyscall, import { invokeFunction } from "plugos-silverbullet-syscall/system";
listPages,
readPage,
writePage,
} from "plugos-silverbullet-syscall/space";
import { invokeFunctionOnServer } from "plugos-silverbullet-syscall/system";
const wikilinkRegex = new RegExp(pageLinkRegex, "g"); const wikilinkRegex = new RegExp(pageLinkRegex, "g");
@ -120,7 +109,7 @@ export async function showBackLinks() {
export async function reindexCommand() { export async function reindexCommand() {
await flashNotification("Reindexing..."); await flashNotification("Reindexing...");
await invokeFunctionOnServer("reindexSpace"); await invokeFunction("server", "reindexSpace");
await flashNotification("Reindexing done"); await flashNotification("Reindexing done");
} }

View File

@ -1,6 +1,6 @@
// @ts-ignore // @ts-ignore
import emojis from "./emoji.json"; import emojis from "./emoji.json";
import {matchBefore} from "plugos-silverbullet-syscall/editor"; import { matchBefore } from "plugos-silverbullet-syscall/editor";
const emojiMatcher = /\(([^\)]+)\)\s+(.+)$/; const emojiMatcher = /\(([^\)]+)\)\s+(.+)$/;

View File

@ -1,6 +1,6 @@
import { run } from "plugos-syscall/shell"; import { run } from "plugos-syscall/shell";
import { flashNotification, prompt } from "plugos-silverbullet-syscall/editor"; import { flashNotification, prompt } from "plugos-silverbullet-syscall/editor";
import { invokeFunctionOnServer } from "plugos-silverbullet-syscall/system"; import { invokeFunction } from "plugos-silverbullet-syscall/system";
export async function commit(message?: string) { export async function commit(message?: string) {
if (!message) { if (!message) {
@ -25,12 +25,12 @@ export async function snapshotCommand() {
revName = "Snapshot"; revName = "Snapshot";
} }
console.log("Revision name", revName); console.log("Revision name", revName);
await invokeFunctionOnServer("commit", revName); await invokeFunction("server", "commit", revName);
} }
export async function syncCommand() { export async function syncCommand() {
await flashNotification("Syncing with git"); await flashNotification("Syncing with git");
await invokeFunctionOnServer("sync"); await invokeFunction("server", "sync");
await flashNotification("Git sync complete!"); await flashNotification("Git sync complete!");
} }

View File

@ -1,6 +1,6 @@
import {expect, test} from "@jest/globals"; import { expect, test } from "@jest/globals";
import {parse} from "../../common/tree"; import { parse } from "../../common/tree";
import {addParentPointers, collectNodesMatching, findParentMatching, nodeAtPos, renderMarkdown,} from "./tree"; import { addParentPointers, collectNodesMatching, findParentMatching, nodeAtPos, renderMarkdown } from "./tree";
const mdTest1 = ` const mdTest1 = `
# Heading # Heading

View File

@ -1,8 +1,8 @@
import MarkdownIt from "markdown-it"; import MarkdownIt from "markdown-it";
import {getText, hideRhs, showRhs} from "plugos-silverbullet-syscall/editor"; import { getText, hideRhs, showRhs } from "plugos-silverbullet-syscall/editor";
import * as clientStore from "plugos-silverbullet-syscall/clientStore"; import * as clientStore from "plugos-silverbullet-syscall/clientStore";
import {parseMarkdown} from "plugos-silverbullet-syscall/markdown"; import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import {addParentPointers, renderMarkdown, replaceNodesMatching,} from "../lib/tree"; import { addParentPointers, renderMarkdown, replaceNodesMatching } from "../lib/tree";
var taskLists = require("markdown-it-task-lists"); var taskLists = require("markdown-it-task-lists");

View File

@ -1,12 +1,12 @@
import type {ClickEvent} from "../../webapp/app_event"; import type { ClickEvent } from "../../webapp/app_event";
import {IndexEvent} from "../../webapp/app_event"; import { IndexEvent } from "../../webapp/app_event";
import {whiteOutQueries} from "../core/materialized_queries"; import { whiteOutQueries } from "../core/materialized_queries";
import {batchSet} from "plugos-silverbullet-syscall/index"; import { batchSet } from "plugos-silverbullet-syscall/index";
import {readPage, writePage} from "plugos-silverbullet-syscall/space"; import { readPage, writePage } from "plugos-silverbullet-syscall/space";
import {parseMarkdown} from "plugos-silverbullet-syscall/markdown"; import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import {dispatch, getText} from "plugos-silverbullet-syscall/editor"; import { dispatch, getText } from "plugos-silverbullet-syscall/editor";
import {addParentPointers, collectNodesMatching, nodeAtPos, renderMarkdown,} from "../lib/tree"; import { addParentPointers, collectNodesMatching, nodeAtPos, renderMarkdown } from "../lib/tree";
type Task = { type Task = {
task: string; task: string;

View File

@ -1,7 +1,7 @@
import {Knex} from "knex"; import { Knex } from "knex";
import {SysCallMapping} from "../../plugos/system"; import { SysCallMapping } from "../../plugos/system";
import {ensureTable, storeSyscalls,} from "../../plugos/syscalls/store.knex_node"; import { ensureTable, storeSyscalls } from "../../plugos/syscalls/store.knex_node";
type IndexItem = { type IndexItem = {
page: string; page: string;

View File

@ -1,6 +1,6 @@
import {PageMeta} from "../../common/types"; import { PageMeta } from "../../common/types";
import {SysCallMapping} from "../../plugos/system"; import { SysCallMapping } from "../../plugos/system";
import {Storage} from "../disk_storage"; import { Storage } from "../disk_storage";
export default (storage: Storage): SysCallMapping => { export default (storage: Storage): SysCallMapping => {
return { return {

View File

@ -1,8 +1,12 @@
import { Editor } from "./editor"; import { Editor } from "./editor";
import { Space } from "./space";
import { safeRun } from "./util"; 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 () => { safeRun(async () => {
await editor.init(); await editor.init();

View File

@ -2,18 +2,9 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link href="panel.scss" rel="stylesheet" /> <link href="panel.scss" rel="stylesheet"/>
<base target="_top"> <base target="_top">
<script type="module"> <script src="panel_page.ts"/>
window.addEventListener("message", (message) => {
const data = message.data;
switch(data.type) {
case "html":
document.body.innerHTML = data.html;
break;
}
})
</script>
</head> </head>
<body> <body>
Send me HTML Send me HTML

View File

@ -1,4 +1,4 @@
import {useEffect, useRef} from "react"; import { useEffect, useRef } from "react";
// @ts-ignore // @ts-ignore
import iframeHtml from "bundle-text:./panel.html"; import iframeHtml from "bundle-text:./panel.html";
@ -23,6 +23,22 @@ export function Panel({ html, flex }: { html: string; flex: number }) {
iframe.onload = null; iframe.onload = null;
}; };
}, [html]); }, [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 ( return (
<div className="panel" style={{ flex }}> <div className="panel" style={{ flex }}>
<iframe srcDoc={iframeHtml} ref={iFrameRef} /> <iframe srcDoc={iframeHtml} ref={iFrameRef} />

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);

View File

@ -1,4 +1,4 @@
import {EditorView} from "@codemirror/view"; import { EditorView } from "@codemirror/view";
import * as util from "../util"; import * as util from "../util";
export function StatusBar({ editorView }: { editorView?: EditorView }) { export function StatusBar({ editorView }: { editorView?: EditorView }) {

View File

@ -1,6 +1,6 @@
import {Notification} from "../types"; import { Notification } from "../types";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {faFileLines} from "@fortawesome/free-solid-svg-icons"; import { faFileLines } from "@fortawesome/free-solid-svg-icons";
function prettyName(s: string | undefined): string { function prettyName(s: string | undefined): string {
if (!s) { if (!s) {

View File

@ -1,10 +1,10 @@
import {autocompletion, completionKeymap} from "@codemirror/autocomplete"; import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
import {closeBrackets, closeBracketsKeymap} from "@codemirror/closebrackets"; import { closeBrackets, closeBracketsKeymap } from "@codemirror/closebrackets";
import {indentWithTab, standardKeymap} from "@codemirror/commands"; import { indentWithTab, standardKeymap } from "@codemirror/commands";
import {history, historyKeymap} from "@codemirror/history"; import { history, historyKeymap } from "@codemirror/history";
import {bracketMatching} from "@codemirror/matchbrackets"; import { bracketMatching } from "@codemirror/matchbrackets";
import {searchKeymap} from "@codemirror/search"; import { searchKeymap } from "@codemirror/search";
import {EditorSelection, EditorState} from "@codemirror/state"; import { EditorSelection, EditorState } from "@codemirror/state";
import { import {
drawSelection, drawSelection,
dropCursor, dropCursor,
@ -13,41 +13,41 @@ import {
KeyBinding, KeyBinding,
keymap, keymap,
ViewPlugin, ViewPlugin,
ViewUpdate, ViewUpdate
} from "@codemirror/view"; } from "@codemirror/view";
import React, {useEffect, useReducer} from "react"; import React, { useEffect, useReducer } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import {createSandbox as createIFrameSandbox} from "../plugos/environments/webworker_sandbox"; import { createSandbox as createIFrameSandbox } from "../plugos/environments/webworker_sandbox";
import {AppEvent, AppEventDispatcher, ClickEvent} from "./app_event"; import { AppEvent, AppEventDispatcher, ClickEvent } from "./app_event";
import * as commands from "./commands"; import * as commands from "./commands";
import {CommandPalette} from "./components/command_palette"; import { CommandPalette } from "./components/command_palette";
import {PageNavigator} from "./components/page_navigator"; import { PageNavigator } from "./components/page_navigator";
import {TopBar} from "./components/top_bar"; import { TopBar } from "./components/top_bar";
import {lineWrapper} from "./line_wrapper"; import { lineWrapper } from "./line_wrapper";
import {markdown} from "./markdown"; import { markdown } from "./markdown";
import {PathPageNavigator} from "./navigator"; import { PathPageNavigator } from "./navigator";
import customMarkDown from "./parser"; import customMarkDown from "./parser";
import reducer from "./reducer"; import reducer from "./reducer";
import {smartQuoteKeymap} from "./smart_quotes"; import { smartQuoteKeymap } from "./smart_quotes";
import {Space} from "./space"; import { Space } from "./spaces/space";
import customMarkdownStyle from "./style"; import customMarkdownStyle from "./style";
import {editorSyscalls} from "./syscalls/editor"; import { editorSyscalls } from "./syscalls/editor";
import {indexerSyscalls} from "./syscalls/indexer"; import { indexerSyscalls } from "./syscalls/indexer";
import {spaceSyscalls} from "./syscalls/space"; import { spaceSyscalls } from "./syscalls/space";
import {Action, AppViewState, initialViewState} from "./types"; import { Action, AppViewState, initialViewState } from "./types";
import {SilverBulletHooks} from "../common/manifest"; import { SilverBulletHooks } from "../common/manifest";
import {safeRun, throttle} from "./util"; import { safeRun, throttle } from "./util";
import {System} from "../plugos/system"; import { System } from "../plugos/system";
import {EventHook} from "../plugos/hooks/event"; import { EventHook } from "../plugos/hooks/event";
import {systemSyscalls} from "./syscalls/system"; import { systemSyscalls } from "./syscalls/system";
import {Panel} from "./components/panel"; import { Panel } from "./components/panel";
import {CommandHook} from "./hooks/command"; import { CommandHook } from "./hooks/command";
import {SlashCommandHook} from "./hooks/slash_command"; import { SlashCommandHook } from "./hooks/slash_command";
import {CompleterHook} from "./hooks/completer"; import { CompleterHook } from "./hooks/completer";
import {pasteLinkExtension} from "./editor_paste"; import { pasteLinkExtension } from "./editor_paste";
import {markdownSyscalls} from "../common/syscalls/markdown"; import { markdownSyscalls } from "../common/syscalls/markdown";
import {clientStoreSyscalls} from "./syscalls/clientStore"; import { clientStoreSyscalls } from "./syscalls/clientStore";
import {StatusBar} from "./components/status_bar"; import { StatusBar } from "./components/status_bar";
class PageState { class PageState {
scrollTop: number; scrollTop: number;
@ -341,6 +341,23 @@ export class Editor implements AppEventDispatcher {
return true; 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({ EditorView.domEventHandlers({
@ -409,10 +426,13 @@ export class Editor implements AppEventDispatcher {
// Persist current page state and nicely close page // Persist current page state and nicely close page
if (this.currentPage) { if (this.currentPage) {
let pageState = this.openPages.get(this.currentPage)!; let pageState = this.openPages.get(this.currentPage);
if (pageState) { if (pageState) {
pageState.selection = this.editorView!.state.selection; 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); this.space.unwatchPage(this.currentPage);
await this.save(true); await this.save(true);
@ -431,11 +451,12 @@ export class Editor implements AppEventDispatcher {
}); });
} else { } else {
// Restore state // Restore state
console.log("Restoring selection state", pageState.selection); // console.log("Restoring selection state", pageState);
editorView.dispatch({ editorView.dispatch({
selection: pageState.selection, selection: pageState.selection,
}); });
editorView.scrollDOM.scrollTop = pageState!.scrollTop; editorView.scrollDOM.parentElement!.parentElement!.scrollTop =
pageState!.scrollTop;
} }
this.space.watchPage(pageName); this.space.watchPage(pageName);

View File

@ -1,4 +1,4 @@
import {safeRun} from "./util"; import { safeRun } from "./util";
function encodePageUrl(name: string): string { function encodePageUrl(name: string): string {
return name.replaceAll(" ", "_"); return name.replaceAll(" ", "_");

View File

@ -1,8 +1,8 @@
import {styleTags, tags as t} from "@codemirror/highlight"; import { styleTags, tags as t } from "@codemirror/highlight";
import {BlockContext, LeafBlock, LeafBlockParser, MarkdownConfig, TaskList,} from "@lezer/markdown"; import { BlockContext, LeafBlock, LeafBlockParser, MarkdownConfig, TaskList } from "@lezer/markdown";
import {commonmark, mkLang} from "./markdown/markdown"; import { commonmark, mkLang } from "./markdown/markdown";
import * as ct from "./customtags"; import * as ct from "./customtags";
import {pageLinkRegex} from "./constant"; import { pageLinkRegex } from "./constant";
const pageLinkRegexPrefix = new RegExp( const pageLinkRegexPrefix = new RegExp(
"^" + pageLinkRegex.toString().slice(1, -1) "^" + pageLinkRegex.toString().slice(1, -1)

View File

@ -1,4 +1,4 @@
import {Action, AppViewState} from "./types"; import { Action, AppViewState } from "./types";
export default function reducer( export default function reducer(
state: AppViewState, state: AppViewState,

View File

@ -1,27 +1,14 @@
import { EventEmitter } from "../common/event"; import { EventEmitter } from "../../common/event";
import { Manifest } from "../common/manifest"; import { PageMeta } from "../../common/types";
import { safeRun } from "./util"; import { safeRun } from "../util";
import { Plug } from "../plugos/plug"; import { Plug } from "../../plugos/plug";
import { PageMeta } from "../common/types"; import { Manifest } from "../../common/manifest";
import { PlugMeta, Space, SpaceEvents } from "./space";
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;
};
const pageWatchInterval = 2000; const pageWatchInterval = 2000;
const plugWatchInterval = 5000; const plugWatchInterval = 5000;
export class Space extends EventEmitter<SpaceEvents> { export class HttpRestSpace extends EventEmitter<SpaceEvents> implements Space {
pageUrl: string; pageUrl: string;
pageMetaCache = new Map<string, PageMeta>(); pageMetaCache = new Map<string, PageMeta>();
plugMetaCache = new Map<string, PlugMeta>(); plugMetaCache = new Map<string, PlugMeta>();
@ -29,7 +16,6 @@ export class Space extends EventEmitter<SpaceEvents> {
saving = false; saving = false;
private plugUrl: string; private plugUrl: string;
private initialPageListLoad = true; private initialPageListLoad = true;
private initialPlugListLoad = true;
constructor(url: string) { constructor(url: string) {
super(); super();
@ -40,11 +26,11 @@ export class Space extends EventEmitter<SpaceEvents> {
this.updatePageListAsync(); this.updatePageListAsync();
} }
public watchPage(pageName: string) { watchPage(pageName: string) {
this.watchedPages.add(pageName); this.watchedPages.add(pageName);
} }
public unwatchPage(pageName: string) { unwatchPage(pageName: string) {
this.watchedPages.delete(pageName); this.watchedPages.delete(pageName);
} }
@ -114,23 +100,11 @@ export class Space extends EventEmitter<SpaceEvents> {
}); });
} }
public async listPages(): Promise<Set<PageMeta>> { async listPages(): Promise<Set<PageMeta>> {
// this.updatePageListAsync();
return new Set([...this.pageMetaCache.values()]); return new Set([...this.pageMetaCache.values()]);
} }
private responseToMetaCacher(name: string, res: Response): PageMeta { async readPage(name: string): Promise<{ text: string; meta: 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 }> {
let res = await fetch(`${this.pageUrl}/${name}`, { let res = await fetch(`${this.pageUrl}/${name}`, {
method: "GET", method: "GET",
}); });
@ -140,11 +114,13 @@ export class Space extends EventEmitter<SpaceEvents> {
}; };
} }
public async writePage( async writePage(
name: string, name: string,
text: string, text: string,
selfUpdate?: boolean selfUpdate?: boolean,
withMeta?: PageMeta
): Promise<PageMeta> { ): Promise<PageMeta> {
// TODO: withMeta ignored for now
try { try {
this.saving = true; this.saving = true;
let res = await fetch(`${this.pageUrl}/${name}`, { 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}`, { let req = await fetch(`${this.pageUrl}/${name}`, {
method: "DELETE", method: "DELETE",
}); });
@ -173,18 +149,7 @@ export class Space extends EventEmitter<SpaceEvents> {
this.emit("pageListUpdated", new Set([...this.pageMetaCache.values()])); this.emit("pageListUpdated", new Set([...this.pageMetaCache.values()]));
} }
private async getPageMeta(name: string): Promise<PageMeta> { async proxySyscall(plug: Plug<any>, name: string, args: any[]): Promise<any> {
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> {
let req = await fetch(`${this.plugUrl}/${plug.name}/syscall/${name}`, { let req = await fetch(`${this.plugUrl}/${plug.name}/syscall/${name}`, {
method: "POST", method: "POST",
headers: { headers: {
@ -202,7 +167,17 @@ export class Space extends EventEmitter<SpaceEvents> {
return await req.json(); 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}`, { let req = await fetch(`${this.plugUrl}/${plug.name}/function/${name}`, {
method: "POST", method: "POST",
headers: { headers: {
@ -220,8 +195,38 @@ export class Space extends EventEmitter<SpaceEvents> {
return await req.json(); 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> { private async pollPlugs(): Promise<void> {
const newPlugs = await this.loadPlugs(); const newPlugs = await this.listPlugs();
let deletedPlugs = new Set<string>(this.plugMetaCache.keys()); let deletedPlugs = new Set<string>(this.plugMetaCache.keys());
for (const newPlugMeta of newPlugs) { for (const newPlugMeta of newPlugs) {
const oldPlugMeta = this.plugMetaCache.get(newPlugMeta.name); const oldPlugMeta = this.plugMetaCache.get(newPlugMeta.name);
@ -247,18 +252,4 @@ export class Space extends EventEmitter<SpaceEvents> {
this.emit("plugUnloaded", deletedPlug); 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;
}
} }

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
webapp/spaces/space.ts Normal file
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;
}

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
webapp/spaces/sync.ts Normal file
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);
});
}
}

View File

@ -1,10 +1,10 @@
import {transportSyscalls} from "../../plugos/syscalls/transport"; import { proxySyscalls } from "../../plugos/syscalls/transport";
import {SysCallMapping} from "../../plugos/system"; import { SysCallMapping } from "../../plugos/system";
import {storeSyscalls} from "../../plugos/syscalls/store.dexie_browser"; import { storeSyscalls } from "../../plugos/syscalls/store.dexie_browser";
export function clientStoreSyscalls(): SysCallMapping { export function clientStoreSyscalls(): SysCallMapping {
const storeCalls = storeSyscalls("local", "localData"); const storeCalls = storeSyscalls("local", "localData");
return transportSyscalls( return proxySyscalls(
["clientStore.get", "clientStore.set", "clientStore.delete"], ["clientStore.get", "clientStore.set", "clientStore.delete"],
(ctx, name, ...args) => { (ctx, name, ...args) => {
return storeCalls[name.replace("clientStore.", "store.")](ctx, ...args); return storeCalls[name.replace("clientStore.", "store.")](ctx, ...args);

View File

@ -1,6 +1,6 @@
import {Editor} from "../editor"; import { Editor } from "../editor";
import {Transaction} from "@codemirror/state"; import { Transaction } from "@codemirror/state";
import {SysCallMapping} from "../../plugos/system"; import { SysCallMapping } from "../../plugos/system";
type SyntaxNode = { type SyntaxNode = {
name: string; name: string;

View File

@ -1,9 +1,9 @@
import {Space} from "../space"; import { Space } from "../spaces/space";
import {SysCallMapping} from "../../plugos/system"; import { SysCallMapping } from "../../plugos/system";
import {transportSyscalls} from "../../plugos/syscalls/transport"; import { proxySyscalls } from "../../plugos/syscalls/transport";
export function indexerSyscalls(space: Space): SysCallMapping { export function indexerSyscalls(space: Space): SysCallMapping {
return transportSyscalls( return proxySyscalls(
[ [
"index.scanPrefixForPage", "index.scanPrefixForPage",
"index.scanPrefixGlobal", "index.scanPrefixGlobal",
@ -12,6 +12,6 @@ export function indexerSyscalls(space: Space): SysCallMapping {
"index.batchSet", "index.batchSet",
"index.delete", "index.delete",
], ],
(ctx, name, ...args) => space.remoteSyscall(ctx.plug, name, args) (ctx, name, ...args) => space.proxySyscall(ctx.plug, name, args)
); );
} }

View File

@ -1,6 +1,6 @@
import {Editor} from "../editor"; import { Editor } from "../editor";
import {SysCallMapping} from "../../plugos/system"; import { SysCallMapping } from "../../plugos/system";
import {PageMeta} from "../../common/types"; import { PageMeta } from "../../common/types";
export function spaceSyscalls(editor: Editor): SysCallMapping { export function spaceSyscalls(editor: Editor): SysCallMapping {
return { return {

View File

@ -1,17 +1,19 @@
import {SysCallMapping} from "../../plugos/system"; import { SysCallMapping } from "../../plugos/system";
import {Space} from "../space"; import { Space } from "../spaces/space";
export function systemSyscalls(space: Space): SysCallMapping { export function systemSyscalls(space: Space): SysCallMapping {
return { return {
"system.invokeFunctionOnServer": async ( "system.invokeFunction": async (
ctx, ctx,
env: string,
name: string, name: string,
...args: any[] ...args: any[]
) => { ) => {
if (!ctx.plug) { if (!ctx.plug) {
throw Error("No plug associated with context"); throw Error("No plug associated with context");
} }
return space.remoteInvoke(ctx.plug, name, args);
return space.invokeFunction(ctx.plug, env, name, args);
}, },
}; };
} }

View File

@ -1,5 +1,5 @@
import {AppCommand} from "./hooks/command"; import { AppCommand } from "./hooks/command";
import {PageMeta} from "../common/types"; import { PageMeta } from "../common/types";
export const slashCommandRegexp = /\/[\w\-]*/; export const slashCommandRegexp = /\/[\w\-]*/;

View File

@ -1883,6 +1883,11 @@ base-x@^3.0.8:
dependencies: dependencies:
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
base64-arraybuffer-es6@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.7.0.tgz#dbe1e6c87b1bf1ca2875904461a7de40f21abc86"
integrity sha512-ESyU/U1CFZDJUdr+neHRhNozeCv72Y7Vm0m1DCbjX3KBjT6eYocvAJlSk6+8+HkVwXlT1FNxhGW6q3UKAlCvvw==
base64-js@^1.3.1: base64-js@^1.3.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
@ -2287,6 +2292,11 @@ cookiejar@^2.1.3:
resolved "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz" resolved "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz"
integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ== integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==
core-js@^3.4:
version "3.21.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94"
integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==
core-util-is@~1.0.0: core-util-is@~1.0.0:
version "1.0.3" version "1.0.3"
resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
@ -2567,6 +2577,11 @@ detect-newline@^3.0.0:
resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz"
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
dexie@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/dexie/-/dexie-3.2.1.tgz#ef21456d725e700c1ab7ac4307896e4fdabaf753"
integrity sha512-Y8oz3t2XC9hvjkP35B5I8rUkKKwM36GGRjWQCMjzIYScg7W+GHKDXobSYswkisW7CxL1/tKQtggMDsiWqDUc1g==
dezalgo@1.0.3: dezalgo@1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz" resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz"
@ -2594,6 +2609,13 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz" resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz"
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
domexception@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
dependencies:
webidl-conversions "^4.0.2"
domexception@^2.0.1: domexception@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz"
@ -2866,6 +2888,13 @@ express@^4.17.3:
utils-merge "1.0.1" utils-merge "1.0.1"
vary "~1.1.2" vary "~1.1.2"
fake-indexeddb@^3.1.7:
version "3.1.7"
resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-3.1.7.tgz#d9efbeade113c15efbe862e4598a4b0a1797ed9f"
integrity sha512-CUGeCzCOVjmeKi2C0pcvSh6NDU6uQIaS+7YyR++tO/atJJujkBYVhDvfePdz/U8bD33BMVWirsr1MKczfAqbjA==
dependencies:
realistic-structured-clone "^2.0.1"
fast-json-stable-stringify@^2.0.0: fast-json-stable-stringify@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz"
@ -5158,6 +5187,16 @@ readdirp@~3.6.0:
dependencies: dependencies:
picomatch "^2.2.1" picomatch "^2.2.1"
realistic-structured-clone@^2.0.1:
version "2.0.4"
resolved "https://registry.yarnpkg.com/realistic-structured-clone/-/realistic-structured-clone-2.0.4.tgz#7eb4c2319fc3cb72f4c8d3c9e888b11647894b50"
integrity sha512-lItAdBIFHUSe6fgztHPtmmWqKUgs+qhcYLi3wTRUl4OTB3Vb8aBVSjGfQZUvkmJCKoX3K9Wf7kyLp/F/208+7A==
dependencies:
core-js "^3.4"
domexception "^1.0.1"
typeson "^6.1.0"
typeson-registry "^1.0.0-alpha.20"
rechoir@^0.8.0: rechoir@^0.8.0:
version "0.8.0" version "0.8.0"
resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz" resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz"
@ -5788,6 +5827,20 @@ typescript@^4.6.2:
resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz" resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz"
integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==
typeson-registry@^1.0.0-alpha.20:
version "1.0.0-alpha.39"
resolved "https://registry.yarnpkg.com/typeson-registry/-/typeson-registry-1.0.0-alpha.39.tgz#9e0f5aabd5eebfcffd65a796487541196f4b1211"
integrity sha512-NeGDEquhw+yfwNhguLPcZ9Oj0fzbADiX4R0WxvoY8nGhy98IbzQy1sezjoEFWOywOboj/DWehI+/aUlRVrJnnw==
dependencies:
base64-arraybuffer-es6 "^0.7.0"
typeson "^6.0.0"
whatwg-url "^8.4.0"
typeson@^6.0.0, typeson@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/typeson/-/typeson-6.1.0.tgz#5b2a53705a5f58ff4d6f82f965917cabd0d7448b"
integrity sha512-6FTtyGr8ldU0pfbvW/eOZrEtEkczHRUtduBnA90Jh9kMPCiFNnXIon3vF41N0S4tV1HHQt4Hk1j4srpESziCaA==
unbox-primitive@^1.0.1: unbox-primitive@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz" resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz"
@ -5937,6 +5990,11 @@ webidl-conversions@^3.0.0:
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
webidl-conversions@^5.0.0: webidl-conversions@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz"
@ -5967,7 +6025,7 @@ whatwg-url@^5.0.0:
tr46 "~0.0.3" tr46 "~0.0.3"
webidl-conversions "^3.0.0" webidl-conversions "^3.0.0"
whatwg-url@^8.0.0, whatwg-url@^8.5.0: whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0:
version "8.7.0" version "8.7.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==