1
0

Preparing for an initial release

This commit is contained in:
Zef Hemel
2022-06-28 14:14:15 +02:00
parent be92bf2311
commit 16fcd91d6c
73 changed files with 3205 additions and 20734 deletions
+6 -2
View File
@@ -4,7 +4,7 @@
"name": "Zef Hemel",
"email": "zef@zef.me"
},
"version": "0.0.2",
"version": "0.0.7",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "6.0.1",
@@ -16,8 +16,12 @@
"@codemirror/search": "6.0.0",
"@codemirror/state": "6.0.0",
"@codemirror/view": "6.0.0",
"@lezer/common": "1.0.0",
"@lezer/highlight": "1.0.0",
"@lezer/markdown": "1.0.0",
"@lezer/common": "1.0.0"
"@silverbulletmd/common": "^0.0.7",
"@silverbulletmd/plugs": "^0.0.7",
"@silverbulletmd/web": "^0.0.7",
"server": "^1.0.37"
}
}
@@ -107,7 +107,7 @@ export class DiskSpacePrimitives implements SpacePrimitives {
perm: "rw",
};
} catch (e) {
console.error("Error while getting page meta", pageName, e);
// console.error("Error while getting page meta", pageName, e);
throw Error(`Could not get meta for ${pageName}`);
}
}
+19 -19
View File
@@ -18,17 +18,17 @@ test("Test store", async () => {
async function conflictResolver(pageMeta1: PageMeta, pageMeta2: PageMeta) {}
// Write one page to primary
await primary.writePage("start", "Hello");
await primary.writePage("index", "Hello");
expect((await secondary.listPages()).size).toBe(0);
await syncPages(conflictResolver);
expect((await secondary.listPages()).size).toBe(1);
expect((await secondary.readPage("start")).text).toBe("Hello");
expect((await secondary.readPage("index")).text).toBe("Hello");
// Should be a no-op
expect(await syncPages()).toBe(0);
// Now let's make a change on the secondary
await secondary.writePage("start", "Hello!!");
await secondary.writePage("index", "Hello!!");
await secondary.writePage("test", "Test page");
// And sync it
@@ -37,13 +37,13 @@ test("Test store", async () => {
expect(primary.listPages().size).toBe(2);
expect(secondary.listPages().size).toBe(2);
expect((await primary.readPage("start")).text).toBe("Hello!!");
expect((await primary.readPage("index")).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 primary.writePage("index", "1");
await primary.writePage("index2", "2");
await secondary.writePage("index3", "3");
await secondary.writePage("index4", "4");
await syncPages();
expect((await primary.listPages()).size).toBe(5);
@@ -53,8 +53,8 @@ test("Test store", async () => {
console.log("Deleting pages");
// Delete some pages
await primary.deletePage("start");
await primary.deletePage("start3");
await primary.deletePage("index");
await primary.deletePage("index3");
console.log("Pages", await primary.listPages());
console.log("Trash", await primary.listTrash());
@@ -67,8 +67,8 @@ test("Test store", async () => {
// No-op
expect(await syncPages()).toBe(0);
await secondary.deletePage("start4");
await primary.deletePage("start2");
await secondary.deletePage("index4");
await primary.deletePage("index2");
await syncPages();
@@ -79,15 +79,15 @@ test("Test store", async () => {
// No-op
expect(await syncPages()).toBe(0);
await secondary.writePage("start", "I'm back");
await secondary.writePage("index", "I'm back");
await syncPages();
expect((await primary.readPage("start")).text).toBe("I'm back");
expect((await primary.readPage("index")).text).toBe("I'm back");
// Cause a conflict
await primary.writePage("start", "Hello 1");
await secondary.writePage("start", "Hello 2");
await primary.writePage("index", "Hello 1");
await secondary.writePage("index", "Hello 2");
await syncPages(SpaceSync.primaryConflictResolver(primary, secondary));
@@ -95,10 +95,10 @@ test("Test store", async () => {
await syncPages();
// Verify that primary won
expect((await primary.readPage("start")).text).toBe("Hello 1");
expect((await secondary.readPage("start")).text).toBe("Hello 1");
expect((await primary.readPage("index")).text).toBe("Hello 1");
expect((await secondary.readPage("index")).text).toBe("Hello 1");
// test + start + start.conflicting copy
// test + index + index.conflicting copy
expect((await primary.listPages()).size).toBe(3);
expect((await secondary.listPages()).size).toBe(3);
@@ -4,6 +4,12 @@
"name": "Zef Hemel",
"email": "zef@zef.me"
},
"version": "0.0.2",
"license": "MIT"
"version": "0.0.7",
"license": "MIT",
"dependencies": {
"@silverbulletmd/common": "^0.0.7",
"@silverbulletmd/plugs": "^0.0.7",
"@silverbulletmd/web": "^0.0.7",
"server": "^1.0.37"
}
}
+8 -2
View File
@@ -4,6 +4,12 @@
"name": "Zef Hemel",
"email": "zef@zef.me"
},
"version": "0.0.2",
"license": "MIT"
"version": "0.0.7",
"license": "MIT",
"dependencies": {
"@silverbulletmd/common": "^0.0.7",
"@silverbulletmd/plugs": "^0.0.7",
"@silverbulletmd/web": "^0.0.7",
"server": "^1.0.37"
}
}
+7 -3
View File
@@ -4,7 +4,7 @@
"name": "Zef Hemel",
"email": "zef@zef.me"
},
"version": "0.0.2",
"version": "0.0.7",
"license": "MIT",
"bin": {
"plugos-bundle": "./dist/plugos/plugos-bundle.js",
@@ -40,6 +40,9 @@
},
"dependencies": {
"@jest/globals": "^27.5.1",
"@silverbulletmd/common": "^0.0.7",
"@silverbulletmd/plugs": "^0.0.7",
"@silverbulletmd/web": "^0.0.7",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"@types/jsonwebtoken": "^8.5.8",
@@ -56,12 +59,13 @@
"node-cron": "^3.0.0",
"node-fetch": "2",
"node-watch": "^0.7.3",
"server": "^1.0.37",
"supertest": "^6.2.2",
"typescript": "^4.6.2",
"vm2": "^3.9.9",
"ws": "^8.5.0",
"yaml": "^1.10.2",
"yargs": "^17.3.1",
"typescript": "^4.6.2"
"yargs": "^17.3.1"
},
"devDependencies": {
"@lezer/lr": "1.0.0",
-1
View File
@@ -279,7 +279,6 @@ function $11a7e2bff790f35a$export$195ba6d62321b933(message, defaultValue = "") {
}
const $c3893eec0c49ec96$var$dateMatchRegex = /(\d{4}\-\d{2}\-\d{2})/g;
function $c3893eec0c49ec96$export$5dc1410f87262ed6(d) {
return d.toISOString().split("T")[0];
}
File diff suppressed because one or more lines are too long
+84 -45
View File
@@ -35,6 +35,13 @@ functions:
path: "./page.ts:parseIndexTextRepublish"
events:
- page:index_text
reindexSpaceCommand:
path: "./page.ts:reindexCommand"
command:
name: "Space: Reindex"
reindexSpace:
path: "./page.ts:reindexSpace"
env: server
indexLinks:
path: "./page.ts:indexLinks"
events:
@@ -43,25 +50,10 @@ functions:
path: ./page.ts:linkQueryProvider
events:
- query:link
# indexItems:
# path: "./item.ts:indexItems"
# events:
# - page:index
# itemQueryProvider:
# path: ./item.ts:queryProvider
# events:
# - query:item
deletePage:
path: "./page.ts:deletePage"
command:
name: "Page: Delete"
reindexSpaceCommand:
path: "./page.ts:reindexCommand"
command:
name: "Space: Reindex"
reindexSpace:
path: "./page.ts:reindexSpace"
env: server
renamePage:
path: "./page.ts:renamePage"
command:
@@ -82,38 +74,39 @@ functions:
path: "./navigate.ts:clickNavigate"
events:
- page:click
insertToday:
path: "./dates.ts:insertToday"
slashCommand:
name: today
insertTomorrow:
path: "./dates.ts:insertTomorrow"
slashCommand:
name: tomorrow
parseServerCommand:
path: ./debug.ts:parseServerPageCommand
command:
name: "Debug: Parse Document on Server"
parsePage:
path: ./debug.ts:parsePage
parseCommand:
path: ./debug.ts:parsePageCommand
command:
name: "Debug: Parse Document"
showLogsCommand:
path: ./debug.ts:showLogsCommand
command:
name: "Debug: Show Logs"
key: "Ctrl-Alt-l"
mac: "Cmd-Alt-l"
# Full text search
searchIndex:
path: ./search.ts:index
events:
- log:reload
hideBhsCommand:
path: ./debug.ts:hideBhsCommand
- page:index
searchUnindex:
path: "./search.ts:unindex"
env: server
events:
- page:deleted
searchQueryProvider:
path: ./search.ts:queryProvider
events:
- query:full-text
searchCommand:
path: ./search.ts:searchCommand
command:
name: "UI: Hide BHS"
key: "Ctrl-Alt-b"
mac: "Cmd-Alt-b"
name: "Search Space"
key: Ctrl-Shift-f
mac: Cmd-Shift-f
readPageSearch:
path: ./search.ts:readPageSearch
pageNamespace:
pattern: "@search/.+"
operation: readPage
getPageMetaSearch:
path: ./search.ts:getPageMetaSearch
pageNamespace:
pattern: "@search/.+"
operation: getPageMeta
# Template commands
insertPageMeta:
path: "./page.ts:insertPageMeta"
slashCommand:
@@ -132,7 +125,16 @@ functions:
path: ./template.ts:instantiateTemplateCommand
command:
name: "Template: Instantiate for Page"
insertToday:
path: "./dates.ts:insertToday"
slashCommand:
name: today
insertTomorrow:
path: "./dates.ts:insertTomorrow"
slashCommand:
name: tomorrow
# Text editing commands
quoteSelection:
path: ./text.ts:quoteSelection
command:
@@ -159,3 +161,40 @@ functions:
name: "Text: Italic"
key: "Ctrl-i"
mac: "Cmd-i"
# Plug manager
updatePlugsCommand:
path: ./plugmanager.ts:updatePlugsCommand
command:
name: "Plugs: Update"
key: "Ctrl-Shift-p"
mac: "Cmd-Shift-p"
updatePlugs:
path: ./plugmanager.ts:updatePlugs
env: server
# Debug commands
parseServerCommand:
path: ./debug.ts:parseServerPageCommand
command:
name: "Debug: Parse Document on Server"
parsePage:
path: ./debug.ts:parsePage
parseCommand:
path: ./debug.ts:parsePageCommand
command:
name: "Debug: Parse Document"
showLogsCommand:
path: ./debug.ts:showLogsCommand
command:
name: "Debug: Show Logs"
key: "Ctrl-Alt-l"
mac: "Cmd-Alt-l"
events:
- log:reload
hideBhsCommand:
path: ./debug.ts:hideBhsCommand
command:
name: "UI: Hide BHS"
key: "Ctrl-Alt-b"
mac: "Cmd-Alt-b"
-2
View File
@@ -1,7 +1,5 @@
import { insertAtCursor } from "@silverbulletmd/plugos-silverbullet-syscall/editor";
const dateMatchRegex = /(\d{4}\-\d{2}\-\d{2})/g;
export function niceDate(d: Date): string {
return d.toISOString().split("T")[0];
}
-1
View File
@@ -1,5 +1,4 @@
import { getLogs } from "@plugos/plugos-syscall/sandbox";
import { LogEntry } from "@plugos/plugos/sandbox";
import {
getText,
hideBhs,
-38
View File
@@ -1,38 +0,0 @@
import {
getCursor,
getText,
insertAtPos,
replaceRange,
} from "@silverbulletmd/plugos-silverbullet-syscall/editor";
export async function toggleH1() {
await togglePrefix("# ");
}
export async function toggleH2() {
await togglePrefix("## ");
}
function lookBack(s: string, pos: number, backString: string): boolean {
return s.substring(pos - backString.length, pos) === backString;
}
async function togglePrefix(prefix: string) {
let text = await getText();
let pos = await getCursor();
if (text[pos] === "\n") {
pos--;
}
while (pos > 0 && text[pos] !== "\n") {
if (lookBack(text, pos, prefix)) {
// Already has this prefix, let's flip it
await replaceRange(pos - prefix.length, pos, "");
return;
}
pos--;
}
if (pos) {
pos++;
}
await insertAtPos(prefix, pos);
}
-2
View File
@@ -8,8 +8,6 @@ import {
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown";
import { nodeAtPos, ParseTree } from "@silverbulletmd/common/tree";
const materializedQueryPrefix = /<!--\s*#query\s+/;
async function actionClickOrActionEnter(mdTree: ParseTree | null) {
if (!mdTree) {
return;
+2 -4
View File
@@ -35,9 +35,7 @@ import {
replaceNodesMatching,
} from "@silverbulletmd/common/tree";
import { applyQuery, QueryProviderEvent } from "../query/engine";
import { PageMeta } from "@silverbulletmd/common/types";
import { extractMeta } from "../query/data";
import { jsonToMDTable } from "../query/util";
// Key space:
// pl:toPage:pos => pageName
@@ -101,8 +99,8 @@ export async function linkQueryProvider({
export async function deletePage() {
let pageName = await getCurrentPage();
console.log("Navigating to start page");
await navigate("start");
console.log("Navigating to index page");
await navigate("index");
console.log("Deleting page from space");
await deletePageSyscall(pageName);
}
+69
View File
@@ -0,0 +1,69 @@
import { dispatch } from "@plugos/plugos-syscall/event";
import { findNodeOfType } from "@silverbulletmd/common/tree";
import { flashNotification } from "@silverbulletmd/plugos-silverbullet-syscall/editor";
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown";
import {
deletePage,
listPages,
readPage,
writePage,
} from "@silverbulletmd/plugos-silverbullet-syscall/space";
import {
invokeFunction,
reloadPlugs,
} from "@silverbulletmd/plugos-silverbullet-syscall/system";
import YAML from "yaml";
async function listPlugs(): Promise<string[]> {
let unfilteredPages = await listPages(true);
return unfilteredPages
.filter((p) => p.name.startsWith("_plug/"))
.map((p) => p.name.substring("_plug/".length));
}
export async function updatePlugsCommand() {
flashNotification("Updating plugs...");
await invokeFunction("server", "updatePlugs");
flashNotification("And... done!");
await reloadPlugs();
}
export async function updatePlugs() {
let { text: plugPageText } = await readPage("PLUGS");
let tree = await parseMarkdown(plugPageText);
let codeTextNode = findNodeOfType(tree, "CodeText");
if (!codeTextNode) {
console.error("Could not find yaml block in PLUGS");
return;
}
let plugYaml = codeTextNode.children![0].text;
let plugList = YAML.parse(plugYaml!);
console.log("Plug YAML", plugList);
let allPlugNames: string[] = [];
for (let plugUri of plugList) {
let [protocol, ...rest] = plugUri.split(":");
let manifests = await dispatch(`get-plug:${protocol}`, rest.join(":"));
if (manifests.length === 0) {
console.error("Could not resolve plug", plugUri);
}
// console.log("Got manifests", plugUri, protocol, manifests);
let manifest = manifests[0];
allPlugNames.push(manifest.name);
// console.log("Writing", `_plug/${manifest.name}`);
await writePage(
`_plug/${manifest.name}`,
JSON.stringify(manifest, null, 2)
);
}
// And delete extra ones
for (let existingPlug of await listPlugs()) {
if (!allPlugNames.includes(existingPlug)) {
console.log("Removing plug", existingPlug);
await deletePage(`_plug/${existingPlug}`);
}
}
await reloadPlugs();
}
@@ -1,4 +1,8 @@
import { fullTextIndex, fullTextSearch } from "@plugos/plugos-syscall/fulltext";
import {
fullTextDelete,
fullTextIndex,
fullTextSearch,
} from "@plugos/plugos-syscall/fulltext";
import { renderToText } from "@silverbulletmd/common/tree";
import { PageMeta } from "@silverbulletmd/common/types";
import { queryPrefix } from "@silverbulletmd/plugos-silverbullet-syscall";
@@ -16,6 +20,10 @@ export async function index(data: IndexTreeEvent) {
await fullTextIndex(data.name, cleanText);
}
export async function unindex(pageName: string) {
await fullTextDelete(pageName);
}
export async function queryProvider({
query,
}: QueryProviderEvent): Promise<any[]> {
-12
View File
@@ -1,12 +0,0 @@
import {
EndpointRequest,
EndpointResponse,
} from "@plugos/plugos/hooks/endpoint";
export function endpointTest(req: EndpointRequest): EndpointResponse {
console.log("I'm running on the server!", req);
return {
status: 200,
body: "Hello world!",
};
}
@@ -1,9 +0,0 @@
function countWords(str: string): number {
const matches = str.match(/[\w\d\'-]+/gi);
return matches ? matches.length : 0;
}
function readingTime(wordCount: number): number {
// 225 is average word reading speed for adults
return Math.ceil(wordCount / 225);
}
-2
View File
@@ -2,8 +2,6 @@
import emojis from "./emoji.json";
import { matchBefore } from "@silverbulletmd/plugos-silverbullet-syscall/editor";
const emojiMatcher = /\(([^\)]+)\)\s+(.+)$/;
export async function emojiCompleter() {
let prefix = await matchBefore(":[\\w]+");
if (!prefix) {
-7
View File
@@ -1,12 +1,5 @@
name: ghost
functions:
downloadAllPostsCommand:
path: "./ghost.ts:downloadAllPostsCommand"
command:
name: "Ghost: Download Posts"
downloadAllPosts:
path: "./ghost.ts:downloadAllPosts"
env: server
publishCommand:
path: "./ghost.ts:publishCommand"
command:
+1 -19
View File
@@ -1,7 +1,4 @@
import {
readPage,
writePage,
} from "@silverbulletmd/plugos-silverbullet-syscall/space";
import { readPage } from "@silverbulletmd/plugos-silverbullet-syscall/space";
import { invokeFunction } from "@silverbulletmd/plugos-silverbullet-syscall/system";
import {
getCurrentPage,
@@ -195,21 +192,6 @@ async function getConfig(): Promise<GhostConfig> {
return pageMeta as GhostConfig;
}
export async function downloadAllPostsCommand() {
await invokeFunction("server", "downloadAllPosts");
}
export async function downloadAllPosts() {
let config = await getConfig();
let admin = new GhostAdmin(config.url, config.adminKey);
await admin.init();
let allPosts = await admin.listMarkdownPosts();
for (let post of allPosts) {
let text = mobileDocToMarkdown(post.mobiledoc);
text = `# ${post.title}\n${text}`;
await writePage(`${config.postPrefix}/${post.slug}`, text);
}
}
export async function publishCommand() {
await invokeFunction(
"server",
-1
View File
@@ -7,7 +7,6 @@ export async function togglePreview() {
await clientStore.set("enableMarkdownPreview", !currentValue);
if (!currentValue) {
await invokeFunction("client", "preview");
// updateMarkdownPreview();
} else {
await hideMarkdownPreview();
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@silverbulletmd/plugs",
"version": "0.0.2",
"version": "0.0.7",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@silverbulletmd/plugs",
"version": "0.0.2",
"version": "0.0.7",
"license": "MIT",
"dependencies": {
"@jest/globals": "^27.5.1",
+9 -5
View File
@@ -4,7 +4,7 @@
"name": "Zef Hemel",
"email": "zef@zef.me"
},
"version": "0.0.2",
"version": "0.0.7",
"license": "MIT",
"scripts": {
"generate": "lezer-generator query/query.grammar -o query/parse-query.js",
@@ -34,13 +34,17 @@
"@jest/globals": "^27.5.1",
"@lezer/generator": "1.0.0",
"@lezer/lr": "1.0.0",
"@mattermost/client": "^6.7.0-0",
"@mattermost/types": "^6.7.0-0",
"@mattermost/client": "^7.0.0",
"@mattermost/types": "^7.0.0",
"@silverbulletmd/common": "^0.0.7",
"@silverbulletmd/plugs": "^0.0.7",
"@silverbulletmd/web": "^0.0.7",
"@types/yaml": "^1.9.7",
"handlebars": "^4.7.7",
"markdown-it": "^12.3.2",
"markdown-it-task-lists": "^2.1.1",
"yaml": "^2.0.0",
"handlebars": "^4.7.7"
"server": "^1.0.37",
"yaml": "^2.0.0"
},
"devDependencies": {
"@types/markdown-it": "^12.2.3"
@@ -1,33 +0,0 @@
name: plugmanager
functions:
updatePlugsCommand:
path: ./plugmanager.ts:updatePlugsCommand
command:
name: "Plugs: Update"
key: "Ctrl-Shift-p"
mac: "Cmd-Shift-p"
updatePlugs:
path: ./plugmanager.ts:updatePlugs
env: server
check:
path: "./plugmanager.ts:checkCommand"
command:
name: "Plug: Check"
mac: "Cmd-Alt-c"
key: "Ctrl-Alt-c"
compile:
path: "./plugmanager.ts:compileCommand"
command:
name: "Plug: Compile"
mac: "Cmd-Shift-c"
key: "Ctrl-Shift-c"
compileJS:
path: "./plugmanager.ts:compileJS"
env: server
compileModule:
path: "./plugmanager.ts:compileModule"
env: server
getPlugPlugMd:
path: "./plugmanager.ts:getPlugPlugMd"
events:
- get-plug:plugmd
+24
View File
@@ -0,0 +1,24 @@
name: plugmd
functions:
check:
path: "./plugmd.ts:checkCommand"
command:
name: "Plug: Check"
mac: "Cmd-Alt-c"
key: "Ctrl-Alt-c"
compile:
path: "./plugmd.ts:compileCommand"
command:
name: "Plug: Compile"
mac: "Cmd-Shift-c"
key: "Ctrl-Shift-c"
compileJS:
path: "./plugmd.ts:compileJS"
env: server
compileModule:
path: "./plugmd.ts:compileModule"
env: server
getPlugPlugMd:
path: "./plugmd.ts:getPlugPlugMd"
events:
- get-plug:plugmd
@@ -1,19 +1,14 @@
import { dispatch } from "@plugos/plugos-syscall/event";
import {
addParentPointers,
collectNodesOfType,
findNodeOfType,
} from "@silverbulletmd/common/tree";
import {
flashNotification,
getText,
hideBhs,
showBhs,
} from "@silverbulletmd/plugos-silverbullet-syscall/editor";
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown";
import {
deletePage,
listPages,
readPage,
writePage,
} from "@silverbulletmd/plugos-silverbullet-syscall/space";
@@ -23,8 +18,6 @@ import {
} from "@silverbulletmd/plugos-silverbullet-syscall/system";
import YAML from "yaml";
import { extractMeta } from "../query/data";
import type { Manifest } from "@silverbulletmd/common/manifest";
export async function compileCommand() {
@@ -131,64 +124,6 @@ export async function compileModule(moduleName: string): Promise<string> {
return self.syscall("esbuild.compileModule", moduleName);
}
async function listPlugs(): Promise<string[]> {
let unfilteredPages = await listPages(true);
return unfilteredPages
.filter((p) => p.name.startsWith("_plug/"))
.map((p) => p.name.substring("_plug/".length));
}
export async function listCommand() {
console.log(await listPlugs());
}
export async function updatePlugsCommand() {
flashNotification("Updating plugs...");
await invokeFunction("server", "updatePlugs");
flashNotification("And... done!");
await reloadPlugs();
}
export async function updatePlugs() {
let { text: plugPageText } = await readPage("PLUGS");
let tree = await parseMarkdown(plugPageText);
let codeTextNode = findNodeOfType(tree, "CodeText");
if (!codeTextNode) {
console.error("Could not find yaml block in PLUGS");
return;
}
let plugYaml = codeTextNode.children![0].text;
let plugList = YAML.parse(plugYaml!);
console.log("Plug YAML", plugList);
let allPlugNames: string[] = [];
for (let plugUri of plugList) {
let [protocol, ...rest] = plugUri.split(":");
let manifests = await dispatch(`get-plug:${protocol}`, rest.join(":"));
if (manifests.length === 0) {
console.error("Could not resolve plug", plugUri);
}
// console.log("Got manifests", plugUri, protocol, manifests);
let manifest = manifests[0];
allPlugNames.push(manifest.name);
// console.log("Writing", `_plug/${manifest.name}`);
await writePage(
`_plug/${manifest.name}`,
JSON.stringify(manifest, null, 2)
);
}
// And delete extra ones
for (let existingPlug of await listPlugs()) {
if (!allPlugNames.includes(existingPlug)) {
console.log("Removing plug", existingPlug);
await deletePage(`_plug/${existingPlug}`);
}
}
await reloadPlugs();
}
export async function getPlugPlugMd(pageName: string): Promise<Manifest> {
let { text } = await readPage(pageName);
console.log("Compiling", pageName);
-3
View File
@@ -1,7 +1,4 @@
name: query
# dependencies:
# yaml: "yaml@2"
# "@lezer/lr": "@lezer/lr@0.15.4"
functions:
updateMaterializedQueriesOnPage:
path: ./materialized_queries.ts:updateMaterializedQueriesOnPage
-26
View File
@@ -1,26 +0,0 @@
name: search
functions:
index:
path: ./search.ts:index
events:
- page:index
queryProvider:
path: ./search.ts:queryProvider
events:
- query:full-text
searchCommand:
path: ./search.ts:searchCommand
command:
name: "Search Space"
key: Ctrl-Shift-f
mac: Cmd-Shift-f
readPageSearch:
path: ./search.ts:readPageSearch
pageNamespace:
pattern: "@search/.+"
operation: readPage
getPageMetaSearch:
path: ./search.ts:getPageMetaSearch
pageNamespace:
pattern: "@search/.+"
operation: getPageMeta
+1 -1
View File
@@ -41,7 +41,7 @@ functions:
taskPostponeCommand:
path: ./task.ts:postponeCommand
command:
name: "Task: Postpone by 1 day"
name: "Task: Postpone"
key: Alt-+
contexts:
- DeadlineDate
+40 -10
View File
@@ -57,7 +57,7 @@ export type ServerOptions = {
pagesPath: string;
distDir: string;
builtinPlugDir: string;
token?: string;
password?: string;
};
export class ExpressServer {
app: Express;
@@ -69,21 +69,27 @@ export class ExpressServer {
private port: number;
private server?: Server;
builtinPlugDir: string;
token?: string;
password?: string;
constructor(options: ServerOptions) {
this.port = options.port;
this.app = express();
this.builtinPlugDir = options.builtinPlugDir;
this.distDir = options.distDir;
this.system = new System<SilverBulletHooks>("server");
this.token = options.token;
this.password = options.password;
// Setup system
// Set up the PlugOS System
this.system = new System<SilverBulletHooks>("server");
// Instantiate the event bus hook
this.eventHook = new EventHook();
this.system.addHook(this.eventHook);
// And the page namespace hook
let namespaceHook = new PageNamespaceHook();
this.system.addHook(namespaceHook);
// The space
this.space = new Space(
new EventedSpacePrimitives(
new PlugSpacePrimitives(
@@ -94,6 +100,8 @@ export class ExpressServer {
),
true
);
// The database used for persistence (SQLite)
this.db = knex({
client: "better-sqlite3",
connection: {
@@ -102,9 +110,11 @@ export class ExpressServer {
useNullAsDefault: true,
});
this.system.registerSyscalls(["shell"], shellSyscalls(options.pagesPath));
// The cron hook
this.system.addHook(new NodeCronHook());
// Register syscalls available on the server sid
this.system.registerSyscalls(["shell"], shellSyscalls(options.pagesPath));
this.system.registerSyscalls(
[],
pageIndexSyscalls(this.db),
@@ -117,10 +127,13 @@ export class ExpressServer {
sandboxSyscalls(this.system),
jwtSyscalls()
);
// Register the HTTP endpoint hook (with "/_/<plug-name>"" prefix, hardcoded for now)
this.system.addHook(new EndpointHook(this.app, "/_"));
this.system.on({
plugLoaded: (plug) => {
// Automatically inject some modules into each plug
safeRun(async () => {
for (let [modName, code] of Object.entries(
globalModules.dependencies
@@ -131,6 +144,8 @@ export class ExpressServer {
},
});
// Hook into some "get-plug:" to allow loading plugs from disk (security of this TBD)
// First, for builtins (loaded from the packages/plugs/ folder)
this.eventHook.addLocalListener(
"get-plug:builtin",
async (plugName: string): Promise<Manifest> => {
@@ -149,6 +164,7 @@ export class ExpressServer {
}
);
// Second, for loading plug JSON files with absolute or relative (from CWD) paths
this.eventHook.addLocalListener(
"get-plug:file",
async (plugPath: string): Promise<Manifest> => {
@@ -169,9 +185,12 @@ export class ExpressServer {
}
);
// Rescan disk every 5s to detect any out-of-process file changes
setInterval(() => {
this.space.updatePageList().catch(console.error);
}, 5000);
// Load plugs
this.reloadPlugs().catch(console.error);
}
@@ -182,6 +201,7 @@ export class ExpressServer {
);
}
// In case of a new space with no `PLUGS` file, generate a default one based on all built-in plugs
private async bootstrapBuiltinPlugs() {
let allPlugFiles = await readdir(this.builtinPlugDir);
let pluginNames = [];
@@ -225,9 +245,10 @@ export class ExpressServer {
}
async start() {
const tokenMiddleware: (req: any, res: any, next: any) => void = this.token
const passwordMiddleware: (req: any, res: any, next: any) => void = this
.password
? (req, res, next) => {
if (req.headers.authorization === `Bearer ${this.token}`) {
if (req.headers.authorization === `Bearer ${this.password}`) {
next();
} else {
res.status(401).send("Unauthorized");
@@ -239,6 +260,7 @@ export class ExpressServer {
await ensureTable(this.db);
await ensureFTSTable(this.db, "fts");
await this.ensureIndexPage();
console.log("Setting up router");
let auth = new Authenticator(this.db);
@@ -328,7 +350,7 @@ export class ExpressServer {
this.app.use(
"/fs",
tokenMiddleware,
passwordMiddleware,
cors({
methods: "GET,HEAD,PUT,OPTIONS,POST,DELETE",
preflightContinue: true,
@@ -393,7 +415,7 @@ export class ExpressServer {
this.app.use(
"/plug",
tokenMiddleware,
passwordMiddleware,
cors({
methods: "GET,HEAD,PUT,OPTIONS,POST,DELETE",
preflightContinue: true,
@@ -412,6 +434,14 @@ export class ExpressServer {
});
}
async ensureIndexPage() {
try {
await this.space.getPageMeta("index");
} catch (e) {
await this.space.writePage("index", `Welcome to your new space!`);
}
}
async stop() {
if (this.server) {
console.log("Stopping");
+6 -1
View File
@@ -4,7 +4,7 @@
"name": "Zef Hemel",
"email": "zef@zef.me"
},
"version": "0.0.2",
"version": "0.0.7",
"license": "MIT",
"bin": {
"silverbullet": "./dist/server/server.js"
@@ -41,11 +41,15 @@
"@codemirror/legacy-modes": "6.0.0",
"@jest/globals": "^27.5.1",
"@lezer/markdown": "1.0.0",
"@silverbulletmd/common": "^0.0.7",
"@silverbulletmd/plugs": "^0.0.7",
"@silverbulletmd/web": "^0.0.7",
"better-sqlite3": "^7.5.0",
"body-parser": "^1.19.2",
"buffer": "^6.0.3",
"cors": "^2.8.5",
"dexie": "^3.2.1",
"esbuild": "^0.14.27",
"events": "^3.3.0",
"express": "^4.17.3",
"fake-indexeddb": "^3.1.7",
@@ -56,6 +60,7 @@
"node-fetch": "2",
"node-watch": "^0.7.3",
"nodemon": "^2.0.15",
"server": "^1.0.37",
"uuid": "^8.3.2",
"vm2": "^3.9.9",
"ws": "^8.5.0",
+3 -3
View File
@@ -11,14 +11,14 @@ let args = yargs(hideBin(process.argv))
type: "number",
default: 3000,
})
.option("token", {
.option("password", {
type: "string",
})
.parse();
if (!args._.length) {
console.error(
"Usage: silverbullet [--port 3000] [--token mysecrettoken] <path-to-pages>"
"Usage: silverbullet [--port 3000] [--password mysecretpassword] <path-to-pages>"
);
process.exit(1);
}
@@ -40,7 +40,7 @@ const expressServer = new ExpressServer({
pagesPath: pagesPath,
distDir: webappDistDir,
builtinPlugDir: plugDistDir,
token: args.token,
password: args.password,
});
expressServer.start().catch((e) => {
console.error(e);
+9 -8
View File
@@ -6,22 +6,23 @@ import { HttpSpacePrimitives } from "@silverbulletmd/common/spaces/http_space_pr
safeRun(async () => {
// let localSpace = new Space(new IndexedDBSpacePrimitives("pages"), true);
// localSpace.watch();
let token: string | undefined = localStorage.getItem("token") || undefined;
let password: string | undefined =
localStorage.getItem("password") || undefined;
let httpPrimitives = new HttpSpacePrimitives("", token);
let httpPrimitives = new HttpSpacePrimitives("", password);
while (true) {
try {
await httpPrimitives.getPageMeta("start");
await httpPrimitives.getPageMeta("index");
break;
} catch (e: any) {
if (e.message === "Unauthorized") {
token = prompt("Token: ") || undefined;
if (!token) {
alert("Sorry, that's it then");
password = prompt("Password: ") || undefined;
if (!password) {
alert("Sorry, need a password");
return;
}
localStorage.setItem("token", token!);
httpPrimitives = new HttpSpacePrimitives("", token);
localStorage.setItem("password", password!);
httpPrimitives = new HttpSpacePrimitives("", password);
}
}
}
+60
View File
@@ -0,0 +1,60 @@
import { EditorSelection, StateCommand, Transaction } from "@codemirror/state";
import { Text } from "@codemirror/state";
export function insertMarker(marker: string): StateCommand {
return ({ state, dispatch }) => {
const changes = state.changeByRange((range) => {
const isBoldBefore =
state.sliceDoc(range.from - marker.length, range.from) === marker;
const isBoldAfter =
state.sliceDoc(range.to, range.to + marker.length) === marker;
const changes = [];
changes.push(
isBoldBefore
? {
from: range.from - marker.length,
to: range.from,
insert: Text.of([""]),
}
: {
from: range.from,
insert: Text.of([marker]),
}
);
changes.push(
isBoldAfter
? {
from: range.to,
to: range.to + marker.length,
insert: Text.of([""]),
}
: {
from: range.to,
insert: Text.of([marker]),
}
);
const extendBefore = isBoldBefore ? -marker.length : marker.length;
const extendAfter = isBoldAfter ? -marker.length : marker.length;
return {
changes,
range: EditorSelection.range(
range.from + extendBefore,
range.to + extendAfter
),
};
});
dispatch(
state.update(changes, {
scrollIntoView: true,
annotations: Transaction.userEvent.of("input"),
})
);
return true;
};
}
+1 -1
View File
@@ -221,7 +221,7 @@ export class Editor {
});
if (this.pageNavigator.getCurrentPage() === "") {
await this.pageNavigator.navigate("start");
await this.pageNavigator.navigate("index");
}
await this.reloadPlugs();
}
+6 -2
View File
@@ -4,7 +4,7 @@
"name": "Zef Hemel",
"email": "zef@zef.me"
},
"version": "0.0.2",
"version": "0.0.7",
"license": "MIT",
"scripts": {
"watch": "rm -rf .parcel-cache && parcel watch",
@@ -41,12 +41,16 @@
"@jest/globals": "^27.5.1",
"@lezer/highlight": "1.0.0",
"@lezer/markdown": "1.0.0",
"@silverbulletmd/common": "^0.0.7",
"@silverbulletmd/plugs": "^0.0.7",
"@silverbulletmd/web": "^0.0.7",
"fake-indexeddb": "^3.1.7",
"fuzzysort": "^1.9.0",
"jest": "^27.5.1",
"knex": "^1.0.4",
"react": "^17.0.2",
"react-dom": "^17.0.2"
"react-dom": "^17.0.2",
"server": "^1.0.37"
},
"devDependencies": {
"@parcel/packager-raw-url": "2.5.0",
+2 -2
View File
@@ -21,9 +21,9 @@ export function spaceSyscalls(editor: Editor): SysCallMapping {
return await editor.space.writePage(name, text);
},
"space.deletePage": async (ctx, name: string) => {
// If we're deleting the current page, navigate to the start page
// If we're deleting the current page, navigate to the index page
if (editor.currentPage === name) {
await editor.navigate("start");
await editor.navigate("index");
}
// Remove page from open pages in editor
editor.openPages.delete(name);