@silverbulletmd/silverbullet 2.9.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -25
- package/client/asset_bundle/bundle.ts +0 -1
- package/client/config.ts +343 -0
- package/client/markdown_parser/constants.ts +3 -1
- package/client/plugos/hooks/command.ts +0 -5
- package/client/plugos/hooks/event.ts +0 -6
- package/client/plugos/hooks/mq.ts +0 -3
- package/client/plugos/hooks/slash_command.ts +1 -7
- package/client/plugos/hooks/syscall.ts +24 -14
- package/client/plugos/manifest_cache.ts +0 -32
- package/client/plugos/plug.ts +0 -3
- package/client/plugos/plug_compile.ts +3 -13
- package/client/plugos/sandboxes/worker_sandbox.ts +19 -9
- package/client/plugos/syscalls/asset.ts +61 -23
- package/client/plugos/syscalls/clientStore.ts +32 -6
- package/client/plugos/syscalls/client_code_widget.ts +9 -4
- package/client/plugos/syscalls/code_widget.ts +26 -11
- package/client/plugos/syscalls/config.ts +146 -30
- package/client/plugos/syscalls/datastore.ts +146 -40
- package/client/plugos/syscalls/editor.ts +1518 -722
- package/client/plugos/syscalls/event.ts +44 -13
- package/client/plugos/syscalls/fetch.ts +120 -83
- package/client/plugos/syscalls/icon.ts +46 -0
- package/client/plugos/syscalls/index.ts +267 -121
- package/client/plugos/syscalls/jsonschema.ts +107 -9
- package/client/plugos/syscalls/language.ts +38 -12
- package/client/plugos/syscalls/markdown.ts +166 -50
- package/client/plugos/syscalls/mq.ts +92 -20
- package/client/plugos/syscalls/navigator.ts +143 -0
- package/client/plugos/syscalls/schema_introspection.ts +33 -0
- package/client/plugos/syscalls/search.ts +44 -0
- package/client/plugos/syscalls/service_registry.ts +74 -20
- package/client/plugos/syscalls/shell.ts +51 -21
- package/client/plugos/syscalls/space.ts +412 -113
- package/client/plugos/syscalls/sync.ts +103 -20
- package/client/plugos/syscalls/system.ts +274 -139
- package/client/plugos/system.ts +28 -10
- package/client/plugos/worker_runtime.ts +0 -2
- package/client/space_lua/aggregates.ts +0 -16
- package/client/space_lua/api_documentation.ts +107 -0
- package/client/space_lua/ast.ts +13 -5
- package/client/space_lua/ast_narrow.ts +0 -5
- package/client/space_lua/budget.ts +113 -0
- package/client/space_lua/budget_ui.ts +49 -0
- package/client/space_lua/eval.ts +125 -92
- package/client/space_lua/labels.ts +0 -4
- package/client/space_lua/numeric.ts +0 -5
- package/client/space_lua/parse.ts +243 -125
- package/client/space_lua/pretty_print.ts +128 -54
- package/client/space_lua/quarantine.ts +127 -0
- package/client/space_lua/query_collection.ts +7 -51
- package/client/space_lua/query_env.ts +0 -2
- package/client/space_lua/render_lua_markdown.ts +2 -7
- package/client/space_lua/render_widget.ts +173 -0
- package/client/space_lua/runtime.ts +133 -92
- package/client/space_lua/stdlib/crypto.ts +8 -3
- package/client/space_lua/stdlib/encoding.ts +27 -9
- package/client/space_lua/stdlib/format.ts +0 -12
- package/client/space_lua/stdlib/js.ts +136 -23
- package/client/space_lua/stdlib/load.ts +24 -16
- package/client/space_lua/stdlib/math.ts +331 -133
- package/client/space_lua/stdlib/net.ts +57 -13
- package/client/space_lua/stdlib/os.ts +111 -54
- package/client/space_lua/stdlib/pattern.ts +10 -15
- package/client/space_lua/stdlib/space_lua.ts +344 -21
- package/client/space_lua/stdlib/string.ts +282 -104
- package/client/space_lua/stdlib/string_pack.ts +58 -22
- package/client/space_lua/stdlib/table.ts +195 -45
- package/client/space_lua/stdlib.ts +354 -125
- package/client/space_lua/syscalls.ts +270 -0
- package/client/space_lua/tonumber.ts +0 -9
- package/dist/plug-compile.js +2 -3
- package/package.json +15 -5
- package/plug-api/lib/async.ts +1 -6
- package/plug-api/lib/collation.ts +22 -0
- package/plug-api/lib/crypto.ts +16 -15
- package/plug-api/lib/dates.ts +33 -0
- package/plug-api/lib/fuzzy.ts +282 -0
- package/plug-api/lib/json.ts +0 -6
- package/plug-api/lib/limited_map.ts +0 -2
- package/plug-api/lib/link_write.ts +27 -0
- package/plug-api/{ui → lib}/panel_styles.ts +4 -3
- package/plug-api/lib/ref.ts +124 -5
- package/plug-api/lib/resolve.ts +0 -1
- package/plug-api/lib/resolve_path.ts +290 -0
- package/plug-api/lib/shortcut.ts +17 -1
- package/plug-api/lib/tags.ts +0 -3
- package/plug-api/lib/transclusion.ts +28 -1
- package/plug-api/lib/tree.ts +0 -5
- package/plug-api/lib/yaml.ts +3 -42
- package/plug-api/syscall.ts +1 -5
- package/plug-api/syscalls/config.ts +7 -5
- package/plug-api/syscalls/editor.ts +70 -208
- package/plug-api/syscalls/icon.ts +18 -0
- package/plug-api/syscalls/index.ts +39 -5
- package/plug-api/syscalls/jsonschema.ts +9 -0
- package/plug-api/syscalls/lua.ts +7 -0
- package/plug-api/syscalls/search.ts +11 -0
- package/plug-api/syscalls/shell.ts +0 -1
- package/plug-api/syscalls/space.ts +122 -5
- package/plug-api/syscalls/sync.ts +23 -2
- package/plug-api/syscalls/system.ts +53 -0
- package/plug-api/syscalls.ts +2 -0
- package/plug-api/system_mock.ts +15 -2
- package/plug-api/types/client.ts +4 -3
- package/plug-api/types/datastore.ts +11 -2
- package/plug-api/types/index.ts +62 -1
- package/plug-api/types/manifest.ts +22 -4
- package/plug-api/types/profile.ts +13 -0
- package/plug-api/types/revisions.ts +67 -0
- package/plug-api/ui/cx.ts +1 -3
- package/plug-api/ui/description.ts +64 -0
- package/plug-api/ui/hover.ts +69 -0
- package/plug-api/ui/index.ts +61 -2
- package/plug-api/ui/scroll.ts +30 -0
- package/plug-api/ui/slugify.ts +44 -0
- package/plug-api/ui/tree_model.ts +263 -0
- package/plug-api/ui/tree_types.ts +66 -0
- package/plug-api/ui/use_fit_collapse.ts +67 -0
- package/plugs/builtin_plugs.ts +0 -1
- package/plugs/index/types.ts +9 -0
- package/client/plugos/syscalls/lua.ts +0 -81
- package/plug-api/lib/memory_cache.ts +0 -21
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Hook, Manifest } from "../types.ts";
|
|
2
|
-
import type { SysCallMapping, System } from "../system.ts";
|
|
2
|
+
import type { SysCallMapping, SyscallContext, System } from "../system.ts";
|
|
3
3
|
import type { SyscallHookT } from "@silverbulletmd/silverbullet/type/manifest";
|
|
4
4
|
|
|
5
5
|
export class SyscallHook implements Hook<SyscallHookT> {
|
|
@@ -13,7 +13,6 @@ export class SyscallHook implements Hook<SyscallHookT> {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
registerSyscalls(system: System<SyscallHookT>) {
|
|
16
|
-
// Register syscalls from all loaded plugs
|
|
17
16
|
for (const plug of system.loadedPlugs.values()) {
|
|
18
17
|
const syscalls: SysCallMapping = {};
|
|
19
18
|
|
|
@@ -24,18 +23,27 @@ export class SyscallHook implements Hook<SyscallHookT> {
|
|
|
24
23
|
continue;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
const
|
|
26
|
+
const syscallDefinition = functionDef.syscall;
|
|
27
|
+
const syscallName =
|
|
28
|
+
typeof syscallDefinition === "string"
|
|
29
|
+
? syscallDefinition
|
|
30
|
+
: syscallDefinition.name;
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// Delegate to the system to invoke the function
|
|
32
|
-
return system.syscall(ctx, "system.invokeFunction", [
|
|
32
|
+
const callback = (ctx: SyscallContext, ...args: any[]) =>
|
|
33
|
+
system.syscall(ctx, "system.invokeFunction", [
|
|
33
34
|
`${plug.manifest!.name}.${name}`,
|
|
34
35
|
...args,
|
|
35
36
|
]);
|
|
36
|
-
|
|
37
|
+
if (typeof syscallDefinition === "string") {
|
|
38
|
+
syscalls[syscallName] = callback;
|
|
39
|
+
} else {
|
|
40
|
+
const { name: _name, ...metadata } = syscallDefinition;
|
|
41
|
+
syscalls[syscallName] = {
|
|
42
|
+
callback,
|
|
43
|
+
...metadata,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
37
46
|
|
|
38
|
-
// Register the syscalls with no required permissions
|
|
39
47
|
system.registerSyscalls([], syscalls);
|
|
40
48
|
}
|
|
41
49
|
}
|
|
@@ -48,16 +56,18 @@ export class SyscallHook implements Hook<SyscallHookT> {
|
|
|
48
56
|
continue;
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
const syscallName =
|
|
60
|
+
typeof functionDef.syscall === "string"
|
|
61
|
+
? functionDef.syscall
|
|
62
|
+
: functionDef.syscall.name;
|
|
63
|
+
if (!syscallName) {
|
|
53
64
|
errors.push(`Function ${name} has a syscall but no name`);
|
|
54
65
|
continue;
|
|
55
66
|
}
|
|
56
67
|
|
|
57
|
-
|
|
58
|
-
if (!functionDef.syscall.includes(".")) {
|
|
68
|
+
if (!syscallName.includes(".")) {
|
|
59
69
|
errors.push(
|
|
60
|
-
`Function ${name} has invalid syscall name "${
|
|
70
|
+
`Function ${name} has invalid syscall name "${syscallName}" - must be in format "namespace.name"`,
|
|
61
71
|
);
|
|
62
72
|
}
|
|
63
73
|
}
|
|
@@ -23,7 +23,6 @@ export class KVPrimitivesManifestCache<T> implements ManifestCache<T> {
|
|
|
23
23
|
): Promise<Manifest<T>> {
|
|
24
24
|
const [cached] = await this.kv.batchGet([[this.manifestPrefix, cacheKey]]);
|
|
25
25
|
if (cached && cached.hash === cacheHash) {
|
|
26
|
-
// console.log("Using KV cached manifest for", plug.name);
|
|
27
26
|
return cached.manifest;
|
|
28
27
|
}
|
|
29
28
|
await plug.sandbox.init();
|
|
@@ -41,34 +40,3 @@ export class KVPrimitivesManifestCache<T> implements ManifestCache<T> {
|
|
|
41
40
|
return manifest;
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
|
-
|
|
45
|
-
export class InMemoryManifestCache<T> implements ManifestCache<T> {
|
|
46
|
-
private cache = new Map<
|
|
47
|
-
string,
|
|
48
|
-
{
|
|
49
|
-
manifest: Manifest<T>;
|
|
50
|
-
hash: number;
|
|
51
|
-
}
|
|
52
|
-
>();
|
|
53
|
-
|
|
54
|
-
async getManifest(
|
|
55
|
-
plug: Plug<T>,
|
|
56
|
-
cacheKey: string,
|
|
57
|
-
cacheHash: number,
|
|
58
|
-
): Promise<Manifest<T>> {
|
|
59
|
-
const cached = this.cache.get(cacheKey);
|
|
60
|
-
if (cached && cached.hash === cacheHash) {
|
|
61
|
-
// console.log("Using memory cached manifest for", plug.name);
|
|
62
|
-
return cached.manifest;
|
|
63
|
-
}
|
|
64
|
-
await plug.sandbox.init();
|
|
65
|
-
const manifest = plug.sandbox.manifest!;
|
|
66
|
-
|
|
67
|
-
// Deliverately removing the assets from the manifest to preserve space, will be re-added upon load of actual worker
|
|
68
|
-
this.cache.set(cacheKey, {
|
|
69
|
-
manifest: { ...manifest, assets: undefined },
|
|
70
|
-
hash: cacheHash,
|
|
71
|
-
});
|
|
72
|
-
return manifest;
|
|
73
|
-
}
|
|
74
|
-
}
|
package/client/plugos/plug.ts
CHANGED
|
@@ -31,7 +31,6 @@ export class Plug<HookT> {
|
|
|
31
31
|
const plug = new Plug(system, sandboxFactory);
|
|
32
32
|
plug.path = path;
|
|
33
33
|
|
|
34
|
-
// Retrieve the manifest, which may either come from a cache or be loaded from the worker
|
|
35
34
|
plug.manifest = await system.options.manifestCache!.getManifest(
|
|
36
35
|
plug,
|
|
37
36
|
path,
|
|
@@ -62,14 +61,12 @@ export class Plug<HookT> {
|
|
|
62
61
|
|
|
63
62
|
// Invoke a function
|
|
64
63
|
async invoke(name: string, args: any[]): Promise<any> {
|
|
65
|
-
// Before we access the manifest
|
|
66
64
|
const funDef = this.manifest!.functions[name];
|
|
67
65
|
if (!funDef) {
|
|
68
66
|
throw new Error(`Function ${name} not found in manifest`);
|
|
69
67
|
}
|
|
70
68
|
const sandbox = this.sandbox!;
|
|
71
69
|
if (funDef.redirect) {
|
|
72
|
-
// Function redirect, look up
|
|
73
70
|
let plug: Plug<HookT> | undefined = this;
|
|
74
71
|
if (funDef.redirect.indexOf(".") !== -1) {
|
|
75
72
|
const [plugName, functionName] = funDef.redirect.split(".");
|
|
@@ -24,7 +24,6 @@ const workerRuntimePlugin: esbuild.Plugin = {
|
|
|
24
24
|
name: "worker-runtime",
|
|
25
25
|
setup(build) {
|
|
26
26
|
if (typeof __EMBEDDED_WORKER_RUNTIME_JS__ !== "undefined") {
|
|
27
|
-
// Bundled CLI: serve pre-bundled JS from the embedded constant
|
|
28
27
|
build.onResolve({ filter: /^worker-runtime$/ }, () => ({
|
|
29
28
|
path: "worker-runtime",
|
|
30
29
|
namespace: "worker-runtime",
|
|
@@ -34,7 +33,6 @@ const workerRuntimePlugin: esbuild.Plugin = {
|
|
|
34
33
|
loader: "js",
|
|
35
34
|
}));
|
|
36
35
|
} else {
|
|
37
|
-
// From source: point directly at the .ts file, esbuild handles it
|
|
38
36
|
build.onResolve({ filter: /^worker-runtime$/ }, () => ({
|
|
39
37
|
path: path.join(import.meta.dirname, "worker_runtime.ts"),
|
|
40
38
|
}));
|
|
@@ -70,14 +68,12 @@ export async function compileManifest(
|
|
|
70
68
|
delete manifest.build;
|
|
71
69
|
}
|
|
72
70
|
|
|
73
|
-
// Assets
|
|
74
71
|
const assetsBundle = await bundleAssets(
|
|
75
72
|
path.resolve(rootPath),
|
|
76
73
|
(manifest.assets as string[]) || [],
|
|
77
74
|
);
|
|
78
75
|
manifest.assets = assetsBundle.toJSON();
|
|
79
76
|
|
|
80
|
-
// Normalize the edge case of a plug with no functions
|
|
81
77
|
if (!manifest.functions) {
|
|
82
78
|
manifest.functions = {};
|
|
83
79
|
}
|
|
@@ -92,13 +88,11 @@ ${Object.entries(manifest.functions)
|
|
|
92
88
|
return "";
|
|
93
89
|
}
|
|
94
90
|
let [filePath, jsFunctionName] = def.path.split(":");
|
|
95
|
-
// Resolve path
|
|
96
91
|
filePath = path.join(rootPath, filePath);
|
|
97
92
|
|
|
98
|
-
return `import {${jsFunctionName} as ${funcName}} from "${
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}";\n`;
|
|
93
|
+
return `import {${jsFunctionName} as ${funcName}} from "${path
|
|
94
|
+
.resolve(filePath)
|
|
95
|
+
.replaceAll("\\", "\\\\")}";\n`;
|
|
102
96
|
})
|
|
103
97
|
.join("")}
|
|
104
98
|
|
|
@@ -122,8 +116,6 @@ export const plug = {manifest, functionMapping};
|
|
|
122
116
|
setupMessageListener(functionMapping, manifest, self.postMessage);
|
|
123
117
|
`;
|
|
124
118
|
|
|
125
|
-
// console.log("Code:", jsFile);
|
|
126
|
-
|
|
127
119
|
const tempDir = await mkdtemp(path.join(tmpdir(), "plug-compile-"));
|
|
128
120
|
const inFile = path.join(tempDir, "input.js");
|
|
129
121
|
const outFile = `${destPath}/${manifest.name}.plug.js`;
|
|
@@ -152,7 +144,6 @@ setupMessageListener(functionMapping, manifest, self.postMessage);
|
|
|
152
144
|
jsCode = patchBundledJS(jsCode);
|
|
153
145
|
await writeFile(outFile, jsCode, "utf-8");
|
|
154
146
|
|
|
155
|
-
// Clean up temp directory
|
|
156
147
|
await rm(tempDir, { recursive: true, force: true });
|
|
157
148
|
|
|
158
149
|
console.log(`Plug ${manifest.name} written to ${outFile}.`);
|
|
@@ -175,7 +166,6 @@ export async function compileManifests(
|
|
|
175
166
|
building = true;
|
|
176
167
|
await mkdir(dist, { recursive: true });
|
|
177
168
|
const startTime = Date.now();
|
|
178
|
-
// Build all plugs in parallel
|
|
179
169
|
await Promise.all(
|
|
180
170
|
manifestFiles.map(async (plugManifestPath) => {
|
|
181
171
|
const manifestPath = plugManifestPath as string;
|
|
@@ -7,7 +7,6 @@ import { race, timeout } from "@silverbulletmd/silverbullet/lib/async";
|
|
|
7
7
|
import { fsEndpoint } from "../../spaces/constants.ts";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Represents a "safe" execution environment for plug code
|
|
11
10
|
* Effectively this wraps a web worker, the reason to have this split from Plugs is to allow plugs to manage multiple sandboxes, e.g. for performance in the future
|
|
12
11
|
*/
|
|
13
12
|
export class WorkerSandbox<HookT> implements Sandbox<HookT> {
|
|
@@ -25,7 +24,6 @@ export class WorkerSandbox<HookT> implements Sandbox<HookT> {
|
|
|
25
24
|
private workerOptions = {},
|
|
26
25
|
) {}
|
|
27
26
|
|
|
28
|
-
/** Factory producing a WorkerSandbox for a plug file at a local space path. */
|
|
29
27
|
static forPath<HookT>(path: string): SandboxFactory<HookT> {
|
|
30
28
|
return (plug) =>
|
|
31
29
|
new WorkerSandbox(
|
|
@@ -40,7 +38,6 @@ export class WorkerSandbox<HookT> implements Sandbox<HookT> {
|
|
|
40
38
|
init(): Promise<void> {
|
|
41
39
|
console.log("Booting up worker from", this.workerUrl.pathname);
|
|
42
40
|
if (this.worker) {
|
|
43
|
-
// Race condition
|
|
44
41
|
console.warn("Double init of sandbox, ignoring");
|
|
45
42
|
return Promise.resolve();
|
|
46
43
|
}
|
|
@@ -54,14 +51,12 @@ export class WorkerSandbox<HookT> implements Sandbox<HookT> {
|
|
|
54
51
|
timeout(5000).catch((_) =>
|
|
55
52
|
Promise.reject(new Error("Plug timed out during creation")),
|
|
56
53
|
),
|
|
57
|
-
new Promise((resolve) => {
|
|
54
|
+
new Promise<void>((resolve) => {
|
|
58
55
|
this.worker!.onmessage = (ev) => {
|
|
59
56
|
if (ev.data.type === "manifest") {
|
|
60
57
|
this.manifest = ev.data.manifest;
|
|
61
|
-
// Set manifest in the plug
|
|
62
58
|
this.plug.manifest = this.manifest!;
|
|
63
59
|
|
|
64
|
-
// Set assets in the plug
|
|
65
60
|
this.plug.assets = new AssetBundle(
|
|
66
61
|
this.manifest?.assets ? (this.manifest.assets as AssetJson) : {},
|
|
67
62
|
);
|
|
@@ -72,7 +67,15 @@ export class WorkerSandbox<HookT> implements Sandbox<HookT> {
|
|
|
72
67
|
void this.onMessage(ev.data);
|
|
73
68
|
};
|
|
74
69
|
}),
|
|
75
|
-
])
|
|
70
|
+
]).catch((e) => {
|
|
71
|
+
// A worker that failed to come up must not linger: its script fetch may
|
|
72
|
+
// still be in flight (e.g. on a slow link), and a late boot would
|
|
73
|
+
// resolve into a sandbox the system already gave up on. Terminating it
|
|
74
|
+
// also lets a retry start from a clean slate.
|
|
75
|
+
this.worker?.terminate();
|
|
76
|
+
this.worker = undefined;
|
|
77
|
+
throw e;
|
|
78
|
+
});
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
async onMessage(data: ControllerMessage) {
|
|
@@ -92,7 +95,6 @@ export class WorkerSandbox<HookT> implements Sandbox<HookT> {
|
|
|
92
95
|
result: result,
|
|
93
96
|
} as WorkerMessage);
|
|
94
97
|
} catch (e: any) {
|
|
95
|
-
// console.error("Syscall fail", e);
|
|
96
98
|
this.worker &&
|
|
97
99
|
this.worker!.postMessage({
|
|
98
100
|
type: "sysr",
|
|
@@ -118,7 +120,6 @@ export class WorkerSandbox<HookT> implements Sandbox<HookT> {
|
|
|
118
120
|
|
|
119
121
|
async invoke(name: string, args: any[]): Promise<any> {
|
|
120
122
|
if (!this.worker) {
|
|
121
|
-
// Lazy initialization
|
|
122
123
|
await this.init();
|
|
123
124
|
}
|
|
124
125
|
this.reqId++;
|
|
@@ -138,5 +139,14 @@ export class WorkerSandbox<HookT> implements Sandbox<HookT> {
|
|
|
138
139
|
this.worker.terminate();
|
|
139
140
|
this.worker = undefined;
|
|
140
141
|
}
|
|
142
|
+
// A terminated worker will never post the `invr` that would otherwise
|
|
143
|
+
// settle these.
|
|
144
|
+
if (this.outstandingInvocations.size > 0) {
|
|
145
|
+
const error = new Error("Plug sandbox stopped");
|
|
146
|
+
for (const { reject } of this.outstandingInvocations.values()) {
|
|
147
|
+
reject(error);
|
|
148
|
+
}
|
|
149
|
+
this.outstandingInvocations.clear();
|
|
150
|
+
}
|
|
141
151
|
}
|
|
142
152
|
}
|
|
@@ -3,31 +3,69 @@ import type { FileMeta } from "../../../plug-api/types/index.ts";
|
|
|
3
3
|
|
|
4
4
|
export default function assetSyscalls(system: System<any>): SysCallMapping {
|
|
5
5
|
return {
|
|
6
|
-
"asset.readAsset":
|
|
7
|
-
|
|
6
|
+
"asset.readAsset": {
|
|
7
|
+
callback: (_ctx, plugName: string, name: string): string => {
|
|
8
|
+
return system.loadedPlugs
|
|
9
|
+
.get(plugName)!
|
|
10
|
+
.assets!.readFileAsDataUrl(name);
|
|
11
|
+
},
|
|
12
|
+
description: "Reads an asset embedded in a plug as a data URL.",
|
|
13
|
+
parameters: [
|
|
14
|
+
{ name: "plugName", type: "string", description: "Plug name." },
|
|
15
|
+
{ name: "name", type: "string", description: "Asset path." },
|
|
16
|
+
],
|
|
17
|
+
returns: [{ type: "string", description: "Asset data URL." }],
|
|
18
|
+
examples: [
|
|
19
|
+
{ code: 'local image = asset.readAsset("myplug", "image.png")' },
|
|
20
|
+
],
|
|
8
21
|
},
|
|
9
|
-
"asset.listFiles":
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
name
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
"asset.listFiles": {
|
|
23
|
+
callback: (_ctx, plugName: string): FileMeta[] => {
|
|
24
|
+
const assets = system.loadedPlugs.get(plugName)!.assets!;
|
|
25
|
+
const fileNames = assets.listFiles();
|
|
26
|
+
return fileNames.map((name) => ({
|
|
27
|
+
name,
|
|
28
|
+
contentType: assets.getMimeType(name),
|
|
29
|
+
created: assets.getMtime(name),
|
|
30
|
+
lastModified: assets.getMtime(name),
|
|
31
|
+
size: -1,
|
|
32
|
+
perm: "ro",
|
|
33
|
+
}));
|
|
34
|
+
},
|
|
35
|
+
description: "Lists the assets embedded in a plug.",
|
|
36
|
+
parameters: [
|
|
37
|
+
{ name: "plugName", type: "string", description: "Plug name." },
|
|
38
|
+
],
|
|
39
|
+
returns: [{ type: "table", description: "List of file metadata." }],
|
|
40
|
+
examples: [
|
|
41
|
+
{
|
|
42
|
+
code: 'for _, file in ipairs(asset.listFiles("myplug")) do\n print(file.name)\nend',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
20
45
|
},
|
|
21
|
-
"asset.getFileMeta":
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
46
|
+
"asset.getFileMeta": {
|
|
47
|
+
callback: (_ctx, plugName: string, name: string): FileMeta => {
|
|
48
|
+
const assets = system.loadedPlugs.get(plugName)!.assets!;
|
|
49
|
+
return {
|
|
50
|
+
name,
|
|
51
|
+
contentType: assets.getMimeType(name),
|
|
52
|
+
created: assets.getMtime(name),
|
|
53
|
+
lastModified: assets.getMtime(name),
|
|
54
|
+
size: -1,
|
|
55
|
+
perm: "ro",
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
description: "Gets metadata for an asset embedded in a plug.",
|
|
59
|
+
parameters: [
|
|
60
|
+
{ name: "plugName", type: "string", description: "Plug name." },
|
|
61
|
+
{ name: "name", type: "string", description: "Asset path." },
|
|
62
|
+
],
|
|
63
|
+
returns: [{ type: "table", description: "File metadata." }],
|
|
64
|
+
examples: [
|
|
65
|
+
{
|
|
66
|
+
code: 'local meta = asset.getFileMeta("myplug", "data.txt")\nprint(meta.lastModified)',
|
|
67
|
+
},
|
|
68
|
+
],
|
|
31
69
|
},
|
|
32
70
|
};
|
|
33
71
|
}
|
|
@@ -8,14 +8,40 @@ export function clientStoreSyscalls(
|
|
|
8
8
|
prefix: KvKey = ["client"],
|
|
9
9
|
): SysCallMapping {
|
|
10
10
|
return {
|
|
11
|
-
"clientStore.get":
|
|
12
|
-
|
|
11
|
+
"clientStore.get": {
|
|
12
|
+
callback: (_ctx, key: string): Promise<any> => {
|
|
13
|
+
return ds.get([...prefix, key]);
|
|
14
|
+
},
|
|
15
|
+
description:
|
|
16
|
+
"Gets a client-specific value from the local key-value store.",
|
|
17
|
+
parameters: [
|
|
18
|
+
{ name: "key", type: "string", description: "Key to read." },
|
|
19
|
+
],
|
|
20
|
+
returns: [{ description: "Stored value, or nil when absent." }],
|
|
21
|
+
examples: [{ code: 'local theme = clientStore.get("theme")' }],
|
|
13
22
|
},
|
|
14
|
-
"clientStore.set":
|
|
15
|
-
|
|
23
|
+
"clientStore.set": {
|
|
24
|
+
callback: (_ctx, key: string, val: any): Promise<void> => {
|
|
25
|
+
return ds.set([...prefix, key], val);
|
|
26
|
+
},
|
|
27
|
+
description:
|
|
28
|
+
"Stores a client-specific value in the local key-value store.",
|
|
29
|
+
parameters: [
|
|
30
|
+
{ name: "key", type: "string", description: "Key to set." },
|
|
31
|
+
{ name: "value", description: "Value to store." },
|
|
32
|
+
],
|
|
33
|
+
examples: [{ code: 'clientStore.set("theme", "dark")' }],
|
|
16
34
|
},
|
|
17
|
-
"clientStore.delete":
|
|
18
|
-
|
|
35
|
+
"clientStore.delete": {
|
|
36
|
+
callback: (_ctx, key: string): Promise<void> => {
|
|
37
|
+
return ds.delete([...prefix, key]);
|
|
38
|
+
},
|
|
39
|
+
description:
|
|
40
|
+
"Deletes a client-specific value from the local key-value store.",
|
|
41
|
+
parameters: [
|
|
42
|
+
{ name: "key", type: "string", description: "Key to delete." },
|
|
43
|
+
],
|
|
44
|
+
examples: [{ code: 'clientStore.delete("theme")' }],
|
|
19
45
|
},
|
|
20
46
|
};
|
|
21
47
|
}
|
|
@@ -5,10 +5,15 @@ import { broadcastReload } from "../../components/widget_sandbox_iframe.ts";
|
|
|
5
5
|
|
|
6
6
|
export function clientCodeWidgetSyscalls(client: Client): SysCallMapping {
|
|
7
7
|
return {
|
|
8
|
-
"codeWidget.refreshAll":
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
"codeWidget.refreshAll": {
|
|
9
|
+
callback: () => {
|
|
10
|
+
client.widgetCache.clearPrewarm();
|
|
11
|
+
broadcastReload();
|
|
12
|
+
return reloadAllWidgets();
|
|
13
|
+
},
|
|
14
|
+
description:
|
|
15
|
+
"Refreshes all code widgets on the current page that support refreshing.",
|
|
16
|
+
examples: [{ code: "codeWidget.refreshAll()" }],
|
|
12
17
|
},
|
|
13
18
|
};
|
|
14
19
|
}
|
|
@@ -6,17 +6,32 @@ export function codeWidgetSyscalls(
|
|
|
6
6
|
codeWidgetHook: CodeWidgetHook,
|
|
7
7
|
): SysCallMapping {
|
|
8
8
|
return {
|
|
9
|
-
"codeWidget.render":
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
"codeWidget.render": {
|
|
10
|
+
callback: (
|
|
11
|
+
_ctx,
|
|
12
|
+
lang: string,
|
|
13
|
+
body: string,
|
|
14
|
+
pageName: string,
|
|
15
|
+
): Promise<CodeWidgetContent | null> => {
|
|
16
|
+
const langCallback = codeWidgetHook.codeWidgetCallbacks.get(lang);
|
|
17
|
+
if (!langCallback) {
|
|
18
|
+
throw new Error(`Code widget ${lang} not found`);
|
|
19
|
+
}
|
|
20
|
+
return langCallback(body, pageName);
|
|
21
|
+
},
|
|
22
|
+
description: "Renders code through the widget registered for a language.",
|
|
23
|
+
parameters: [
|
|
24
|
+
{ name: "language", type: "string", description: "Widget language." },
|
|
25
|
+
{ name: "body", type: "string", description: "Code block body." },
|
|
26
|
+
{
|
|
27
|
+
name: "pageName",
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Containing page name.",
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
returns: [
|
|
33
|
+
{ type: "table", description: "Rendered widget content, or nil." },
|
|
34
|
+
],
|
|
20
35
|
},
|
|
21
36
|
};
|
|
22
37
|
}
|