From cafd0012147babd1ec4823a50a5c28a79b4ef243 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Wed, 7 Aug 2024 13:27:25 +0200 Subject: [PATCH] Added a ton of JS Doc --- cmd/server.ts | 25 ++-- deno.json | 3 +- plug-api/lib/frontmatter.ts | 4 +- plug-api/lib/json.ts | 22 ++- plug-api/lib/markdown.ts | 3 + plug-api/lib/page_ref.ts | 32 +++- plug-api/syscall.ts | 2 +- plug-api/syscalls/asset.ts | 7 + plug-api/syscalls/clientStore.ts | 14 ++ plug-api/syscalls/code_widget.ts | 11 +- plug-api/syscalls/datastore.ts | 48 +++++- plug-api/syscalls/debug.ts | 7 + plug-api/syscalls/editor.ts | 190 ++++++++++++++++++++++-- plug-api/syscalls/event.ts | 13 ++ plug-api/syscalls/jsonschema.ts | 8 +- plug-api/syscalls/language.ts | 4 + plug-api/syscalls/markdown.ts | 6 +- plug-api/syscalls/mq.ts | 28 ++++ plug-api/syscalls/shell.ts | 6 + plug-api/syscalls/space.ts | 70 ++++++++- plug-api/syscalls/sync.ts | 17 +++ plug-api/syscalls/system.ts | 52 ++++++- plug-api/syscalls/template.ts | 18 ++- plug-api/syscalls/yaml.ts | 10 ++ server/http_server.ts | 168 ++++++++------------- server/shell_backend.ts | 6 +- server/{instance.ts => space_server.ts} | 35 ++--- 27 files changed, 612 insertions(+), 197 deletions(-) rename server/{instance.ts => space_server.ts} (85%) diff --git a/cmd/server.ts b/cmd/server.ts index 98d38971..49660b54 100644 --- a/cmd/server.ts +++ b/cmd/server.ts @@ -8,7 +8,6 @@ import plugAssetBundle from "../dist/plug_asset_bundle.json" with { import { AssetBundle, type AssetJson } from "../lib/asset_bundle/bundle.ts"; import { determineDatabaseBackend } from "../server/db_backend.ts"; -import type { SpaceServerConfig } from "../server/instance.ts"; import { runPlug } from "../cmd/plug_run.ts"; import { PrefixedKvPrimitives } from "$lib/data/prefixed_kv_primitives.ts"; import { sleep } from "$lib/async.ts"; @@ -73,19 +72,6 @@ export async function serveCommand( const backendConfig = Deno.env.get("SB_SHELL_BACKEND") || "local"; const enableSpaceScript = Deno.env.get("SB_SPACE_SCRIPT") !== "off"; - const configs = new Map(); - configs.set("*", { - hostname, - namespace: "*", - auth: userCredentials, - authToken: Deno.env.get("SB_AUTH_TOKEN"), - syncOnly, - readOnly, - shellBackend: backendConfig, - enableSpaceScript, - pagesPath: folder, - }); - const plugAssets = new AssetBundle(plugAssetBundle as AssetJson); if (readOnly) { @@ -137,9 +123,16 @@ export async function serveCommand( baseKvPrimitives, keyFile: options.key, certFile: options.cert, - configs, + + auth: userCredentials, + authToken: Deno.env.get("SB_AUTH_TOKEN"), + syncOnly, + readOnly, + shellBackend: backendConfig, + enableSpaceScript, + pagesPath: folder, }); - httpServer.start(); + await httpServer.start(); // Wait in an infinite loop (to keep the HTTP server running, only cancelable via Ctrl+C or other signal) while (true) { diff --git a/deno.json b/deno.json index f53b87a1..ae67efd6 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,7 @@ { "name": "@silverbulletmd/silverbullet", - "version": "0.0.1", + "version": "0.9.0", + "description": "Silverbullet is a Personal Knowledge Management System", "exports": { "./syscall": "./plug-api/syscall.ts", "./syscalls": "./plug-api/syscalls.ts", diff --git a/plug-api/lib/frontmatter.ts b/plug-api/lib/frontmatter.ts index 4b3167b3..4dd6a6c6 100644 --- a/plug-api/lib/frontmatter.ts +++ b/plug-api/lib/frontmatter.ts @@ -136,7 +136,9 @@ export async function extractFrontmatter( return data; } -// Updates the front matter of a markdown document and returns the text as a rendered string +/** + * Updates the front matter of a markdown document and returns the text as a rendered string + */ export async function prepareFrontmatterDispatch( tree: ParseTree, data: string | Record, diff --git a/plug-api/lib/json.ts b/plug-api/lib/json.ts index d3a096a6..5e379258 100644 --- a/plug-api/lib/json.ts +++ b/plug-api/lib/json.ts @@ -1,4 +1,9 @@ -// Compares two objects deeply +/** + * Performs a deep comparison of two objects, returning true if they are equal + * @param a first object + * @param b second object + * @returns + */ export function deepEqual(a: any, b: any): boolean { if (a === b) { return true; @@ -40,7 +45,10 @@ export function deepEqual(a: any, b: any): boolean { return false; } -// Converts a Date object to a date string in the format YYYY-MM-DD if it just contains a date (and no significant time), or a full ISO string otherwise +/** + * Converts a Date object to a date string in the format YYYY-MM-DD if it just contains a date (and no significant time), or a full ISO string otherwise + * @param d the date to convert + */ export function cleanStringDate(d: Date): string { // If no significant time, return a date string only if ( @@ -54,9 +62,13 @@ export function cleanStringDate(d: Date): string { } } -// Processes a JSON (typically coming from parse YAML frontmatter) in two ways: -// 1. Expands property names in an object containing a .-separated path -// 2. Converts dates to strings in sensible ways +/** + * Processes a JSON (typically coming from parse YAML frontmatter) in two ways: + * 1. Expands property names in an object containing a .-separated path + * 2. Converts dates to strings in sensible ways + * @param a + * @returns + */ export function cleanupJSON(a: any): any { if (!a) { return a; diff --git a/plug-api/lib/markdown.ts b/plug-api/lib/markdown.ts index 3f78a919..55072797 100644 --- a/plug-api/lib/markdown.ts +++ b/plug-api/lib/markdown.ts @@ -4,6 +4,9 @@ import { renderToText, } from "@silverbulletmd/silverbullet/lib/tree"; +/** + * Strips markdown from a ParseTree + */ export function stripMarkdown( tree: ParseTree, ): string { diff --git a/plug-api/lib/page_ref.ts b/plug-api/lib/page_ref.ts index 5ed74c37..f89814ed 100644 --- a/plug-api/lib/page_ref.ts +++ b/plug-api/lib/page_ref.ts @@ -1,3 +1,14 @@ +/** + * Represents a reference to a page, with optional position, anchor and header. + */ +export type PageRef = { + page: string; + pos?: number | { line: number; column: number }; + anchor?: string; + header?: string; + meta?: boolean; +}; + /** * Checks if a name looks like a full path (with a file extension), is not a conflicted file and not a search page. */ @@ -6,6 +17,9 @@ export function looksLikePathWithExtension(name: string): boolean { !name.startsWith("🔍 "); } +/** + * Checks if a name looks like a full path (with a file extension), is not a conflicted file and not a search page. + */ export function validatePageName(name: string) { // Page can not be empty and not end with a file extension (e.g. "bla.md") if (name === "") { @@ -19,19 +33,16 @@ export function validatePageName(name: string) { } } -export type PageRef = { - page: string; - pos?: number | { line: number; column: number }; - anchor?: string; - header?: string; - meta?: boolean; -}; - const posRegex = /@(\d+)$/; const linePosRegex = /@[Ll](\d+)(?:[Cc](\d+))?$/; // column is optional, implicit 1 const anchorRegex = /\$([a-zA-Z\.\-\/]+[\w\.\-\/]*)$/; const headerRegex = /#([^#]*)$/; +/** + * Parses a page reference string into a PageRef object. + * @param name the name of the page reference to parse + * @returns the parsed PageRef object + */ export function parsePageRef(name: string): PageRef { // Normalize the page name if (name.startsWith("[[") && name.endsWith("]]")) { @@ -71,6 +82,11 @@ export function parsePageRef(name: string): PageRef { return pageRef; } +/** + * The inverse of parsePageRef, encodes a PageRef object into a string. + * @param pageRef the page reference to encode + * @returns a string representation of the page reference + */ export function encodePageRef(pageRef: PageRef): string { let name = pageRef.page; if (pageRef.pos) { diff --git a/plug-api/syscall.ts b/plug-api/syscall.ts index 0061606a..00de7358 100644 --- a/plug-api/syscall.ts +++ b/plug-api/syscall.ts @@ -12,6 +12,6 @@ if (typeof self === "undefined") { } // Late binding syscall -export function syscall(name: string, ...args: any[]) { +export function syscall(name: string, ...args: any[]): Promise { return (globalThis as any).syscall(name, ...args); } diff --git a/plug-api/syscalls/asset.ts b/plug-api/syscalls/asset.ts index 8d8a8db1..39de4daf 100644 --- a/plug-api/syscalls/asset.ts +++ b/plug-api/syscalls/asset.ts @@ -1,6 +1,13 @@ import { base64DecodeDataUrl } from "../../lib/crypto.ts"; import { syscall } from "../syscall.ts"; +/** + * Reads an asset embedded in a plug (via the `assets` field in the plug manifest). + * @param plugName name of the plug to read asset from + * @param name name of the asset to read + * @param encoding either "utf8" or "dataurl" + * @returns the content of the asset in the requested encoding + */ export async function readAsset( plugName: string, name: string, diff --git a/plug-api/syscalls/clientStore.ts b/plug-api/syscalls/clientStore.ts index 761ea72f..0c5cd84c 100644 --- a/plug-api/syscalls/clientStore.ts +++ b/plug-api/syscalls/clientStore.ts @@ -5,14 +5,28 @@ import { syscall } from "../syscall.ts"; * Generally should only be used to set some client-specific states, such as preferences. */ +/** + * Sets a value in the client store. + * @param key the key to set + * @param value the value to set + */ export function set(key: string, value: any): Promise { return syscall("clientStore.set", key, value); } +/** + * Gets a value from the client store. + * @param key the key to get + * @returns the value associated with the key + */ export function get(key: string): Promise { return syscall("clientStore.get", key); } +/** + * Deletes a value from the client store. + * @param key the key to delete + */ export function del(key: string): Promise { return syscall("clientStore.delete", key); } diff --git a/plug-api/syscalls/code_widget.ts b/plug-api/syscalls/code_widget.ts index aea8ada8..66fd7d28 100644 --- a/plug-api/syscalls/code_widget.ts +++ b/plug-api/syscalls/code_widget.ts @@ -1,6 +1,13 @@ import type { CodeWidgetContent } from "../types.ts"; import { syscall } from "../syscall.ts"; +/** + * Renders a code widget. + * @param lang the language of the fenced code block + * @param body the body of the code to render + * @param pageName the name of the page the code widget appears on + * @returns the rendered code widget content + */ export function render( lang: string, body: string, @@ -9,7 +16,9 @@ export function render( return syscall("codeWidget.render", lang, body, pageName); } -// Refresh all code widgets on the page that support it +/** + * Refreshes all code widgets on the page that support it. + */ export function refreshAll(): Promise { return syscall("codeWidget.refreshAll"); } diff --git a/plug-api/syscalls/datastore.ts b/plug-api/syscalls/datastore.ts index 2101fcd2..7caccaf5 100644 --- a/plug-api/syscalls/datastore.ts +++ b/plug-api/syscalls/datastore.ts @@ -1,30 +1,67 @@ import { syscall } from "../syscall.ts"; import type { KV, KvKey, KvQuery } from "../types.ts"; +/** + * Exposes a key value story with query capabilities. + */ + +/** + * Sets a value in the key value store. + * @param key the key to set + * @param value the value to set + */ export function set(key: KvKey, value: any): Promise { return syscall("datastore.set", key, value); } +/** + * Sets multiple values in the key value store. + * @param kvs the key value pairs to set + */ export function batchSet(kvs: KV[]): Promise { return syscall("datastore.batchSet", kvs); } -export function get(key: KvKey): Promise { +/** + * Gets a value from the key value store. + * @param key the key to get + * @returns the value associated with the key (or undefined if not found) + */ +export function get(key: KvKey): Promise { return syscall("datastore.get", key); } +/** + * Gets multiple values from the key value store. + * @param keys the keys to get + * @returns the values associated with the keys (or undefined if not found) + */ export function batchGet(keys: KvKey[]): Promise<(any | undefined)[]> { return syscall("datastore.batchGet", keys); } +/** + * Deletes a value from the key value store. + * @param key the key to delete + */ export function del(key: KvKey): Promise { return syscall("datastore.delete", key); } +/** + * Deletes multiple values from the key value store. + * @param keys the keys to delete + */ export function batchDel(keys: KvKey[]): Promise { return syscall("datastore.batchDelete", keys); } +/** + * Queries the key value store. + * @param query the query to run + * @param variables the variables that can be referenced inside the query + * @returns the results of the query + */ export function query( query: KvQuery, variables: Record = {}, @@ -32,6 +69,11 @@ export function query( return syscall("datastore.query", query, variables); } +/** + * Queries the key value store and deletes all matching items + * @param query the query to run + * @param variables the variables that can be referenced inside the query + */ export function queryDelete( query: KvQuery, variables?: Record, @@ -39,6 +81,10 @@ export function queryDelete( return syscall("datastore.queryDelete", query, variables); } +/** + * Lists all functions currently defined and available for use in queries + * @returns the names of all functions in the key value store + */ export function listFunctions(): Promise { return syscall("datastore.listFunctions"); } diff --git a/plug-api/syscalls/debug.ts b/plug-api/syscalls/debug.ts index e81a0f08..a540aa3c 100644 --- a/plug-api/syscalls/debug.ts +++ b/plug-api/syscalls/debug.ts @@ -1,5 +1,12 @@ import { syscall } from "../syscall.ts"; +/** + * Exposes various debugging utilities. + */ + +/** + * Completely wipes the client state, both cached files as well as databases (best effort) + */ export function resetClient(): Promise { return syscall("debug.resetClient"); } diff --git a/plug-api/syscalls/editor.ts b/plug-api/syscalls/editor.ts index 503b0405..2b325065 100644 --- a/plug-api/syscalls/editor.ts +++ b/plug-api/syscalls/editor.ts @@ -3,14 +3,22 @@ import { syscall } from "../syscall.ts"; import type { PageRef } from "../lib/page_ref.ts"; import type { FilterOption } from "@silverbulletmd/silverbullet/type/client"; +/** + * Exposes various editor utilities. + * Important: These syscalls are only available in the client. + */ + +/** + * Returns the name of the page currently open in the editor. + * @returns the current page name + */ export function getCurrentPage(): Promise { return syscall("editor.getCurrentPage"); } -export function setPage(newName: string): Promise { - return syscall("editor.setPage", newName); -} - +/** + * Returns the full text of the currently open page + */ export function getText(): Promise { return syscall("editor.getText"); } @@ -23,22 +31,42 @@ export function setText(newText: string): Promise { return syscall("editor.setText", newText); } +/** + * Returns the position (in # of characters from the beginning of the file) of the cursor in the editor + */ export function getCursor(): Promise { return syscall("editor.getCursor"); } +/** + * Returns the line number and column number of the cursor in the editor + */ export function getSelection(): Promise<{ from: number; to: number }> { return syscall("editor.getSelection"); } +/** + * Sets the position of the cursor in the editor + * @param from the start position of the selection + * @param to the end position of the selection + */ export function setSelection(from: number, to: number): Promise { return syscall("editor.setSelection", from, to); } +/** + * Forces a save of the current page + */ export function save(): Promise { return syscall("editor.save"); } +/** + * Navigates to the specified page reference + * @param pageRef the page reference to navigate to + * @param replaceState whether to replace the current history state in the browser history + * @param newWindow whether to open the page in a new window + */ export function navigate( pageRef: PageRef, replaceState = false, @@ -47,28 +75,49 @@ export function navigate( return syscall("editor.navigate", pageRef, replaceState, newWindow); } +/** + * Opens the page navigator + * @param mode the mode to open the navigator in + */ export function openPageNavigator( mode: "page" | "meta" | "all" = "page", ): Promise { return syscall("editor.openPageNavigator", mode); } +/** + * Opens the command palette + */ export function openCommandPalette(): Promise { return syscall("editor.openCommandPalette"); } +/** + * Force reloads the current page + */ export function reloadPage(): Promise { return syscall("editor.reloadPage"); } +/** + * Force reloads the browser UI + */ export function reloadUI(): Promise { return syscall("editor.reloadUI"); } +/** + * Reloads the config and commands, also in the server + */ export function reloadConfigAndCommands(): Promise { return syscall("editor.reloadConfigAndCommands"); } +/** + * Opens the specified URL in the browser + * @param url the URL to open + * @param existingWindow whether to open the URL in an existing window + */ export function openUrl(url: string, existingWindow = false): Promise { return syscall("editor.openUrl", url, existingWindow); } @@ -82,11 +131,20 @@ export function goHistory(delta: number): Promise { return syscall("editor.goHistory", delta); } -// Force the client to download the file in dataUrl with filename as file name +/** + * Force the client to download the file in dataUrl with filename as file name + * @param filename the name of the file to download + * @param dataUrl the dataUrl of the file to download + */ export function downloadFile(filename: string, dataUrl: string): Promise { return syscall("editor.downloadFile", filename, dataUrl); } +/** + * Triggers the browser's native file upload dialog/popup + * @param accept the file types to accept + * @param capture the capture mode for the file input + */ export function uploadFile( accept?: string, capture?: string, @@ -94,6 +152,11 @@ export function uploadFile( return syscall("editor.uploadFile", accept, capture); } +/** + * Shows a flash notification to the user (top right corner) + * @param message the message to show + * @param type the type of notification to show + */ export function flashNotification( message: string, type: "info" | "error" = "info", @@ -101,6 +164,13 @@ export function flashNotification( return syscall("editor.flashNotification", message, type); } +/** + * Exposes a filter box UI (similar to the page navigator and command palette) + * @param label the label to show left of the input box + * @param options the options to show and to filter on + * @param helpText the help text to show below the input box + * @param placeHolder the placeholder text to show in the input box + */ export function filterBox( label: string, options: FilterOption[], @@ -110,6 +180,13 @@ export function filterBox( return syscall("editor.filterBox", label, options, helpText, placeHolder); } +/** + * Shows a panel in the editor + * @param id the location of the panel to show + * @param mode the mode or "size" of the panel + * @param html the html content of the panel + * @param script the script content of the panel + */ export function showPanel( id: "lhs" | "rhs" | "bhs" | "modal", mode: number, @@ -119,16 +196,31 @@ export function showPanel( return syscall("editor.showPanel", id, mode, html, script); } +/** + * Hides a panel in the editor + * @param id the location of the panel to hide + */ export function hidePanel( id: "lhs" | "rhs" | "bhs" | "modal", ): Promise { return syscall("editor.hidePanel", id); } +/** + * Insert text at the specified position into the editor + * @param text the text to insert + * @param pos + */ export function insertAtPos(text: string, pos: number): Promise { return syscall("editor.insertAtPos", text, pos); } +/** + * Replace the text in the specified range in the editor + * @param from the start position of the range + * @param to the end position of the range + * @param text the text to replace with + */ export function replaceRange( from: number, to: number, @@ -137,10 +229,21 @@ export function replaceRange( return syscall("editor.replaceRange", from, to, text); } +/** + * Move the cursor to the specified position in the editor + * @param pos the position to move the cursor to + * @param center whether to center the cursor in the editor after moving + */ export function moveCursor(pos: number, center = false): Promise { return syscall("editor.moveCursor", pos, center); } +/** + * Move the cursor to the specified line and column in the editor + * @param line the line number to move the cursor to + * @param column the column number to move the cursor to + * @param center whether to center the cursor in the editor after moving + */ export function moveCursorToLine( line: number, column = 1, @@ -149,14 +252,27 @@ export function moveCursorToLine( return syscall("editor.moveCursorToLine", line, column, center); } +/** + * Insert text at the cursor position in the editor + * @param text the text to insert + */ export function insertAtCursor(text: string): Promise { return syscall("editor.insertAtCursor", text); } +/** + * Dispatch a CodeMirror transaction: https://codemirror.net/docs/ref/#state.Transaction + */ export function dispatch(change: any): Promise { return syscall("editor.dispatch", change); } +/** + * Prompt the user for text input + * @param message the message to show in the prompt + * @param defaultValue a default value pre-filled in the prompt + * @returns + */ export function prompt( message: string, defaultValue = "", @@ -164,62 +280,112 @@ export function prompt( return syscall("editor.prompt", message, defaultValue); } +/** + * Prompt the user for confirmation + * @param message the message to show in the confirmation dialog + * @returns + */ export function confirm( message: string, ): Promise { return syscall("editor.confirm", message); } + +/** + * Get the value of a UI option + * @param key the key of the UI option to get + * @returns + */ export function getUiOption(key: string): Promise { return syscall("editor.getUiOption", key); } +/** + * Set the value of a UI option + * @param key the key of the UI option to set + * @param value the value to set the UI option to + */ export function setUiOption(key: string, value: any): Promise { return syscall("editor.setUiOption", key, value); } -// Vim specific -export function vimEx(exCommand: string): Promise { - return syscall("editor.vimEx", exCommand); -} - -// Folding +/** + * Perform a fold at the current cursor position + */ export function fold(): Promise { return syscall("editor.fold"); } +/** + * Perform an unfold at the current cursor position + */ export function unfold(): Promise { return syscall("editor.unfold"); } +/** + * Toggle the fold at the current cursor position + */ export function toggleFold(): Promise { return syscall("editor.toggleFold"); } +/** + * Fold all code blocks in the editor + */ export function foldAll(): Promise { return syscall("editor.foldAll"); } +/** + * Unfold all code blocks in the editor + */ export function unfoldAll(): Promise { return syscall("editor.unfoldAll"); } -// Undo/redo +/** + * Perform an undo operation of the last edit in the editor + */ export function undo(): Promise { return syscall("editor.undo"); } +/** + * Perform a redo operation of the last undo in the editor + */ export function redo(): Promise { return syscall("editor.redo"); } +/** + * Open the editor's native search panel + */ export function openSearchPanel(): Promise { return syscall("editor.openSearchPanel"); } +/** + * Copy the specified data to the clipboard + * @param data the data to copy + */ export function copyToClipboard(data: string | Blob): Promise { return syscall("editor.copyToClipboard", data); } +/** + * Delete the current line in the editor + */ export function deleteLine(): Promise { return syscall("editor.deleteLine"); } + +// Vim-mode specific syscalls + +/** + * Execute a Vim ex command + * @param exCommand the ex command to execute + */ +export function vimEx(exCommand: string): Promise { + return syscall("editor.vimEx", exCommand); +} diff --git a/plug-api/syscalls/event.ts b/plug-api/syscalls/event.ts index 97d36150..b2163ea8 100644 --- a/plug-api/syscalls/event.ts +++ b/plug-api/syscalls/event.ts @@ -1,5 +1,14 @@ import { syscall } from "../syscall.ts"; +/** + * Triggers an event on the SilverBullet event bus. + * This can be used to implement an RPC-style system too, because event handlers can return values, + * which are then accumulated in an array and returned to the caller. + * @param eventName the name of the event to trigger + * @param data payload to send with the event + * @param timeout optional timeout in milliseconds to wait for a response + * @returns an array of responses from the event handlers (if any) + */ export function dispatchEvent( eventName: string, data: any, @@ -24,6 +33,10 @@ export function dispatchEvent( }); } +/** + * List all events currently registered (listened to) on the SilverBullet event bus. + * @returns an array of event names + */ export function listEvents(): Promise { return syscall("event.list"); } diff --git a/plug-api/syscalls/jsonschema.ts b/plug-api/syscalls/jsonschema.ts index 317cafb2..79908a85 100644 --- a/plug-api/syscalls/jsonschema.ts +++ b/plug-api/syscalls/jsonschema.ts @@ -1,8 +1,14 @@ import { syscall } from "../syscall.ts"; +/** + * Validates a JSON object against a JSON schema. + * @param schema the JSON schema to validate against + * @param object the JSON object to validate + * @returns an error message if the object is invalid, or undefined if it is valid + */ export function validateObject( schema: any, object: any, -): Promise { +): Promise { return syscall("jsonschema.validateObject", schema, object); } diff --git a/plug-api/syscalls/language.ts b/plug-api/syscalls/language.ts index 710a08e2..81bc243c 100644 --- a/plug-api/syscalls/language.ts +++ b/plug-api/syscalls/language.ts @@ -14,6 +14,10 @@ export function parseLanguage( return syscall("language.parseLanguage", language, code); } +/** + * Lists all supported languages in fenced code blocks + * @returns a list of all supported languages + */ export function listLanguages(): Promise { return syscall("language.listLanguages"); } diff --git a/plug-api/syscalls/markdown.ts b/plug-api/syscalls/markdown.ts index 068e51f6..fa14614a 100644 --- a/plug-api/syscalls/markdown.ts +++ b/plug-api/syscalls/markdown.ts @@ -1,7 +1,11 @@ import { syscall } from "../syscall.ts"; - import type { ParseTree } from "../lib/tree.ts"; +/** + * Parses a piece of markdown text into a ParseTree. + * @param text the markdown text to parse + * @returns a ParseTree representation of the markdown text + */ export function parseMarkdown(text: string): Promise { return syscall("markdown.parseMarkdown", text); } diff --git a/plug-api/syscalls/mq.ts b/plug-api/syscalls/mq.ts index 56e67aba..43f87173 100644 --- a/plug-api/syscalls/mq.ts +++ b/plug-api/syscalls/mq.ts @@ -1,22 +1,50 @@ import { syscall } from "../syscall.ts"; import type { MQStats } from "../types.ts"; +/** + * Implements a simple Message Queue system. + */ + +/** + * Sends a message to a queue. + * @param queue the name of the queue to send the message to + * @param body the body of the message to send + */ export function send(queue: string, body: any): Promise { return syscall("mq.send", queue, body); } +/** + * Sends a batch of messages to a queue. + * @param queue the name of the queue + * @param bodies the bodies of the messages to send + */ export function batchSend(queue: string, bodies: any[]): Promise { return syscall("mq.batchSend", queue, bodies); } +/** + * Acknowledges a message from a queue, in case it needs to be explicitly acknowledged. + * @param queue the name of the queue the message came from + * @param id the id of the message to acknowledge + */ export function ack(queue: string, id: string): Promise { return syscall("mq.ack", queue, id); } +/** + * Acknowledges a batch of messages from a queue, in case they need to be explicitly acknowledged. + * @param queue the name of the queue the messages came from + * @param ids the ids of the messages to acknowledge + */ export function batchAck(queue: string, ids: string[]): Promise { return syscall("mq.batchAck", queue, ids); } +/** + * Retrieves stats on a particular queue. + * @param queue the name of the queue + */ export function getQueueStats(queue: string): Promise { return syscall("mq.getQueueStats", queue); } diff --git a/plug-api/syscalls/shell.ts b/plug-api/syscalls/shell.ts index 1a56e6dd..708b877e 100644 --- a/plug-api/syscalls/shell.ts +++ b/plug-api/syscalls/shell.ts @@ -1,5 +1,11 @@ import { syscall } from "../syscall.ts"; +/** + * Runs a shell command. + * @param cmd the command to run + * @param args the arguments to pass to the command + * @returns the stdout, stderr, and exit code of the command + */ export function run( cmd: string, args: string[], diff --git a/plug-api/syscalls/space.ts b/plug-api/syscalls/space.ts index e06b9ec6..f0d6f40f 100644 --- a/plug-api/syscalls/space.ts +++ b/plug-api/syscalls/space.ts @@ -1,37 +1,74 @@ import { syscall } from "../syscall.ts"; - import type { AttachmentMeta, FileMeta, PageMeta } from "../types.ts"; -export function listPages(unfiltered = false): Promise { - return syscall("space.listPages", unfiltered); +/** + * Lists all pages (files ending in .md) in the space. + * @param unfiltered + * @returns a list of all pages in the space represented as PageMeta objects + */ +export function listPages(): Promise { + return syscall("space.listPages"); } +/** + * Get metadata for a page in the space. + * @param name the name of the page to get metadata for + * @returns the metadata for the page + */ export function getPageMeta(name: string): Promise { return syscall("space.getPageMeta", name); } +/** + * Read a page from the space as text. + * @param name the name of the page to read + * @returns the text of the page + */ export function readPage( name: string, ): Promise { return syscall("space.readPage", name); } +/** + * Write a page to the space. + * @param name the name of the page to write + * @param text the text of the page to write + * @returns the metadata for the written page + */ export function writePage(name: string, text: string): Promise { return syscall("space.writePage", name, text); } +/** + * Delete a page from the space. + * @param name the name of the page to delete + */ export function deletePage(name: string): Promise { return syscall("space.deletePage", name); } +/** + * List all plugs in the space. + * @returns a list of all plugs in the space represented as FileMeta objects + */ export function listPlugs(): Promise { return syscall("space.listPlugs"); } +/** + * Lists all attachments in the space (all files not ending in .md). + * @returns a list of all attachments in the space represented as AttachmentMeta objects + */ export function listAttachments(): Promise { return syscall("space.listAttachments"); } +/** + * Get metadata for an attachment in the space. + * @param name the path of the attachment to get metadata for + * @returns the metadata for the attachment + */ export function getAttachmentMeta(name: string): Promise { return syscall("space.getAttachmentMeta", name); } @@ -68,19 +105,40 @@ export function deleteAttachment(name: string): Promise { return syscall("space.deleteAttachment", name); } -// FS +// Lower level-file operations + +/** + * List all files in the space (pages, attachments and plugs). + * @returns a list of all files in the space represented as FileMeta objects + */ export function listFiles(): Promise { return syscall("space.listFiles"); } +/** + * Read a file from the space as a Uint8Array. + * @param name the name of the file to read + * @returns the data of the file + */ export function readFile(name: string): Promise { return syscall("space.readFile", name); } +/** + * Get metadata for a file in the space. + * @param name the name of the file to get metadata for + * @returns the metadata for the file + */ export function getFileMeta(name: string): Promise { return syscall("space.getFileMeta", name); } +/** + * Write a file to the space. + * @param name the name of the file to write + * @param data the data of the file to write + * @returns the metadata for the written file + */ export function writeFile( name: string, data: Uint8Array, @@ -88,6 +146,10 @@ export function writeFile( return syscall("space.writeFile", name, data); } +/** + * Delete a file from the space. + * @param name the name of the file to delete + */ export function deleteFile(name: string): Promise { return syscall("space.deleteFile", name); } diff --git a/plug-api/syscalls/sync.ts b/plug-api/syscalls/sync.ts index b5beb381..9c9314b5 100644 --- a/plug-api/syscalls/sync.ts +++ b/plug-api/syscalls/sync.ts @@ -1,17 +1,34 @@ import { syscall } from "../syscall.ts"; +/** + * Syscalls that interact with the sync engine (when the client runs in Sync mode) + */ + +/** + * Checks if a sync is currently in progress + */ export function isSyncing(): Promise { return syscall("sync.isSyncing"); } +/** + * Checks if an initial sync has completed + */ export function hasInitialSyncCompleted(): Promise { return syscall("sync.hasInitialSyncCompleted"); } +/** + * Actively schedules a file to be synced. Sync will happen by default too, but this prioritizes the file. + * @param path the path to the file to sync + */ export function scheduleFileSync(path: string): Promise { return syscall("sync.scheduleFileSync", path); } +/** + * Schedules a sync of without waiting for the usual sync interval. + */ export function scheduleSpaceSync(): Promise { return syscall("sync.scheduleSpaceSync"); } diff --git a/plug-api/syscalls/system.ts b/plug-api/syscalls/system.ts index 7a64e318..70d07207 100644 --- a/plug-api/syscalls/system.ts +++ b/plug-api/syscalls/system.ts @@ -4,6 +4,12 @@ import type { ParseTree } from "../lib/tree.ts"; import { syscall } from "../syscall.ts"; import type { Config } from "../../type/config.ts"; +/** + * Invoke a plug function + * @param name a string representing the name of the function to invoke ("plug.functionName") + * @param args arguments to pass to the function + * @returns + */ export function invokeFunction( name: string, ...args: any[] @@ -11,20 +17,38 @@ export function invokeFunction( return syscall("system.invokeFunction", name, ...args); } -// Only available on the client +/** + * Invoke a client command by name + * Note: only available on the client + * @param name name of the command + * @param args arguments to pass to the command + */ export function invokeCommand(name: string, args?: string[]): Promise { return syscall("system.invokeCommand", name, args); } -// Only available on the client -export function listCommands(): Promise<{ [key: string]: CommandDef }> { +/** + * Lists all commands available + * @returns a map of all available commands + */ +export function listCommands(): Promise> { return syscall("system.listCommands"); } +/** + * Lists all syscalls available + * @returns a list of all available syscalls + */ export function listSyscalls(): Promise { return syscall("system.listSyscalls"); } +/** + * Invoke a space function by name + * @param name a string representing the name of the function to invoke + * @param args arguments to pass to the function + * @returns the value returned by the function + */ export function invokeSpaceFunction( name: string, ...args: any[] @@ -32,6 +56,9 @@ export function invokeSpaceFunction( return syscall("system.invokeSpaceFunction", name, ...args); } +/** + * Applies attribute extractors to a ParseTree + */ export function applyAttributeExtractors( tags: string[], text: string, @@ -43,6 +70,7 @@ export function applyAttributeExtractors( /** * Loads a particular space configuration key (or all of them when no key is spacified) * @param key the key to load, when not specified, all keys are loaded + * @param defaultValue the default value to return when the key is not found * @returns either the value of the key or all keys as a Record */ export async function getSpaceConfig( @@ -52,23 +80,39 @@ export async function getSpaceConfig( return (await syscall("system.getSpaceConfig", key)) ?? defaultValue; } +/** + * Trigger a reload of all plugs + * @returns + */ export function reloadPlugs(): Promise { return syscall("system.reloadPlugs"); } +/** + * Trigger an explicit reload of the configuration + * @returns the new configuration + */ export function reloadConfig(): Promise { return syscall("system.reloadConfig"); } -// Returns what runtime environment this plug is run in, e.g. "server" or "client" can be undefined, which would mean a hybrid environment (such as mobile) +/** + * Returns what runtime environment this plug is run in, e.g. "server" or "client" can be undefined, which would mean a hybrid environment (such as mobile) + */ export function getEnv(): Promise { return syscall("system.getEnv"); } +/** + * Returns the current mode of the system, either "ro" (read-only) or "rw" (read-write) + */ export function getMode(): Promise<"ro" | "rw"> { return syscall("system.getMode"); } +/** + * Returns the SilverBullet version + */ export function getVersion(): Promise { return syscall("system.getVersion"); } diff --git a/plug-api/syscalls/template.ts b/plug-api/syscalls/template.ts index 540b39e2..66cbd011 100644 --- a/plug-api/syscalls/template.ts +++ b/plug-api/syscalls/template.ts @@ -1,11 +1,12 @@ +import type { AST } from "@silverbulletmd/silverbullet/lib/tree"; import { syscall } from "../syscall.ts"; /** - * Renders - * @param template - * @param obj - * @param globals - * @returns + * Renders a template with the given object and globals. + * @param template the text of the template to render + * @param obj the object to render the template with + * @param globals the globals to render the template with + * @returns the rendered template */ export function renderTemplate( template: string, @@ -15,8 +16,13 @@ export function renderTemplate( return syscall("template.renderTemplate", template, obj, globals); } +/** + * Parses a template into an AST. + * @param template the text of the template to parse + * @returns an AST representation of the template + */ export function parseTemplate( template: string, -): Promise { +): Promise { return syscall("template.parseTemplate", template); } diff --git a/plug-api/syscalls/yaml.ts b/plug-api/syscalls/yaml.ts index 3fb8fa60..bcd2eb02 100644 --- a/plug-api/syscalls/yaml.ts +++ b/plug-api/syscalls/yaml.ts @@ -1,11 +1,21 @@ import { syscall } from "../syscall.ts"; +/** + * Parses a YAML string into a JavaScript object. + * @param text the YAML text to parse + * @returns a JavaScript object representation of the YAML text + */ export function parse( text: string, ): Promise { return syscall("yaml.parse", text); } +/** + * Converts a JavaScript object into a YAML string. + * @param obj the object to stringify + * @returns a YAML string representation of the object + */ export function stringify( obj: any, ): Promise { diff --git a/server/http_server.ts b/server/http_server.ts index fdf5db07..4dc1f883 100644 --- a/server/http_server.ts +++ b/server/http_server.ts @@ -1,6 +1,6 @@ import { deleteCookie, getCookie, setCookie } from "hono/helper.ts"; import { cors } from "hono/middleware.ts"; -import { type Context, Hono, type HonoRequest, validator } from "hono/mod.ts"; +import { type Context, Hono, validator } from "hono/mod.ts"; import type { AssetBundle } from "$lib/asset_bundle/bundle.ts"; import type { EndpointRequest, @@ -8,8 +8,7 @@ import type { FileMeta, } from "@silverbulletmd/silverbullet/types"; import type { ShellRequest } from "@silverbulletmd/silverbullet/type/rpc"; -import { SpaceServer } from "./instance.ts"; -import type { SpaceServerConfig } from "./instance.ts"; +import { SpaceServer } from "./space_server.ts"; import type { KvPrimitives } from "$lib/data/kv_primitives.ts"; import { PrefixedKvPrimitives } from "$lib/data/prefixed_kv_primitives.ts"; import { extendedMarkdownLanguage } from "$common/markdown_parser/parser.ts"; @@ -33,7 +32,15 @@ export type ServerOptions = { certFile?: string; keyFile?: string; - configs: Map; + // Enable username/password auth + auth?: { user: string; pass: string }; + // Additional API auth token + authToken?: string; + pagesPath: string; + shellBackend: string; + syncOnly: boolean; + readOnly: boolean; + enableSpaceScript: boolean; }; export class HttpServer { @@ -46,11 +53,11 @@ export class HttpServer { keyFile: string | undefined; certFile: string | undefined; - spaceServers = new Map>(); + // Available after start() + spaceServer!: SpaceServer; baseKvPrimitives: KvPrimitives; - configs: Map; - constructor(options: ServerOptions) { + constructor(private options: ServerOptions) { this.app = new Hono(); this.clientAssetBundle = options.clientAssetBundle; this.plugAssetBundle = options.plugAssetBundle; @@ -59,60 +66,6 @@ export class HttpServer { this.keyFile = options.keyFile; this.certFile = options.certFile; this.baseKvPrimitives = options.baseKvPrimitives; - this.configs = options.configs; - } - - async bootSpaceServer(config: SpaceServerConfig): Promise { - const spaceServer = new SpaceServer( - config, - this.plugAssetBundle, - new PrefixedKvPrimitives(this.baseKvPrimitives, [ - config.namespace, - ]), - ); - await spaceServer.init(); - - return spaceServer; - } - - determineConfig(req: HonoRequest): [string, SpaceServerConfig] { - const url = new URL(req.url); - let hostname = url.host; // hostname:port - - // First try a full match - let config = this.configs.get(hostname); - if (config) { - return [hostname, config]; - } - - // Then rip off the port and try again - hostname = hostname.split(":")[0]; - config = this.configs.get(hostname); - if (config) { - return [hostname, config]; - } - - // If all else fails, try the wildcard - config = this.configs.get("*"); - - if (config) { - return ["*", config]; - } - - throw new Error(`No space server config found for hostname ${hostname}`); - } - - ensureSpaceServer(req: HonoRequest): Promise { - const [matchedHostname, config] = this.determineConfig(req); - const spaceServer = this.spaceServers.get(matchedHostname); - if (spaceServer) { - return spaceServer; - } - // And then boot the thing, async - const spaceServerPromise = this.bootSpaceServer(config); - // But immediately write the promise to the map so that we don't boot it twice - this.spaceServers.set(matchedHostname, spaceServerPromise); - return spaceServerPromise; } // Replaces some template variables in index.html in a rather ad-hoc manner, but YOLO @@ -179,19 +132,26 @@ export class HttpServer { ); } - start() { + async start() { // Serve static files (javascript, css, html) this.serveStatic(); this.serveCustomEndpoints(); this.addAuth(); this.addFsRoutes(); + // Boot space server + this.spaceServer = new SpaceServer( + this.options, + this.plugAssetBundle, + new PrefixedKvPrimitives(this.baseKvPrimitives, ["*"]), // * for backwards compatibility reasons + ); + await this.spaceServer.init(); + // Fallback, serve the UI index.html - this.app.use("*", async (c) => { - const spaceServer = await this.ensureSpaceServer(c.req); + this.app.use("*", (c) => { const url = new URL(c.req.url); const pageName = decodeURI(url.pathname.slice(1)); - return this.renderHtmlPage(spaceServer, pageName, c); + return this.renderHtmlPage(this.spaceServer, pageName, c); }); this.abortController = new AbortController(); @@ -221,16 +181,16 @@ export class HttpServer { // Custom endpoints can be defined in the server serveCustomEndpoints() { this.app.use("/_/*", async (ctx) => { - const spaceServer = await this.ensureSpaceServer(ctx.req); const req = ctx.req; const url = new URL(req.url); - if (!spaceServer.serverSystem) { + if (!this.spaceServer.serverSystem) { return ctx.text("No server system available", 500); } try { const path = url.pathname.slice(2); // Remove the /_ - const responses: EndpointResponse[] = await spaceServer.serverSystem + const responses: EndpointResponse[] = await this.spaceServer + .serverSystem .eventHook.dispatchEvent(`http:request:${path}`, { fullPath: url.pathname, path, @@ -278,17 +238,17 @@ export class HttpServer { } serveStatic() { - this.app.use("*", async (c, next) => { + this.app.use("*", (c, next): Promise => { const req = c.req; - const spaceServer = await this.ensureSpaceServer(req); const url = new URL(req.url); // console.log("URL", url); if ( url.pathname === "/" ) { // Serve the UI (index.html) - const indexPage = parsePageRef(spaceServer.config?.indexPage!).page; - return this.renderHtmlPage(spaceServer, indexPage, c); + const indexPage = + parsePageRef(this.spaceServer.config?.indexPage!).page; + return this.renderHtmlPage(this.spaceServer, indexPage, c); } try { const assetName = url.pathname.slice(1); @@ -301,7 +261,7 @@ export class HttpServer { utcDateString(this.clientAssetBundle.getMtime(assetName)) && assetName !== "service_worker.js" ) { - return c.body(null, 304); + return Promise.resolve(c.body(null, 304)); } c.status(200); c.header("Content-type", this.clientAssetBundle.getMimeType(assetName)); @@ -327,18 +287,19 @@ export class HttpServer { "{{CONFIG_HASH}}", base64Encode( JSON.stringify([ - spaceServer.syncOnly, - spaceServer.readOnly, - spaceServer.enableSpaceScript, + this.spaceServer.syncOnly, + this.spaceServer.readOnly, + this.spaceServer.enableSpaceScript, ]), ), ); } - return c.body(data); + return Promise.resolve(c.body(data)); } // else e.g. HEAD, OPTIONS, don't send body } catch { return next(); } + return Promise.resolve(); }); } @@ -381,16 +342,14 @@ export class HttpServer { const url = new URL(c.req.url); const { username, password } = req.valid("form"); - const spaceServer = await this.ensureSpaceServer(req); - const { user: expectedUser, pass: expectedPassword, - } = spaceServer.auth!; + } = this.spaceServer.auth!; if (username === expectedUser && password === expectedPassword) { // Generate a JWT and set it as a cookie - const jwt = await spaceServer.jwtIssuer.createJWT( + const jwt = await this.spaceServer.jwtIssuer.createJWT( { username }, authenticationExpirySeconds, ); @@ -417,8 +376,7 @@ export class HttpServer { // Check auth this.app.use("*", async (c, next) => { const req = c.req; - const spaceServer = await this.ensureSpaceServer(req); - if (!spaceServer.auth && !spaceServer.authToken) { + if (!this.spaceServer.auth && !this.spaceServer.authToken) { // Auth disabled in this config, skip return next(); } @@ -435,12 +393,12 @@ export class HttpServer { if (!excludedPaths.includes(url.pathname)) { const authCookie = getCookie(c, authCookieName(host)); - if (!authCookie && spaceServer.authToken) { + if (!authCookie && this.spaceServer.authToken) { // Attempt Bearer Authorization based authentication const authHeader = req.header("Authorization"); if (authHeader && authHeader.startsWith("Bearer ")) { const authToken = authHeader.slice("Bearer ".length); - if (authToken === spaceServer.authToken) { + if (authToken === this.spaceServer.authToken) { // All good, let's proceed return next(); } else { @@ -455,12 +413,13 @@ export class HttpServer { console.log("Unauthorized access, redirecting to auth page"); return redirectToAuth(); } - const { user: expectedUser } = spaceServer.auth!; + const { user: expectedUser } = this.spaceServer.auth!; try { - const verifiedJwt = await spaceServer.jwtIssuer.verifyAndDecodeJWT( - authCookie, - ); + const verifiedJwt = await this.spaceServer.jwtIssuer + .verifyAndDecodeJWT( + authCookie, + ); if (verifiedJwt.username !== expectedUser) { throw new Error("Username mismatch"); } @@ -490,12 +449,11 @@ export class HttpServer { // File list this.app.get("/index.json", async (c) => { const req = c.req; - const spaceServer = await this.ensureSpaceServer(req); if (req.header("X-Sync-Mode")) { // Only handle direct requests for a JSON representation of the file list - const files = await spaceServer.spacePrimitives.fetchFileList(); + const files = await this.spaceServer.spacePrimitives.fetchFileList(); return c.json(files, 200, { - "X-Space-Path": spaceServer.pagesPath, + "X-Space-Path": this.spaceServer.pagesPath, }); } else { // Otherwise, redirect to the UI @@ -514,11 +472,10 @@ export class HttpServer { // RPC shell this.app.post("/.rpc/shell", async (c) => { const req = c.req; - const spaceServer = await this.ensureSpaceServer(req); const body = await req.json(); try { const shellCommand: ShellRequest = body; - const shellResponse = await spaceServer.shellBackend.handle( + const shellResponse = await this.spaceServer.shellBackend.handle( shellCommand, ); return c.json(shellResponse); @@ -533,15 +490,14 @@ export class HttpServer { const req = c.req; const syscall = req.param("syscall")!; const plugName = req.param("plugName")!; - const spaceServer = await this.ensureSpaceServer(req); const body = await req.json(); try { - if (spaceServer.syncOnly) { + if (this.spaceServer.syncOnly) { return c.text("Sync only mode, no syscalls allowed", 400); } const args: string[] = body; try { - const result = await spaceServer.system!.syscall( + const result = await this.spaceServer.system!.syscall( { plug: plugName === "_" ? undefined : plugName }, syscall, args, @@ -566,7 +522,6 @@ export class HttpServer { this.app.get(filePathRegex, async (c) => { const req = c.req; const name = req.param("path")!; - const spaceServer = await this.ensureSpaceServer(req); console.log("Requested file", name); if ( @@ -627,12 +582,12 @@ export class HttpServer { try { if (req.header("X-Get-Meta")) { // Getting meta via GET request - const fileData = await spaceServer.spacePrimitives.getFileMeta( + const fileData = await this.spaceServer.spacePrimitives.getFileMeta( name, ); return c.text("", 200, this.fileMetaToHeaders(fileData)); } - const fileData = await spaceServer.spacePrimitives.readFile(name); + const fileData = await this.spaceServer.spacePrimitives.readFile(name); const lastModifiedHeader = new Date(fileData.meta.lastModified) .toUTCString(); if ( @@ -652,8 +607,7 @@ export class HttpServer { async (c) => { const req = c.req; const name = req.param("path")!; - const spaceServer = await this.ensureSpaceServer(req); - if (spaceServer.readOnly) { + if (this.spaceServer.readOnly) { return c.text("Read only mode, no writes allowed", 405); } console.log("Writing file", name); @@ -670,7 +624,7 @@ export class HttpServer { const body = await req.arrayBuffer(); try { - const meta = await spaceServer.spacePrimitives.writeFile( + const meta = await this.spaceServer.spacePrimitives.writeFile( name, new Uint8Array(body), ); @@ -683,8 +637,7 @@ export class HttpServer { ).delete(async (c) => { const req = c.req; const name = req.param("path")!; - const spaceServer = await this.ensureSpaceServer(req); - if (spaceServer.readOnly) { + if (this.spaceServer.readOnly) { return c.text("Read only mode, no writes allowed", 405); } console.log("Deleting file", name); @@ -693,7 +646,7 @@ export class HttpServer { return c.text("Forbidden", 403); } try { - await spaceServer.spacePrimitives.deleteFile(name); + await this.spaceServer.spacePrimitives.deleteFile(name); return c.text("OK"); } catch (e: any) { console.error("Error deleting attachment", e); @@ -707,8 +660,7 @@ export class HttpServer { proxyPathRegex, async (c, next) => { const req = c.req; - const spaceServer = await this.ensureSpaceServer(req); - if (spaceServer.readOnly) { + if (this.spaceServer.readOnly) { return c.text("Read only mode, no federation proxy allowed", 405); } let url = req.param("uri")!.slice(1); diff --git a/server/shell_backend.ts b/server/shell_backend.ts index c3425fc4..755bebb8 100644 --- a/server/shell_backend.ts +++ b/server/shell_backend.ts @@ -1,5 +1,5 @@ -import type { SpaceServerConfig } from "./instance.ts"; import type { ShellRequest, ShellResponse } from "../type/rpc.ts"; +import type { ServerOptions } from "./http_server.ts"; /** * Configuration via environment variables: @@ -7,12 +7,12 @@ import type { ShellRequest, ShellResponse } from "../type/rpc.ts"; */ export function determineShellBackend( - spaceServerConfig: SpaceServerConfig, + serverOptions: ServerOptions, ): ShellBackend { const backendConfig = Deno.env.get("SB_SHELL_BACKEND") || "local"; switch (backendConfig) { case "local": - return new LocalShell(spaceServerConfig.pagesPath); + return new LocalShell(serverOptions.pagesPath); default: console.info( "Running in shellless mode, meaning shell commands are disabled", diff --git a/server/instance.ts b/server/space_server.ts similarity index 85% rename from server/instance.ts rename to server/space_server.ts index 1c7039b2..3217b54f 100644 --- a/server/instance.ts +++ b/server/space_server.ts @@ -22,20 +22,7 @@ import { determineShellBackend, NotSupportedShell } from "./shell_backend.ts"; import type { ShellBackend } from "./shell_backend.ts"; import { determineStorageBackend } from "./storage_backend.ts"; import type { Config } from "../type/config.ts"; - -export type SpaceServerConfig = { - hostname: string; - namespace: string; - // Enable username/password auth - auth?: { user: string; pass: string }; - // Additional API auth token - authToken?: string; - pagesPath: string; - shellBackend: string; - syncOnly: boolean; - readOnly: boolean; - enableSpaceScript: boolean; -}; +import type { ServerOptions } from "./http_server.ts"; // Equivalent of Client on the server export class SpaceServer implements ConfigContainer { @@ -58,24 +45,24 @@ export class SpaceServer implements ConfigContainer { enableSpaceScript: boolean; constructor( - config: SpaceServerConfig, + options: ServerOptions, private plugAssetBundle: AssetBundle, private kvPrimitives: KvPrimitives, ) { - this.pagesPath = config.pagesPath; - this.hostname = config.hostname; - this.auth = config.auth; - this.authToken = config.authToken; - this.syncOnly = config.syncOnly; - this.readOnly = config.readOnly; + this.pagesPath = options.pagesPath; + this.hostname = options.hostname; + this.auth = options.auth; + this.authToken = options.authToken; + this.syncOnly = options.syncOnly; + this.readOnly = options.readOnly; this.config = defaultConfig; - this.enableSpaceScript = config.enableSpaceScript; + this.enableSpaceScript = options.enableSpaceScript; this.jwtIssuer = new JWTIssuer(kvPrimitives); - this.shellBackend = config.readOnly + this.shellBackend = options.readOnly ? new NotSupportedShell() // No shell for read only mode - : determineShellBackend(config); + : determineShellBackend(options); } async init() {