Work on sync stuff
This commit is contained in:
parent
1b0048cdcf
commit
38faf50ab8
4
.idea/silverbullet.iml
generated
4
.idea/silverbullet.iml
generated
@ -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>
|
||||||
|
@ -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": {
|
||||||
|
@ -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");
|
||||||
|
@ -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);
|
||||||
|
@ -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> {
|
||||||
|
@ -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", {
|
||||||
|
@ -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<
|
||||||
|
@ -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)
|
||||||
|
@ -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");
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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");
|
||||||
|
@ -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;
|
||||||
|
@ -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";
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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+/;
|
||||||
|
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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+(.+)$/;
|
||||||
|
|
||||||
|
@ -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!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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");
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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} />
|
||||||
|
22
webapp/components/panel_page.ts
Normal file
22
webapp/components/panel_page.ts
Normal 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,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 }) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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(" ", "_");
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
130
webapp/spaces/indexeddb_space.ts
Normal file
130
webapp/spaces/indexeddb_space.ts
Normal 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
52
webapp/spaces/space.ts
Normal 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
webapp/spaces/sync.test.ts
Normal file
62
webapp/spaces/sync.test.ts
Normal 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
99
webapp/spaces/sync.ts
Normal 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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\-]*/;
|
||||||
|
|
||||||
|
60
yarn.lock
60
yarn.lock
@ -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==
|
||||||
|
Loading…
Reference in New Issue
Block a user