@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
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import type { ASTCtx, LuaBlock, LuaExpression } from "./ast.ts";
|
|
2
|
+
import { evalExpression } from "./eval.ts";
|
|
3
|
+
import { parseBlock, parseExpressionString } from "./parse.ts";
|
|
4
|
+
import {
|
|
5
|
+
type PrintOptions,
|
|
6
|
+
prettyPrintBlock,
|
|
7
|
+
prettyPrintExpression,
|
|
8
|
+
} from "./pretty_print.ts";
|
|
9
|
+
import {
|
|
10
|
+
isILuaFunction,
|
|
11
|
+
LuaEnv,
|
|
12
|
+
LuaStackFrame,
|
|
13
|
+
LuaTable,
|
|
14
|
+
luaGet,
|
|
15
|
+
luaKeys,
|
|
16
|
+
luaToString,
|
|
17
|
+
luaTypeOf,
|
|
18
|
+
luaValueToJS,
|
|
19
|
+
} from "./runtime.ts";
|
|
20
|
+
import { buildThreadLocalEnv } from "../space_lua_api.ts";
|
|
21
|
+
import type { SysCallMapping, System } from "../plugos/system.ts";
|
|
22
|
+
import { isSendable } from "../plugos/util.ts";
|
|
23
|
+
import type {
|
|
24
|
+
LuaFunctionInfo,
|
|
25
|
+
LuaPropertyInspection,
|
|
26
|
+
LuaValueInspection,
|
|
27
|
+
} from "../../plug-api/types/index.ts";
|
|
28
|
+
import { encodeRef } from "../../plug-api/lib/ref.ts";
|
|
29
|
+
import { resolveASTReference } from "../space_lua.ts";
|
|
30
|
+
|
|
31
|
+
async function inspectValue(
|
|
32
|
+
value: unknown,
|
|
33
|
+
path: string[],
|
|
34
|
+
): Promise<Omit<LuaValueInspection, "properties">> {
|
|
35
|
+
const type = value instanceof LuaEnv ? "table" : await luaTypeOf(value);
|
|
36
|
+
if (!isILuaFunction(value)) {
|
|
37
|
+
return { type };
|
|
38
|
+
}
|
|
39
|
+
const functionInfo: LuaFunctionInfo = {
|
|
40
|
+
...(value.info ?? { kind: "builtin" }),
|
|
41
|
+
name: value.info?.name ?? path.join("."),
|
|
42
|
+
};
|
|
43
|
+
const definitionRef =
|
|
44
|
+
functionInfo.kind === "lua" && functionInfo.source
|
|
45
|
+
? resolveASTReference(functionInfo.source as ASTCtx)
|
|
46
|
+
: null;
|
|
47
|
+
return {
|
|
48
|
+
type,
|
|
49
|
+
functionInfo,
|
|
50
|
+
definition: definitionRef ? encodeRef(definitionRef) : undefined,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function inspectLuaPath(
|
|
55
|
+
env: LuaEnv,
|
|
56
|
+
path: string[],
|
|
57
|
+
): Promise<LuaValueInspection | null> {
|
|
58
|
+
const sf = LuaStackFrame.createWithGlobalEnv(env);
|
|
59
|
+
let value: unknown = env;
|
|
60
|
+
for (const key of path) {
|
|
61
|
+
value = await luaGet(value, key, null, sf);
|
|
62
|
+
if (value === null || value === undefined) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const properties: LuaPropertyInspection[] = [];
|
|
68
|
+
const keys =
|
|
69
|
+
value instanceof LuaEnv
|
|
70
|
+
? value.keys()
|
|
71
|
+
: value instanceof LuaTable ||
|
|
72
|
+
Array.isArray(value) ||
|
|
73
|
+
(typeof value === "object" && value !== null)
|
|
74
|
+
? luaKeys(value)
|
|
75
|
+
: [];
|
|
76
|
+
for (const key of [...new Set(keys)]
|
|
77
|
+
.filter((candidate): candidate is string => typeof candidate === "string")
|
|
78
|
+
.sort()) {
|
|
79
|
+
const child = await luaGet(value, key, null, sf);
|
|
80
|
+
if (child === null || child === undefined) continue;
|
|
81
|
+
properties.push({
|
|
82
|
+
key,
|
|
83
|
+
...(await inspectValue(child, [...path, key])),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
...(await inspectValue(value, path)),
|
|
89
|
+
properties,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function luaSyscalls(
|
|
94
|
+
system: System<any>,
|
|
95
|
+
getEnvironment: () => LuaEnv,
|
|
96
|
+
): SysCallMapping {
|
|
97
|
+
return {
|
|
98
|
+
"lua.parseBlock": {
|
|
99
|
+
callback: (_ctx, code: string): LuaBlock => parseBlock(code),
|
|
100
|
+
description:
|
|
101
|
+
"Parses a Space Lua chunk and returns its AST. Blocks retain comments in source order with their exact text, kind, and source range.",
|
|
102
|
+
parameters: [
|
|
103
|
+
{ name: "code", type: "string", description: "Lua code to parse." },
|
|
104
|
+
],
|
|
105
|
+
returns: [{ type: "table", description: "Parsed Lua block AST." }],
|
|
106
|
+
examples: [
|
|
107
|
+
{
|
|
108
|
+
code: 'local ast = lua.parseBlock("print(\\"Hello\\")")',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
see: "API/lua",
|
|
112
|
+
},
|
|
113
|
+
// Deprecated alias for `lua.parseBlock`, kept for backwards compatibility.
|
|
114
|
+
"lua.parse": {
|
|
115
|
+
callback: (_ctx, code: string): LuaBlock => parseBlock(code),
|
|
116
|
+
description: "Deprecated alias for lua.parseBlock.",
|
|
117
|
+
parameters: [
|
|
118
|
+
{ name: "code", type: "string", description: "Lua code to parse." },
|
|
119
|
+
],
|
|
120
|
+
returns: [{ type: "table", description: "Parsed Lua block AST." }],
|
|
121
|
+
deprecated: "Use lua.parseBlock instead.",
|
|
122
|
+
see: "API/lua",
|
|
123
|
+
},
|
|
124
|
+
"lua.parseExpression": {
|
|
125
|
+
callback: (_ctx, expression: string): LuaExpression =>
|
|
126
|
+
parseExpressionString(expression),
|
|
127
|
+
description: "Parses a Space Lua expression and returns its AST.",
|
|
128
|
+
parameters: [
|
|
129
|
+
{
|
|
130
|
+
name: "expression",
|
|
131
|
+
type: "string",
|
|
132
|
+
description: "Lua expression to parse.",
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
returns: [{ type: "table", description: "Parsed expression AST." }],
|
|
136
|
+
examples: [
|
|
137
|
+
{
|
|
138
|
+
code: 'local expression = lua.parseExpression("1 + 2 * 3")',
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
see: "API/lua",
|
|
142
|
+
},
|
|
143
|
+
"lua.prettyPrintBlock": {
|
|
144
|
+
callback: (_ctx, block: LuaBlock, opts?: PrintOptions): string =>
|
|
145
|
+
prettyPrintBlock(block, opts),
|
|
146
|
+
description:
|
|
147
|
+
"Pretty-prints a parsed Space Lua block. Comments are preserved while their placement and indentation are normalized.",
|
|
148
|
+
parameters: [
|
|
149
|
+
{ name: "block", type: "table", description: "Parsed block AST." },
|
|
150
|
+
{
|
|
151
|
+
name: "options",
|
|
152
|
+
type: "table",
|
|
153
|
+
description:
|
|
154
|
+
"Formatting options: `indentWidth`, `quote`, and `trailingComma`.",
|
|
155
|
+
optional: true,
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
returns: [{ type: "string", description: "Formatted Lua source." }],
|
|
159
|
+
examples: [
|
|
160
|
+
{
|
|
161
|
+
code: 'local formatted = lua.prettyPrintBlock(lua.parseBlock("if a then return 1 end"))',
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
see: "API/lua",
|
|
165
|
+
},
|
|
166
|
+
"lua.prettyPrintExpression": {
|
|
167
|
+
callback: (
|
|
168
|
+
_ctx,
|
|
169
|
+
expression: LuaExpression,
|
|
170
|
+
opts?: PrintOptions,
|
|
171
|
+
): string => prettyPrintExpression(expression, opts),
|
|
172
|
+
description: "Pretty-prints a parsed Space Lua expression.",
|
|
173
|
+
parameters: [
|
|
174
|
+
{
|
|
175
|
+
name: "expression",
|
|
176
|
+
type: "table",
|
|
177
|
+
description: "Parsed expression AST.",
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "options",
|
|
181
|
+
type: "table",
|
|
182
|
+
description:
|
|
183
|
+
"Formatting options: `indentWidth`, `quote`, and `trailingComma`.",
|
|
184
|
+
optional: true,
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
returns: [{ type: "string", description: "Formatted Lua source." }],
|
|
188
|
+
examples: [
|
|
189
|
+
{
|
|
190
|
+
code: 'local formatted = lua.prettyPrintExpression(lua.parseExpression("{a=1,b=2}"))',
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
see: "API/lua",
|
|
194
|
+
},
|
|
195
|
+
/**
|
|
196
|
+
* Evaluates a Lua expression and returns the result as a JavaScript value
|
|
197
|
+
* @param _ctx
|
|
198
|
+
* @param expression
|
|
199
|
+
* @returns
|
|
200
|
+
*/
|
|
201
|
+
"lua.evalExpression": {
|
|
202
|
+
callback: async (_ctx, expression: string) => {
|
|
203
|
+
try {
|
|
204
|
+
const ast = parseExpressionString(expression);
|
|
205
|
+
const globalEnv = getEnvironment();
|
|
206
|
+
const env = await buildThreadLocalEnv(system, globalEnv);
|
|
207
|
+
const sf = new LuaStackFrame(env, null);
|
|
208
|
+
const luaResult = await evalExpression(ast, globalEnv, sf);
|
|
209
|
+
const jsResult = luaValueToJS(luaResult, sf);
|
|
210
|
+
if (isSendable(jsResult)) {
|
|
211
|
+
return jsResult;
|
|
212
|
+
} else {
|
|
213
|
+
// This may evaluate to e.g. a function, which is not sendable, in this case we'll console.warn and return a stringified version of the result
|
|
214
|
+
console.warn(
|
|
215
|
+
"Lua eval result is not sendable, returning stringified version",
|
|
216
|
+
jsResult,
|
|
217
|
+
);
|
|
218
|
+
return luaToString(luaResult);
|
|
219
|
+
}
|
|
220
|
+
} catch (e: any) {
|
|
221
|
+
console.error("Lua eval error: ", e.message, e.sf?.astCtx);
|
|
222
|
+
throw e;
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
description: "Evaluates a Space Lua expression.",
|
|
226
|
+
parameters: [
|
|
227
|
+
{
|
|
228
|
+
name: "expression",
|
|
229
|
+
type: "string",
|
|
230
|
+
description: "Lua expression to evaluate.",
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
returns: [{ description: "Evaluated result." }],
|
|
234
|
+
examples: [
|
|
235
|
+
{
|
|
236
|
+
code: 'local result = lua.evalExpression("1 + 2 * 3")\nprint(result)',
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
see: "API/lua",
|
|
240
|
+
},
|
|
241
|
+
"lua.inspect": {
|
|
242
|
+
callback: (_ctx, path: string[] = []) =>
|
|
243
|
+
inspectLuaPath(getEnvironment(), path),
|
|
244
|
+
description:
|
|
245
|
+
"Inspects a value in the live Space Lua environment and returns serializable type, function, definition, and property metadata.",
|
|
246
|
+
parameters: [
|
|
247
|
+
{
|
|
248
|
+
name: "path",
|
|
249
|
+
type: "table",
|
|
250
|
+
description:
|
|
251
|
+
"Sequence of property names from the global environment; omit to inspect globals.",
|
|
252
|
+
optional: true,
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
returns: [
|
|
256
|
+
{
|
|
257
|
+
type: "table|nil",
|
|
258
|
+
description:
|
|
259
|
+
"Inspection metadata, or nil when the requested path does not exist.",
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
examples: [
|
|
263
|
+
{
|
|
264
|
+
code: 'local info = lua.inspect({"editor", "getText"})',
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
see: "API/lua",
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
}
|
|
@@ -142,7 +142,6 @@ function parseInt(s: string): { ok: boolean; value: number } {
|
|
|
142
142
|
let acc = 0;
|
|
143
143
|
let any = false;
|
|
144
144
|
|
|
145
|
-
// hex?
|
|
146
145
|
if (s.charCodeAt(i) === 48 && i + 1 < n) {
|
|
147
146
|
// '0'
|
|
148
147
|
const x = s.charCodeAt(i + 1);
|
|
@@ -179,7 +178,6 @@ function parseInt(s: string): { ok: boolean; value: number } {
|
|
|
179
178
|
}
|
|
180
179
|
}
|
|
181
180
|
|
|
182
|
-
// decimal integer
|
|
183
181
|
while (i < n) {
|
|
184
182
|
const c = s.charCodeAt(i);
|
|
185
183
|
if (c < 48 || c > 57) {
|
|
@@ -221,7 +219,6 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
221
219
|
let fracAny = false;
|
|
222
220
|
let val = 0;
|
|
223
221
|
|
|
224
|
-
// integer part
|
|
225
222
|
while (i < n) {
|
|
226
223
|
const c = s.charCodeAt(i);
|
|
227
224
|
if (c < 48 || c > 57) {
|
|
@@ -233,7 +230,6 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
233
230
|
i++;
|
|
234
231
|
}
|
|
235
232
|
|
|
236
|
-
// fractional part
|
|
237
233
|
if (i < n) {
|
|
238
234
|
if (s.charCodeAt(i) === 46) {
|
|
239
235
|
// '.'
|
|
@@ -253,7 +249,6 @@ function parseDecFloat(s: string): { ok: boolean; value: number } {
|
|
|
253
249
|
}
|
|
254
250
|
}
|
|
255
251
|
|
|
256
|
-
// exponent
|
|
257
252
|
let exp = 0;
|
|
258
253
|
let hasExp = false;
|
|
259
254
|
if (i < n) {
|
|
@@ -347,7 +342,6 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
347
342
|
let anyHex = false;
|
|
348
343
|
let sawDot = false;
|
|
349
344
|
|
|
350
|
-
// integer hex digits
|
|
351
345
|
while (i < n) {
|
|
352
346
|
const c = s.charCodeAt(i);
|
|
353
347
|
let d = -1;
|
|
@@ -371,7 +365,6 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
371
365
|
i++;
|
|
372
366
|
}
|
|
373
367
|
|
|
374
|
-
// optional fractional part
|
|
375
368
|
if (i < n) {
|
|
376
369
|
if (s.charCodeAt(i) === 46) {
|
|
377
370
|
// '.'
|
|
@@ -444,7 +437,6 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
444
437
|
return { ok: false, value: 0 };
|
|
445
438
|
}
|
|
446
439
|
} else {
|
|
447
|
-
// no exponent marker
|
|
448
440
|
if (!sawDot) {
|
|
449
441
|
// without dot we must reject so parseInt can claim it as integer
|
|
450
442
|
return { ok: false, value: 0 };
|
|
@@ -456,7 +448,6 @@ function parseHexFloat(s: string): { ok: boolean; value: number } {
|
|
|
456
448
|
exp = 0; // implicit p0
|
|
457
449
|
}
|
|
458
450
|
} else {
|
|
459
|
-
// end of string
|
|
460
451
|
if (!sawDot) {
|
|
461
452
|
return { ok: false, value: 0 };
|
|
462
453
|
}
|
package/dist/plug-compile.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// version.ts
|
|
7
|
-
var version = "2.
|
|
7
|
+
var version = "2.11.0";
|
|
8
8
|
|
|
9
9
|
// client/plugos/plug_compile.ts
|
|
10
10
|
import * as path from "node:path";
|
|
@@ -1371,8 +1371,7 @@ ${Object.entries(manifest.functions).map(([funcName, def]) => {
|
|
|
1371
1371
|
}
|
|
1372
1372
|
let [filePath, jsFunctionName] = def.path.split(":");
|
|
1373
1373
|
filePath = path.join(rootPath, filePath);
|
|
1374
|
-
return `import {${jsFunctionName} as ${funcName}} from "${
|
|
1375
|
-
path.resolve(filePath).replaceAll("\\", "\\\\")}";
|
|
1374
|
+
return `import {${jsFunctionName} as ${funcName}} from "${path.resolve(filePath).replaceAll("\\", "\\\\")}";
|
|
1376
1375
|
`;
|
|
1377
1376
|
}).join("")}
|
|
1378
1377
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silverbulletmd/silverbullet",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A self-hosted, web-based note taking app",
|
|
6
6
|
"publishConfig": {
|
|
@@ -15,20 +15,26 @@
|
|
|
15
15
|
"./lib/tree": "./plug-api/lib/tree.ts",
|
|
16
16
|
"./lib/ref": "./plug-api/lib/ref.ts",
|
|
17
17
|
"./lib/resolve": "./plug-api/lib/resolve.ts",
|
|
18
|
+
"./lib/resolve_path": "./plug-api/lib/resolve_path.ts",
|
|
19
|
+
"./lib/link_write": "./plug-api/lib/link_write.ts",
|
|
18
20
|
"./lib/dates": "./plug-api/lib/dates.ts",
|
|
21
|
+
"./lib/fuzzy": "./plug-api/lib/fuzzy.ts",
|
|
19
22
|
"./lib/async": "./plug-api/lib/async.ts",
|
|
20
23
|
"./lib/crypto": "./plug-api/lib/crypto.ts",
|
|
21
24
|
"./lib/limited_map": "./plug-api/lib/limited_map.ts",
|
|
22
25
|
"./lib/tags": "./plug-api/lib/tags.ts",
|
|
26
|
+
"./lib/collation": "./plug-api/lib/collation.ts",
|
|
23
27
|
"./lib/transclusion": "./plug-api/lib/transclusion.ts",
|
|
24
28
|
"./lib/native_fetch": "./plug-api/lib/native_fetch.ts",
|
|
25
29
|
"./lib/query_expression": "./plug-api/lib/query_expression.ts",
|
|
30
|
+
"./lib/panel_styles": "./plug-api/lib/panel_styles.ts",
|
|
26
31
|
"./type/client": "./plug-api/types/client.ts",
|
|
27
32
|
"./type/config": "./plug-api/types/config.ts",
|
|
28
33
|
"./type/manifest": "./plug-api/types/manifest.ts",
|
|
29
34
|
"./type/namespace": "./plug-api/types/namespace.ts",
|
|
30
35
|
"./type/datastore": "./plug-api/types/datastore.ts",
|
|
31
36
|
"./type/event": "./plug-api/types/event.ts",
|
|
37
|
+
"./type/revisions": "./plug-api/types/revisions.ts",
|
|
32
38
|
"./type/index": "./plug-api/types/index.ts"
|
|
33
39
|
},
|
|
34
40
|
"bin": {
|
|
@@ -41,6 +47,7 @@
|
|
|
41
47
|
"client/plugos/**/*.ts",
|
|
42
48
|
"!client/plugos/**/*.test.ts",
|
|
43
49
|
"client/asset_bundle/bundle.ts",
|
|
50
|
+
"client/config.ts",
|
|
44
51
|
"client/data/kv_primitives.ts",
|
|
45
52
|
"client/data/datastore.ts",
|
|
46
53
|
"client/space_lua/**/*.ts",
|
|
@@ -48,6 +55,7 @@
|
|
|
48
55
|
"!client/space_lua/**/*.bench.ts",
|
|
49
56
|
"client/markdown_parser/constants.ts",
|
|
50
57
|
"plugs/builtin_plugs.ts",
|
|
58
|
+
"plugs/index/types.ts",
|
|
51
59
|
"dist/plug-compile.js"
|
|
52
60
|
],
|
|
53
61
|
"scripts": {
|
|
@@ -55,16 +63,17 @@
|
|
|
55
63
|
"build:plugs": "tsx build/build_plugs.ts",
|
|
56
64
|
"build:client": "tsx build/build_client.ts",
|
|
57
65
|
"build:plug-compile": "tsx build/build_plug_compile.ts",
|
|
66
|
+
"prebuild:client": "node build/patch_codemirror_view.mjs",
|
|
58
67
|
"prepublishOnly": "npm run build:plug-compile",
|
|
59
68
|
"test": "vitest run",
|
|
60
|
-
"check": "tsc --noEmit",
|
|
69
|
+
"check": "tsc --noEmit && tsc -p tsconfig.e2e.json",
|
|
61
70
|
"lint": "biome lint .",
|
|
62
71
|
"fmt": "biome format --write .",
|
|
63
72
|
"fmt:check": "biome format .",
|
|
64
73
|
"bench": "vitest bench",
|
|
65
74
|
"index-stats": "tsx plugs/index/index_stats.ts",
|
|
66
|
-
"test:e2e": "npx playwright test",
|
|
67
|
-
"test:e2e:headed": "npx playwright test --headed"
|
|
75
|
+
"test:e2e": "npx playwright test --project=chromium",
|
|
76
|
+
"test:e2e:headed": "npx playwright test --project=chromium --headed"
|
|
68
77
|
},
|
|
69
78
|
"peerDependencies": {
|
|
70
79
|
"preact": "^10.25.0"
|
|
@@ -92,8 +101,10 @@
|
|
|
92
101
|
"@lezer/lr": "1.4.8",
|
|
93
102
|
"@lezer/markdown": "1.6.3",
|
|
94
103
|
"@msgpack/msgpack": "3.1.3",
|
|
104
|
+
"@noble/hashes": "2.0.1",
|
|
95
105
|
"@replit/codemirror-lang-nix": "6.0.1",
|
|
96
106
|
"@replit/codemirror-vim": "6.3.0",
|
|
107
|
+
"@types/js-yaml": "^4.0.9",
|
|
97
108
|
"commander": "^14.0.3",
|
|
98
109
|
"crelt": "1.0.6",
|
|
99
110
|
"d3-force": "^3.0.0",
|
|
@@ -118,7 +129,6 @@
|
|
|
118
129
|
"@preact/preset-vite": "^2.10.3",
|
|
119
130
|
"@types/d3-force": "^3.0.10",
|
|
120
131
|
"@types/gitignore-parser": "^0.0.3",
|
|
121
|
-
"@types/js-yaml": "^4.0.9",
|
|
122
132
|
"@types/node": "^22.0.0",
|
|
123
133
|
"@types/turndown": "^5.0.6",
|
|
124
134
|
"fake-indexeddb": "6.0.1",
|
package/plug-api/lib/async.ts
CHANGED
|
@@ -11,7 +11,6 @@ export function throttle(
|
|
|
11
11
|
}, limit);
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
|
-
// Immediately execute any pending call and cancel the timer
|
|
15
14
|
throttled.flush = () => {
|
|
16
15
|
if (timer) {
|
|
17
16
|
clearTimeout(timer);
|
|
@@ -91,7 +90,7 @@ export class PromiseQueue {
|
|
|
91
90
|
reject(error);
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
void this.process();
|
|
93
|
+
void this.process();
|
|
95
94
|
}
|
|
96
95
|
}
|
|
97
96
|
|
|
@@ -108,17 +107,13 @@ export async function batchRequests<I, O>(
|
|
|
108
107
|
batchSize: number,
|
|
109
108
|
): Promise<O[]> {
|
|
110
109
|
const results: O[] = [];
|
|
111
|
-
// Split values into batches of batchSize
|
|
112
110
|
const batches: I[][] = [];
|
|
113
111
|
for (let i = 0; i < values.length; i += batchSize) {
|
|
114
112
|
batches.push(values.slice(i, i + batchSize));
|
|
115
113
|
}
|
|
116
|
-
// Run fn on them in parallel
|
|
117
114
|
const batchResults = await Promise.all(batches.map(fn));
|
|
118
|
-
// Flatten the results
|
|
119
115
|
for (const batchResult of batchResults) {
|
|
120
116
|
if (Array.isArray(batchResult)) {
|
|
121
|
-
// If fn returns an array, collect them
|
|
122
117
|
results.push(...batchResult);
|
|
123
118
|
}
|
|
124
119
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { QueryCollationConfig } from "../types/config.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Compares two values the same way the query engine's `order by` does:
|
|
5
|
+
* codepoint order by default, or `Intl.Collator` order when the space's
|
|
6
|
+
* `queryCollation` config turns it on and both sides are strings.
|
|
7
|
+
*/
|
|
8
|
+
export function compareCollated(
|
|
9
|
+
// deno-lint-ignore no-explicit-any
|
|
10
|
+
a: any,
|
|
11
|
+
// deno-lint-ignore no-explicit-any
|
|
12
|
+
b: any,
|
|
13
|
+
collation: QueryCollationConfig | undefined,
|
|
14
|
+
collator: Intl.Collator,
|
|
15
|
+
): number {
|
|
16
|
+
if (collation?.enabled && typeof a === "string" && typeof b === "string") {
|
|
17
|
+
return collator.compare(a, b);
|
|
18
|
+
}
|
|
19
|
+
if (a < b) return -1;
|
|
20
|
+
if (a > b) return 1;
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
package/plug-api/lib/crypto.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
2
|
+
|
|
1
3
|
export function base64Decode(s: string): Uint8Array {
|
|
2
4
|
const binString = atob(s);
|
|
3
5
|
const len = binString.length;
|
|
@@ -32,31 +34,33 @@ export function base64DecodeDataUrl(dataUrl: string): Uint8Array {
|
|
|
32
34
|
return base64Decode(b64Encoded);
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
/**
|
|
36
|
-
* Perform sha256 hash using the browser's crypto APIs
|
|
37
|
-
* Note: this will only work over HTTPS
|
|
38
|
-
* @param message
|
|
39
|
-
*/
|
|
40
37
|
export async function hashSHA256(
|
|
41
38
|
message: string | Uint8Array,
|
|
42
39
|
): Promise<string> {
|
|
43
|
-
// Transform the string into an ArrayBuffer
|
|
44
40
|
const encoder = new TextEncoder();
|
|
45
41
|
const data: Uint8Array =
|
|
46
42
|
typeof message === "string" ? encoder.encode(message) : message;
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
data as BufferSource,
|
|
52
|
-
);
|
|
44
|
+
const hashBuffer = globalThis.crypto.subtle
|
|
45
|
+
? await globalThis.crypto.subtle.digest("SHA-256", data as BufferSource)
|
|
46
|
+
: sha256(data);
|
|
53
47
|
|
|
54
|
-
// Transform the hash into a hex string
|
|
55
48
|
return Array.from(new Uint8Array(hashBuffer))
|
|
56
49
|
.map((b) => b.toString(16).padStart(2, "0"))
|
|
57
50
|
.join("");
|
|
58
51
|
}
|
|
59
52
|
|
|
53
|
+
export function randomUUID(): string {
|
|
54
|
+
if (globalThis.crypto.randomUUID) return globalThis.crypto.randomUUID();
|
|
55
|
+
const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16));
|
|
56
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
57
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
58
|
+
const hex = Array.from(bytes, (byte) =>
|
|
59
|
+
byte.toString(16).padStart(2, "0"),
|
|
60
|
+
).join("");
|
|
61
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
60
64
|
/**
|
|
61
65
|
* To avoid database clashes based on space folder path name, base URLs and encryption keys we derive
|
|
62
66
|
* a database name from a hash of all these combined together
|
|
@@ -117,7 +121,6 @@ export async function encryptAesGcm(
|
|
|
117
121
|
);
|
|
118
122
|
const encrypted = new Uint8Array(encryptedBuffer);
|
|
119
123
|
|
|
120
|
-
// Prepend IV to ciphertext
|
|
121
124
|
const result = new Uint8Array(iv.length + encrypted.length);
|
|
122
125
|
result.set(iv, 0);
|
|
123
126
|
result.set(encrypted, iv.length);
|
|
@@ -143,10 +146,8 @@ export async function deriveCTRKeyFromPassword(
|
|
|
143
146
|
password: string,
|
|
144
147
|
salt: Uint8Array,
|
|
145
148
|
): Promise<CryptoKey> {
|
|
146
|
-
// Encode password to ArrayBuffer
|
|
147
149
|
const passwordBytes = new TextEncoder().encode(password);
|
|
148
150
|
|
|
149
|
-
// Import password as a CryptoKey
|
|
150
151
|
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
151
152
|
"raw",
|
|
152
153
|
passwordBytes,
|
package/plug-api/lib/dates.ts
CHANGED
|
@@ -2,6 +2,39 @@ export function niceDate(d: Date): string {
|
|
|
2
2
|
return localDateString(d).split("T")[0];
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
/** Largest-unit-first, so the cascade below divides down to the right one. */
|
|
6
|
+
const RELATIVE_DIVISIONS: {
|
|
7
|
+
amount: number;
|
|
8
|
+
unit: Intl.RelativeTimeFormatUnit;
|
|
9
|
+
}[] = [
|
|
10
|
+
{ amount: 60, unit: "second" },
|
|
11
|
+
{ amount: 60, unit: "minute" },
|
|
12
|
+
{ amount: 24, unit: "hour" },
|
|
13
|
+
{ amount: 7, unit: "day" },
|
|
14
|
+
{ amount: 4.34524, unit: "week" },
|
|
15
|
+
{ amount: 12, unit: "month" },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* "10 minutes ago", "yesterday", "3 months ago". `numeric: "auto"` is what
|
|
20
|
+
* turns the -1 cases into words rather than "1 day ago".
|
|
21
|
+
*/
|
|
22
|
+
export function relativeTime(
|
|
23
|
+
timestamp: number,
|
|
24
|
+
locale?: string,
|
|
25
|
+
now: number = Date.now(),
|
|
26
|
+
): string {
|
|
27
|
+
const formatter = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
|
|
28
|
+
let duration = (timestamp - now) / 1000;
|
|
29
|
+
for (const { amount, unit } of RELATIVE_DIVISIONS) {
|
|
30
|
+
if (Math.abs(duration) < amount) {
|
|
31
|
+
return formatter.format(Math.round(duration), unit);
|
|
32
|
+
}
|
|
33
|
+
duration /= amount;
|
|
34
|
+
}
|
|
35
|
+
return formatter.format(Math.round(duration), "year");
|
|
36
|
+
}
|
|
37
|
+
|
|
5
38
|
export function localDateString(d: Date): string {
|
|
6
39
|
return (
|
|
7
40
|
d.getFullYear() +
|