pull/1017/head
Zef Hemel 2024-08-02 17:14:40 +02:00
parent 351c3c7088
commit 47f504f2ce
21 changed files with 153 additions and 117 deletions

View File

@ -1,10 +1,10 @@
import * as YAML from "js-yaml"; import * as YAML from "js-yaml";
export async function hello() { export async function hello() {
// @ts-ignore: syscall is a global function // @ts-ignore: syscall is a global function
const numbers = await syscall("addNumbers", 1, 2); const numbers = await syscall("addNumbers", 1, 2);
return { return {
yamlMessage: YAML.dump({ hello: "world" }), yamlMessage: YAML.dump({ hello: "world" }),
addedNumbers: numbers, addedNumbers: numbers,
}; };
} }

View File

@ -2,6 +2,6 @@ import { assertEquals } from "@std/assert";
import { parseExpression } from "$common/expression_parser.ts"; import { parseExpression } from "$common/expression_parser.ts";
Deno.test("Test expression parser", () => { Deno.test("Test expression parser", () => {
// Just a sanity check here // Just a sanity check here
assertEquals(parseExpression("1 + 2"), ["+", ["number", 1], ["number", 2]]); assertEquals(parseExpression("1 + 2"), ["+", ["number", 1], ["number", 2]]);
}); });

View File

@ -5,8 +5,8 @@ import { expressionToKvQueryExpression } from "$sb/lib/parse-query.ts";
import { lezerToParseTree } from "$common/markdown_parser/parse_tree.ts"; import { lezerToParseTree } from "$common/markdown_parser/parse_tree.ts";
export function parseExpression(s: string): QueryExpression { export function parseExpression(s: string): QueryExpression {
const ast = parseTreeToAST( const ast = parseTreeToAST(
lezerToParseTree(s, expressionLanguage.parser.parse(s).topNode), lezerToParseTree(s, expressionLanguage.parser.parse(s).topNode),
); );
return expressionToKvQueryExpression(ast[1]); return expressionToKvQueryExpression(ast[1]);
} }

View File

@ -1,5 +1,5 @@
export const wikiLinkRegex = /(!?\[\[)([^\]\|]+)(?:\|([^\]]+))?(\]\])/g; // [fullMatch, firstMark, url, alias, lastMark] export const wikiLinkRegex = /(!?\[\[)([^\]\|]+)(?:\|([^\]]+))?(\]\])/g; // [fullMatch, firstMark, url, alias, lastMark]
export const mdLinkRegex = /!?\[(?<title>[^\]]*)\]\((?<url>.+)\)/g; // [fullMatch, alias, url] export const mdLinkRegex = /!?\[(?<title>[^\]]*)\]\((?<url>.+)\)/g; // [fullMatch, alias, url]
export const tagRegex = export const tagRegex =
/#[^\d\s!@#$%^&*(),.?":{}|<>\\][^\s!@#$%^&*(),.?":{}|<>\\]*/; /#[^\d\s!@#$%^&*(),.?":{}|<>\\][^\s!@#$%^&*(),.?":{}|<>\\]*/;
export const pWikiLinkRegex = new RegExp("^" + wikiLinkRegex.source); // Modified regex used only in parser export const pWikiLinkRegex = new RegExp("^" + wikiLinkRegex.source); // Modified regex used only in parser

View File

@ -4,21 +4,21 @@ import Ajv from "ajv";
const ajv = new Ajv(); const ajv = new Ajv();
export function jsonschemaSyscalls(): SysCallMapping { export function jsonschemaSyscalls(): SysCallMapping {
return { return {
"jsonschema.validateObject": ( "jsonschema.validateObject": (
_ctx, _ctx,
schema: any, schema: any,
object: any, object: any,
): undefined | string => { ): undefined | string => {
const validate = ajv.compile(schema); const validate = ajv.compile(schema);
if (validate(object)) { if (validate(object)) {
return; return;
} else { } else {
let text = ajv.errorsText(validate.errors); let text = ajv.errorsText(validate.errors);
text = text.replaceAll("/", "."); text = text.replaceAll("/", ".");
text = text.replace(/^data\./, ""); text = text.replace(/^data\./, "");
return text; return text;
} }
}, },
}; };
} }

View File

@ -18,6 +18,7 @@
"check": "find . -name '*.ts*' | xargs deno check", "check": "find . -name '*.ts*' | xargs deno check",
"lint": "deno lint", "lint": "deno lint",
"fmt": "deno fmt",
"test": "deno test -A --unstable-kv --unstable-worker-options", "test": "deno test -A --unstable-kv --unstable-worker-options",
"bench": "deno bench", "bench": "deno bench",
@ -53,12 +54,13 @@
}, },
"fmt": { "fmt": {
"exclude": [ "exclude": [
"dist", "dist*",
"dist_bundle",
"website", "website",
"website_build", "website_build",
"test_space", "test_space",
"README.md" "**/*.md",
"**/*.js",
"**/*.json"
] ]
}, },
"compilerOptions": { "compilerOptions": {

View File

@ -2,59 +2,59 @@ import { DataStore } from "$lib/data/datastore.ts";
import { MemoryKvPrimitives } from "$lib/data/memory_kv_primitives.ts"; import { MemoryKvPrimitives } from "$lib/data/memory_kv_primitives.ts";
Deno.bench("DataStore enrichment benchmark with match", (b) => { Deno.bench("DataStore enrichment benchmark with match", (b) => {
// Dummy datastore with a single object enricher // Dummy datastore with a single object enricher
const datastore = new DataStore(new MemoryKvPrimitives(), {}); const datastore = new DataStore(new MemoryKvPrimitives(), {});
datastore.objectDecorators = [ datastore.objectDecorators = [
{ {
where: ["=", ["attr", "tags"], ["string", "person"]], where: ["=", ["attr", "tags"], ["string", "person"]],
attributes: { attributes: {
fullName: ["+", ["+", ["attr", "firstName"], ["string", " "]], [ fullName: ["+", ["+", ["attr", "firstName"], ["string", " "]], [
"attr", "attr",
"lastName", "lastName",
]], ]],
}, },
}, },
]; ];
b.start(); b.start();
// Let's try with half a million entries // Let's try with half a million entries
for (let i = 0; i < 500000; i++) { for (let i = 0; i < 500000; i++) {
const obj = { const obj = {
firstName: "Pete", firstName: "Pete",
lastName: "Smith", lastName: "Smith",
tags: ["person"], tags: ["person"],
}; };
datastore.enrichObject(obj); datastore.enrichObject(obj);
} }
b.end(); b.end();
}); });
Deno.bench("DataStore enrichment benchmark without match", (b) => { Deno.bench("DataStore enrichment benchmark without match", (b) => {
// Dummy datastore with a single object enricher // Dummy datastore with a single object enricher
const datastore = new DataStore(new MemoryKvPrimitives(), {}); const datastore = new DataStore(new MemoryKvPrimitives(), {});
datastore.objectDecorators = [ datastore.objectDecorators = [
{ {
where: ["=", ["attr", "tags"], ["string", "person"]], where: ["=", ["attr", "tags"], ["string", "person"]],
attributes: { attributes: {
fullName: ["+", ["+", ["attr", "firstName"], ["string", " "]], [ fullName: ["+", ["+", ["attr", "firstName"], ["string", " "]], [
"attr", "attr",
"lastName", "lastName",
]], ]],
}, },
}, },
]; ];
b.start(); b.start();
// Let's try with half a million entries // Let's try with half a million entries
for (let i = 0; i < 500000; i++) { for (let i = 0; i < 500000; i++) {
const obj = { const obj = {
firstName: "Pete", firstName: "Pete",
lastName: "Smith", lastName: "Smith",
tags: ["peson"], tags: ["peson"],
}; };
datastore.enrichObject(obj); datastore.enrichObject(obj);
} }
b.end(); b.end();
}); });

View File

@ -2,19 +2,19 @@ import { assert, assertEquals } from "@std/assert";
import { federatedPathToLocalPath, wildcardPathToRegex } from "./util.ts"; import { federatedPathToLocalPath, wildcardPathToRegex } from "./util.ts";
Deno.test("Test wildcardPathToRegex", () => { Deno.test("Test wildcardPathToRegex", () => {
assert(wildcardPathToRegex("test").test("test")); assert(wildcardPathToRegex("test").test("test"));
assert(wildcardPathToRegex("test").test("test.md")); assert(wildcardPathToRegex("test").test("test.md"));
assert(wildcardPathToRegex("test*").test("test")); assert(wildcardPathToRegex("test*").test("test"));
assert(wildcardPathToRegex("test/*").test("test/bla")); assert(wildcardPathToRegex("test/*").test("test/bla"));
assert(wildcardPathToRegex("test/*").test("test/bla.md")); assert(wildcardPathToRegex("test/*").test("test/bla.md"));
assert(wildcardPathToRegex("test/*").test("test/bla/bla")); assert(wildcardPathToRegex("test/*").test("test/bla/bla"));
assert(!wildcardPathToRegex("test/*").test("tests/bla/bla")); assert(!wildcardPathToRegex("test/*").test("tests/bla/bla"));
}); });
Deno.test("Test federatedPathToLocalPath", () => { Deno.test("Test federatedPathToLocalPath", () => {
assertEquals(federatedPathToLocalPath("!silverbullet.md"), ""); assertEquals(federatedPathToLocalPath("!silverbullet.md"), "");
assertEquals( assertEquals(
federatedPathToLocalPath("!silverbullet.md/Library/Core/test"), federatedPathToLocalPath("!silverbullet.md/Library/Core/test"),
"Library/Core/test", "Library/Core/test",
); );
}); });

View File

@ -1,14 +1,14 @@
export function wildcardPathToRegex(pattern: string): RegExp { export function wildcardPathToRegex(pattern: string): RegExp {
// Escape special characters in the pattern except for the wildcard "*" // Escape special characters in the pattern except for the wildcard "*"
const escapedPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&"); const escapedPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
// Replace the wildcard "*" with ".*" to match any character sequence // Replace the wildcard "*" with ".*" to match any character sequence
const regexPattern = escapedPattern.replace(/\*/g, ".*"); const regexPattern = escapedPattern.replace(/\*/g, ".*");
// Create a new regular expression with the converted pattern // Create a new regular expression with the converted pattern
return new RegExp(`^${regexPattern}(\\.md)?$`); return new RegExp(`^${regexPattern}(\\.md)?$`);
} }
export function federatedPathToLocalPath(path: string): string { export function federatedPathToLocalPath(path: string): string {
return path.split("/").slice(1).join("/"); return path.split("/").slice(1).join("/");
} }

View File

@ -1,4 +1,7 @@
import type { IndexTreeEvent, QueryProviderEvent } from "../../plug-api/types.ts"; import type {
IndexTreeEvent,
QueryProviderEvent,
} from "../../plug-api/types.ts";
import { renderToText } from "$sb/lib/tree.ts"; import { renderToText } from "$sb/lib/tree.ts";
import { applyQuery, liftAttributeFilter } from "$sb/lib/query.ts"; import { applyQuery, liftAttributeFilter } from "$sb/lib/query.ts";
import { editor } from "$sb/syscalls.ts"; import { editor } from "$sb/syscalls.ts";

View File

@ -1,4 +1,8 @@
import type { AttachmentMeta, FileMeta, PageMeta } from "../../plug-api/types.ts"; import type {
AttachmentMeta,
FileMeta,
PageMeta,
} from "../../plug-api/types.ts";
import type { SysCallMapping } from "../../lib/plugos/system.ts"; import type { SysCallMapping } from "../../lib/plugos/system.ts";
import type { Space } from "../../common/space.ts"; import type { Space } from "../../common/space.ts";

View File

@ -1,4 +1,7 @@
import type { CodeWidgetCallback, WidgetContent } from "../../plug-api/types.ts"; import type {
CodeWidgetCallback,
WidgetContent,
} from "../../plug-api/types.ts";
import { WidgetType } from "@codemirror/view"; import { WidgetType } from "@codemirror/view";
import type { Client } from "../client.ts"; import type { Client } from "../client.ts";
import { createWidgetSandboxIFrame } from "../components/widget_sandbox_iframe.ts"; import { createWidgetSandboxIFrame } from "../components/widget_sandbox_iframe.ts";

View File

@ -93,7 +93,7 @@ export class MarkdownWidget extends WidgetType {
extendedMarkdownLanguage, extendedMarkdownLanguage,
trimmedMarkdown, trimmedMarkdown,
); );
const html = renderMarkdownToHtml(mdTree, { const html = renderMarkdownToHtml(mdTree, {
// Annotate every element with its position so we can use it to put // Annotate every element with its position so we can use it to put
// the cursor there when the user clicks on the table. // the cursor there when the user clicks on the table.

View File

@ -1,7 +1,11 @@
// Forked from https://codeberg.org/retronav/ixora // Forked from https://codeberg.org/retronav/ixora
// Original author: Pranav Karawale // Original author: Pranav Karawale
// License: Apache License 2.0. // License: Apache License 2.0.
import { type EditorState, StateField, type Transaction } from "@codemirror/state"; import {
type EditorState,
StateField,
type Transaction,
} from "@codemirror/state";
import type { DecorationSet } from "@codemirror/view"; import type { DecorationSet } from "@codemirror/view";
import { Decoration, EditorView, WidgetType } from "@codemirror/view"; import { Decoration, EditorView, WidgetType } from "@codemirror/view";
type LinkOptions = { type LinkOptions = {

View File

@ -1,4 +1,8 @@
import { EditorSelection, type StateCommand, Transaction } from "@codemirror/state"; import {
EditorSelection,
type StateCommand,
Transaction,
} from "@codemirror/state";
import { Text } from "@codemirror/state"; import { Text } from "@codemirror/state";
export function insertMarker(marker: string): StateCommand { export function insertMarker(marker: string): StateCommand {

View File

@ -200,7 +200,9 @@ export function FilterList({
className={(selectedOption === idx className={(selectedOption === idx
? "sb-option sb-selected-option" ? "sb-option sb-selected-option"
: "sb-option") + : "sb-option") +
(option.cssClass ? " sb-decorated-object " + option.cssClass : "")} (option.cssClass
? " sb-decorated-object " + option.cssClass
: "")}
onMouseMove={() => { onMouseMove={() => {
if (selectedOption !== idx) { if (selectedOption !== idx) {
setSelectionOption(idx); setSelectionOption(idx);

View File

@ -21,7 +21,7 @@ export function Panel({
type: "html", type: "html",
html: config.html, html: config.html,
script: config.script, script: config.script,
theme: document.getElementsByTagName("html")[0].dataset.theme theme: document.getElementsByTagName("html")[0].dataset.theme,
}); });
} }
@ -31,7 +31,7 @@ export function Panel({
return; return;
} }
iframe.addEventListener("load", updateContent) iframe.addEventListener("load", updateContent);
updateContent(); updateContent();
return () => { return () => {
@ -89,7 +89,12 @@ export function Panel({
return ( return (
<div className="sb-panel" style={{ flex: config.mode }}> <div className="sb-panel" style={{ flex: config.mode }}>
<iframe srcDoc={panelHtml} ref={iFrameRef} style={{ visibility: 'hidden' }} onLoad={() => iFrameRef.current!.style.visibility = "visible" }/> <iframe
srcDoc={panelHtml}
ref={iFrameRef}
style={{ visibility: "hidden" }}
onLoad={() => iFrameRef.current!.style.visibility = "visible"}
/>
</div> </div>
); );
} }

View File

@ -69,10 +69,8 @@ export function TopBar({
? "sb-loading" ? "sb-loading"
: unsavedChanges : unsavedChanges
? "sb-unsaved" ? "sb-unsaved"
: "sb-saved") + : "sb-saved") +
(cssClass (cssClass ? " sb-decorated-object " + cssClass : "")}
? " sb-decorated-object " + cssClass
: "")}
> >
<MiniEditor <MiniEditor
text={pageName ?? ""} text={pageName ?? ""}

View File

@ -1,6 +1,10 @@
import { plugPrefix } from "$common/spaces/constants.ts"; import { plugPrefix } from "$common/spaces/constants.ts";
import type { SpacePrimitives } from "$common/spaces/space_primitives.ts"; import type { SpacePrimitives } from "$common/spaces/space_primitives.ts";
import { SpaceSync, type SyncStatus, type SyncStatusItem } from "$common/spaces/sync.ts"; import {
SpaceSync,
type SyncStatus,
type SyncStatusItem,
} from "$common/spaces/sync.ts";
import { sleep } from "$lib/async.ts"; import { sleep } from "$lib/async.ts";
import type { EventHook } from "../common/hooks/event.ts"; import type { EventHook } from "../common/hooks/event.ts";
import type { DataStore } from "$lib/data/datastore.ts"; import type { DataStore } from "$lib/data/datastore.ts";

View File

@ -1,6 +1,10 @@
import type { Client } from "../client.ts"; import type { Client } from "../client.ts";
import type { SysCallMapping } from "../../lib/plugos/system.ts"; import type { SysCallMapping } from "../../lib/plugos/system.ts";
import type { AttachmentMeta, FileMeta, PageMeta } from "../../plug-api/types.ts"; import type {
AttachmentMeta,
FileMeta,
PageMeta,
} from "../../plug-api/types.ts";
export function spaceReadSyscalls(editor: Client): SysCallMapping { export function spaceReadSyscalls(editor: Client): SysCallMapping {
return { return {

View File

@ -1,5 +1,8 @@
import type { HttpSpacePrimitives } from "$common/spaces/http_space_primitives.ts"; import type { HttpSpacePrimitives } from "$common/spaces/http_space_primitives.ts";
import type { SyscallContext, SysCallMapping } from "../../lib/plugos/system.ts"; import type {
SyscallContext,
SysCallMapping,
} from "../../lib/plugos/system.ts";
import type { Client } from "../client.ts"; import type { Client } from "../client.ts";
export function proxySyscalls(client: Client, names: string[]): SysCallMapping { export function proxySyscalls(client: Client, names: string[]): SysCallMapping {