@silverbulletmd/silverbullet 2.4.2 → 2.6.1
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 +19 -4
- package/client/asset_bundle/bundle.ts +3 -9
- package/client/data/datastore.ts +4 -5
- package/client/markdown_parser/constants.ts +5 -4
- package/client/plugos/hooks/code_widget.ts +3 -8
- package/client/plugos/hooks/command.ts +8 -8
- package/client/plugos/hooks/document_editor.ts +10 -15
- package/client/plugos/hooks/event.ts +33 -36
- package/client/plugos/hooks/mq.ts +17 -17
- package/client/plugos/hooks/plug_namespace.ts +3 -8
- package/client/plugos/hooks/slash_command.ts +13 -28
- package/client/plugos/hooks/syscall.ts +3 -3
- package/client/plugos/manifest_cache.ts +22 -15
- package/client/plugos/plug.ts +2 -6
- package/client/plugos/plug_compile.ts +79 -78
- package/client/plugos/protocol.ts +28 -28
- package/client/plugos/proxy_fetch.ts +7 -6
- package/client/plugos/sandboxes/web_worker_sandbox.ts +1 -1
- package/client/plugos/sandboxes/worker_sandbox.ts +18 -18
- package/client/plugos/syscalls/asset.ts +1 -3
- package/client/plugos/syscalls/code_widget.ts +1 -3
- package/client/plugos/syscalls/config.ts +1 -5
- package/client/plugos/syscalls/datastore.ts +1 -1
- package/client/plugos/syscalls/editor.ts +72 -69
- package/client/plugos/syscalls/event.ts +9 -12
- package/client/plugos/syscalls/fetch.ts +31 -23
- package/client/plugos/syscalls/index.ts +10 -1
- package/client/plugos/syscalls/jsonschema.ts +72 -32
- package/client/plugos/syscalls/language.ts +9 -5
- package/client/plugos/syscalls/markdown.ts +29 -7
- package/client/plugos/syscalls/mq.ts +4 -12
- package/client/plugos/syscalls/service_registry.ts +1 -4
- package/client/plugos/syscalls/shell.ts +2 -5
- package/client/plugos/syscalls/space.ts +1 -1
- package/client/plugos/syscalls/sync.ts +69 -60
- package/client/plugos/syscalls/system.ts +2 -3
- package/client/plugos/system.ts +6 -12
- package/client/plugos/worker_runtime.ts +12 -33
- package/client/space_lua/aggregates.ts +782 -0
- package/client/space_lua/ast.ts +42 -8
- package/client/space_lua/ast_narrow.ts +4 -2
- package/client/space_lua/eval.ts +886 -575
- package/client/space_lua/labels.ts +7 -12
- package/client/space_lua/liq_null.ts +6 -0
- package/client/space_lua/numeric.ts +5 -8
- package/client/space_lua/parse.ts +346 -120
- package/client/space_lua/query_collection.ts +926 -82
- package/client/space_lua/query_env.ts +26 -0
- package/client/space_lua/render_lua_markdown.ts +369 -0
- package/client/space_lua/rp.ts +5 -4
- package/client/space_lua/runtime.ts +288 -155
- package/client/space_lua/stdlib/format.ts +53 -39
- package/client/space_lua/stdlib/js.ts +3 -7
- package/client/space_lua/stdlib/load.ts +1 -3
- package/client/space_lua/stdlib/math.ts +84 -58
- package/client/space_lua/stdlib/net.ts +27 -17
- package/client/space_lua/stdlib/os.ts +81 -85
- package/client/space_lua/stdlib/pattern.ts +695 -0
- package/client/space_lua/stdlib/prng.ts +148 -0
- package/client/space_lua/stdlib/space_lua.ts +17 -23
- package/client/space_lua/stdlib/string.ts +102 -190
- package/client/space_lua/stdlib/string_pack.ts +490 -0
- package/client/space_lua/stdlib/table.ts +76 -16
- package/client/space_lua/stdlib.ts +53 -39
- package/client/space_lua/tonumber.ts +82 -42
- package/client/space_lua/util.ts +53 -15
- package/dist/plug-compile.js +55 -98
- package/package.json +27 -20
- package/plug-api/constants.ts +0 -32
- package/plug-api/lib/async.ts +20 -7
- package/plug-api/lib/crypto.ts +16 -17
- package/plug-api/lib/dates.ts +15 -7
- package/plug-api/lib/json.ts +11 -5
- package/plug-api/lib/limited_map.ts +1 -1
- package/plug-api/lib/native_fetch.ts +2 -0
- package/plug-api/lib/ref.ts +23 -23
- package/plug-api/lib/resolve.ts +7 -11
- package/plug-api/lib/tags.ts +13 -4
- package/plug-api/lib/transclusion.ts +10 -21
- package/plug-api/lib/tree.ts +165 -45
- package/plug-api/lib/yaml.ts +35 -25
- package/plug-api/syscalls/asset.ts +1 -1
- package/plug-api/syscalls/config.ts +1 -4
- package/plug-api/syscalls/editor.ts +15 -15
- package/plug-api/syscalls/jsonschema.ts +1 -3
- package/plug-api/syscalls/lua.ts +3 -9
- package/plug-api/syscalls/mq.ts +1 -4
- package/plug-api/syscalls/shell.ts +4 -1
- package/plug-api/syscalls/space.ts +3 -10
- package/plug-api/syscalls/system.ts +1 -4
- package/plug-api/syscalls/yaml.ts +2 -6
- package/plug-api/system_mock.ts +0 -1
- package/plug-api/types/client.ts +16 -1
- package/plug-api/types/event.ts +6 -4
- package/plug-api/types/manifest.ts +8 -9
- package/plugs/builtin_plugs.ts +2 -2
- package/client/plugos/sandboxes/deno_worker_sandbox.ts +0 -6
package/dist/plug-compile.js
CHANGED
|
@@ -4,69 +4,18 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// version.ts
|
|
7
|
-
var version = "2.
|
|
7
|
+
var version = "2.6.1";
|
|
8
8
|
|
|
9
9
|
// client/plugos/plug_compile.ts
|
|
10
10
|
import * as path from "node:path";
|
|
11
11
|
import { readFile as readFile2, writeFile, mkdtemp, rm, mkdir } from "node:fs/promises";
|
|
12
12
|
import { tmpdir } from "node:os";
|
|
13
13
|
import * as YAML from "js-yaml";
|
|
14
|
-
|
|
15
|
-
// build_deps.ts
|
|
16
14
|
import * as esbuild from "esbuild";
|
|
17
|
-
import { readFileSync } from "node:fs";
|
|
18
|
-
import { resolve, dirname } from "node:path";
|
|
19
|
-
import { fileURLToPath } from "node:url";
|
|
20
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
21
|
-
var __dirname = dirname(__filename);
|
|
22
|
-
function denoPlugin(options) {
|
|
23
|
-
const configPath = options?.configPath || resolve(__dirname, "deno.json");
|
|
24
|
-
const denoConfig = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
25
|
-
const imports = denoConfig.imports || {};
|
|
26
|
-
return {
|
|
27
|
-
name: "deno-resolver",
|
|
28
|
-
setup(build) {
|
|
29
|
-
build.onResolve({ filter: /^file:\/\// }, (args) => {
|
|
30
|
-
const filePath = args.path.replace(/^file:\/\//, "");
|
|
31
|
-
return { path: filePath };
|
|
32
|
-
});
|
|
33
|
-
build.onResolve({ filter: /^@silverbulletmd\/silverbullet\// }, (args) => {
|
|
34
|
-
const importPath = args.path;
|
|
35
|
-
if (imports[importPath]) {
|
|
36
|
-
const resolvedPath = resolve(__dirname, imports[importPath]);
|
|
37
|
-
return { path: resolvedPath };
|
|
38
|
-
}
|
|
39
|
-
return null;
|
|
40
|
-
});
|
|
41
|
-
build.onResolve({ filter: /.*/ }, (args) => {
|
|
42
|
-
if (args.path.startsWith(".") || args.path.startsWith("/")) {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
const importPath = args.path;
|
|
46
|
-
if (imports[importPath]) {
|
|
47
|
-
let mapped = imports[importPath];
|
|
48
|
-
if (mapped.startsWith("npm:")) {
|
|
49
|
-
return { path: mapped.replace(/^npm:/, ""), external: true };
|
|
50
|
-
}
|
|
51
|
-
if (mapped.startsWith("jsr:")) {
|
|
52
|
-
return { path: args.path, external: true };
|
|
53
|
-
}
|
|
54
|
-
if (mapped.startsWith("http://") || mapped.startsWith("https://")) {
|
|
55
|
-
return { path: args.path, external: true };
|
|
56
|
-
}
|
|
57
|
-
const resolvedPath = resolve(__dirname, mapped);
|
|
58
|
-
return { path: resolvedPath };
|
|
59
|
-
}
|
|
60
|
-
return null;
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
15
|
|
|
66
16
|
// client/asset_bundle/builder.ts
|
|
67
|
-
import picomatch from "picomatch";
|
|
68
17
|
import { readFile, readdir } from "node:fs/promises";
|
|
69
|
-
import { join } from "node:path";
|
|
18
|
+
import { join, matchesGlob } from "node:path";
|
|
70
19
|
|
|
71
20
|
// node_modules/mime/dist/types/other.js
|
|
72
21
|
var types = {
|
|
@@ -1350,10 +1299,9 @@ async function bundleAssets(rootPath, patterns) {
|
|
|
1350
1299
|
if (patterns.length === 0) {
|
|
1351
1300
|
return bundle;
|
|
1352
1301
|
}
|
|
1353
|
-
const isMatch = picomatch(patterns);
|
|
1354
1302
|
for await (const file of walk(rootPath)) {
|
|
1355
1303
|
const cleanPath = file.path.substring(rootPath.length + 1);
|
|
1356
|
-
if (
|
|
1304
|
+
if (patterns.some((p) => matchesGlob(cleanPath, p))) {
|
|
1357
1305
|
bundle.writeFileSync(
|
|
1358
1306
|
cleanPath,
|
|
1359
1307
|
src_default.getType(cleanPath) || "application/octet-stream",
|
|
@@ -1365,7 +1313,25 @@ async function bundleAssets(rootPath, patterns) {
|
|
|
1365
1313
|
}
|
|
1366
1314
|
|
|
1367
1315
|
// client/plugos/plug_compile.ts
|
|
1368
|
-
var
|
|
1316
|
+
var workerRuntimePlugin = {
|
|
1317
|
+
name: "worker-runtime",
|
|
1318
|
+
setup(build2) {
|
|
1319
|
+
if (true) {
|
|
1320
|
+
build2.onResolve({ filter: /^worker-runtime$/ }, () => ({
|
|
1321
|
+
path: "worker-runtime",
|
|
1322
|
+
namespace: "worker-runtime"
|
|
1323
|
+
}));
|
|
1324
|
+
build2.onLoad({ filter: /.*/, namespace: "worker-runtime" }, () => ({
|
|
1325
|
+
contents: '// plug-api/lib/crypto.ts\nfunction base64Decode(s) {\n const binString = atob(s);\n const len = binString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binString.charCodeAt(i);\n }\n return bytes;\n}\nfunction base64Encode(buffer) {\n if (typeof buffer === "string") {\n buffer = new TextEncoder().encode(buffer);\n }\n let binary = "";\n const len = buffer.byteLength;\n for (let i = 0; i < len; i++) {\n binary += String.fromCharCode(buffer[i]);\n }\n return btoa(binary);\n}\nvar fixedCounter = new Uint8Array(16);\n\n// client/lib/logger.ts\nvar Logger = class {\n constructor(prefix = "", maxCaptureSize = 1e3) {\n this.prefix = prefix;\n this.maxCaptureSize = maxCaptureSize;\n this.prefix = prefix;\n this.originalConsole = {\n log: console.log.bind(console),\n info: console.info.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n debug: console.debug.bind(console)\n };\n this.patchConsole();\n }\n originalConsole;\n logBuffer = [];\n patchConsole() {\n const createPatchedMethod = (level) => {\n return (...args) => {\n const prefixedArgs = this.prefix ? [this.prefix, ...args] : args;\n this.originalConsole[level](...prefixedArgs);\n this.captureLog(level, args);\n };\n };\n console.log = createPatchedMethod("log");\n console.info = createPatchedMethod("info");\n console.warn = createPatchedMethod("warn");\n console.error = createPatchedMethod("error");\n console.debug = createPatchedMethod("debug");\n }\n captureLog(level, args) {\n const entry = {\n level,\n timestamp: Date.now(),\n message: args.map((arg) => {\n if (typeof arg === "string") {\n return arg;\n }\n try {\n return JSON.stringify(arg);\n } catch {\n return String(arg);\n }\n }).join(" ")\n };\n this.logBuffer.push(entry);\n if (this.logBuffer.length > this.maxCaptureSize) {\n this.logBuffer.shift();\n }\n }\n /**\n * Posts all buffered logs to a server endpoint\n */\n async postToServer(logEndpoint, source) {\n const logs = this.logBuffer;\n if (logs.length > 0) {\n const logCopy = [...this.logBuffer];\n this.logBuffer = [];\n try {\n const resp = await fetch(logEndpoint, {\n method: "POST",\n headers: {\n "Content-Type": "application/json"\n },\n body: JSON.stringify(logCopy.map((entry) => ({ ...entry, source })))\n });\n if (!resp.ok) {\n throw new Error("Failed to post logs to server");\n }\n } catch (e) {\n console.warn("Could not post logs to server", e.message);\n this.logBuffer.unshift(...logCopy);\n }\n }\n }\n};\nvar globalLogger;\nfunction initLogger(prefix = "") {\n globalLogger = new Logger(prefix);\n return globalLogger;\n}\n\n// client/plugos/worker_runtime.ts\nvar workerPostMessage = (_msg) => {\n throw new Error("Not initialized yet");\n};\nvar runningAsWebWorker = typeof window === "undefined" && // @ts-expect-error: globalThis\ntypeof globalThis.WebSocketPair === "undefined";\nvar pendingRequests = /* @__PURE__ */ new Map();\nvar syscallReqId = 0;\nif (runningAsWebWorker) {\n globalThis.syscall = async (name, ...args) => {\n return await new Promise((resolve, reject) => {\n syscallReqId++;\n pendingRequests.set(syscallReqId, { resolve, reject });\n workerPostMessage({\n type: "sys",\n id: syscallReqId,\n name,\n args\n });\n });\n };\n}\nfunction setupMessageListener(functionMapping, manifest, postMessageFn) {\n if (!runningAsWebWorker) {\n return;\n }\n workerPostMessage = postMessageFn;\n self.addEventListener("message", (event) => {\n (async () => {\n const data = event.data;\n switch (data.type) {\n case "inv":\n {\n const fn = functionMapping[data.name];\n if (!fn) {\n throw new Error(`Function not loaded: ${data.name}`);\n }\n try {\n const result = await Promise.resolve(fn(...data.args || []));\n workerPostMessage({\n type: "invr",\n id: data.id,\n result\n });\n } catch (e) {\n console.error(\n "An exception was thrown as a result of invoking function",\n data.name,\n "error:",\n e.message\n );\n workerPostMessage({\n type: "invr",\n id: data.id,\n error: e.message\n });\n }\n }\n break;\n case "sysr":\n {\n const syscallId = data.id;\n const lookup = pendingRequests.get(syscallId);\n if (!lookup) {\n throw Error("Invalid request id");\n }\n pendingRequests.delete(syscallId);\n if (data.error) {\n lookup.reject(new Error(data.error));\n } else {\n lookup.resolve(data.result);\n }\n }\n break;\n }\n })().catch(console.error);\n });\n workerPostMessage({\n type: "manifest",\n manifest\n });\n initLogger(`[${manifest.name} plug]`);\n}\nasync function sandboxFetch(reqInfo, options) {\n if (typeof reqInfo !== "string") {\n const body = new Uint8Array(await reqInfo.arrayBuffer());\n const encodedBody = body.length > 0 ? base64Encode(body) : void 0;\n options = {\n method: reqInfo.method,\n headers: Object.fromEntries(reqInfo.headers.entries()),\n base64Body: encodedBody\n };\n reqInfo = reqInfo.url;\n }\n return syscall("sandboxFetch.fetch", reqInfo, options);\n}\nglobalThis.nativeFetch = globalThis.fetch;\nfunction monkeyPatchFetch() {\n globalThis.fetch = async (reqInfo, init) => {\n const encodedBody = init?.body ? base64Encode(\n new Uint8Array(await new Response(init.body).arrayBuffer())\n ) : void 0;\n const r = await sandboxFetch(\n reqInfo,\n init && {\n method: init.method,\n headers: init.headers,\n base64Body: encodedBody\n }\n );\n return new Response(\n r.base64Body ? base64Decode(r.base64Body) : null,\n {\n status: r.status,\n headers: r.headers\n }\n );\n };\n}\nif (runningAsWebWorker) {\n monkeyPatchFetch();\n}\nexport {\n monkeyPatchFetch,\n sandboxFetch,\n setupMessageListener\n};\n',
|
|
1326
|
+
loader: "js"
|
|
1327
|
+
}));
|
|
1328
|
+
} else {
|
|
1329
|
+
build2.onResolve({ filter: /^worker-runtime$/ }, () => ({
|
|
1330
|
+
path: path.join(import.meta.dirname, "worker_runtime.ts")
|
|
1331
|
+
}));
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
};
|
|
1369
1335
|
async function compileManifest(manifestPath, destPath, options = {}) {
|
|
1370
1336
|
const rootPath = path.dirname(manifestPath);
|
|
1371
1337
|
const manifestContent = await readFile2(manifestPath, "utf-8");
|
|
@@ -1382,7 +1348,7 @@ async function compileManifest(manifestPath, destPath, options = {}) {
|
|
|
1382
1348
|
manifest.functions = {};
|
|
1383
1349
|
}
|
|
1384
1350
|
const jsFile = `
|
|
1385
|
-
import { setupMessageListener } from "
|
|
1351
|
+
import { setupMessageListener } from "worker-runtime";
|
|
1386
1352
|
|
|
1387
1353
|
// Imports
|
|
1388
1354
|
${Object.entries(manifest.functions).map(([funcName, def]) => {
|
|
@@ -1391,11 +1357,8 @@ ${Object.entries(manifest.functions).map(([funcName, def]) => {
|
|
|
1391
1357
|
}
|
|
1392
1358
|
let [filePath, jsFunctionName] = def.path.split(":");
|
|
1393
1359
|
filePath = path.join(rootPath, filePath);
|
|
1394
|
-
return `import {${jsFunctionName} as ${funcName}} from "
|
|
1395
|
-
path.resolve(filePath).replaceAll(
|
|
1396
|
-
"\\",
|
|
1397
|
-
"\\\\"
|
|
1398
|
-
)}";
|
|
1360
|
+
return `import {${jsFunctionName} as ${funcName}} from "${// Replacing \ with / for Windows
|
|
1361
|
+
path.resolve(filePath).replaceAll("\\", "\\\\")}";
|
|
1399
1362
|
`;
|
|
1400
1363
|
}).join("")}
|
|
1401
1364
|
|
|
@@ -1432,18 +1395,14 @@ setupMessageListener(functionMapping, manifest, self.postMessage);
|
|
|
1432
1395
|
outfile: outFile,
|
|
1433
1396
|
metafile: options.info,
|
|
1434
1397
|
treeShaking: true,
|
|
1435
|
-
plugins: [
|
|
1436
|
-
denoPlugin({
|
|
1437
|
-
configPath: options.configPath && path.resolve(process.cwd(), options.configPath)
|
|
1438
|
-
})
|
|
1439
|
-
]
|
|
1398
|
+
plugins: [workerRuntimePlugin]
|
|
1440
1399
|
});
|
|
1441
1400
|
if (options.info) {
|
|
1442
1401
|
const text = await esbuild.analyzeMetafile(result.metafile);
|
|
1443
1402
|
console.log("Bundle info for", manifestPath, text);
|
|
1444
1403
|
}
|
|
1445
1404
|
let jsCode = await readFile2(outFile, "utf-8");
|
|
1446
|
-
jsCode =
|
|
1405
|
+
jsCode = patchBundledJS(jsCode);
|
|
1447
1406
|
await writeFile(outFile, jsCode, "utf-8");
|
|
1448
1407
|
await rm(tempDir, { recursive: true, force: true });
|
|
1449
1408
|
console.log(`Plug ${manifest.name} written to ${outFile}.`);
|
|
@@ -1460,52 +1419,50 @@ async function compileManifests(manifestFiles, dist, options = {}) {
|
|
|
1460
1419
|
building = true;
|
|
1461
1420
|
await mkdir(dist, { recursive: true });
|
|
1462
1421
|
const startTime = Date.now();
|
|
1463
|
-
await Promise.all(
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
manifestPath,
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
}
|
|
1475
|
-
}));
|
|
1422
|
+
await Promise.all(
|
|
1423
|
+
manifestFiles.map(async (plugManifestPath) => {
|
|
1424
|
+
const manifestPath = plugManifestPath;
|
|
1425
|
+
try {
|
|
1426
|
+
await compileManifest(manifestPath, dist, options);
|
|
1427
|
+
} catch (e) {
|
|
1428
|
+
console.error(`Error building ${manifestPath}:`, e.message);
|
|
1429
|
+
throw e;
|
|
1430
|
+
}
|
|
1431
|
+
})
|
|
1432
|
+
);
|
|
1476
1433
|
console.log(`Done building plugs in ${Date.now() - startTime}ms`);
|
|
1477
1434
|
building = false;
|
|
1478
1435
|
}
|
|
1479
1436
|
await buildAll();
|
|
1480
1437
|
}
|
|
1481
|
-
function
|
|
1438
|
+
function patchBundledJS(code) {
|
|
1482
1439
|
return code.replaceAll("/(?<=\\n)/", "/()/");
|
|
1483
1440
|
}
|
|
1484
|
-
async function plugCompileCommand({
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
);
|
|
1495
|
-
esbuild.stop();
|
|
1441
|
+
async function plugCompileCommand({
|
|
1442
|
+
dist,
|
|
1443
|
+
debug,
|
|
1444
|
+
info
|
|
1445
|
+
}, ...manifestPaths) {
|
|
1446
|
+
await compileManifests(manifestPaths, dist, {
|
|
1447
|
+
debug,
|
|
1448
|
+
info
|
|
1449
|
+
});
|
|
1450
|
+
await esbuild.stop();
|
|
1496
1451
|
process.exit(0);
|
|
1497
1452
|
}
|
|
1498
1453
|
|
|
1499
1454
|
// bin/plug-compile.ts
|
|
1500
1455
|
var program = new Command();
|
|
1501
|
-
program.name("plug-compile").description("Bundle (compile) one or more plug manifests").version(version).usage("<options> <manifest paths>").argument("<manifestPaths...>", "One or more .plug.yaml manifest files").option("--debug", "Do not minify code", false).option("--info", "Print out size info per function", false).option("-w, --watch", "Watch for changes and rebuild", false).option(
|
|
1456
|
+
program.name("plug-compile").description("Bundle (compile) one or more plug manifests").version(version).usage("<options> <manifest paths>").argument("<manifestPaths...>", "One or more .plug.yaml manifest files").option("--debug", "Do not minify code", false).option("--info", "Print out size info per function", false).option("-w, --watch", "Watch for changes and rebuild", false).option(
|
|
1457
|
+
"--dist <path>",
|
|
1458
|
+
"Folder to put the resulting .plug.json file into",
|
|
1459
|
+
"."
|
|
1460
|
+
).action(async (manifestPaths, options) => {
|
|
1502
1461
|
await plugCompileCommand(
|
|
1503
1462
|
{
|
|
1504
1463
|
dist: options.dist,
|
|
1505
1464
|
debug: options.debug,
|
|
1506
|
-
info: options.info
|
|
1507
|
-
config: options.config,
|
|
1508
|
-
runtimeUrl: options.runtimeUrl
|
|
1465
|
+
info: options.info
|
|
1509
1466
|
},
|
|
1510
1467
|
...manifestPaths
|
|
1511
1468
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silverbulletmd/silverbullet",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A self-hosted, web-based note taking app",
|
|
6
6
|
"publishConfig": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"./lib/tags": "./plug-api/lib/tags.ts",
|
|
22
22
|
"./lib/transclusion": "./plug-api/lib/transclusion.ts",
|
|
23
23
|
"./lib/native_fetch": "./plug-api/lib/native_fetch.ts",
|
|
24
|
+
"./lib/query_expression": "./plug-api/lib/query_expression.ts",
|
|
24
25
|
"./type/client": "./plug-api/types/client.ts",
|
|
25
26
|
"./type/config": "./plug-api/types/config.ts",
|
|
26
27
|
"./type/manifest": "./plug-api/types/manifest.ts",
|
|
@@ -50,39 +51,44 @@
|
|
|
50
51
|
],
|
|
51
52
|
"scripts": {
|
|
52
53
|
"build": "npm run build:plugs && npm run build:client",
|
|
53
|
-
"build:plugs": "tsx
|
|
54
|
-
"build:client": "tsx build_client.ts",
|
|
55
|
-
"build:plug-compile": "tsx build_plug_compile.ts",
|
|
54
|
+
"build:plugs": "tsx build/build_plugs.ts",
|
|
55
|
+
"build:client": "tsx build/build_client.ts",
|
|
56
|
+
"build:plug-compile": "tsx build/build_plug_compile.ts",
|
|
56
57
|
"prepublishOnly": "npm run build:plug-compile",
|
|
57
58
|
"test": "vitest run",
|
|
58
59
|
"check": "tsc --noEmit",
|
|
59
|
-
"
|
|
60
|
+
"lint": "biome lint .",
|
|
61
|
+
"fmt": "biome format --write .",
|
|
62
|
+
"fmt:check": "biome format .",
|
|
63
|
+
"bench": "vitest bench",
|
|
64
|
+
"test:e2e": "npx playwright test",
|
|
65
|
+
"test:e2e:headed": "npx playwright test --headed"
|
|
60
66
|
},
|
|
61
67
|
"dependencies": {
|
|
62
|
-
"@
|
|
63
|
-
"@codemirror/
|
|
68
|
+
"@cfworker/json-schema": "^4.1.1",
|
|
69
|
+
"@codemirror/autocomplete": "6.20.1",
|
|
70
|
+
"@codemirror/commands": "6.10.3",
|
|
64
71
|
"@codemirror/lang-css": "6.3.1",
|
|
65
72
|
"@codemirror/lang-html": "6.4.11",
|
|
66
|
-
"@codemirror/lang-javascript": "6.2.
|
|
73
|
+
"@codemirror/lang-javascript": "6.2.5",
|
|
67
74
|
"@codemirror/lang-markdown": "6.5.0",
|
|
68
|
-
"@codemirror/language": "6.12.
|
|
75
|
+
"@codemirror/language": "6.12.3",
|
|
69
76
|
"@codemirror/legacy-modes": "6.5.2",
|
|
70
|
-
"@codemirror/lint": "6.9.
|
|
77
|
+
"@codemirror/lint": "6.9.5",
|
|
71
78
|
"@codemirror/search": "6.6.0",
|
|
72
|
-
"@codemirror/state": "6.
|
|
73
|
-
"@codemirror/view": "6.
|
|
79
|
+
"@codemirror/state": "6.6.0",
|
|
80
|
+
"@codemirror/view": "6.41.0",
|
|
74
81
|
"@joplin/turndown-plugin-gfm": "1.0.64",
|
|
75
|
-
"@lezer/common": "1.5.
|
|
76
|
-
"@lezer/css": "1.3.
|
|
82
|
+
"@lezer/common": "1.5.1",
|
|
83
|
+
"@lezer/css": "1.3.3",
|
|
77
84
|
"@lezer/highlight": "1.2.3",
|
|
78
85
|
"@lezer/html": "1.3.13",
|
|
79
86
|
"@lezer/javascript": "1.5.4",
|
|
80
|
-
"@lezer/lr": "1.4.
|
|
87
|
+
"@lezer/lr": "1.4.8",
|
|
81
88
|
"@lezer/markdown": "1.6.3",
|
|
82
89
|
"@msgpack/msgpack": "3.1.3",
|
|
83
90
|
"@replit/codemirror-lang-nix": "6.0.1",
|
|
84
91
|
"@replit/codemirror-vim": "6.3.0",
|
|
85
|
-
"ajv": "8.17.1",
|
|
86
92
|
"commander": "^14.0.3",
|
|
87
93
|
"crelt": "1.0.6",
|
|
88
94
|
"esbuild": "^0.27.3",
|
|
@@ -93,20 +99,20 @@
|
|
|
93
99
|
"idb": "8.0.3",
|
|
94
100
|
"js-yaml": "^4.1.1",
|
|
95
101
|
"mime": "4.1.0",
|
|
96
|
-
"picomatch": "^4.0.3",
|
|
97
102
|
"preact": "10.28.2",
|
|
98
103
|
"preact-feather": "4.2.1",
|
|
99
104
|
"react-icons": "5.5.0",
|
|
100
105
|
"sass": "^1.97.3",
|
|
101
|
-
"style-mod": "4.1.
|
|
106
|
+
"style-mod": "4.1.3",
|
|
102
107
|
"turndown": "7.2.2"
|
|
103
108
|
},
|
|
104
109
|
"devDependencies": {
|
|
110
|
+
"@biomejs/biome": "2.4.6",
|
|
111
|
+
"@playwright/test": "^1.58.2",
|
|
105
112
|
"@preact/preset-vite": "^2.10.3",
|
|
106
113
|
"@types/gitignore-parser": "^0.0.3",
|
|
107
114
|
"@types/js-yaml": "^4.0.9",
|
|
108
115
|
"@types/node": "^22.0.0",
|
|
109
|
-
"@types/picomatch": "^4.0.2",
|
|
110
116
|
"@types/turndown": "^5.0.6",
|
|
111
117
|
"fake-indexeddb": "6.0.1",
|
|
112
118
|
"tsx": "^4.19.0",
|
|
@@ -115,6 +121,7 @@
|
|
|
115
121
|
"vitest": "^4.0.18"
|
|
116
122
|
},
|
|
117
123
|
"engines": {
|
|
118
|
-
"node": ">=
|
|
124
|
+
"node": ">=24.13.0",
|
|
125
|
+
"npm": ">=10.0.0"
|
|
119
126
|
}
|
|
120
127
|
}
|
package/plug-api/constants.ts
CHANGED
|
@@ -8,35 +8,3 @@ export const wrongSpacePathError: Error = new Error(
|
|
|
8
8
|
);
|
|
9
9
|
export const pingTimeout: number = 2000;
|
|
10
10
|
export const pingInterval: number = 5000;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* HTTP status codes that should be treated as "offline" conditions.
|
|
14
|
-
*
|
|
15
|
-
* This is particularly useful for cases where a proxy (such as Cloudflare or other reverse proxies)
|
|
16
|
-
* indicates that the backend server is down, but there is still network connectivity between
|
|
17
|
-
* the user and the proxy. In these scenarios, we want to allow the user to continue working
|
|
18
|
-
* with their cached data rather than showing an error, even though technically there is network
|
|
19
|
-
* connectivity to the proxy.
|
|
20
|
-
*
|
|
21
|
-
* This enables SilverBullet to work in a true "offline-first" manner, falling back to cached
|
|
22
|
-
* content when the backend is unavailable through no fault of the user's network connection.
|
|
23
|
-
*
|
|
24
|
-
* All 5xx server errors are included to prevent the client from caching error HTML pages
|
|
25
|
-
* (e.g., Nginx 500 error pages) which would prevent the client from booting in offline mode.
|
|
26
|
-
*/
|
|
27
|
-
export const offlineStatusCodes = {
|
|
28
|
-
500: "Internal Server Error", // Server encountered an unexpected condition
|
|
29
|
-
501: "Not Implemented", // Server does not support the functionality required
|
|
30
|
-
502: "Bad Gateway", // Proxy server received invalid response from upstream server
|
|
31
|
-
503: "Service Unavailable", // Server is temporarily unable to handle the request
|
|
32
|
-
504: "Gateway Timeout", // Proxy server did not receive a timely response from upstream server
|
|
33
|
-
505: "HTTP Version Not Supported", // Server does not support the HTTP version
|
|
34
|
-
506: "Variant Also Negotiates", // Server has an internal configuration error
|
|
35
|
-
507: "Insufficient Storage", // Server is unable to store the representation
|
|
36
|
-
508: "Loop Detected", // Server detected an infinite loop while processing
|
|
37
|
-
509: "Bandwidth Limit Exceeded", // Server bandwidth limit has been exceeded
|
|
38
|
-
510: "Not Extended", // Further extensions to the request are required
|
|
39
|
-
511: "Network Authentication Required", // Client needs to authenticate to gain network access
|
|
40
|
-
|
|
41
|
-
530: "Unable to resolve origin hostname", // Served when cloudflared is down on the host
|
|
42
|
-
} as const;
|
package/plug-api/lib/async.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
export function throttle(
|
|
1
|
+
export function throttle(
|
|
2
|
+
func: () => void,
|
|
3
|
+
limit: number,
|
|
4
|
+
): (() => void) & { flush(): void } {
|
|
2
5
|
let timer: any = null;
|
|
3
|
-
|
|
6
|
+
const throttled = () => {
|
|
4
7
|
if (!timer) {
|
|
5
8
|
timer = setTimeout(() => {
|
|
6
9
|
func();
|
|
@@ -8,6 +11,15 @@ export function throttle(func: () => void, limit: number): () => void {
|
|
|
8
11
|
}, limit);
|
|
9
12
|
}
|
|
10
13
|
};
|
|
14
|
+
// Immediately execute any pending call and cancel the timer
|
|
15
|
+
throttled.flush = () => {
|
|
16
|
+
if (timer) {
|
|
17
|
+
clearTimeout(timer);
|
|
18
|
+
timer = null;
|
|
19
|
+
func();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
return throttled;
|
|
11
23
|
}
|
|
12
24
|
|
|
13
25
|
export function throttleImmediately(
|
|
@@ -15,7 +27,7 @@ export function throttleImmediately(
|
|
|
15
27
|
limit: number,
|
|
16
28
|
): () => void {
|
|
17
29
|
let timer: any = null;
|
|
18
|
-
return
|
|
30
|
+
return () => {
|
|
19
31
|
if (!timer) {
|
|
20
32
|
func();
|
|
21
33
|
timer = setTimeout(() => {
|
|
@@ -38,7 +50,7 @@ export function timeout(ms: number): Promise<never> {
|
|
|
38
50
|
return new Promise((_resolve, reject) =>
|
|
39
51
|
setTimeout(() => {
|
|
40
52
|
reject(new Error("timeout"));
|
|
41
|
-
}, ms)
|
|
53
|
+
}, ms),
|
|
42
54
|
);
|
|
43
55
|
}
|
|
44
56
|
|
|
@@ -58,7 +70,7 @@ export class PromiseQueue {
|
|
|
58
70
|
return new Promise((resolve, reject) => {
|
|
59
71
|
this.queue.push({ fn, resolve, reject });
|
|
60
72
|
if (!this.processing) {
|
|
61
|
-
this.process();
|
|
73
|
+
void this.process();
|
|
62
74
|
}
|
|
63
75
|
});
|
|
64
76
|
}
|
|
@@ -79,7 +91,7 @@ export class PromiseQueue {
|
|
|
79
91
|
reject(error);
|
|
80
92
|
}
|
|
81
93
|
|
|
82
|
-
this.process(); // Continue processing the next promise in the queue
|
|
94
|
+
void this.process(); // Continue processing the next promise in the queue
|
|
83
95
|
}
|
|
84
96
|
}
|
|
85
97
|
|
|
@@ -105,7 +117,8 @@ export async function batchRequests<I, O>(
|
|
|
105
117
|
const batchResults = await Promise.all(batches.map(fn));
|
|
106
118
|
// Flatten the results
|
|
107
119
|
for (const batchResult of batchResults) {
|
|
108
|
-
if (Array.isArray(batchResult)) {
|
|
120
|
+
if (Array.isArray(batchResult)) {
|
|
121
|
+
// If fn returns an array, collect them
|
|
109
122
|
results.push(...batchResult);
|
|
110
123
|
}
|
|
111
124
|
}
|
package/plug-api/lib/crypto.ts
CHANGED
|
@@ -42,9 +42,8 @@ export async function hashSHA256(
|
|
|
42
42
|
): Promise<string> {
|
|
43
43
|
// Transform the string into an ArrayBuffer
|
|
44
44
|
const encoder = new TextEncoder();
|
|
45
|
-
const data: Uint8Array =
|
|
46
|
-
? encoder.encode(message)
|
|
47
|
-
: message;
|
|
45
|
+
const data: Uint8Array =
|
|
46
|
+
typeof message === "string" ? encoder.encode(message) : message;
|
|
48
47
|
|
|
49
48
|
// Generate the hash
|
|
50
49
|
const hashBuffer = await globalThis.crypto.subtle.digest(
|
|
@@ -53,9 +52,9 @@ export async function hashSHA256(
|
|
|
53
52
|
);
|
|
54
53
|
|
|
55
54
|
// Transform the hash into a hex string
|
|
56
|
-
return Array.from(new Uint8Array(hashBuffer))
|
|
57
|
-
b.toString(16).padStart(2, "0")
|
|
58
|
-
|
|
55
|
+
return Array.from(new Uint8Array(hashBuffer))
|
|
56
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
57
|
+
.join("");
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
/**
|
|
@@ -85,7 +84,7 @@ export async function encryptStringDeterministic(
|
|
|
85
84
|
key: CryptoKey,
|
|
86
85
|
clearText: string,
|
|
87
86
|
): Promise<string> {
|
|
88
|
-
const encrypted = await crypto.subtle.encrypt(
|
|
87
|
+
const encrypted = await globalThis.crypto.subtle.encrypt(
|
|
89
88
|
{ name: "AES-CTR", counter: fixedCounter, length: fixedCounter.length * 8 },
|
|
90
89
|
key,
|
|
91
90
|
new TextEncoder().encode(clearText),
|
|
@@ -97,7 +96,7 @@ export async function decryptStringDeterministic(
|
|
|
97
96
|
key: CryptoKey,
|
|
98
97
|
cipherText: string,
|
|
99
98
|
): Promise<string> {
|
|
100
|
-
const decrypted = await crypto.subtle.decrypt(
|
|
99
|
+
const decrypted = await globalThis.crypto.subtle.decrypt(
|
|
101
100
|
{ name: "AES-CTR", counter: fixedCounter, length: fixedCounter.length * 8 },
|
|
102
101
|
key,
|
|
103
102
|
base64Decode(cipherText) as BufferSource,
|
|
@@ -110,8 +109,8 @@ export async function encryptAesGcm(
|
|
|
110
109
|
key: CryptoKey,
|
|
111
110
|
data: Uint8Array,
|
|
112
111
|
): Promise<Uint8Array> {
|
|
113
|
-
const iv = crypto.getRandomValues(new Uint8Array(12)); // 96-bit IV recommended for GCM
|
|
114
|
-
const encryptedBuffer = await crypto.subtle.encrypt(
|
|
112
|
+
const iv = globalThis.crypto.getRandomValues(new Uint8Array(12)); // 96-bit IV recommended for GCM
|
|
113
|
+
const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
|
|
115
114
|
{ name: "AES-GCM", iv },
|
|
116
115
|
key,
|
|
117
116
|
data as BufferSource,
|
|
@@ -132,7 +131,7 @@ export async function decryptAesGcm(
|
|
|
132
131
|
): Promise<Uint8Array> {
|
|
133
132
|
const iv = encryptedData.slice(0, 12); // extract IV (first 12 bytes)
|
|
134
133
|
const ciphertext = encryptedData.slice(12);
|
|
135
|
-
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
134
|
+
const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
|
|
136
135
|
{ name: "AES-GCM", iv },
|
|
137
136
|
key,
|
|
138
137
|
ciphertext,
|
|
@@ -148,7 +147,7 @@ export async function deriveCTRKeyFromPassword(
|
|
|
148
147
|
const passwordBytes = new TextEncoder().encode(password);
|
|
149
148
|
|
|
150
149
|
// Import password as a CryptoKey
|
|
151
|
-
const baseKey = await crypto.subtle.importKey(
|
|
150
|
+
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
152
151
|
"raw",
|
|
153
152
|
passwordBytes,
|
|
154
153
|
{ name: "PBKDF2" },
|
|
@@ -156,7 +155,7 @@ export async function deriveCTRKeyFromPassword(
|
|
|
156
155
|
["deriveBits", "deriveKey"],
|
|
157
156
|
);
|
|
158
157
|
|
|
159
|
-
return crypto.subtle.deriveKey(
|
|
158
|
+
return globalThis.crypto.subtle.deriveKey(
|
|
160
159
|
{
|
|
161
160
|
name: "PBKDF2",
|
|
162
161
|
salt: salt as BufferSource,
|
|
@@ -174,7 +173,7 @@ export async function deriveCTRKeyFromPassword(
|
|
|
174
173
|
}
|
|
175
174
|
|
|
176
175
|
export function importKey(b64EncodedKey: string): Promise<CryptoKey> {
|
|
177
|
-
return crypto.subtle.importKey(
|
|
176
|
+
return globalThis.crypto.subtle.importKey(
|
|
178
177
|
"raw",
|
|
179
178
|
base64Decode(b64EncodedKey) as BufferSource,
|
|
180
179
|
{ name: "AES-CTR" },
|
|
@@ -184,15 +183,15 @@ export function importKey(b64EncodedKey: string): Promise<CryptoKey> {
|
|
|
184
183
|
}
|
|
185
184
|
|
|
186
185
|
export async function exportKey(ctrKey: CryptoKey): Promise<string> {
|
|
187
|
-
const key = await crypto.subtle.exportKey("raw", ctrKey);
|
|
186
|
+
const key = await globalThis.crypto.subtle.exportKey("raw", ctrKey);
|
|
188
187
|
return base64Encode(new Uint8Array(key));
|
|
189
188
|
}
|
|
190
189
|
|
|
191
190
|
export async function deriveGCMKeyFromCTR(
|
|
192
191
|
ctrKey: CryptoKey,
|
|
193
192
|
): Promise<CryptoKey> {
|
|
194
|
-
const rawKey = await crypto.subtle.exportKey("raw", ctrKey);
|
|
195
|
-
return crypto.subtle.importKey(
|
|
193
|
+
const rawKey = await globalThis.crypto.subtle.exportKey("raw", ctrKey);
|
|
194
|
+
return globalThis.crypto.subtle.importKey(
|
|
196
195
|
"raw",
|
|
197
196
|
rawKey,
|
|
198
197
|
{ name: "AES-GCM" },
|
package/plug-api/lib/dates.ts
CHANGED
|
@@ -3,11 +3,19 @@ export function niceDate(d: Date): string {
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
export function localDateString(d: Date): string {
|
|
6
|
-
return
|
|
7
|
-
|
|
8
|
-
"-" +
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
"
|
|
6
|
+
return (
|
|
7
|
+
d.getFullYear() +
|
|
8
|
+
"-" +
|
|
9
|
+
String(d.getMonth() + 1).padStart(2, "0") +
|
|
10
|
+
"-" +
|
|
11
|
+
String(d.getDate()).padStart(2, "0") +
|
|
12
|
+
"T" +
|
|
13
|
+
String(d.getHours()).padStart(2, "0") +
|
|
14
|
+
":" +
|
|
15
|
+
String(d.getMinutes()).padStart(2, "0") +
|
|
16
|
+
":" +
|
|
17
|
+
String(d.getSeconds()).padStart(2, "0") +
|
|
18
|
+
"." +
|
|
19
|
+
String(d.getMilliseconds()).padStart(3, "0")
|
|
20
|
+
);
|
|
13
21
|
}
|
package/plug-api/lib/json.ts
CHANGED
|
@@ -52,11 +52,17 @@ export function deepEqual(a: any, b: any): boolean {
|
|
|
52
52
|
export function cleanStringDate(d: Date): string {
|
|
53
53
|
// If no significant time, return a date string only
|
|
54
54
|
if (
|
|
55
|
-
d.getUTCHours() === 0 &&
|
|
55
|
+
d.getUTCHours() === 0 &&
|
|
56
|
+
d.getUTCMinutes() === 0 &&
|
|
57
|
+
d.getUTCSeconds() === 0
|
|
56
58
|
) {
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
return (
|
|
60
|
+
d.getUTCFullYear() +
|
|
61
|
+
"-" +
|
|
62
|
+
String(d.getUTCMonth() + 1).padStart(2, "0") +
|
|
63
|
+
"-" +
|
|
64
|
+
String(d.getUTCDate()).padStart(2, "0")
|
|
65
|
+
);
|
|
60
66
|
} else {
|
|
61
67
|
return d.toISOString();
|
|
62
68
|
}
|
|
@@ -125,7 +131,7 @@ export function deepClone<T>(obj: T, ignoreKeys: string[] = []): T {
|
|
|
125
131
|
for (const key in obj) {
|
|
126
132
|
if (ignoreKeys.includes(key)) {
|
|
127
133
|
objClone[key] = obj[key];
|
|
128
|
-
} else if (Object.
|
|
134
|
+
} else if (Object.hasOwn(obj, key)) {
|
|
129
135
|
objClone[key] = deepClone(obj[key], ignoreKeys);
|
|
130
136
|
}
|
|
131
137
|
}
|