Runs-ish?

pull/87/head
Zef Hemel 2022-10-06 15:14:21 +02:00
parent 4bb89563a5
commit f051e3de6b
38 changed files with 805 additions and 518 deletions

6
.gitignore vendored
View File

@ -1,14 +1,10 @@
pages pages
test_space
.DS_Store .DS_Store
node_modules
.parcel-cache
dist dist
build build
generated generated
.yarnrc.yml
*.test.js
*.js.map *.js.map
.vscode
website_build website_build
data.db data.db
/index.json /index.json

1
.vscode/configurationCache.log vendored Normal file
View File

@ -0,0 +1 @@
{"buildTargets":[],"launchTargets":[],"customConfigurationProvider":{"workspaceBrowse":{"browsePath":[],"compilerArgs":[]},"fileIndex":[]}}

6
.vscode/dryrun.log vendored Normal file
View File

@ -0,0 +1,6 @@
make --dry-run --always-make --keep-going --print-directory
make: Entering directory `/Users/zef/git/silverbullet'
make: Leaving directory `/Users/zef/git/silverbullet'
make: *** No targets specified and no makefile found. Stop.

7
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"editor.formatOnSave": true,
"typescriptHero.imports.stringQuoteStyle": "\"",
"deno.enable": true,
"deno.importMap": "import_map.json",
"deno.config": "deno.json"
}

254
.vscode/targets.log vendored Normal file
View File

@ -0,0 +1,254 @@
make all --print-data-base --no-builtin-variables --no-builtin-rules --question
# GNU Make 3.81
# Copyright (C) 2006 Free Software Foundation, Inc.
# This is free software; see the source for copying conditions.
# There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
# This program built for i386-apple-darwin11.3.0
make: *** No rule to make target `all'. Stop.
# Make data base, printed on Mon Jul 4 09:43:18 2022
# Variables
# automatic
<D = $(patsubst %/,%,$(dir $<))
# automatic
?F = $(notdir $?)
# environment
VSCODE_LOG_NATIVE = false
# environment
NVM_INC = /Users/zef/.nvm/versions/node/v18.4.0/include/node
# automatic
?D = $(patsubst %/,%,$(dir $?))
# automatic
@D = $(patsubst %/,%,$(dir $@))
# automatic
@F = $(notdir $@)
# makefile
CURDIR := /Users/zef/git/silverbullet
# makefile
SHELL = /bin/sh
# environment
VSCODE_NLS_CONFIG = {"locale":"en-us","availableLanguages":{},"_languagePackSupport":true}
# environment
_ = /usr/bin/make
# makefile
MAKEFILE_LIST :=
# environment
VSCODE_VERBOSE_LOGGING = true
# environment
__CFBundleIdentifier = com.microsoft.VSCode
# environment
INFOPATH = /opt/homebrew/share/info:
# environment
VSCODE_IPC_HOOK_EXTHOST = /var/folders/s2/4nqrw2192hngtxg672qzc0nr0000gn/T/vscode-ipc-15b47298-2e0d-4c34-99d9-3434e4f1806c.sock
# environment
VSCODE_CWD = /
# environment
PATH = /Users/zef/.nvm/versions/node/v18.4.0/bin:/Library/Frameworks/Python.framework/Versions/2.7/bin:/Users/zef/.local/share/solana/install/active_release/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/Users/zef/.cargo/bin:/Users/zef/.fig/bin:/Users/zef/.local/bin
# environment
LSCOLORS = Gxfxcxdxbxegedabagacad
# environment
NVM_BIN = /Users/zef/.nvm/versions/node/v18.4.0/bin
# environment
VSCODE_LOG_STACK = false
# environment
ELECTRON_RUN_AS_NODE = 1
# default
.FEATURES := target-specific order-only second-expansion else-if archives jobserver check-symlink
# environment
SSH_AUTH_SOCK = /private/tmp/com.apple.launchd.qE7FAVvbDO/Listeners
# automatic
%F = $(notdir $%)
# environment
TTY = not a tty
# environment
VSCODE_PIPE_LOGGING = true
# environment
FIG_PID = 75947
# environment
PWD = /Users/zef/git/silverbullet
# environment
HOMEBREW_CELLAR = /opt/homebrew/Cellar
# environment
ORIGINAL_XDG_CURRENT_DESKTOP = undefined
# environment
MANPATH = /Users/zef/.nvm/versions/node/v18.4.0/share/man:/opt/homebrew/share/man::
# environment
VSCODE_AMD_ENTRYPOINT = vs/workbench/api/node/extensionHostProcess
# environment
HOME = /Users/zef
# default
MAKEFILEPATH := /Applications/Xcode.app/Contents/Developer/Makefiles
# environment
VSCODE_CODE_CACHE_PATH = /Users/zef/Library/Application Support/Code/CachedData/30d9c6cd9483b2cc586687151bcbcd635f373630
# environment
LOGNAME = zef
# environment
APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL = 1
# environment
NVM_CD_FLAGS = -q
# environment
ZSH = /Users/zef/.local/share/fig/plugins/ohmyzsh
# environment
VSCODE_HANDLES_UNCAUGHT_ERRORS = true
# automatic
^D = $(patsubst %/,%,$(dir $^))
# environment
XPC_FLAGS = 0x0
# default
MAKE = $(MAKE_COMMAND)
# default
MAKECMDGOALS := all
# environment
SHLVL = 1
# default
MAKE_VERSION := 3.81
# environment
USER = zef
# makefile
.DEFAULT_GOAL :=
# environment
LESS = -R
# automatic
%D = $(patsubst %/,%,$(dir $%))
# default
MAKE_COMMAND := /Applications/Xcode.app/Contents/Developer/usr/bin/make
# default
.VARIABLES :=
# environment
TMPDIR = /var/folders/s2/4nqrw2192hngtxg672qzc0nr0000gn/T/
# automatic
*F = $(notdir $*)
# environment
VSCODE_IPC_HOOK = /Users/zef/Library/Application Support/Code/1.68.1-main.sock
# makefile
MAKEFLAGS = Rrqp
# environment
MFLAGS = -Rrqp
# automatic
*D = $(patsubst %/,%,$(dir $*))
# environment
NVM_DIR = /Users/zef/.nvm
# environment
XPC_SERVICE_NAME = application.com.microsoft.VSCode.109834161.109834167
# environment
HOMEBREW_PREFIX = /opt/homebrew
# automatic
+D = $(patsubst %/,%,$(dir $+))
# automatic
+F = $(notdir $+)
# environment
HOMEBREW_REPOSITORY = /opt/homebrew
# environment
__CF_USER_TEXT_ENCODING = 0x1F5:0x0:0x0
# environment
COMMAND_MODE = unix2003
# default
MAKEFILES :=
# automatic
<F = $(notdir $<)
# environment
PAGER = less
# environment
LC_ALL = C
# automatic
^F = $(notdir $^)
# default
SUFFIXES :=
# environment
MAKELEVEL := 0
# environment
LANG = C
# environment
VSCODE_PID = 75884
# variable set hash-table stats:
# Load=76/1024=7%, Rehash=0, Collisions=1/96=1%
# Pattern-specific Variable Values
# No pattern-specific variable values.
# Directories
# . (device 16777232, inode 91684251): 20 files, no impossibilities.
# 20 files, no impossibilities in 1 directories.
# Implicit Rules
# No implicit rules.
# Files
# Not a target:
all:
# Command-line target.
# Implicit rule search has been done.
# File does not exist.
# File has not been updated.
# variable set hash-table stats:
# Load=0/32=0%, Rehash=0, Collisions=0/0=0%
# Not a target:
.SUFFIXES:
# Implicit rule search has not been done.
# Modification time never checked.
# File has not been updated.
# Not a target:
Makefile:
# A default, MAKEFILES, or -include/sinclude makefile.
# Implicit rule search has been done.
# File does not exist.
# File has been updated.
# Failed to be updated.
# variable set hash-table stats:
# Load=0/32=0%, Rehash=0, Collisions=0/0=0%
# Not a target:
makefile:
# A default, MAKEFILES, or -include/sinclude makefile.
# Implicit rule search has been done.
# File does not exist.
# File has been updated.
# Failed to be updated.
# variable set hash-table stats:
# Load=0/32=0%, Rehash=0, Collisions=0/0=0%
# Not a target:
.DEFAULT:
# Implicit rule search has not been done.
# Modification time never checked.
# File has not been updated.
# Not a target:
GNUmakefile:
# A default, MAKEFILES, or -include/sinclude makefile.
# Implicit rule search has been done.
# File does not exist.
# File has been updated.
# Failed to be updated.
# variable set hash-table stats:
# Load=0/32=0%, Rehash=0, Collisions=0/0=0%
# files hash-table stats:
# Load=6/1024=1%, Rehash=0, Collisions=0/17=0%
# VPATH Search Paths
# No `vpath' search paths.
# No general (`VPATH' variable) search path.
# # of strings in strcache: 0
# # of strcache buffers: 0
# strcache size: total = 0 / max = 0 / min = 4096 / avg = 0
# strcache free: total = 0 / max = 0 / min = 4096 / avg = 0
# Finished Make data base on Mon Jul 4 09:43:18 2022

16
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "test",
"problemMatcher": [],
"label": "npm: test",
"detail": "jest packages/*/{dist,build}/test",
"group": {
"kind": "test",
"isDefault": true
}
}
]
}

9
Makefile Normal file
View File

@ -0,0 +1,9 @@
plugs:
deno run -A --unstable packages/plugos/bin/plugos-bundle.ts --dist dist packages/plugs/global.plug.yaml
deno run -A --unstable packages/plugos/bin/plugos-bundle.ts --dist packages/plugs/dist --exclude=https://esm.sh/handlebars,https://deno.land/std@0.158.0/encoding/yaml.ts packages/plugs/*/*.plug.yaml
test:
deno test -A --unstable
watch:
deno task watch

View File

@ -28,10 +28,13 @@ async function copyAssets(dest: string) {
await Deno.writeTextFile("dist/main.css", compiler.to_string() as string); await Deno.writeTextFile("dist/main.css", compiler.to_string() as string);
} }
async function bundle(appFile: string): Promise<void> { async function bundle(): Promise<void> {
await Promise.all([ await Promise.all([
esbuild.build({ esbuild.build({
entryPoints: [appFile], entryPoints: {
"client": "packages/web/boot.ts",
"worker": "packages/plugos/environments/sandbox_worker.ts",
},
outdir: "./dist", outdir: "./dist",
absWorkingDir: Deno.cwd(), absWorkingDir: Deno.cwd(),
bundle: true, bundle: true,
@ -57,5 +60,5 @@ async function bundle(appFile: string): Promise<void> {
await copyAssets("dist"); await copyAssets("dist");
console.log("Built!"); console.log("Built!");
} }
await bundle("packages/web/boot.ts"); await bundle();
// esbuild.stop(); // esbuild.stop();

View File

@ -5,7 +5,8 @@
"importMap": "import_map.json", "importMap": "import_map.json",
"tasks": { "tasks": {
"test": "deno test -A --unstable", "test": "deno test -A --unstable",
"watch": "deno run -A build.ts" "watch": "deno run -A build.ts",
"plugs": "deno run -A --unstable packages/plugos/bin/plugos-bundle.ts --dist packages/plugs/dist --exclude=https://esm.sh/handlebars,https://deno.land/std@0.158.0/encoding/yaml.ts packages/plugs/*/*.plug.yaml"
} }
} }

View File

@ -1,2 +1,3 @@
export * from "./dep_common.ts"; export * from "./dep_common.ts";
export { Database as SQLite } from "https://deno.land/x/sqlite3@0.6.1/mod.ts"; export { Database as SQLite } from "https://deno.land/x/sqlite3@0.6.1/mod.ts";
export { Application, Router } from "https://deno.land/x/oak@v11.1.0/mod.ts";

View File

@ -2,6 +2,9 @@
"imports": { "imports": {
"@codemirror/state": "https://esm.sh/@codemirror/state", "@codemirror/state": "https://esm.sh/@codemirror/state",
"@codemirror/language": "https://esm.sh/@codemirror/language", "@codemirror/language": "https://esm.sh/@codemirror/language",
"@lezer/lr": "https://esm.sh/@lezer/lr@1.2.3" "@lezer/lr": "https://esm.sh/@lezer/lr@1.2.3",
"yaml": "https://deno.land/std@0.158.0/encoding/yaml.ts",
"$sb/": "./packages/",
"handlebars": "https://esm.sh/handlebars"
} }
} }

View File

@ -40,7 +40,7 @@ async function bundle(
jsFunctionName, jsFunctionName,
debug, debug,
allModulesToExclude, allModulesToExclude,
true, false,
); );
delete def.path; delete def.path;
} }

View File

@ -2,6 +2,22 @@ import { safeRun } from "../util.ts";
import { ConsoleLogger } from "./custom_logger.ts"; import { ConsoleLogger } from "./custom_logger.ts";
import { ControllerMessage, WorkerMessage } from "./worker.ts"; import { ControllerMessage, WorkerMessage } from "./worker.ts";
if (typeof Deno === "undefined") {
// @ts-ignore: Deno hack
self.Deno = {
args: [],
// @ts-ignore: Deno hack
build: {
arch: "x86_64",
},
env: {
// @ts-ignore: Deno hack
get() {
},
},
};
}
let loadedFunctions = new Map<string, Function>(); let loadedFunctions = new Map<string, Function>();
let pendingRequests = new Map< let pendingRequests = new Map<
number, number,
@ -43,7 +59,7 @@ let loadedModules = new Map<string, any>();
// @ts-ignore // @ts-ignore
self.require = (moduleName: string): any => { self.require = (moduleName: string): any => {
console.log("Requiring", moduleName, loadedModules.get(moduleName)); // console.log("Requiring", moduleName, loadedModules.get(moduleName));
return loadedModules.get(moduleName); return loadedModules.get(moduleName);
}; };
@ -73,7 +89,7 @@ self.addEventListener("message", (event: { data: WorkerMessage }) => {
break; break;
case "load-dependency": case "load-dependency":
{ {
console.log("Received dep", data.name); // console.log("Received dep", data.name);
let fn3 = new Function(`return ${data.code!}`); let fn3 = new Function(`return ${data.code!}`);
let v = fn3(); let v = fn3();
loadedModules.set(data.name!, v); loadedModules.set(data.name!, v);
@ -119,7 +135,7 @@ self.addEventListener("message", (event: { data: WorkerMessage }) => {
"Current outstanding requests", "Current outstanding requests",
pendingRequests, pendingRequests,
"looking up", "looking up",
syscallId syscallId,
); );
throw Error("Invalid request id"); throw Error("Invalid request id");
} }

View File

@ -29,9 +29,13 @@ class WebWorkerWrapper implements WorkerLike {
} }
export function createSandbox(plug: Plug<any>) { export function createSandbox(plug: Plug<any>) {
// ParcelJS will build this file into a worker. const worker = new Worker(
let worker = new Worker(new URL("sandbox_worker.ts", import.meta.url), { import.meta.url
type: "module", ? new URL("sandbox_worker.ts", import.meta.url)
}); : new URL("worker.js", location.origin),
{
type: "module",
},
);
return new Sandbox(plug, new WebWorkerWrapper(worker)); return new Sandbox(plug, new WebWorkerWrapper(worker));
} }

View File

@ -3,8 +3,8 @@ import { Manifest } from "../types.ts";
import { EndpointHook, EndpointHookT } from "./endpoint.ts"; import { EndpointHook, EndpointHookT } from "./endpoint.ts";
import { System } from "../system.ts"; import { System } from "../system.ts";
import { Application } from "https://deno.land/x/oak/mod.ts"; import { Application } from "../../../dep_server.ts";
import { assertEquals } from "https://deno.land/std@0.123.0/testing/asserts.ts"; import { assertEquals } from "../../../test_dep.ts";
Deno.test("Run a plugos endpoint server", async () => { Deno.test("Run a plugos endpoint server", async () => {
let system = new System<EndpointHookT>("server"); let system = new System<EndpointHookT>("server");

View File

@ -1,6 +1,6 @@
import { Hook, Manifest } from "../types.ts"; import { Hook, Manifest } from "../types.ts";
import { System } from "../system.ts"; import { System } from "../system.ts";
import { Application } from "https://deno.land/x/oak@v10.2.1/application.ts"; import { Application } from "../../../dep_server.ts";
export type EndpointRequest = { export type EndpointRequest = {
method: string; method: string;
@ -103,7 +103,7 @@ export class EndpointHook implements Hook<EndpointHookT> {
} }
} }
} }
console.log("Shouldn't get here"); // console.log("Shouldn't get here");
next(); next();
}); });
} }

View File

@ -65,7 +65,7 @@ export class Sandbox {
} }
loadDependency(name: string, code: string): Promise<void> { loadDependency(name: string, code: string): Promise<void> {
console.log("Loading dependency", name); // console.log("Loading dependency", name);
this.worker.postMessage({ this.worker.postMessage({
type: "load-dependency", type: "load-dependency",
name: name, name: name,
@ -116,7 +116,7 @@ export class Sandbox {
if (data.error) { if (data.error) {
resultCbs && resultCbs &&
resultCbs.reject( resultCbs.reject(
new Error(`${data.error}\nStack trace: ${data.stack}`) new Error(`${data.error}\nStack trace: ${data.stack}`),
); );
} else { } else {
resultCbs && resultCbs.resolve(data.result); resultCbs && resultCbs.resolve(data.result);

View File

@ -1,4 +1,4 @@
import { sandboxCompile, sandboxCompileModule } from "../compile"; import { sandboxCompileModule } from "../compile.ts";
import { SysCallMapping } from "../system.ts"; import { SysCallMapping } from "../system.ts";
// TODO: FIgure out a better way to do this // TODO: FIgure out a better way to do this
@ -11,22 +11,22 @@ export function esbuildSyscalls(): SysCallMapping {
filename: string, filename: string,
code: string, code: string,
): Promise<any> => {}, ): Promise<any> => {},
"esbuild.compile": async ( // "esbuild.compile": async (
ctx, // ctx,
filename: string, // filename: string,
code: string, // code: string,
functionName?: string, // functionName?: string,
excludeModules: string[] = [], // excludeModules: string[] = [],
): Promise<string> => { // ): Promise<string> => {
return await sandboxCompile( // return await sandboxCompile(
filename, // filename,
code, // code,
functionName, // functionName,
true, // true,
[], // [],
[...builtinModules, ...excludeModules], // [...builtinModules, ...excludeModules],
); // );
}, // },
"esbuild.compileModule": async ( "esbuild.compileModule": async (
ctx, ctx,
moduleName: string, moduleName: string,

View File

@ -1,41 +1,56 @@
import { Knex } from "knex"; import { SQLite } from "../../../dep_server.ts";
import { SysCallMapping } from "../system"; import { SysCallMapping } from "../system.ts";
import { asyncExecute, asyncQuery } from "./store.deno.ts";
type Item = { type Item = {
key: string; key: string;
value: string; value: string;
}; };
export async function ensureFTSTable( export function ensureFTSTable(
db: Knex<any, unknown>, db: SQLite,
tableName: string tableName: string,
) { ) {
if (!(await db.schema.hasTable(tableName))) { const stmt = db.prepare(
await db.raw(`CREATE VIRTUAL TABLE ${tableName} USING fts5(key, value);`); `SELECT name FROM sqlite_master WHERE type='table' AND name=?`,
);
const result = stmt.all(tableName);
if (result.length === 0) {
asyncExecute(
db,
`CREATE VIRTUAL TABLE ${tableName} USING fts5(key, value);`,
);
console.log(`Created fts5 table ${tableName}`); console.log(`Created fts5 table ${tableName}`);
} }
return Promise.resolve();
} }
export function fullTextSearchSyscalls( export function fullTextSearchSyscalls(
db: Knex<any, unknown>, db: SQLite,
tableName: string tableName: string,
): SysCallMapping { ): SysCallMapping {
return { return {
"fulltext.index": async (ctx, key: string, value: string) => { "fulltext.index": async (ctx, key: string, value: string) => {
await db<Item>(tableName).where({ key }).del(); await asyncExecute(db, `DELETE FROM ${tableName} WHERE key = ?`, key);
await db<Item>(tableName).insert({ key, value }); await asyncExecute(
db,
`INSERT INTO ${tableName} (key, value) VALUES (?, ?)`,
key,
value,
);
}, },
"fulltext.delete": async (ctx, key: string) => { "fulltext.delete": async (ctx, key: string) => {
await db<Item>(tableName).where({ key }).del(); await asyncExecute(db, `DELETE FROM ${tableName} WHERE key = ?`, key);
}, },
"fulltext.search": async (ctx, phrase: string, limit: number) => { "fulltext.search": async (ctx, phrase: string, limit: number) => {
return ( return (
await db<any>(tableName) await asyncQuery<any>(
.whereRaw(`value MATCH ?`, [phrase]) db,
.select(["key", "rank"]) `SELECT key, rank FROM ${tableName} WHERE value MATCH ? ORDER BY key, rank LIMIT ?`,
.orderBy("rank") phrase,
.limit(limit) limit,
)
).map((item) => ({ name: item.key, rank: item.rank })); ).map((item) => ({ name: item.key, rank: item.rank }));
}, },
}; };

View File

@ -1,19 +1,22 @@
import { promisify } from "util"; import type { SysCallMapping } from "../system.ts";
import { execFile } from "child_process";
import type { SysCallMapping } from "../system";
const execFilePromise = promisify(execFile);
export default function (cwd: string): SysCallMapping { export default function (cwd: string): SysCallMapping {
return { return {
"shell.run": async ( "shell.run": async (
ctx, _ctx,
cmd: string, cmd: string,
args: string[] args: string[],
): Promise<{ stdout: string; stderr: string }> => { ): Promise<{ stdout: string; stderr: string }> => {
let { stdout, stderr } = await execFilePromise(cmd, args, { const p = Deno.run({
cmd: [cmd, ...args],
cwd: cwd, cwd: cwd,
stdout: "piped",
stderr: "piped",
}); });
await p.status();
const stdout = new TextDecoder().decode(await p.output());
const stderr = new TextDecoder().decode(await p.stderrOutput());
return { stdout, stderr }; return { stdout, stderr };
}, },
}; };

View File

@ -70,7 +70,7 @@ export function queryToSql(
}; };
} }
function asyncQuery<T extends Record<string, unknown>>( export function asyncQuery<T extends Record<string, unknown>>(
db: SQLite, db: SQLite,
query: string, query: string,
...params: any[] ...params: any[]
@ -79,7 +79,7 @@ function asyncQuery<T extends Record<string, unknown>>(
return Promise.resolve(db.prepare(query).all<T>(params)); return Promise.resolve(db.prepare(query).all<T>(params));
} }
function asyncExecute( export function asyncExecute(
db: SQLite, db: SQLite,
query: string, query: string,
...params: any[] ...params: any[]

View File

@ -136,15 +136,15 @@ functions:
- page:complete - page:complete
# Full text search # Full text search
searchIndex: # searchIndex:
path: ./search.ts:index # path: ./search.ts:index
events: # events:
- page:index # - page:index
searchUnindex: # searchUnindex:
path: "./search.ts:unindex" # path: "./search.ts:unindex"
env: server # env: server
events: # events:
- page:deleted # - page:deleted
searchQueryProvider: searchQueryProvider:
path: ./search.ts:queryProvider path: ./search.ts:queryProvider
events: events:

View File

@ -1,6 +1,5 @@
// @ts-ignore import emojis from "./emoji.json" assert { type: "json" };
import emojis from "./emoji.json"; import { matchBefore } from "../../plugos-silverbullet-syscall/editor.ts";
import { matchBefore } from "@silverbulletmd/plugos-silverbullet-syscall/editor";
export async function emojiCompleter() { export async function emojiCompleter() {
let prefix = await matchBefore(":[\\w]+"); let prefix = await matchBefore(":[\\w]+");

View File

@ -1,8 +1,7 @@
import { hideRhs, hideLhs } from "@silverbulletmd/plugos-silverbullet-syscall/editor"; import { hideLhs, hideRhs } from "../../plugos-silverbullet-syscall/editor.ts";
import { invokeFunction } from "@silverbulletmd/plugos-silverbullet-syscall/system"; import { invokeFunction } from "../../plugos-silverbullet-syscall/system.ts";
import * as clientStore from "@silverbulletmd/plugos-silverbullet-syscall/clientStore"; import * as clientStore from "../../plugos-silverbullet-syscall/clientStore.ts";
import { readSettings, writeSettings } from "@silverbulletmd/plugs/lib/settings_page";; import { readSettings } from "../lib/settings_page.ts";
export async function togglePreview() { export async function togglePreview() {
let currentValue = !!(await clientStore.get("enableMarkdownPreview")); let currentValue = !!(await clientStore.get("enableMarkdownPreview"));
@ -15,7 +14,7 @@ export async function togglePreview() {
} }
async function hideMarkdownPreview() { async function hideMarkdownPreview() {
const setting = await readSettings({previewOnRHS: true}); const setting = await readSettings({ previewOnRHS: true });
const hide = setting.previewOnRHS ? hideRhs : hideLhs; const hide = setting.previewOnRHS ? hideRhs : hideLhs;
await hide(); await hide();
} }

View File

@ -1,14 +1,10 @@
import MarkdownIt from "markdown-it"; import MarkdownIt from "https://esm.sh/markdown-it@13.0.1";
import { import {
getText, getText,
showPanel, showPanel,
} from "@silverbulletmd/plugos-silverbullet-syscall/editor"; } from "../../plugos-silverbullet-syscall/editor.ts";
import * as clientStore from "@silverbulletmd/plugos-silverbullet-syscall/clientStore"; import * as clientStore from "../../plugos-silverbullet-syscall/clientStore.ts";
import { cleanMarkdown } from "./util"; import { cleanMarkdown } from "./util.ts";
import {
readSettings,
writeSettings,
} from "@silverbulletmd/plugs/lib/settings_page";
const css = ` const css = `
<style> <style>
@ -66,7 +62,7 @@ hr:after {
</style> </style>
`; `;
var taskLists = require("markdown-it-task-lists"); import taskLists from "https://esm.sh/markdown-it-task-lists@2.1.1";
const md = new MarkdownIt({ const md = new MarkdownIt({
linkify: true, linkify: true,
@ -83,6 +79,6 @@ export async function updateMarkdownPreview() {
await showPanel( await showPanel(
"rhs", "rhs",
2, 2,
`<html><head>${css}</head><body>${md.render(cleanMd)}</body></html>` `<html><head>${css}</head><body>${md.render(cleanMd)}</body></html>`,
); );
} }

View File

@ -2,8 +2,8 @@ import {
findNodeOfType, findNodeOfType,
renderToText, renderToText,
replaceNodesMatching, replaceNodesMatching,
} from "@silverbulletmd/common/tree"; } from "../../common/tree.ts";
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown"; import { parseMarkdown } from "../../plugos-silverbullet-syscall/markdown.ts";
export function encodePageUrl(name: string): string { export function encodePageUrl(name: string): string {
return name.replaceAll(" ", "_"); return name.replaceAll(" ", "_");
@ -11,7 +11,7 @@ export function encodePageUrl(name: string): string {
export async function cleanMarkdown( export async function cleanMarkdown(
text: string, text: string,
validPages?: string[] validPages?: string[],
): Promise<string> { ): Promise<string> {
let mdTree = await parseMarkdown(text); let mdTree = await parseMarkdown(text);
replaceNodesMatching(mdTree, (n) => { replaceNodesMatching(mdTree, (n) => {

View File

@ -1,47 +0,0 @@
{
"name": "@silverbulletmd/plugs",
"author": {
"name": "Zef Hemel",
"email": "zef@zef.me"
},
"version": "0.0.35",
"license": "MIT",
"scripts": {
"generate": "lezer-generator query/query.grammar -o query/parse-query.js",
"watch": "plugos-bundle --dist ../web/dist global.plug.yaml && plugos-bundle --debug -w --dist dist --exclude @lezer/lr yaml handlebars -- */*.plug.yaml",
"build": "plugos-bundle --dist ../web/dist global.plug.yaml && plugos-bundle --dist dist --exclude @lezer/lr yaml handlebars -- */*.plug.yaml",
"test": "jest build/test"
},
"files": [
"*"
],
"targets": {
"test": {
"source": [
"query/engine.test.ts"
],
"outputFormat": "commonjs",
"isLibrary": true,
"context": "node",
"includeNodeModules": [
"@silverbulletmd/common",
"@silverbulletmd/plugos-silverbullet-syscall"
],
"distDir": "build/test"
}
},
"dependencies": {
"@jest/globals": "^27.5.1",
"@lezer/generator": "1.0.0",
"@lezer/lr": "1.0.0",
"@silverbulletmd/common": "^0.0.35",
"@types/yaml": "^1.9.7",
"handlebars": "^4.7.7",
"markdown-it": "^12.3.2",
"markdown-it-task-lists": "^2.1.1",
"yaml": "^1.10.2"
},
"devDependencies": {
"@types/markdown-it": "^12.2.3"
}
}

View File

@ -1,24 +1,21 @@
import { import { collectNodesOfType, findNodeOfType } from "../../common/tree.ts";
collectNodesOfType,
findNodeOfType,
} from "@silverbulletmd/common/tree";
import { import {
getText, getText,
hideBhs, hideBhs,
showBhs, showBhs,
} from "@silverbulletmd/plugos-silverbullet-syscall/editor"; } from "../../plugos-silverbullet-syscall/editor.ts";
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown"; import { parseMarkdown } from "../../plugos-silverbullet-syscall/markdown.ts";
import { import {
readPage, readPage,
writePage, writePage,
} from "@silverbulletmd/plugos-silverbullet-syscall/space"; } from "../../plugos-silverbullet-syscall/space.ts";
import { import {
invokeFunction, invokeFunction,
reloadPlugs, reloadPlugs,
} from "@silverbulletmd/plugos-silverbullet-syscall/system"; } from "../../plugos-silverbullet-syscall/system.ts";
import YAML from "yaml"; import * as YAML from "yaml";
import type { Manifest } from "@silverbulletmd/common/manifest"; import type { Manifest } from "../../common/manifest.ts";
export async function compileCommand() { export async function compileCommand() {
let text = await getText(); let text = await getText();
@ -26,7 +23,7 @@ export async function compileCommand() {
let manifest = await compileDefinition(text); let manifest = await compileDefinition(text);
await writePage( await writePage(
`_plug/${manifest.name}`, `_plug/${manifest.name}`,
JSON.stringify(manifest, null, 2) JSON.stringify(manifest, null, 2),
); );
console.log("Wrote this plug", manifest); console.log("Wrote this plug", manifest);
await hideBhs(); await hideBhs();
@ -94,7 +91,7 @@ async function compileDefinition(text: string): Promise<Manifest> {
`file.${language}`, `file.${language}`,
code, code,
name, name,
Object.keys(manifest.dependencies) Object.keys(manifest.dependencies),
); );
func.code = compiled; func.code = compiled;
} }
@ -108,7 +105,7 @@ export async function compileJS(
filename: string, filename: string,
code: string, code: string,
functionName: string, functionName: string,
excludeModules: string[] excludeModules: string[],
): Promise<string> { ): Promise<string> {
// console.log("Compiling JS", filename, excludeModules); // console.log("Compiling JS", filename, excludeModules);
return self.syscall( return self.syscall(
@ -116,7 +113,7 @@ export async function compileJS(
filename, filename,
code, code,
functionName, functionName,
excludeModules excludeModules,
); );
} }

View File

@ -1,6 +1,6 @@
import { listEvents } from "@plugos/plugos-syscall/event"; import { listEvents } from "$sb/plugos-syscall/event.ts";
import { matchBefore } from "@silverbulletmd/plugos-silverbullet-syscall/editor"; import { matchBefore } from "$sb/plugos-silverbullet-syscall/editor.ts";
import { listPages } from "@silverbulletmd/plugos-silverbullet-syscall/space"; import { listPages } from "$sb/plugos-silverbullet-syscall/space.ts";
export async function queryComplete() { export async function queryComplete() {
let prefix = await matchBefore("#query [\\w\\-_]*"); let prefix = await matchBefore("#query [\\w\\-_]*");

View File

@ -2,23 +2,21 @@ import {
getCurrentPage, getCurrentPage,
reloadPage, reloadPage,
save, save,
} from "@silverbulletmd/plugos-silverbullet-syscall/editor"; } from "$sb/plugos-silverbullet-syscall/editor.ts";
import Handlebars from "https://esm.sh/handlebars";
import { import Handlebars from "handlebars";
readPage,
writePage, import { readPage, writePage } from "$sb/plugos-silverbullet-syscall/space.ts";
} from "@silverbulletmd/plugos-silverbullet-syscall/space"; import { invokeFunction } from "$sb/plugos-silverbullet-syscall/system.ts";
import { invokeFunction } from "@silverbulletmd/plugos-silverbullet-syscall/system"; import { renderQuery } from "./engine.ts";
import { renderQuery } from "./engine"; import { parseQuery } from "./parser.ts";
import { parseQuery } from "./parser"; import { replaceTemplateVars } from "../core/template.ts";
import { replaceTemplateVars } from "../core/template"; import { jsonToMDTable, queryRegex } from "./util.ts";
import { jsonToMDTable, queryRegex } from "./util"; import { dispatch } from "$sb/plugos-syscall/event.ts";
import { dispatch } from "@plugos/plugos-syscall/event"; import { replaceAsync } from "../lib/util.ts";
import { replaceAsync } from "../lib/util"; import { parseMarkdown } from "$sb/plugos-silverbullet-syscall/markdown.ts";
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown"; import { nodeAtPos, renderToText } from "$sb/common/tree.ts";
import { nodeAtPos, renderToText } from "@silverbulletmd/common/tree"; import { extractMeta } from "./data.ts";
import { extractMeta } from "./data";
export async function updateMaterializedQueriesCommand() { export async function updateMaterializedQueriesCommand() {
const currentPage = await getCurrentPage(); const currentPage = await getCurrentPage();
@ -27,7 +25,7 @@ export async function updateMaterializedQueriesCommand() {
await invokeFunction( await invokeFunction(
"server", "server",
"updateMaterializedQueriesOnPage", "updateMaterializedQueriesOnPage",
currentPage currentPage,
) )
) { ) {
await reloadPage(); await reloadPage();
@ -39,7 +37,7 @@ export const templateInstRegex =
async function updateTemplateInstantiations( async function updateTemplateInstantiations(
text: string, text: string,
pageName: string pageName: string,
): Promise<string> { ): Promise<string> {
return replaceAsync( return replaceAsync(
text, text,
@ -74,12 +72,12 @@ async function updateTemplateInstantiations(
templateText = renderToText(tree); templateText = renderToText(tree);
let templateFn = Handlebars.compile( let templateFn = Handlebars.compile(
replaceTemplateVars(templateText, pageName), replaceTemplateVars(templateText, pageName),
{ noEscape: true } { noEscape: true },
); );
newBody = templateFn(parsedArgs); newBody = templateFn(parsedArgs);
} }
return `${startInst}\n${newBody.trim()}\n${endInst}`; return `${startInst}\n${newBody.trim()}\n${endInst}`;
} },
); );
} }
@ -95,20 +93,20 @@ async function cleanTemplateInstantiations(text: string): Promise<string> {
fullMatch: string, fullMatch: string,
startQuery: string, startQuery: string,
query: string, query: string,
body: string body: string,
) => { ) => {
return body.trim(); return body.trim();
} },
); );
} }
return `${startInst}${body}${endInst}`; return `${startInst}${body}${endInst}`;
} },
); );
} }
// Called from client, running on server // Called from client, running on server
export async function updateMaterializedQueriesOnPage( export async function updateMaterializedQueriesOnPage(
pageName: string pageName: string,
): Promise<boolean> { ): Promise<boolean> {
let text = ""; let text = "";
try { try {
@ -117,7 +115,7 @@ export async function updateMaterializedQueriesOnPage(
console.warn( console.warn(
"Could not read page", "Could not read page",
pageName, pageName,
"perhaps it doesn't yet exist" "perhaps it doesn't yet exist",
); );
return false; return false;
} }
@ -147,7 +145,7 @@ export async function updateMaterializedQueriesOnPage(
let results = await dispatch( let results = await dispatch(
`query:${parsedQuery.table}`, `query:${parsedQuery.table}`,
{ query: parsedQuery, pageName: pageName }, { query: parsedQuery, pageName: pageName },
10 * 1000 10 * 1000,
); );
if (results.length === 0) { if (results.length === 0) {
return `${startQuery}\n${endQuery}`; return `${startQuery}\n${endQuery}`;
@ -162,7 +160,7 @@ export async function updateMaterializedQueriesOnPage(
console.error("Too many query results", results); console.error("Too many query results", results);
return fullMatch; return fullMatch;
} }
} },
); );
newText = await cleanTemplateInstantiations(newText); newText = await cleanTemplateInstantiations(newText);
if (text !== newText) { if (text !== newText) {

View File

@ -1,20 +1,17 @@
import type { ClickEvent, IndexTreeEvent } from "@silverbulletmd/web/app_event"; import type { ClickEvent, IndexTreeEvent } from "$sb/web/app_event.ts";
import { import {
batchSet, batchSet,
queryPrefix, queryPrefix,
} from "@silverbulletmd/plugos-silverbullet-syscall/index"; } from "$sb/plugos-silverbullet-syscall/index.ts";
import { import { readPage, writePage } from "$sb/plugos-silverbullet-syscall/space.ts";
readPage, import { parseMarkdown } from "$sb/plugos-silverbullet-syscall/markdown.ts";
writePage,
} from "@silverbulletmd/plugos-silverbullet-syscall/space";
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown";
import { import {
dispatch, dispatch,
filterBox, filterBox,
getCursor, getCursor,
getText, getText,
} from "@silverbulletmd/plugos-silverbullet-syscall/editor"; } from "$sb/plugos-silverbullet-syscall/editor.ts";
import { import {
addParentPointers, addParentPointers,
collectNodesMatching, collectNodesMatching,
@ -24,10 +21,10 @@ import {
ParseTree, ParseTree,
renderToText, renderToText,
replaceNodesMatching, replaceNodesMatching,
} from "@silverbulletmd/common/tree"; } from "$sb/common/tree.ts";
import { removeQueries } from "../query/util"; import { removeQueries } from "../query/util.ts";
import { applyQuery, QueryProviderEvent } from "../query/engine"; import { applyQuery, QueryProviderEvent } from "../query/engine.ts";
import { niceDate } from "../core/dates"; import { niceDate } from "../core/dates.ts";
export type Task = { export type Task = {
name: string; name: string;
@ -111,7 +108,7 @@ async function toggleTaskMarker(node: ParseTree, moveToPos: number) {
let parentWikiLinks = collectNodesMatching( let parentWikiLinks = collectNodesMatching(
node.parent!, node.parent!,
(n) => n.type === "WikiLinkPage" (n) => n.type === "WikiLinkPage",
); );
for (let wikiLink of parentWikiLinks) { for (let wikiLink of parentWikiLinks) {
let ref = wikiLink.children![0].text!; let ref = wikiLink.children![0].text!;
@ -126,7 +123,7 @@ async function toggleTaskMarker(node: ParseTree, moveToPos: number) {
if (!taskMarkerNode || taskMarkerNode.type !== "TaskMarker") { if (!taskMarkerNode || taskMarkerNode.type !== "TaskMarker") {
console.error( console.error(
"Reference not a task marker, out of date?", "Reference not a task marker, out of date?",
taskMarkerNode taskMarkerNode,
); );
return; return;
} }
@ -177,7 +174,7 @@ export async function postponeCommand() {
{ name: "a week", orderId: 2 }, { name: "a week", orderId: 2 },
{ name: "following Monday", orderId: 3 }, { name: "following Monday", orderId: 3 },
], ],
"Select the desired time span to delay this task" "Select the desired time span to delay this task",
); );
if (!option) { if (!option) {
return; return;

View File

@ -1,60 +1,45 @@
import express, { Express } from "express"; import { Manifest, SilverBulletHooks } from "../common/manifest.ts";
import { Manifest, SilverBulletHooks } from "@silverbulletmd/common/manifest"; import { EndpointHook } from "../plugos/hooks/endpoint.ts";
import { EndpointHook } from "@plugos/plugos/hooks/endpoint"; import { System } from "../plugos/system.ts";
import { readdir, readFile, rm } from "fs/promises"; import { DiskSpacePrimitives } from "../common/spaces/disk_space_primitives.ts";
import { System } from "@plugos/plugos/system"; import { path, SQLite } from "../../dep_server.ts";
import { DiskSpacePrimitives } from "@silverbulletmd/common/spaces/disk_space_primitives"; import { EventHook } from "../plugos/hooks/event.ts";
import path from "path"; import spaceSyscalls from "./syscalls/space.ts";
import bodyParser from "body-parser"; import { eventSyscalls } from "../plugos/syscalls/event.ts";
import { EventHook } from "@plugos/plugos/hooks/event";
import spaceSyscalls from "./syscalls/space";
import { eventSyscalls } from "@plugos/plugos/syscalls/event";
import { ensureTable as ensureIndexTable, pageIndexSyscalls } from "./syscalls";
import knex, { Knex } from "knex";
import shellSyscalls from "@plugos/plugos/syscalls/shell.node";
import { NodeCronHook } from "@plugos/plugos/hooks/node_cron";
import { markdownSyscalls } from "@silverbulletmd/common/syscalls/markdown";
import { EventedSpacePrimitives } from "@silverbulletmd/common/spaces/evented_space_primitives";
import { Space } from "@silverbulletmd/common/spaces/space";
import { import {
createSandbox, ensureTable as ensureIndexTable,
nodeModulesDir, pageIndexSyscalls,
} from "@plugos/plugos/environments/node_sandbox"; } from "./syscalls/index.ts";
import { jwtSyscalls } from "@plugos/plugos/syscalls/jwt"; import shellSyscalls from "../plugos/syscalls/shell.node.ts";
import buildMarkdown from "@silverbulletmd/common/parser"; import { NodeCronHook } from "../plugos/hooks/node_cron.ts";
import { loadMarkdownExtensions } from "@silverbulletmd/common/markdown_ext"; import { markdownSyscalls } from "../common/syscalls/markdown.ts";
import http, { Server } from "http"; import { EventedSpacePrimitives } from "../common/spaces/evented_space_primitives.ts";
import { esbuildSyscalls } from "@plugos/plugos/syscalls/esbuild"; import { Space } from "../common/spaces/space.ts";
import { systemSyscalls } from "./syscalls/system"; import { createSandbox } from "../plugos/environments/deno_sandbox.ts";
import { plugPrefix } from "@silverbulletmd/common/spaces/constants"; // import { jwtSyscalls } from "../plugos/syscalls/jwt.ts";
import buildMarkdown from "../common/parser.ts";
import { loadMarkdownExtensions } from "../common/markdown_ext.ts";
import { esbuildSyscalls } from "../plugos/syscalls/esbuild.ts";
import { systemSyscalls } from "./syscalls/system.ts";
import { plugPrefix } from "../common/spaces/constants.ts";
import sandboxSyscalls from "@plugos/plugos/syscalls/sandbox"; import sandboxSyscalls from "../plugos/syscalls/sandbox.ts";
// @ts-ignore // import settingsTemplate from "bundle-text:./SETTINGS_template.md";
import settingsTemplate from "bundle-text:./SETTINGS_template.md"; import { safeRun } from "./util.ts";
import { safeRun } from "./util";
import { import {
ensureFTSTable, ensureFTSTable,
fullTextSearchSyscalls, fullTextSearchSyscalls,
} from "@plugos/plugos/syscalls/fulltext.knex_sqlite"; } from "../plugos/syscalls/fulltext.knex_sqlite.ts";
import { PlugSpacePrimitives } from "./hooks/plug_space_primitives"; import { PlugSpacePrimitives } from "./hooks/plug_space_primitives.ts";
import { PageNamespaceHook } from "./hooks/page_namespace"; import { PageNamespaceHook } from "./hooks/page_namespace.ts";
import { readFileSync } from "fs"; import fileSystemSyscalls from "../plugos/syscalls/fs.deno.ts";
import fileSystemSyscalls from "@plugos/plugos/syscalls/fs.node";
import { import {
ensureTable as ensureStoreTable, ensureTable as ensureStoreTable,
storeSyscalls, storeSyscalls,
} from "@plugos/plugos/syscalls/store.knex_node"; } from "../plugos/syscalls/store.deno.ts";
import { parseYamlSettings } from "@silverbulletmd/common/util"; import { parseYamlSettings } from "../common/util.ts";
import { SpacePrimitives } from "@silverbulletmd/common/spaces/space_primitives"; import { SpacePrimitives } from "../common/spaces/space_primitives.ts";
import { Application, Router } from "../../dep_server.ts";
import { version } from "./package.json";
const globalModules: any = JSON.parse(
readFileSync(
nodeModulesDir + "/node_modules/@silverbulletmd/web/dist/global.plug.json",
"utf-8"
)
);
const safeFilename = /^[a-zA-Z0-9_\-\.]+$/; const safeFilename = /^[a-zA-Z0-9_\-\.]+$/;
@ -70,26 +55,31 @@ const storeVersionKey = "$silverBulletVersion";
const indexRequiredKey = "$spaceIndexed"; const indexRequiredKey = "$spaceIndexed";
export class ExpressServer { export class ExpressServer {
app: Express; app: Application;
system: System<SilverBulletHooks>; system: System<SilverBulletHooks>;
private space: Space; private space: Space;
private distDir: string; private distDir: string;
private eventHook: EventHook; private eventHook: EventHook;
private db: Knex<any, unknown[]>; private db: SQLite;
private port: number; private port: number;
private server?: Server;
builtinPlugDir: string; builtinPlugDir: string;
password?: string; password?: string;
settings: { [key: string]: any } = {}; settings: { [key: string]: any } = {};
spacePrimitives: SpacePrimitives; spacePrimitives: SpacePrimitives;
abortController?: AbortController;
globalModules: Manifest;
constructor(options: ServerOptions) { constructor(options: ServerOptions) {
this.port = options.port; this.port = options.port;
this.app = express(); this.app = new Application();
this.builtinPlugDir = options.builtinPlugDir; this.builtinPlugDir = options.builtinPlugDir;
this.distDir = options.distDir; this.distDir = options.distDir;
this.password = options.password; this.password = options.password;
this.globalModules = JSON.parse(
Deno.readTextFileSync(`${this.distDir}/global.plug.json`),
);
// Set up the PlugOS System // Set up the PlugOS System
this.system = new System<SilverBulletHooks>("server"); this.system = new System<SilverBulletHooks>("server");
@ -105,20 +95,14 @@ export class ExpressServer {
this.spacePrimitives = new EventedSpacePrimitives( this.spacePrimitives = new EventedSpacePrimitives(
new PlugSpacePrimitives( new PlugSpacePrimitives(
new DiskSpacePrimitives(options.pagesPath), new DiskSpacePrimitives(options.pagesPath),
namespaceHook namespaceHook,
), ),
this.eventHook this.eventHook,
); );
this.space = new Space(this.spacePrimitives); this.space = new Space(this.spacePrimitives);
// The database used for persistence (SQLite) // The database used for persistence (SQLite)
this.db = knex({ this.db = new SQLite(path.join(options.pagesPath, "data.db"));
client: "better-sqlite3",
connection: {
filename: path.join(options.pagesPath, "data.db"),
},
useNullAsDefault: true,
});
// The cron hook // The cron hook
this.system.addHook(new NodeCronHook()); this.system.addHook(new NodeCronHook());
@ -135,7 +119,7 @@ export class ExpressServer {
esbuildSyscalls(), esbuildSyscalls(),
systemSyscalls(this), systemSyscalls(this),
sandboxSyscalls(this.system), sandboxSyscalls(this.system),
jwtSyscalls() // jwtSyscalls(),
); );
// Danger zone // Danger zone
this.system.registerSyscalls(["shell"], shellSyscalls(options.pagesPath)); this.system.registerSyscalls(["shell"], shellSyscalls(options.pagesPath));
@ -148,9 +132,11 @@ export class ExpressServer {
plugLoaded: (plug) => { plugLoaded: (plug) => {
// Automatically inject some modules into each plug // Automatically inject some modules into each plug
safeRun(async () => { safeRun(async () => {
for (let [modName, code] of Object.entries( for (
globalModules.dependencies let [modName, code] of Object.entries(
)) { this.globalModules.dependencies!,
)
) {
await plug.sandbox.loadDependency(modName, code as string); await plug.sandbox.loadDependency(modName, code as string);
} }
}); });
@ -166,15 +152,14 @@ export class ExpressServer {
throw new Error(`Invalid plug name: ${plugName}`); throw new Error(`Invalid plug name: ${plugName}`);
} }
try { try {
let manifestJson = await readFile( let manifestJson = await Deno.readTextFile(
path.join(this.builtinPlugDir, `${plugName}.plug.json`), path.join(this.builtinPlugDir, `${plugName}.plug.json`),
"utf8"
); );
return JSON.parse(manifestJson); return JSON.parse(manifestJson);
} catch (e) { } catch {
throw new Error(`No such builtin: ${plugName}`); throw new Error(`No such builtin: ${plugName}`);
} }
} },
); );
// Second, for loading plug JSON files with absolute or relative (from CWD) paths // Second, for loading plug JSON files with absolute or relative (from CWD) paths
@ -182,20 +167,20 @@ export class ExpressServer {
"get-plug:file", "get-plug:file",
async (plugPath: string): Promise<Manifest> => { async (plugPath: string): Promise<Manifest> => {
let resolvedPath = path.resolve(plugPath); let resolvedPath = path.resolve(plugPath);
if (!resolvedPath.startsWith(process.cwd())) { if (!resolvedPath.startsWith(Deno.cwd())) {
throw new Error( throw new Error(
`Plugin path outside working directory, this is disallowed: ${resolvedPath}` `Plugin path outside working directory, this is disallowed: ${resolvedPath}`,
); );
} }
try { try {
let manifestJson = await readFile(resolvedPath, "utf8"); let manifestJson = await Deno.readTextFile(resolvedPath);
return JSON.parse(manifestJson); return JSON.parse(manifestJson);
} catch (e) { } catch (e) {
throw new Error( throw new Error(
`No such file: ${resolvedPath} or could not parse as JSON` `No such file: ${resolvedPath} or could not parse as JSON`,
); );
} }
} },
); );
// Rescan disk every 5s to detect any out-of-process file changes // Rescan disk every 5s to detect any out-of-process file changes
@ -207,26 +192,25 @@ export class ExpressServer {
rebuildMdExtensions() { rebuildMdExtensions() {
this.system.registerSyscalls( this.system.registerSyscalls(
[], [],
markdownSyscalls(buildMarkdown(loadMarkdownExtensions(this.system))) markdownSyscalls(buildMarkdown(loadMarkdownExtensions(this.system))),
); );
} }
// In case of a new space with no `PLUGS` file, generate a default one based on all built-in plugs // In case of a new space with no `PLUGS` file, generate a default one based on all built-in plugs
private async bootstrapBuiltinPlugs() { private async bootstrapBuiltinPlugs() {
let allPlugFiles = await readdir(this.builtinPlugDir); let allPlugFiles = await Deno.readDir(this.builtinPlugDir);
let pluginNames = []; let pluginNames = [];
for (let file of allPlugFiles) { for await (let file of allPlugFiles) {
if (file.endsWith(".plug.json")) { if (file.name.endsWith(".plug.json")) {
let manifestJson = await readFile( let manifestJson = await Deno.readTextFile(
path.join(this.builtinPlugDir, file), path.join(this.builtinPlugDir, file.name),
"utf8"
); );
let manifest: Manifest = JSON.parse(manifestJson); let manifest: Manifest = JSON.parse(manifestJson);
pluginNames.push(manifest.name); pluginNames.push(manifest.name);
await this.spacePrimitives.writeFile( await this.spacePrimitives.writeFile(
`${plugPrefix}${file}`, `${plugPrefix}${file.name}`,
"string", "string",
manifestJson manifestJson,
); );
} }
} }
@ -240,7 +224,7 @@ export class ExpressServer {
"PLUGS", "PLUGS",
"This file lists all plugs that SilverBullet will load. Run the `Plugs: Update` command to update and reload this list of plugs.\n\n```yaml\n- " + "This file lists all plugs that SilverBullet will load. Run the `Plugs: Update` command to update and reload this list of plugs.\n\n```yaml\n- " +
pluginNames.map((name) => `builtin:${name}`).join("\n- ") + pluginNames.map((name) => `builtin:${name}`).join("\n- ") +
"\n```" "\n```",
); );
} }
} }
@ -250,31 +234,31 @@ export class ExpressServer {
let lastRunningVersion = await this.system.localSyscall( let lastRunningVersion = await this.system.localSyscall(
"core", "core",
"store.get", "store.get",
[storeVersionKey] [storeVersionKey],
); );
let upgrading = false; let upgrading = false;
if (lastRunningVersion !== version) { // if (lastRunningVersion !== version) {
upgrading = true; // upgrading = true;
console.log("Version change detected!"); // console.log("Version change detected!");
console.log("Going to re-bootstrap with the builtin set of plugs..."); // console.log("Going to re-bootstrap with the builtin set of plugs...");
console.log("First removing existing plug files..."); // console.log("First removing existing plug files...");
const existingPlugFiles = ( // const existingPlugFiles = (
await this.spacePrimitives.fetchFileList() // await this.spacePrimitives.fetchFileList()
).filter((meta) => meta.name.startsWith(plugPrefix)); // ).filter((meta) => meta.name.startsWith(plugPrefix));
for (let plugFile of existingPlugFiles) { // for (let plugFile of existingPlugFiles) {
await this.spacePrimitives.deleteFile(plugFile.name); // await this.spacePrimitives.deleteFile(plugFile.name);
} // }
console.log("Now writing the default set of plugs..."); // console.log("Now writing the default set of plugs...");
await this.bootstrapBuiltinPlugs(); // await this.bootstrapBuiltinPlugs();
await this.system.localSyscall("core", "store.set", [ // await this.system.localSyscall("core", "store.set", [
storeVersionKey, // storeVersionKey,
version, // version,
]); // ]);
await this.system.localSyscall("core", "store.set", [ // await this.system.localSyscall("core", "store.set", [
"$spaceIndexed", // "$spaceIndexed",
false, // false,
]); // ]);
} // }
await this.space.updatePageList(); await this.space.updatePageList();
@ -322,120 +306,142 @@ export class ExpressServer {
async start() { async start() {
const passwordMiddleware: (req: any, res: any, next: any) => void = this const passwordMiddleware: (req: any, res: any, next: any) => void = this
.password .password
? (req, res, next) => { ? (req, res, next) => {
if (req.headers.authorization === `Bearer ${this.password}`) { if (req.headers.authorization === `Bearer ${this.password}`) {
next();
} else {
res.status(401).send("Unauthorized");
}
}
: (req, res, next) => {
next(); next();
}; } else {
res.status(401).send("Unauthorized");
}
}
: (req, res, next) => {
next();
};
await ensureIndexTable(this.db); await ensureIndexTable(this.db);
await ensureStoreTable(this.db, "store"); await ensureStoreTable(this.db, "store");
await ensureFTSTable(this.db, "fts"); // await ensureFTSTable(this.db, "fts");
await this.ensureAndLoadSettings(); await this.ensureAndLoadSettings();
// Load plugs // Load plugs
this.reloadPlugs().catch(console.error); this.reloadPlugs().catch(console.error);
// Serve static files (javascript, css, html) // Serve static files (javascript, css, html)
this.app.use("/", express.static(this.distDir)); this.app.use(async (ctx, next) => {
if (ctx.request.url.pathname === "/") {
return ctx.send({
root: "/",
path: `${this.distDir}/index.html`,
});
}
const root = this.distDir;
try {
await ctx.send({ root });
} catch {
await next();
}
});
// Pages API // Pages API
this.app.use( const fsRouter = buildFsRouter(this.spacePrimitives);
"/fs", this.app.use(fsRouter.routes());
passwordMiddleware, this.app.use(fsRouter.allowedMethods());
buildFsRouter(this.spacePrimitives)
);
// Plug API // Plug API
this.app.use("/plug", passwordMiddleware, this.buildPlugRouter()); const plugRouter = this.buildPlugRouter();
this.app.use(plugRouter.routes());
this.app.use(plugRouter.allowedMethods());
// Fallback, serve index.html // Fallback, serve index.html
this.app.get(/^(\/((?!fs\/).)+)$/, async (req, res) => { this.app.use((ctx) => {
res.sendFile(`${this.distDir}/index.html`, {}); console.log("Here!!");
return ctx.send({
root: "/",
path: `${this.distDir}/index.html`,
});
}); });
this.server = http.createServer(this.app); this.abortController = new AbortController();
this.server.listen(this.port, () => { this.app.listen({ port: this.port, signal: this.abortController.signal })
console.log( .catch(console.error);
`Silver Bullet is now running: http://localhost:${this.port}` console.log(
); `Silver Bullet is now running: http://localhost:${this.port}`,
console.log("--------------"); );
}); console.log("--------------");
} }
private buildPlugRouter() { private buildPlugRouter(): Router {
let plugRouter = express.Router(); let plugRouter = new Router();
plugRouter.post( plugRouter.post(
"/:plug/syscall/:name", "/:plug/syscall/:name",
bodyParser.json(), async (ctx) => {
async (req, res) => { const name = ctx.params.name;
const name = req.params.name; const plugName = ctx.params.plug;
const plugName = req.params.plug; const args = await ctx.request.body().value;
const args = req.body as any;
const plug = this.system.loadedPlugs.get(plugName); const plug = this.system.loadedPlugs.get(plugName);
if (!plug) { if (!plug) {
res.status(404); ctx.response.status = 404;
return res.send(`Plug ${plugName} not found`); ctx.response.body = `Plug ${plugName} not found`;
return;
} }
try { try {
const result = await this.system.syscallWithContext( const result = await this.system.syscallWithContext(
{ plug }, { plug },
name, name,
args args,
); );
res.status(200); ctx.response.headers.set("Content-Type", "application/json");
res.header("Content-Type", "application/json"); ctx.response.body = JSON.stringify(result);
res.send(JSON.stringify(result));
} catch (e: any) { } catch (e: any) {
res.status(500); ctx.response.status = 500;
return res.send(e.message); ctx.response.body = e.message;
return;
} }
} },
); );
plugRouter.post( plugRouter.post(
"/:plug/function/:name", "/:plug/function/:name",
bodyParser.json(), async (ctx) => {
async (req, res) => { const name = ctx.params.name;
const name = req.params.name; const plugName = ctx.params.plug;
const plugName = req.params.plug; const args = await ctx.request.body().value;
const args = req.body as any[];
const plug = this.system.loadedPlugs.get(plugName); const plug = this.system.loadedPlugs.get(plugName);
if (!plug) { if (!plug) {
res.status(404); ctx.response.status = 404;
return res.send(`Plug ${plugName} not found`); ctx.response.body = `Plug ${plugName} not found`;
return;
} }
try { try {
const result = await plug.invoke(name, args); const result = await plug.invoke(name, args);
res.status(200); ctx.response.headers.set("Content-Type", "application/json");
res.header("Content-Type", "application/json"); ctx.response.body = JSON.stringify(result);
res.send(JSON.stringify(result));
} catch (e: any) { } catch (e: any) {
res.status(500); ctx.response.status = 500;
// console.log("Error invoking function", e); // console.log("Error invoking function", e);
return res.send(e.message); ctx.response.body = e.message;
} }
} },
); );
return plugRouter; return new Router().use("/plug", plugRouter.routes());
} }
async ensureAndLoadSettings() { async ensureAndLoadSettings() {
try { try {
await this.space.getPageMeta("SETTINGS"); await this.space.getPageMeta("SETTINGS");
} catch (e) { } catch (e) {
await this.space.writePage("SETTINGS", settingsTemplate, true); await this.space.writePage(
"SETTINGS",
await Deno.readTextFile(
new URL("SETTINGS_template.md", import.meta.url).pathname,
),
true,
);
} }
let { text: settingsText } = await this.space.readPage("SETTINGS"); const { text: settingsText } = await this.space.readPage("SETTINGS");
this.settings = parseYamlSettings(settingsText); this.settings = parseYamlSettings(settingsText);
if (!this.settings.indexPage) { if (!this.settings.indexPage) {
this.settings.indexPage = "index"; this.settings.indexPage = "index";
@ -446,106 +452,102 @@ export class ExpressServer {
} catch (e) { } catch (e) {
await this.space.writePage( await this.space.writePage(
this.settings.indexPage, this.settings.indexPage,
`Welcome to your new space!` `Welcome to your new space!`,
); );
} }
} }
async stop() { async stop() {
if (this.server) { if (this.abortController) {
console.log("Stopping"); console.log("Stopping");
await this.system.unloadAll(); await this.system.unloadAll();
console.log("Stopped plugs"); console.log("Stopped plugs");
return new Promise<void>((resolve, reject) => { this.abortController.abort();
this.server!.close((err) => { console.log("stopped server");
this.server = undefined;
console.log("stopped server");
if (err) {
reject(err);
} else {
resolve();
}
});
});
} }
} }
} }
function buildFsRouter(spacePrimitives: SpacePrimitives) { function buildFsRouter(spacePrimitives: SpacePrimitives): Router {
let fsRouter = express.Router(); const fsRouter = new Router();
// File list // File list
fsRouter.route("/").get(async (req, res, next) => { fsRouter.get("/", async ({ response }) => {
res.json(await spacePrimitives.fetchFileList()); const list = await spacePrimitives.fetchFileList();
// console.log("List", list);
response.headers.set("Content-type", "application/json");
response.body = JSON.stringify(list);
}); });
fsRouter fsRouter
.route(/\/(.+)/) .get("\/(.+)", async ({ params, request, response }, next) => {
.get(async (req, res, next) => { let name = params[0];
let name = req.params[0];
console.log("Loading file", name); console.log("Loading file", name);
try { try {
let attachmentData = await spacePrimitives.readFile( let attachmentData = await spacePrimitives.readFile(
name, name,
"arraybuffer" "arraybuffer",
); );
res.status(200); response.status = 200;
res.header("Last-Modified", "" + attachmentData.meta.lastModified); response.headers.set(
res.header("X-Permission", attachmentData.meta.perm); "Last-Modified",
res.header("Content-Type", attachmentData.meta.contentType); "" + attachmentData.meta.lastModified,
res.send(Buffer.from(attachmentData.data as ArrayBuffer)); );
} catch (e) { response.headers.set("X-Permission", attachmentData.meta.perm);
response.headers.set("Content-Type", attachmentData.meta.contentType);
response.body = attachmentData.data as ArrayBuffer;
} catch (e: any) {
console.error("Error in main router", e);
next(); next();
} }
}) })
.put(bodyParser.raw({ type: "*/*", limit: "100mb" }), async (req, res) => { .put("\/(.+)", async ({ request, response, params }) => {
let name = req.params[0]; let name = params[0];
console.log("Saving file", name); console.log("Saving file", name);
try { try {
let meta = await spacePrimitives.writeFile( let meta = await spacePrimitives.writeFile(
name, name,
"arraybuffer", "arraybuffer",
req.body, await request.body().value,
false false,
); );
res.status(200); response.status = 200;
res.header("Last-Modified", "" + meta.lastModified); response.headers.set("Last-Modified", "" + meta.lastModified);
res.header("Content-Type", meta.contentType); response.headers.set("Content-Type", meta.contentType);
res.header("Content-Length", "" + meta.size); response.headers.set("Content-Length", "" + meta.size);
res.header("X-Permission", meta.perm); response.headers.set("X-Permission", meta.perm);
res.send("OK"); response.body = "OK";
} catch (err) { } catch (err) {
res.status(500); response.status = 500;
res.send("Write failed"); response.body = "Write failed";
console.error("Pipeline failed", err); console.error("Pipeline failed", err);
} }
}) })
.options(async (req, res, next) => { .options("\/(.+)", async ({ request, response, params }, next) => {
let name = req.params[0]; let name = params[0];
try { try {
const meta = await spacePrimitives.getFileMeta(name); const meta = await spacePrimitives.getFileMeta(name);
res.status(200); response.status = 200;
res.header("Last-Modified", "" + meta.lastModified); response.headers.set("Last-Modified", "" + meta.lastModified);
res.header("X-Permission", meta.perm); response.headers.set("Content-Type", meta.contentType);
res.header("Content-Length", "" + meta.size); response.headers.set("Content-Length", "" + meta.size);
res.header("Content-Type", meta.contentType); response.headers.set("X-Permission", meta.perm);
res.send(""); } catch {
} catch (e) {
next(); next();
} }
}) })
.delete(async (req, res) => { .delete("\/(.+)", async ({ request, response, params }) => {
let name = req.params[0]; let name = params[0];
try { try {
await spacePrimitives.deleteFile(name); await spacePrimitives.deleteFile(name);
res.status(200); response.status = 200;
res.send("OK"); response.body = "OK";
} catch (e) { } catch (e: any) {
console.error("Error deleting attachment", e); console.error("Error deleting attachment", e);
res.status(500); response.status = 200;
res.send("OK"); response.body = e.message;
} }
}); });
return fsRouter; return new Router().use("/fs", fsRouter.routes());
} }

View File

@ -1,20 +1,16 @@
import { Plug } from "@plugos/plugos/plug"; import { Plug } from "../../plugos/plug.ts";
import { import {
FileData, FileData,
FileEncoding, FileEncoding,
SpacePrimitives, SpacePrimitives,
} from "@silverbulletmd/common/spaces/space_primitives"; } from "../../common/spaces/space_primitives.ts";
import { import { AttachmentMeta, FileMeta, PageMeta } from "../../common/types.ts";
AttachmentMeta, import { NamespaceOperation, PageNamespaceHook } from "./page_namespace.ts";
FileMeta,
PageMeta,
} from "@silverbulletmd/common/types";
import { PageNamespaceHook, NamespaceOperation } from "./page_namespace";
export class PlugSpacePrimitives implements SpacePrimitives { export class PlugSpacePrimitives implements SpacePrimitives {
constructor( constructor(
private wrapped: SpacePrimitives, private wrapped: SpacePrimitives,
private hook: PageNamespaceHook private hook: PageNamespaceHook,
) {} ) {}
performOperation( performOperation(
@ -52,7 +48,7 @@ export class PlugSpacePrimitives implements SpacePrimitives {
readFile( readFile(
name: string, name: string,
encoding: FileEncoding encoding: FileEncoding,
): Promise<{ data: FileData; meta: FileMeta }> { ): Promise<{ data: FileData; meta: FileMeta }> {
let result = this.performOperation("readFile", name); let result = this.performOperation("readFile", name);
if (result) { if (result) {
@ -73,14 +69,14 @@ export class PlugSpacePrimitives implements SpacePrimitives {
name: string, name: string,
encoding: FileEncoding, encoding: FileEncoding,
data: FileData, data: FileData,
selfUpdate?: boolean selfUpdate?: boolean,
): Promise<FileMeta> { ): Promise<FileMeta> {
let result = this.performOperation( let result = this.performOperation(
"writeFile", "writeFile",
name, name,
encoding, encoding,
data, data,
selfUpdate selfUpdate,
); );
if (result) { if (result) {
return result; return result;
@ -105,7 +101,7 @@ export class PlugSpacePrimitives implements SpacePrimitives {
plug: Plug<any>, plug: Plug<any>,
env: string, env: string,
name: string, name: string,
args: any[] args: any[],
): Promise<any> { ): Promise<any> {
return this.wrapped.invokeFunction(plug, env, name, args); return this.wrapped.invokeFunction(plug, env, name, args);
} }

View File

@ -1,39 +1,29 @@
#!/usr/bin/env -S node --enable-source-maps #!/usr/bin/env -S node --enable-source-maps
import { nodeModulesDir } from "@plugos/plugos/environments/node_sandbox"; import * as flags from "https://deno.land/std@0.158.0/flags/mod.ts";
import { realpathSync } from "fs"; import * as path from "https://deno.land/std@0.158.0/path/mod.ts";
import yargs from "yargs"; import { ExpressServer } from "./express_server.ts";
import { hideBin } from "yargs/helpers";
import { ExpressServer } from "./express_server"; type ServerArgs = {
_: string[];
let args = yargs(hideBin(process.argv)) port: number;
.option("port", { password: string;
type: "number", };
default: 3000, let args: ServerArgs = flags.parse(Deno.args);
})
.option("password", {
type: "string",
})
.parse();
if (!args._.length) { if (!args._.length) {
console.error( console.error(
"Usage: silverbullet [--port 3000] [--password mysecretpassword] <path-to-pages>" "Usage: silverbullet [--port 3000] [--password mysecretpassword] <path-to-pages>",
); );
process.exit(1); Deno.exit(1);
} }
const pagesPath = args._[0] as string; const pagesPath = path.resolve(Deno.cwd(), args._[0] as string);
const port = args.port; const port = args.port ? +args.port : 3000;
const webappDistDir = realpathSync( const webappDistDir = new URL("./../../dist", import.meta.url).pathname;
`${nodeModulesDir}/node_modules/@silverbulletmd/web/dist` console.log("Webapp dist dir", webappDistDir);
); const plugDistDir = new URL("./../plugs/dist", import.meta.url).pathname;
// console.log("Webapp dist dir", webappDistDir); console.log("Pages dir", pagesPath);
const plugDistDir = realpathSync(
`${nodeModulesDir}/node_modules/@silverbulletmd/plugs/dist`
);
// console.log("Builtin plug dist dir", plugDistDir);
const expressServer = new ExpressServer({ const expressServer = new ExpressServer({
port: port, port: port,

View File

@ -1,6 +1,12 @@
import { Knex } from "knex"; // import { Knex } from "knex";
import { SysCallMapping } from "@plugos/plugos/system"; import { SysCallMapping } from "../../plugos/system.ts";
import { Query, queryToKnex } from "@plugos/plugos/syscalls/store.knex_node"; import {
asyncExecute,
asyncQuery,
Query,
queryToSql,
} from "../../plugos/syscalls/store.deno.ts";
import { SQLite } from "../../../dep_server.ts";
type Item = { type Item = {
page: string; page: string;
@ -15,32 +21,41 @@ export type KV = {
const tableName = "page_index"; const tableName = "page_index";
export async function ensureTable(db: Knex<any, unknown>) { export function ensureTable(db: SQLite): Promise<void> {
if (!(await db.schema.hasTable(tableName))) { const stmt = db.prepare(
await db.schema.createTable(tableName, (table) => { `SELECT name FROM sqlite_master WHERE type='table' AND name=?`,
table.string("page"); );
table.string("key"); const result = stmt.all(tableName);
table.text("value"); if (result.length === 0) {
table.primary(["page", "key"]); db.exec(
table.index(["key"]); `CREATE TABLE ${tableName} (key STRING, page STRING, value TEXT, PRIMARY KEY (page, key));`,
}); );
db.exec(
`CREATE INDEX ${tableName}_idx ON ${tableName}(key);`,
);
console.log(`Created table ${tableName}`); console.log(`Created table ${tableName}`);
} }
return Promise.resolve();
} }
export function pageIndexSyscalls(db: Knex<any, unknown>): SysCallMapping { export function pageIndexSyscalls(db: SQLite): SysCallMapping {
const apiObj: SysCallMapping = { const apiObj: SysCallMapping = {
"index.set": async (ctx, page: string, key: string, value: any) => { "index.set": async (ctx, page: string, key: string, value: any) => {
let changed = await db<Item>(tableName) await asyncExecute(
.where({ key, page }) db,
.update("value", JSON.stringify(value)); `UPDATE ${tableName} SET value = ? WHERE key = ? AND page = ?`,
if (changed === 0) { JSON.stringify(value),
await db<Item>(tableName).insert({ key,
page,
);
if (db.changes === 0) {
await asyncExecute(
db,
`INSERT INTO ${tableName} (key, page, value) VALUES (?, ?, ?)`,
key, key,
page, page,
value: JSON.stringify(value), JSON.stringify(value),
}); );
} }
}, },
"index.batchSet": async (ctx, page: string, kvs: KV[]) => { "index.batchSet": async (ctx, page: string, kvs: KV[]) => {
@ -52,9 +67,12 @@ export function pageIndexSyscalls(db: Knex<any, unknown>): SysCallMapping {
await db<Item>(tableName).where({ key, page }).del(); await db<Item>(tableName).where({ key, page }).del();
}, },
"index.get": async (ctx, page: string, key: string) => { "index.get": async (ctx, page: string, key: string) => {
let result = await db<Item>(tableName) const result = await asyncQuery<Item>(
.where({ key, page }) db,
.select("value"); `SELECT value FROM ${tableName} WHERE key = ? AND page = ?`,
key,
page,
);
if (result.length) { if (result.length) {
return JSON.parse(result[0].value); return JSON.parse(result[0].value);
} else { } else {
@ -63,9 +81,11 @@ export function pageIndexSyscalls(db: Knex<any, unknown>): SysCallMapping {
}, },
"index.queryPrefix": async (ctx, prefix: string) => { "index.queryPrefix": async (ctx, prefix: string) => {
return ( return (
await db<Item>(tableName) await asyncQuery<Item>(
.andWhereLike("key", `${prefix}%`) db,
.select("key", "value", "page") `SELECT key, page, value FROM ${tableName} WHERE key LIKE "?%"`,
prefix,
)
).map(({ key, value, page }) => ({ ).map(({ key, value, page }) => ({
key, key,
page, page,
@ -73,11 +93,12 @@ export function pageIndexSyscalls(db: Knex<any, unknown>): SysCallMapping {
})); }));
}, },
"index.query": async (ctx, query: Query) => { "index.query": async (ctx, query: Query) => {
const { sql, params } = queryToSql(query);
return ( return (
await queryToKnex(db<Item>(tableName), query).select( await asyncQuery<Item>(
"key", db,
"value", `SELECT key, value FROM ${tableName} ${sql}`,
"page" ...params,
) )
).map(({ key, value, page }: any) => ({ ).map(({ key, value, page }: any) => ({
key, key,
@ -89,13 +110,18 @@ export function pageIndexSyscalls(db: Knex<any, unknown>): SysCallMapping {
await apiObj["index.deletePrefixForPage"](ctx, page, ""); await apiObj["index.deletePrefixForPage"](ctx, page, "");
}, },
"index.deletePrefixForPage": async (ctx, page: string, prefix: string) => { "index.deletePrefixForPage": async (ctx, page: string, prefix: string) => {
return db<Item>(tableName) await asyncExecute(
.where({ page }) db,
.andWhereLike("key", `${prefix}%`) `DELETE FROM ${tableName} WHERE key LIKE "?%" AND page = ?`,
.del(); prefix,
page,
);
}, },
"index.clearPageIndex": async (ctx) => { "index.clearPageIndex": async (ctx) => {
await db<Item>(tableName).del(); await asyncExecute(
db,
`DELETE FROM ${tableName}`,
);
}, },
}; };
return apiObj; return apiObj;

View File

@ -1,10 +1,10 @@
import { AttachmentMeta, PageMeta } from "@silverbulletmd/common/types"; import { AttachmentMeta, PageMeta } from "../../common/types.ts";
import { SysCallMapping } from "@plugos/plugos/system"; import { SysCallMapping } from "../../plugos/system.ts";
import { Space } from "@silverbulletmd/common/spaces/space"; import { Space } from "../../common/spaces/space.ts";
import { import {
FileData, FileData,
FileEncoding, FileEncoding,
} from "@silverbulletmd/common/spaces/space_primitives"; } from "../../common/spaces/space_primitives.ts";
export default (space: Space): SysCallMapping => { export default (space: Space): SysCallMapping => {
return { return {
@ -13,7 +13,7 @@ export default (space: Space): SysCallMapping => {
}, },
"space.readPage": async ( "space.readPage": async (
ctx, ctx,
name: string name: string,
): Promise<{ text: string; meta: PageMeta }> => { ): Promise<{ text: string; meta: PageMeta }> => {
return space.readPage(name); return space.readPage(name);
}, },
@ -23,7 +23,7 @@ export default (space: Space): SysCallMapping => {
"space.writePage": async ( "space.writePage": async (
ctx, ctx,
name: string, name: string,
text: string text: string,
): Promise<PageMeta> => { ): Promise<PageMeta> => {
return space.writePage(name, text); return space.writePage(name, text);
}, },
@ -38,13 +38,13 @@ export default (space: Space): SysCallMapping => {
}, },
"space.readAttachment": async ( "space.readAttachment": async (
ctx, ctx,
name: string name: string,
): Promise<{ data: FileData; meta: AttachmentMeta }> => { ): Promise<{ data: FileData; meta: AttachmentMeta }> => {
return await space.readAttachment(name, "dataurl"); return await space.readAttachment(name, "dataurl");
}, },
"space.getAttachmentMeta": async ( "space.getAttachmentMeta": async (
ctx, ctx,
name: string name: string,
): Promise<AttachmentMeta> => { ): Promise<AttachmentMeta> => {
return await space.getAttachmentMeta(name); return await space.getAttachmentMeta(name);
}, },
@ -52,7 +52,7 @@ export default (space: Space): SysCallMapping => {
ctx, ctx,
name: string, name: string,
encoding: FileEncoding, encoding: FileEncoding,
data: string data: string,
): Promise<AttachmentMeta> => { ): Promise<AttachmentMeta> => {
return await space.writeAttachment(name, encoding, data); return await space.writeAttachment(name, encoding, data);
}, },

View File

@ -1,10 +1,9 @@
import { SysCallMapping } from "@plugos/plugos/system"; import { SysCallMapping } from "../../plugos/system.ts";
import type { ExpressServer } from "../express_server"; import type { ExpressServer } from "../express_server.ts";
import { version } from "../package.json";
export function systemSyscalls(expressServer: ExpressServer): SysCallMapping { export function systemSyscalls(expressServer: ExpressServer): SysCallMapping {
return { return {
"system.invokeFunction": async ( "system.invokeFunction": (
ctx, ctx,
env: string, env: string,
name: string, name: string,
@ -15,7 +14,7 @@ export function systemSyscalls(expressServer: ExpressServer): SysCallMapping {
} }
return ctx.plug.invoke(name, args); return ctx.plug.invoke(name, args);
}, },
"system.reloadPlugs": async () => { "system.reloadPlugs": () => {
return expressServer.reloadPlugs(); return expressServer.reloadPlugs();
}, },
}; };

View File

@ -33,7 +33,7 @@
document.documentElement.dataset.theme = localStorage.theme ?? "light"; document.documentElement.dataset.theme = localStorage.theme ?? "light";
</script> </script>
<link rel="stylesheet" href="main.css" /> <link rel="stylesheet" href="main.css" />
<script type="module" src="boot.js"></script> <script type="module" src="client.js"></script>
<!--link rel="manifest" href="manifest.json" /--> <!--link rel="manifest" href="manifest.json" /-->
<link rel="icon" type="image/x-icon" href="images/favicon.gif" /> <link rel="icon" type="image/x-icon" href="images/favicon.gif" />
</head> </head>