Basic ghost plugin

pull/3/head
Zef Hemel 2022-04-09 14:28:41 +02:00
parent 6ebf8e7f15
commit 8fafd1cd4a
19 changed files with 594 additions and 53 deletions

View File

@ -9,7 +9,7 @@ export type MarkdownTree = {
children?: MarkdownTree[];
};
function treeToAST(text: string, n: SyntaxNode): MarkdownTree {
function treeToAST(text: string, n: SyntaxNode, offset = 0): MarkdownTree {
let children: MarkdownTree[] = [];
let nodeText: string | undefined;
let child = n.firstChild;
@ -21,8 +21,8 @@ function treeToAST(text: string, n: SyntaxNode): MarkdownTree {
if (children.length === 0) {
children = [
{
from: n.from,
to: n.to,
from: n.from + offset,
to: n.to + offset,
text: text.substring(n.from, n.to),
},
];
@ -33,8 +33,8 @@ function treeToAST(text: string, n: SyntaxNode): MarkdownTree {
let s = text.substring(index, child.from);
if (s) {
newChildren.push({
from: index,
to: child.from,
from: index + offset,
to: child.from! + offset,
text: s,
});
}
@ -43,15 +43,15 @@ function treeToAST(text: string, n: SyntaxNode): MarkdownTree {
}
let s = text.substring(index, n.to);
if (s) {
newChildren.push({ from: index, to: n.to, text: s });
newChildren.push({ from: index + offset, to: n.to + offset, text: s });
}
children = newChildren;
}
let result: MarkdownTree = {
type: n.name,
from: n.from,
to: n.to,
from: n.from + offset,
to: n.to + offset,
};
if (children.length > 0) {
result.children = children;
@ -63,5 +63,25 @@ function treeToAST(text: string, n: SyntaxNode): MarkdownTree {
}
export function parse(text: string): MarkdownTree {
return treeToAST(text, wikiMarkdownLang.parser.parse(text).topNode);
let tree = treeToAST(text, wikiMarkdownLang.parser.parse(text).topNode);
// replaceNodesMatching(tree, (n): MarkdownTree | undefined | null => {
// if (n.type === "FencedCode") {
// let infoN = findNodeMatching(n, (n) => n.type === "CodeInfo");
// let language = infoN!.children![0].text;
// let textN = findNodeMatching(n, (n) => n.type === "CodeText");
// let text = textN!.children![0].text!;
//
// console.log(language, text);
// switch (language) {
// case "yaml":
// let parsed = StreamLanguage.define(yaml).parser.parse(text);
// let subTree = treeToAST(text, parsed.topNode, n.from);
// // console.log(JSON.stringify(subTree, null, 2));
// subTree.type = "yaml";
// return subTree;
// }
// }
// return;
// });
return tree;
}

View File

@ -35,7 +35,11 @@
"context": "node"
},
"test": {
"source": ["plugs/lib/tree.test.ts", "common/spaces/sync.test.ts"],
"source": [
"plugs/lib/tree.test.ts",
"common/spaces/sync.test.ts"
],
"includeNodeModules": ["@codemirror/legacy-modes"],
"outputFormat": "commonjs",
"isLibrary": true,
"context": "node"
@ -45,27 +49,32 @@
"@codemirror/closebrackets": "^0.19.1",
"@codemirror/collab": "^0.19.0",
"@codemirror/commands": "^0.19.8",
"@codemirror/highlight": "^0.19.0",
"@codemirror/history": "^0.19.2",
"@codemirror/lang-javascript": "^0.19.7",
"@codemirror/lang-markdown": "^0.19.6",
"@codemirror/language": "^0.19.0",
"@codemirror/legacy-modes": "^0.19.1",
"@codemirror/matchbrackets": "^0.19.4",
"@codemirror/search": "^0.19.9",
"@codemirror/state": "^0.19.7",
"@codemirror/stream-parser": "^0.19.9",
"@codemirror/view": "^0.19.42",
"@fortawesome/fontawesome-svg-core": "1.3.0",
"@fortawesome/free-solid-svg-icons": "6.0.0",
"@fortawesome/react-fontawesome": "0.1.17",
"@codemirror/highlight": "^0.19.0",
"@codemirror/language": "^0.19.0",
"@lezer/markdown": "^0.15.0",
"@jest/globals": "^27.5.1",
"@lezer/markdown": "^0.15.0",
"better-sqlite3": "^7.5.0",
"body-parser": "^1.19.2",
"buffer": "^6.0.3",
"cors": "^2.8.5",
"dexie": "^3.2.1",
"events": "^3.3.0",
"express": "^4.17.3",
"dexie": "^3.2.1",
"fake-indexeddb": "^3.1.7",
"jest": "^27.5.1",
"jsonwebtoken": "^8.5.1",
"knex": "^1.0.4",
"node-cron": "^3.0.0",
"node-fetch": "2",
@ -76,8 +85,6 @@
"supertest": "^6.2.2",
"vm2": "^3.9.9",
"yaml": "^1.10.2",
"fake-indexeddb": "^3.1.7",
"yargs": "^17.3.1"
},
"devDependencies": {

View File

@ -11,8 +11,8 @@ import fs from "fs";
class NodeWorkerWrapper implements WorkerLike {
onMessage?: (message: any) => Promise<void>;
private worker: Worker;
ready: Promise<void>;
private worker: Worker;
constructor(worker: Worker) {
this.worker = worker;

View File

@ -1,4 +1,5 @@
const { parentPort, workerData } = require("worker_threads");
// @ts-ignore
let vm2 = `${workerData}/vm2`;
const { VM, VMScript } = require(vm2);
@ -15,9 +16,11 @@ let pendingRequests = new Map<
let syscallReqId = 0;
// console.log("Here's crypto", crypto);
let vm = new VM({
sandbox: {
console: console,
console,
self: {
syscall: (name: string, ...args: any[]) => {
return new Promise((resolve, reject) => {

View File

@ -41,6 +41,7 @@
"@jest/globals": "^27.5.1",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"@types/jsonwebtoken": "^8.5.8",
"better-sqlite3": "^7.5.0",
"body-parser": "^1.19.2",
"cors": "^2.8.5",
@ -49,6 +50,7 @@
"express": "^4.17.3",
"fake-indexeddb": "^3.1.7",
"jest": "^27.5.1",
"jsonwebtoken": "^8.5.1",
"knex": "^1.0.4",
"node-cron": "^3.0.0",
"node-fetch": "2",

22
plugos/syscalls/jwt.ts Normal file
View File

@ -0,0 +1,22 @@
import jwt, { Algorithm } from "jsonwebtoken";
import { SysCallMapping } from "../system";
export function jwtSyscalls(): SysCallMapping {
return {
"jwt.jwt": (
ctx,
hexSecret: string,
id: string,
algorithm: Algorithm,
expiry: string,
audience: string
): string => {
return jwt.sign({}, Buffer.from(hexSecret, "hex"), {
keyid: id,
algorithm: algorithm,
expiresIn: expiry,
audience: audience,
});
},
};
}

View File

@ -1260,6 +1260,13 @@
jest-matcher-utils "^27.0.0"
pretty-format "^27.0.0"
"@types/jsonwebtoken@^8.5.8":
version "8.5.8"
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz#01b39711eb844777b7af1d1f2b4cf22fda1c0c44"
integrity sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A==
dependencies:
"@types/node" "*"
"@types/mime@^1":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
@ -1675,6 +1682,11 @@ bser@2.1.1:
dependencies:
node-int64 "^0.4.0"
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
@ -2243,6 +2255,13 @@ dotenv@^7.0.0:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c"
integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@ -3709,6 +3728,39 @@ json5@^2.1.2, json5@^2.2.0:
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
jsonwebtoken@^8.5.1:
version "8.5.1"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
dependencies:
jws "^3.2.2"
lodash.includes "^4.3.0"
lodash.isboolean "^3.0.3"
lodash.isinteger "^4.0.4"
lodash.isnumber "^3.0.3"
lodash.isplainobject "^4.0.6"
lodash.isstring "^4.0.1"
lodash.once "^4.0.0"
ms "^2.1.1"
semver "^5.6.0"
jwa@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
jws@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
dependencies:
jwa "^1.4.1"
safe-buffer "^5.0.1"
kleur@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
@ -3774,11 +3826,46 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
lodash.isboolean@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
lodash.isinteger@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
lodash.isnumber@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
lodash.isstring@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
@ -3914,7 +4001,7 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@2.1.3:
ms@2.1.3, ms@^2.1.1:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
@ -4754,7 +4841,7 @@ saxes@^5.0.1:
dependencies:
xmlchars "^2.2.0"
semver@^5.7.0, semver@^5.7.1:
semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==

View File

@ -60,3 +60,8 @@ functions:
path: ./materialized_queries.ts:updateMaterializedQueriesCommand
command:
name: "Materialized Queries: Update"
parseCommand:
path: ./page.ts:parsePage
command:
name: Parse Document

View File

@ -182,3 +182,7 @@ export async function clearPageIndex(page: string) {
console.log("Clearing page index for page", page);
await clearPageIndexForPage(page);
}
export async function parsePage() {
console.log(await parseMarkdown(await getText()));
}

View File

@ -0,0 +1,15 @@
functions:
downloadAllPostsCommand:
path: "./ghost.ts:downloadAllPostsCommand"
command:
name: "Ghost: Download Posts"
downloadAllPosts:
path: "./ghost.ts:downloadAllPosts"
env: server
publishPostCommand:
path: "./ghost.ts:publishPostCommand"
command:
name: "Ghost: Publish Post"
publishPost:
path: "./ghost.ts:publishPost"
env: server

219
plugs/ghost/ghost.ts Normal file
View File

@ -0,0 +1,219 @@
import { readPage, writePage } from "plugos-silverbullet-syscall/space";
import { json } from "plugos-syscall/fetch";
import YAML from "yaml";
import { invokeFunction } from "plugos-silverbullet-syscall/system";
import { getCurrentPage, getText } from "plugos-silverbullet-syscall/editor";
type Post = {
id: string;
uuid: string;
title: string;
slug: string;
mobiledoc: string;
status: "draft" | "published";
visibility: string;
created_at: string;
upblished_at: string;
updated_at: string;
tags: Tag[];
primary_tag: Tag;
url: string;
excerpt: string;
};
type Tag = {
id: string;
name: string;
slug: string;
description: string | null;
};
type MobileDoc = {
version: string;
atoms: any[];
cards: Card[];
};
type Card = any[];
function mobileDocToMarkdown(doc: string): string | null {
let mobileDoc = JSON.parse(doc) as MobileDoc;
if (mobileDoc.cards.length > 0 && mobileDoc.cards[0][0] === "markdown") {
return mobileDoc.cards[0][1].markdown;
}
return null;
}
function markdownToMobileDoc(text: string): string {
return JSON.stringify({
version: "0.3.1",
atoms: [],
cards: [["markdown", { markdown: text }]],
markups: [],
sections: [
[10, 0],
[1, "p", []],
],
});
}
class GhostAdmin {
private token?: string;
constructor(private url: string, private key: string) {}
async init() {
const [id, secret] = this.key.split(":");
this.token = await self.syscall(
"jwt.jwt",
secret,
id,
"HS256",
"5m",
"/v3/admin/"
);
}
async listPosts(): Promise<Post[]> {
let result = await json(
`${this.url}/ghost/api/v3/admin/posts?order=published_at+DESC`,
{
headers: {
Authorization: `Ghost ${this.token}`,
},
}
);
return result.posts;
}
async listMarkdownPosts(): Promise<Post[]> {
let markdownPosts: Post[] = [];
for (let post of await this.listPosts()) {
let mobileDoc = JSON.parse(post.mobiledoc) as MobileDoc;
if (mobileDoc.cards.length > 0 && mobileDoc.cards[0][0] === "markdown") {
markdownPosts.push(post);
}
}
return markdownPosts;
}
async createPost(post: Partial<Post>): Promise<Post> {
let result = await json(`${this.url}/ghost/api/v3/admin/posts`, {
method: "POST",
headers: {
Authorization: `Ghost ${this.token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
posts: [post],
}),
});
return result.posts[0];
}
async updatePost(post: Partial<Post>): Promise<any> {
let oldPost = await json(
`${this.url}/ghost/api/v3/admin/posts/${post.id}`,
{
headers: {
Authorization: `Ghost ${this.token}`,
"Content-Type": "application/json",
},
}
);
post.updated_at = oldPost.posts[0].updated_at;
let result = await json(`${this.url}/ghost/api/v3/admin/posts/${post.id}`, {
method: "PUT",
headers: {
Authorization: `Ghost ${this.token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
posts: [post],
}),
});
return result.posts[0];
}
}
type GhostConfig = {
url: string;
adminKey: string;
pagePrefix: string;
};
function postToMarkdown(post: Post): string {
let text = mobileDocToMarkdown(post.mobiledoc);
return `<!-- #ghost-id: ${post.id} -->\n# ${post.title}\n${text}`;
}
const publishedPostRegex =
/<!-- #ghost-id:\s*(\w+)\s*-->\n#\s*([^\n]+)\n([^$]+)$/;
const newPostRegex = /#\s*([^\n]+)\n([^$]+)$/;
function markdownToPost(text: string): Partial<Post> {
let match = publishedPostRegex.exec(text);
if (match) {
let [, id, title, content] = match;
return {
id,
title,
mobiledoc: markdownToMobileDoc(content),
};
}
match = newPostRegex.exec(text);
if (match) {
let [, title, content] = match;
return {
title,
status: "draft",
mobiledoc: markdownToMobileDoc(content),
};
}
throw Error("Not a valid ghost post");
}
async function getConfig(): Promise<GhostConfig> {
let configPage = await readPage("ghost-config");
return YAML.parse(configPage.text) 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 = `<!-- #ghost-id: ${post.id} -->\n# ${post.title}\n${text}`;
await writePage(`${config.pagePrefix}${post.slug}`, text);
}
}
export async function publishPostCommand() {
await invokeFunction(
"server",
"publishPost",
await getCurrentPage(),
await getText()
);
}
export async function publishPost(name: string, text: string) {
let config = await getConfig();
let admin = new GhostAdmin(config.url, config.adminKey);
await admin.init();
let post = markdownToPost(text);
post.slug = name.substring(config.pagePrefix.length);
if (post.id) {
await admin.updatePost(post);
} else {
let newPost = await admin.createPost(post);
text = `<!-- #ghost-id: ${newPost.id} -->\n${text}`;
await writePage(name, text);
}
}

View File

@ -1,6 +1,14 @@
import { expect, test } from "@jest/globals";
import { parse } from "../../common/tree";
import { addParentPointers, collectNodesMatching, findParentMatching, nodeAtPos, renderMarkdown } from "./tree";
import {
addParentPointers,
collectNodesMatching,
findParentMatching,
nodeAtPos,
removeParentPointers,
renderMarkdown,
replaceNodesMatching
} from "./tree";
const mdTest1 = `
# Heading
@ -31,6 +39,12 @@ Hello
Sup`;
const mdTest3 = `
\`\`\`yaml
name: something
\`\`\`
`;
test("Run a Node sandbox", async () => {
let mdTree = parse(mdTest1);
addParentPointers(mdTree);
@ -47,6 +61,15 @@ test("Run a Node sandbox", async () => {
// Render back into markdown should be equivalent
expect(renderMarkdown(mdTree)).toBe(mdTest1);
let mdTree2 = parse(mdTest2);
console.log(JSON.stringify(mdTree2, null, 2));
removeParentPointers(mdTree);
replaceNodesMatching(mdTree, (n) => {
if (n.type === "Task") {
return {
type: "Tosk",
};
}
});
console.log(JSON.stringify(mdTree, null, 2));
let mdTree3 = parse(mdTest3);
console.log(JSON.stringify(mdTree3, null, 2));
});

View File

@ -62,22 +62,20 @@ export function replaceNodesMatching(
mdTree: MarkdownTree,
substituteFn: (mdTree: MarkdownTree) => MarkdownTree | null | undefined
) {
let subst = substituteFn(mdTree);
if (subst !== undefined) {
if (!mdTree.parent) {
throw Error("Need parent pointers for this");
}
let parentChildren = mdTree.parent.children!;
let pos = parentChildren.indexOf(mdTree);
if (subst) {
parentChildren.splice(pos, 1, subst);
} else {
// null = delete
parentChildren.splice(pos, 1);
}
} else if (mdTree.children) {
if (mdTree.children) {
for (let child of mdTree.children) {
replaceNodesMatching(child, substituteFn);
let subst = substituteFn(child);
if (subst !== undefined) {
let pos = mdTree.children.indexOf(child);
if (subst) {
mdTree.children.splice(pos, 1, subst);
} else {
// null = delete
mdTree.children.splice(pos, 1);
}
} else {
replaceNodesMatching(child, substituteFn);
}
}
}
}

View File

@ -3,7 +3,9 @@
"version": "1.0.0",
"dependencies": {
"@jest/globals": "^27.5.1",
"@types/yaml": "^1.9.7",
"plugos-silverbullet-syscall": "file:../plugos-silverbullet-syscall",
"plugos-syscall": "file:../plugos-syscall"
"plugos-syscall": "file:../plugos-syscall",
"yaml": "^2.0.0"
}
}

View File

@ -19,6 +19,8 @@ import { EventedSpacePrimitives } from "../common/spaces/evented_space_primitive
import { Space } from "../common/spaces/space";
import { safeRun } from "../webapp/util";
import { createSandbox } from "../plugos/environments/node_sandbox";
import { jwtSyscalls } from "../plugos/syscalls/jwt";
import { fetchSyscalls } from "../plugos/syscalls/fetch.node";
export class ExpressServer {
app: Express;
@ -65,6 +67,8 @@ export class ExpressServer {
system.registerSyscalls([], spaceSyscalls(this.space));
system.registerSyscalls([], eventSyscalls(this.eventHook));
system.registerSyscalls([], markdownSyscalls());
system.registerSyscalls([], fetchSyscalls());
system.registerSyscalls([], jwtSyscalls());
system.addHook(new EndpointHook(app, "/_/"));
this.space.on({
@ -220,7 +224,7 @@ export class ExpressServer {
return res.send(`Plug ${plugName} not found`);
}
try {
console.log("Invoking", name, "with args", args);
console.log("Invoking", name);
const result = await plug.invoke(name, args);
res.status(200);
res.send(result);

View File

@ -1,5 +1,5 @@
{
"include": ["webapp/**/*", "server/**/*", "plugos/**/*", "plugs/**/*"],
"include": ["webapp/**/*", "server/**/*", "plugos/**/*", "plugs/**/*", "common/**/*"],
"compilerOptions": {
"target": "esnext",
"strict": true,

View File

@ -33,15 +33,15 @@ export function TopBar({
<FontAwesomeIcon icon={faFileLines} />
</span>
<span className="current-page">{prettyName(pageName)}</span>
<button
onClick={(e) => {
// @ts-ignore
window.syncer();
e.stopPropagation();
}}
>
Sync
</button>
{/*<button*/}
{/* onClick={(e) => {*/}
{/* // @ts-ignore*/}
{/* window.syncer();*/}
{/* e.stopPropagation();*/}
{/* }}*/}
{/*>*/}
{/* Sync*/}
{/*</button>*/}
{notifications.length > 0 && (
<div className="status">
{notifications.map((notification) => (

View File

@ -1,7 +1,11 @@
import { styleTags, tags as t } from "@codemirror/highlight";
import { BlockContext, LeafBlock, LeafBlockParser, MarkdownConfig, TaskList } from "@lezer/markdown";
import { commonmark, mkLang } from "./markdown/markdown";
import { BlockContext, LeafBlock, LeafBlockParser, MarkdownConfig, parseCode, TaskList } from "@lezer/markdown";
import { commonmark, getCodeParser, mkLang } from "./markdown/markdown";
import * as ct from "./customtags";
import { LanguageDescription, LanguageSupport } from "@codemirror/language";
import { StreamLanguage } from "@codemirror/stream-parser";
import { yaml } from "@codemirror/legacy-modes/mode/yaml";
import { javascriptLanguage } from "@codemirror/lang-javascript";
export const pageLinkRegexPrefix = /^\[\[([\w\s\/:,\.@\-]+)\]\]/;
@ -126,6 +130,7 @@ const TagLink: MarkdownConfig = {
},
],
};
const WikiMarkdown = commonmark.configure([
WikiLink,
AtMention,
@ -133,6 +138,19 @@ const WikiMarkdown = commonmark.configure([
TaskList,
UnmarkedUrl,
Comment,
parseCode({
codeParser: getCodeParser([
LanguageDescription.of({
name: "yaml",
support: new LanguageSupport(StreamLanguage.define(yaml)),
}),
LanguageDescription.of({
name: "javascript",
alias: ["js"],
support: new LanguageSupport(javascriptLanguage),
}),
]),
}),
{
props: [
styleTags({

114
yarn.lock
View File

@ -402,6 +402,19 @@
"@codemirror/view" "^0.19.0"
"@lezer/javascript" "^0.15.1"
"@codemirror/lang-javascript@^0.19.7":
version "0.19.7"
resolved "https://registry.yarnpkg.com/@codemirror/lang-javascript/-/lang-javascript-0.19.7.tgz#84581ef6abf2a16d78f017ffc96c2d6227de5eb5"
integrity sha512-DL9f3JLqOEHH9cIwEqqjnP5bkjdVXeECksLtV+/MbPm+l4H+AG+PkwZaJQ2oR1GfPZKh8MVSIE94aGWNkJP8WQ==
dependencies:
"@codemirror/autocomplete" "^0.19.0"
"@codemirror/highlight" "^0.19.7"
"@codemirror/language" "^0.19.0"
"@codemirror/lint" "^0.19.0"
"@codemirror/state" "^0.19.0"
"@codemirror/view" "^0.19.0"
"@lezer/javascript" "^0.15.1"
"@codemirror/lang-markdown@^0.19.6":
version "0.19.6"
resolved "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-0.19.6.tgz"
@ -426,6 +439,13 @@
"@lezer/common" "^0.15.5"
"@lezer/lr" "^0.15.0"
"@codemirror/legacy-modes@^0.19.1":
version "0.19.1"
resolved "https://registry.yarnpkg.com/@codemirror/legacy-modes/-/legacy-modes-0.19.1.tgz#7dc3b5df1842060648f75764ab6919fcfce3ea1a"
integrity sha512-vYPLsD/ON+3SXhlGj9Qb3fpFNNU3Ya/AtDiv/g3OyqVzhh5vs5rAnOvk8xopGWRwppdhlNPD9VyXjiOmZUQtmQ==
dependencies:
"@codemirror/stream-parser" "^0.19.0"
"@codemirror/lint@^0.19.0":
version "0.19.6"
resolved "https://registry.npmjs.org/@codemirror/lint/-/lint-0.19.6.tgz"
@ -483,6 +503,18 @@
dependencies:
"@codemirror/text" "^0.19.0"
"@codemirror/stream-parser@^0.19.0", "@codemirror/stream-parser@^0.19.9":
version "0.19.9"
resolved "https://registry.yarnpkg.com/@codemirror/stream-parser/-/stream-parser-0.19.9.tgz#34955ea91a8047cf72abebd5ce28f0d332aeca48"
integrity sha512-WTmkEFSRCetpk8xIOvV2yyXdZs3DgYckM0IP7eFi4ewlxWnJO/H4BeJZLs4wQaydWsAqTQoDyIwNH1BCzK5LUQ==
dependencies:
"@codemirror/highlight" "^0.19.0"
"@codemirror/language" "^0.19.0"
"@codemirror/state" "^0.19.0"
"@codemirror/text" "^0.19.0"
"@lezer/common" "^0.15.0"
"@lezer/lr" "^0.15.0"
"@codemirror/text@^0.19.0", "@codemirror/text@^0.19.2", "@codemirror/text@^0.19.6":
version "0.19.6"
resolved "https://registry.npmjs.org/@codemirror/text/-/text-0.19.6.tgz"
@ -1995,6 +2027,11 @@ bser@2.1.1:
dependencies:
node-int64 "^0.4.0"
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
@ -2661,6 +2698,13 @@ duplexer3@^0.1.4:
resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz"
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
@ -4106,6 +4150,39 @@ json5@^2.1.2, json5@^2.2.0:
dependencies:
minimist "^1.2.5"
jsonwebtoken@^8.5.1:
version "8.5.1"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
dependencies:
jws "^3.2.2"
lodash.includes "^4.3.0"
lodash.isboolean "^3.0.3"
lodash.isinteger "^4.0.4"
lodash.isnumber "^3.0.3"
lodash.isplainobject "^4.0.6"
lodash.isstring "^4.0.1"
lodash.once "^4.0.0"
ms "^2.1.1"
semver "^5.6.0"
jwa@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
jws@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
dependencies:
jwa "^1.4.1"
safe-buffer "^5.0.1"
keyv@^3.0.0:
version "3.1.0"
resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz"
@ -4185,11 +4262,46 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
lodash.isboolean@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
lodash.isinteger@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
lodash.isnumber@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
lodash.isstring@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz"
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz"
@ -5319,7 +5431,7 @@ semver-diff@^3.1.1:
dependencies:
semver "^6.3.0"
semver@^5.7.0, semver@^5.7.1:
semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
version "5.7.1"
resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==