Merge branch 'main' of github.com:silverbulletmd/silverbullet
commit
2de149dca1
|
@ -1,8 +1,3 @@
|
||||||
import type {
|
|
||||||
KvQuery,
|
|
||||||
ObjectQuery,
|
|
||||||
ObjectValue,
|
|
||||||
} from "@silverbulletmd/silverbullet/types";
|
|
||||||
import type { SysCallMapping } from "$lib/plugos/system.ts";
|
import type { SysCallMapping } from "$lib/plugos/system.ts";
|
||||||
import {
|
import {
|
||||||
findAllQueryVariables,
|
findAllQueryVariables,
|
||||||
|
@ -21,61 +16,6 @@ import type { CommonSystem } from "$common/common_system.ts";
|
||||||
|
|
||||||
export function indexSyscalls(commonSystem: CommonSystem): SysCallMapping {
|
export function indexSyscalls(commonSystem: CommonSystem): SysCallMapping {
|
||||||
return {
|
return {
|
||||||
"index.indexObjects": (ctx, page: string, objects: ObjectValue<any>[]) => {
|
|
||||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
|
||||||
"index.indexObjects",
|
|
||||||
page,
|
|
||||||
objects,
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
"index.queryObjects": (
|
|
||||||
ctx,
|
|
||||||
tag: string,
|
|
||||||
query: ObjectQuery,
|
|
||||||
ttlSecs?: number,
|
|
||||||
) => {
|
|
||||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
|
||||||
"index.queryObjects",
|
|
||||||
tag,
|
|
||||||
query,
|
|
||||||
ttlSecs,
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
"index.queryLuaObjects": (
|
|
||||||
ctx,
|
|
||||||
tag: string,
|
|
||||||
query: LuaCollectionQuery,
|
|
||||||
scopedVariables?: Record<string, any>,
|
|
||||||
) => {
|
|
||||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
|
||||||
"index.queryLuaObjects",
|
|
||||||
tag,
|
|
||||||
query,
|
|
||||||
scopedVariables,
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
"index.queryDeleteObjects": (ctx, tag: string, query: ObjectQuery) => {
|
|
||||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
|
||||||
"index.queryDeleteObjects",
|
|
||||||
tag,
|
|
||||||
query,
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
"index.query": (ctx, query: KvQuery, variables?: Record<string, any>) => {
|
|
||||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
|
||||||
"index.query",
|
|
||||||
query,
|
|
||||||
variables,
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
"index.getObjectByRef": (ctx, page: string, tag: string, ref: string) => {
|
|
||||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
|
||||||
"index.getObjectByRef",
|
|
||||||
page,
|
|
||||||
tag,
|
|
||||||
ref,
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
"index.tag": (_ctx, tagName: string): LuaQueryCollection => {
|
"index.tag": (_ctx, tagName: string): LuaQueryCollection => {
|
||||||
return {
|
return {
|
||||||
query: async (
|
query: async (
|
||||||
|
|
|
@ -62,6 +62,10 @@ export type SlashCommandHookT = {
|
||||||
slashCommand?: SlashCommandDef;
|
slashCommand?: SlashCommandDef;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SyscallHookT = {
|
||||||
|
syscall?: string;
|
||||||
|
};
|
||||||
|
|
||||||
/** Silverbullet hooks give plugs access to silverbullet core systems.
|
/** Silverbullet hooks give plugs access to silverbullet core systems.
|
||||||
*
|
*
|
||||||
* Hooks are associated with typescript functions through a manifest file.
|
* Hooks are associated with typescript functions through a manifest file.
|
||||||
|
@ -78,7 +82,8 @@ export type SilverBulletHooks =
|
||||||
& CodeWidgetT
|
& CodeWidgetT
|
||||||
& PanelWidgetT
|
& PanelWidgetT
|
||||||
& EndpointHookT
|
& EndpointHookT
|
||||||
& PlugNamespaceHookT;
|
& PlugNamespaceHookT
|
||||||
|
& SyscallHookT;
|
||||||
|
|
||||||
/** A plug manifest configures hooks, declares syntax extensions, and describes plug metadata.
|
/** A plug manifest configures hooks, declares syntax extensions, and describes plug metadata.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { datastore, system } from "@silverbulletmd/silverbullet/syscalls";
|
import {
|
||||||
|
datastore,
|
||||||
|
markdown,
|
||||||
|
system,
|
||||||
|
} from "@silverbulletmd/silverbullet/syscalls";
|
||||||
import type {
|
import type {
|
||||||
KV,
|
KV,
|
||||||
KvKey,
|
KvKey,
|
||||||
|
@ -10,6 +14,12 @@ import type { QueryProviderEvent } from "../../plug-api/types.ts";
|
||||||
import { determineType, type SimpleJSONType } from "./attributes.ts";
|
import { determineType, type SimpleJSONType } from "./attributes.ts";
|
||||||
import { ttlCache } from "$lib/memory_cache.ts";
|
import { ttlCache } from "$lib/memory_cache.ts";
|
||||||
import type { LuaCollectionQuery } from "$common/space_lua/query_collection.ts";
|
import type { LuaCollectionQuery } from "$common/space_lua/query_collection.ts";
|
||||||
|
import {
|
||||||
|
extractFrontmatter as extractFrontmatterFromTree,
|
||||||
|
type FrontMatter,
|
||||||
|
type FrontmatterExtractOptions,
|
||||||
|
} from "../../plug-api/lib/frontmatter.ts";
|
||||||
|
import { renderToText } from "@silverbulletmd/silverbullet/lib/tree";
|
||||||
|
|
||||||
const indexKey = "idx";
|
const indexKey = "idx";
|
||||||
const pageKey = "ridx";
|
const pageKey = "ridx";
|
||||||
|
@ -245,3 +255,12 @@ export async function discoverSources() {
|
||||||
// And concatenate all the tags from the schema
|
// And concatenate all the tags from the schema
|
||||||
.concat(Object.keys(schema.tag));
|
.concat(Object.keys(schema.tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function extractFrontmatter(
|
||||||
|
text: string,
|
||||||
|
extractOptions: FrontmatterExtractOptions = {},
|
||||||
|
): Promise<{ frontmatter: FrontMatter; text: string }> {
|
||||||
|
const tree = await markdown.parseMarkdown(text);
|
||||||
|
const frontmatter = await extractFrontmatterFromTree(tree, extractOptions);
|
||||||
|
return { frontmatter, text: renderToText(tree) };
|
||||||
|
}
|
||||||
|
|
|
@ -6,18 +6,29 @@ functions:
|
||||||
env: server
|
env: server
|
||||||
query:
|
query:
|
||||||
path: api.ts:query
|
path: api.ts:query
|
||||||
|
syscall: index.query
|
||||||
indexObjects:
|
indexObjects:
|
||||||
path: api.ts:indexObjects
|
path: api.ts:indexObjects
|
||||||
|
syscall: index.indexObjects
|
||||||
env: server
|
env: server
|
||||||
queryObjects:
|
queryObjects:
|
||||||
path: api.ts:queryObjects
|
path: api.ts:queryObjects
|
||||||
|
syscall: index.queryObjects
|
||||||
# Note: not setting env: server to allow for client-side datastore query caching
|
# Note: not setting env: server to allow for client-side datastore query caching
|
||||||
queryLuaObjects:
|
queryLuaObjects:
|
||||||
path: api.ts:queryLuaObjects
|
path: api.ts:queryLuaObjects
|
||||||
|
syscall: index.queryLuaObjects
|
||||||
# Note: not setting env: server to allow for client-side datastore query caching
|
# Note: not setting env: server to allow for client-side datastore query caching
|
||||||
getObjectByRef:
|
getObjectByRef:
|
||||||
path: api.ts:getObjectByRef
|
path: api.ts:getObjectByRef
|
||||||
|
syscall: index.getObjectByRef
|
||||||
env: server
|
env: server
|
||||||
|
|
||||||
|
extractFrontmatter:
|
||||||
|
path: api.ts:extractFrontmatter
|
||||||
|
syscall: index.extractFrontmatter
|
||||||
|
|
||||||
|
# Event handlers
|
||||||
objectSourceProvider:
|
objectSourceProvider:
|
||||||
path: api.ts:objectSourceProvider
|
path: api.ts:objectSourceProvider
|
||||||
events:
|
events:
|
||||||
|
|
|
@ -37,6 +37,7 @@ import { ensureSpaceIndex } from "$common/space_index.ts";
|
||||||
import type { FileMeta } from "../plug-api/types.ts";
|
import type { FileMeta } from "../plug-api/types.ts";
|
||||||
import { CommandHook } from "$common/hooks/command.ts";
|
import { CommandHook } from "$common/hooks/command.ts";
|
||||||
import { CommonSystem } from "$common/common_system.ts";
|
import { CommonSystem } from "$common/common_system.ts";
|
||||||
|
import { SyscallHook } from "../web/hooks/syscall.ts";
|
||||||
import type { DataStoreMQ } from "$lib/data/mq.datastore.ts";
|
import type { DataStoreMQ } from "$lib/data/mq.datastore.ts";
|
||||||
import { plugPrefix } from "$common/spaces/constants.ts";
|
import { plugPrefix } from "$common/spaces/constants.ts";
|
||||||
import { base64EncodedDataUrl } from "$lib/crypto.ts";
|
import { base64EncodedDataUrl } from "$lib/crypto.ts";
|
||||||
|
@ -100,6 +101,9 @@ export class ServerSystem extends CommonSystem {
|
||||||
|
|
||||||
this.system.addHook(new MQHook(this.system, this.mq));
|
this.system.addHook(new MQHook(this.system, this.mq));
|
||||||
|
|
||||||
|
// Syscall hook
|
||||||
|
this.system.addHook(new SyscallHook());
|
||||||
|
|
||||||
const codeWidgetHook = new CodeWidgetHook();
|
const codeWidgetHook = new CodeWidgetHook();
|
||||||
|
|
||||||
this.system.addHook(codeWidgetHook);
|
this.system.addHook(codeWidgetHook);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import type { Client } from "./client.ts";
|
||||||
import { CodeWidgetHook } from "./hooks/code_widget.ts";
|
import { CodeWidgetHook } from "./hooks/code_widget.ts";
|
||||||
import { CommandHook } from "$common/hooks/command.ts";
|
import { CommandHook } from "$common/hooks/command.ts";
|
||||||
import { SlashCommandHook } from "./hooks/slash_command.ts";
|
import { SlashCommandHook } from "./hooks/slash_command.ts";
|
||||||
|
import { SyscallHook } from "./hooks/syscall.ts";
|
||||||
import { clientStoreSyscalls } from "./syscalls/clientStore.ts";
|
import { clientStoreSyscalls } from "./syscalls/clientStore.ts";
|
||||||
import { debugSyscalls } from "./syscalls/debug.ts";
|
import { debugSyscalls } from "./syscalls/debug.ts";
|
||||||
import { editorSyscalls } from "./syscalls/editor.ts";
|
import { editorSyscalls } from "./syscalls/editor.ts";
|
||||||
|
@ -128,6 +129,9 @@ export class ClientSystem extends CommonSystem {
|
||||||
this.slashCommandHook = new SlashCommandHook(this.client, this);
|
this.slashCommandHook = new SlashCommandHook(this.client, this);
|
||||||
this.system.addHook(this.slashCommandHook);
|
this.system.addHook(this.slashCommandHook);
|
||||||
|
|
||||||
|
// Syscall hook
|
||||||
|
this.system.addHook(new SyscallHook());
|
||||||
|
|
||||||
this.eventHook.addLocalListener(
|
this.eventHook.addLocalListener(
|
||||||
"file:changed",
|
"file:changed",
|
||||||
async (path: string, _selfUpdate, _oldHash, newHash) => {
|
async (path: string, _selfUpdate, _oldHash, newHash) => {
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
import type { Hook, Manifest } from "$lib/plugos/types.ts";
|
||||||
|
import type { System } from "$lib/plugos/system.ts";
|
||||||
|
import type { SyscallHookT } from "$lib/manifest.ts";
|
||||||
|
import type { SysCallMapping } from "$lib/plugos/system.ts";
|
||||||
|
|
||||||
|
export class SyscallHook implements Hook<SyscallHookT> {
|
||||||
|
apply(system: System<SyscallHookT>): void {
|
||||||
|
this.registerSyscalls(system);
|
||||||
|
system.on({
|
||||||
|
plugLoaded: () => {
|
||||||
|
this.registerSyscalls(system);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
registerSyscalls(system: System<SyscallHookT>) {
|
||||||
|
// Register syscalls from all loaded plugs
|
||||||
|
for (const plug of system.loadedPlugs.values()) {
|
||||||
|
const syscalls: SysCallMapping = {};
|
||||||
|
|
||||||
|
for (
|
||||||
|
const [name, functionDef] of Object.entries(plug.manifest!.functions)
|
||||||
|
) {
|
||||||
|
if (!functionDef.syscall) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const syscallName = functionDef.syscall;
|
||||||
|
|
||||||
|
console.log("Registering plug syscall", syscallName, "for", name);
|
||||||
|
// Add the syscall to our mapping
|
||||||
|
syscalls[syscallName] = (ctx, ...args) => {
|
||||||
|
// Delegate to the system to invoke the function
|
||||||
|
return system.syscall(ctx, "system.invokeFunction", [
|
||||||
|
`${plug.manifest!.name}.${name}`,
|
||||||
|
...args,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register the syscalls with no required permissions
|
||||||
|
system.registerSyscalls([], syscalls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateManifest(manifest: Manifest<SyscallHookT>): string[] {
|
||||||
|
const errors: string[] = [];
|
||||||
|
for (const [name, functionDef] of Object.entries(manifest.functions)) {
|
||||||
|
if (!functionDef.syscall) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate syscall name is provided
|
||||||
|
if (!functionDef.syscall) {
|
||||||
|
errors.push(`Function ${name} has a syscall but no name`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate syscall name format (should be namespaced)
|
||||||
|
if (!functionDef.syscall.includes(".")) {
|
||||||
|
errors.push(
|
||||||
|
`Function ${name} has invalid syscall name "${functionDef.syscall}" - must be in format "namespace.name"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,9 @@
|
||||||
|
---
|
||||||
|
testattribute: 10
|
||||||
|
---
|
||||||
|
|
||||||
|
#apidoc
|
||||||
|
|
||||||
The `index` API provides functions for interacting with SilverBullet's [[Objects]], allowing you to store and query page-associated data.
|
The `index` API provides functions for interacting with SilverBullet's [[Objects]], allowing you to store and query page-associated data.
|
||||||
|
|
||||||
## Object Operations
|
## Object Operations
|
||||||
|
@ -38,3 +44,19 @@ local task = index.getObjectByRef("my page", "mytask", "task1")
|
||||||
if task then
|
if task then
|
||||||
print("Found task: " .. task.content)
|
print("Found task: " .. task.content)
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## index.extractFrontmatter(text, extractOptions)
|
||||||
|
Extracts frontmatter from a markdown document (whose text is provided as argument), possibly cleaning it up. It also parses top-level tags consistent with SilverBullet's tag indexing system.
|
||||||
|
|
||||||
|
It returns a table with two keys:
|
||||||
|
- `frontmatter`: A table containing the parsed frontmatter.
|
||||||
|
- `text`: The text of the document, with any changes applied requested with the `extractOptions`.
|
||||||
|
|
||||||
|
The `extractOptions` is an optional table that can contain the following keys (which will affect the returned `text`):
|
||||||
|
- `removeKeys`: An array of keys to remove from the frontmatter.
|
||||||
|
- `removeTags`: A boolean or array of tags to remove from the frontmatter.
|
||||||
|
- `removeFrontmatterSection`: A boolean to remove the frontmatter section from the document.
|
||||||
|
|
||||||
|
Example applied to this page:
|
||||||
|
${(index.extractFrontmatter(editor.getText())).frontmatter}
|
Loading…
Reference in New Issue