@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.
Files changed (123) hide show
  1. package/README.md +38 -25
  2. package/client/asset_bundle/bundle.ts +0 -1
  3. package/client/config.ts +343 -0
  4. package/client/markdown_parser/constants.ts +3 -1
  5. package/client/plugos/hooks/command.ts +0 -5
  6. package/client/plugos/hooks/event.ts +0 -6
  7. package/client/plugos/hooks/mq.ts +0 -3
  8. package/client/plugos/hooks/slash_command.ts +1 -7
  9. package/client/plugos/hooks/syscall.ts +24 -14
  10. package/client/plugos/manifest_cache.ts +0 -32
  11. package/client/plugos/plug.ts +0 -3
  12. package/client/plugos/plug_compile.ts +3 -13
  13. package/client/plugos/sandboxes/worker_sandbox.ts +19 -9
  14. package/client/plugos/syscalls/asset.ts +61 -23
  15. package/client/plugos/syscalls/clientStore.ts +32 -6
  16. package/client/plugos/syscalls/client_code_widget.ts +9 -4
  17. package/client/plugos/syscalls/code_widget.ts +26 -11
  18. package/client/plugos/syscalls/config.ts +146 -30
  19. package/client/plugos/syscalls/datastore.ts +146 -40
  20. package/client/plugos/syscalls/editor.ts +1518 -722
  21. package/client/plugos/syscalls/event.ts +44 -13
  22. package/client/plugos/syscalls/fetch.ts +120 -83
  23. package/client/plugos/syscalls/icon.ts +46 -0
  24. package/client/plugos/syscalls/index.ts +267 -121
  25. package/client/plugos/syscalls/jsonschema.ts +107 -9
  26. package/client/plugos/syscalls/language.ts +38 -12
  27. package/client/plugos/syscalls/markdown.ts +166 -50
  28. package/client/plugos/syscalls/mq.ts +92 -20
  29. package/client/plugos/syscalls/navigator.ts +143 -0
  30. package/client/plugos/syscalls/schema_introspection.ts +33 -0
  31. package/client/plugos/syscalls/search.ts +44 -0
  32. package/client/plugos/syscalls/service_registry.ts +74 -20
  33. package/client/plugos/syscalls/shell.ts +51 -21
  34. package/client/plugos/syscalls/space.ts +412 -113
  35. package/client/plugos/syscalls/sync.ts +103 -20
  36. package/client/plugos/syscalls/system.ts +274 -139
  37. package/client/plugos/system.ts +28 -10
  38. package/client/plugos/worker_runtime.ts +0 -2
  39. package/client/space_lua/aggregates.ts +0 -16
  40. package/client/space_lua/api_documentation.ts +107 -0
  41. package/client/space_lua/ast.ts +13 -5
  42. package/client/space_lua/ast_narrow.ts +0 -5
  43. package/client/space_lua/budget.ts +113 -0
  44. package/client/space_lua/budget_ui.ts +49 -0
  45. package/client/space_lua/eval.ts +125 -92
  46. package/client/space_lua/labels.ts +0 -4
  47. package/client/space_lua/numeric.ts +0 -5
  48. package/client/space_lua/parse.ts +243 -125
  49. package/client/space_lua/pretty_print.ts +128 -54
  50. package/client/space_lua/quarantine.ts +127 -0
  51. package/client/space_lua/query_collection.ts +7 -51
  52. package/client/space_lua/query_env.ts +0 -2
  53. package/client/space_lua/render_lua_markdown.ts +2 -7
  54. package/client/space_lua/render_widget.ts +173 -0
  55. package/client/space_lua/runtime.ts +133 -92
  56. package/client/space_lua/stdlib/crypto.ts +8 -3
  57. package/client/space_lua/stdlib/encoding.ts +27 -9
  58. package/client/space_lua/stdlib/format.ts +0 -12
  59. package/client/space_lua/stdlib/js.ts +136 -23
  60. package/client/space_lua/stdlib/load.ts +24 -16
  61. package/client/space_lua/stdlib/math.ts +331 -133
  62. package/client/space_lua/stdlib/net.ts +57 -13
  63. package/client/space_lua/stdlib/os.ts +111 -54
  64. package/client/space_lua/stdlib/pattern.ts +10 -15
  65. package/client/space_lua/stdlib/space_lua.ts +344 -21
  66. package/client/space_lua/stdlib/string.ts +282 -104
  67. package/client/space_lua/stdlib/string_pack.ts +58 -22
  68. package/client/space_lua/stdlib/table.ts +195 -45
  69. package/client/space_lua/stdlib.ts +354 -125
  70. package/client/space_lua/syscalls.ts +270 -0
  71. package/client/space_lua/tonumber.ts +0 -9
  72. package/dist/plug-compile.js +2 -3
  73. package/package.json +15 -5
  74. package/plug-api/lib/async.ts +1 -6
  75. package/plug-api/lib/collation.ts +22 -0
  76. package/plug-api/lib/crypto.ts +16 -15
  77. package/plug-api/lib/dates.ts +33 -0
  78. package/plug-api/lib/fuzzy.ts +282 -0
  79. package/plug-api/lib/json.ts +0 -6
  80. package/plug-api/lib/limited_map.ts +0 -2
  81. package/plug-api/lib/link_write.ts +27 -0
  82. package/plug-api/{ui → lib}/panel_styles.ts +4 -3
  83. package/plug-api/lib/ref.ts +124 -5
  84. package/plug-api/lib/resolve.ts +0 -1
  85. package/plug-api/lib/resolve_path.ts +290 -0
  86. package/plug-api/lib/shortcut.ts +17 -1
  87. package/plug-api/lib/tags.ts +0 -3
  88. package/plug-api/lib/transclusion.ts +28 -1
  89. package/plug-api/lib/tree.ts +0 -5
  90. package/plug-api/lib/yaml.ts +3 -42
  91. package/plug-api/syscall.ts +1 -5
  92. package/plug-api/syscalls/config.ts +7 -5
  93. package/plug-api/syscalls/editor.ts +70 -208
  94. package/plug-api/syscalls/icon.ts +18 -0
  95. package/plug-api/syscalls/index.ts +39 -5
  96. package/plug-api/syscalls/jsonschema.ts +9 -0
  97. package/plug-api/syscalls/lua.ts +7 -0
  98. package/plug-api/syscalls/search.ts +11 -0
  99. package/plug-api/syscalls/shell.ts +0 -1
  100. package/plug-api/syscalls/space.ts +122 -5
  101. package/plug-api/syscalls/sync.ts +23 -2
  102. package/plug-api/syscalls/system.ts +53 -0
  103. package/plug-api/syscalls.ts +2 -0
  104. package/plug-api/system_mock.ts +15 -2
  105. package/plug-api/types/client.ts +4 -3
  106. package/plug-api/types/datastore.ts +11 -2
  107. package/plug-api/types/index.ts +62 -1
  108. package/plug-api/types/manifest.ts +22 -4
  109. package/plug-api/types/profile.ts +13 -0
  110. package/plug-api/types/revisions.ts +67 -0
  111. package/plug-api/ui/cx.ts +1 -3
  112. package/plug-api/ui/description.ts +64 -0
  113. package/plug-api/ui/hover.ts +69 -0
  114. package/plug-api/ui/index.ts +61 -2
  115. package/plug-api/ui/scroll.ts +30 -0
  116. package/plug-api/ui/slugify.ts +44 -0
  117. package/plug-api/ui/tree_model.ts +263 -0
  118. package/plug-api/ui/tree_types.ts +66 -0
  119. package/plug-api/ui/use_fit_collapse.ts +67 -0
  120. package/plugs/builtin_plugs.ts +0 -1
  121. package/plugs/index/types.ts +9 -0
  122. package/client/plugos/syscalls/lua.ts +0 -81
  123. package/plug-api/lib/memory_cache.ts +0 -21
@@ -1,139 +1,438 @@
1
- import { parseToRef, type Ref } from "@silverbulletmd/silverbullet/lib/ref";
2
- import type { Client } from "../../client.ts";
3
- import type { SysCallMapping } from "../system.ts";
4
-
1
+ import {
2
+ encodePageURI,
3
+ type Path,
4
+ parseToRef,
5
+ type Ref,
6
+ } from "@silverbulletmd/silverbullet/lib/ref";
7
+ import { fileName } from "@silverbulletmd/silverbullet/lib/resolve";
8
+ import {
9
+ BasenameIndex,
10
+ type PathLookup,
11
+ resolvePath,
12
+ } from "@silverbulletmd/silverbullet/lib/resolve_path";
5
13
  import type {
6
14
  DocumentMeta,
7
15
  FileMeta,
8
16
  PageMeta,
9
17
  } from "@silverbulletmd/silverbullet/type/index";
18
+ import type {
19
+ FileRevisions,
20
+ GitSyncSnapshot,
21
+ GitConflicts,
22
+ GitConflictAction,
23
+ SpaceLog,
24
+ } from "@silverbulletmd/silverbullet/type/revisions";
25
+ import type { Client } from "../../client.ts";
26
+ import { fsEndpoint } from "../../spaces/constants.ts";
27
+ import type { SysCallMapping } from "../system.ts";
28
+
29
+ function revisionsUrl(client: Client, suffix: string): string {
30
+ return `${client.httpSpacePrimitives.url.slice(0, -fsEndpoint.length)}/.revisions/${suffix}`;
31
+ }
32
+
33
+ async function fetchRevisionsJson(
34
+ client: Client,
35
+ suffix: string,
36
+ body?: unknown,
37
+ ): Promise<any> {
38
+ const resp = await client.httpSpacePrimitives.authenticatedFetch(
39
+ revisionsUrl(client, suffix),
40
+ {
41
+ method: body === undefined ? "GET" : "POST",
42
+ headers: {
43
+ Accept: "application/json",
44
+ ...(body === undefined ? {} : { "Content-Type": "application/json" }),
45
+ },
46
+ body: body === undefined ? undefined : JSON.stringify(body),
47
+ },
48
+ );
49
+ if (resp.status === 404) {
50
+ throw Object.assign(
51
+ new Error("Revisions are not available for this space"),
52
+ { status: resp.status },
53
+ );
54
+ }
55
+ if (!resp.ok) {
56
+ throw Object.assign(new Error(`Revisions request failed: ${resp.status}`), {
57
+ status: resp.status,
58
+ });
59
+ }
60
+ return resp.json();
61
+ }
62
+
63
+ /**
64
+ * The index resolution syscalls answer from. The client's own index is only
65
+ * trustworthy once the file list has loaded; before that, ask the space
66
+ * directly rather than reporting everything as missing.
67
+ */
68
+ async function resolutionIndex(client: Client): Promise<BasenameIndex> {
69
+ if (client.clientSystem.knownFilesLoaded) {
70
+ return client.clientSystem.allKnownFiles;
71
+ }
72
+ const listing = new BasenameIndex();
73
+ listing.rebuild(
74
+ (await client.space.spacePrimitives.fetchFileList()).map((f) => f.name),
75
+ );
76
+ return listing;
77
+ }
10
78
 
11
79
  export function spaceReadSyscalls(client: Client): SysCallMapping {
12
80
  return {
13
- "space.listPages": (): Promise<PageMeta[]> => {
14
- return client.space.fetchPageList();
15
- },
16
- "space.readPage": async (_ctx, name: string): Promise<string> => {
17
- return (await client.space.readPage(name)).text;
18
- },
19
- "space.readPageWithMeta": (
20
- _ctx,
21
- name: string,
22
- ): Promise<{ text: string; meta: PageMeta }> => {
23
- return client.space.readPage(name);
24
- },
25
- "space.readRef": async (_ctx, ref: string | Ref): Promise<string> => {
26
- if (typeof ref === "string") {
27
- ref = parseToRef(ref)!;
28
- if (!ref) {
29
- throw new Error(`Invalid ref: ${ref}`);
81
+ "space.getGitSyncStatus": {
82
+ callback: (): Promise<GitSyncSnapshot> =>
83
+ fetchRevisionsJson(client, "_sync"),
84
+ description: "Reads Git sync status and its last successful check.",
85
+ },
86
+ "space.getGitConflictVersion": {
87
+ callback: async (
88
+ _ctx,
89
+ id: string,
90
+ generation: string,
91
+ side: "local" | "remote",
92
+ ): Promise<Uint8Array> => {
93
+ if (side !== "local" && side !== "remote")
94
+ throw new Error("Unknown conflict side");
95
+ const response = await client.httpSpacePrimitives.authenticatedFetch(
96
+ revisionsUrl(
97
+ client,
98
+ `_conflicts/${encodeURIComponent(id)}/${side}?generation=${encodeURIComponent(generation)}`,
99
+ ),
100
+ { method: "GET" },
101
+ );
102
+ if (!response.ok)
103
+ throw Object.assign(
104
+ new Error("Could not read this conflict version"),
105
+ { status: response.status },
106
+ );
107
+ return new Uint8Array(await response.arrayBuffer());
108
+ },
109
+ description: "Downloads one original side of an unresolved Git conflict.",
110
+ signatures: ["space.getGitConflictVersion(id, generation, side)"],
111
+ },
112
+ "space.getGitConflicts": {
113
+ callback: (): Promise<GitConflicts> =>
114
+ fetchRevisionsJson(client, "_conflicts"),
115
+ description:
116
+ "Lists unresolved Git conflicts with saved content versions.",
117
+ },
118
+ "space.listPages": {
119
+ callback: (): Promise<PageMeta[]> => client.space.fetchPageList(),
120
+ description: "Lists all pages in the space.",
121
+ },
122
+ "space.readPage": {
123
+ callback: async (_ctx, name: string): Promise<string> =>
124
+ (await client.space.readPage(name)).text,
125
+ description: "Reads a page and returns its Markdown text.",
126
+ signatures: ["space.readPage(name)"],
127
+ },
128
+ "space.readPageWithMeta": {
129
+ callback: (
130
+ _ctx,
131
+ name: string,
132
+ ): Promise<{ text: string; meta: PageMeta }> =>
133
+ client.space.readPage(name),
134
+ description:
135
+ "Reads a page and returns both its Markdown text and metadata.",
136
+ signatures: ["space.readPageWithMeta(name)"],
137
+ },
138
+ "space.readRef": {
139
+ callback: async (_ctx, ref: string | Ref): Promise<string> => {
140
+ if (typeof ref === "string") {
141
+ ref = parseToRef(ref)!;
142
+ if (!ref) {
143
+ throw new Error(`Invalid ref: ${ref}`);
144
+ }
30
145
  }
31
- }
32
- return (await client.space.readRef(ref)).text;
146
+ return (await client.space.readRef(ref)).text;
147
+ },
148
+ description:
149
+ "Reads the text addressed by a page, header, or position reference.",
150
+ signatures: ["space.readRef(ref)"],
33
151
  },
34
- "space.pageExists": (_ctx, name: string): boolean => {
35
- return client.clientSystem.allKnownFiles.has(`${name}.md`);
152
+ "space.pageExists": {
153
+ callback: (_ctx, name: string): boolean =>
154
+ resolvePath(`${name}.md`, "", client.clientSystem.allKnownFiles).exists,
155
+ description: "Checks whether a page exists in the space.",
156
+ signatures: ["space.pageExists(name)"],
36
157
  },
37
- "space.getPageMeta": (_ctx, name: string): Promise<PageMeta> => {
38
- return client.space.getPageMeta(name);
158
+ "space.getPageMeta": {
159
+ callback: (_ctx, name: string): Promise<PageMeta> =>
160
+ client.space.getPageMeta(name),
161
+ description: "Returns metadata for a page.",
162
+ signatures: ["space.getPageMeta(name)"],
39
163
  },
40
- "space.listPlugs": (): Promise<FileMeta[]> => {
41
- return client.space.listPlugs();
164
+ "space.listPlugs": {
165
+ callback: (): Promise<FileMeta[]> => client.space.listPlugs(),
166
+ description: "Lists all plug files in the space.",
42
167
  },
43
- "space.listDocuments": async (): Promise<DocumentMeta[]> => {
44
- return await client.space.fetchDocumentList();
168
+ "space.listDocuments": {
169
+ callback: async (): Promise<DocumentMeta[]> =>
170
+ await client.space.fetchDocumentList(),
171
+ description: "Lists all non-page documents in the space.",
45
172
  },
46
- "space.readDocument": async (_ctx, name: string): Promise<Uint8Array> => {
47
- return (await client.space.readDocument(name)).data;
173
+ "space.readDocument": {
174
+ callback: async (_ctx, name: string): Promise<Uint8Array> =>
175
+ (await client.space.readDocument(name)).data,
176
+ description: "Reads a document as binary data.",
177
+ signatures: ["space.readDocument(name)"],
48
178
  },
49
- "space.getDocumentMeta": async (
50
- _ctx,
51
- name: string,
52
- ): Promise<DocumentMeta> => {
53
- return await client.space.getDocumentMeta(name);
179
+ "space.getDocumentMeta": {
180
+ callback: async (_ctx, name: string): Promise<DocumentMeta> =>
181
+ await client.space.getDocumentMeta(name),
182
+ description: "Returns metadata for a document.",
183
+ signatures: ["space.getDocumentMeta(name)"],
54
184
  },
55
185
  // DEPRECATED, please use document versions instead, left here for backwards compatibility
56
- "space.listAttachments": async (): Promise<DocumentMeta[]> => {
57
- return await client.space.fetchDocumentList();
58
- },
59
- "space.readAttachment": async (_ctx, name: string): Promise<Uint8Array> => {
60
- return (await client.space.readDocument(name)).data;
61
- },
62
- "space.getAttachmentMeta": async (
63
- _ctx,
64
- name: string,
65
- ): Promise<DocumentMeta> => {
66
- return await client.space.getDocumentMeta(name);
67
- },
68
- // FS
69
- "space.listFiles": (): Promise<FileMeta[]> => {
70
- return client.space.spacePrimitives.fetchFileList();
71
- },
72
- "space.getFileMeta": (_ctx, name: string): Promise<FileMeta> => {
73
- return client.space.spacePrimitives.getFileMeta(name);
74
- },
75
- "space.readFile": async (_ctx, name: string): Promise<Uint8Array> => {
76
- return (await client.space.spacePrimitives.readFile(name)).data;
77
- },
78
- "space.readFileWithMeta": async (
79
- _ctx,
80
- name: string,
81
- ): Promise<{ data: Uint8Array; meta: FileMeta }> => {
82
- return await client.space.spacePrimitives.readFile(name);
83
- },
84
- "space.fileExists": async (_ctx, name: string): Promise<boolean> => {
85
- // If a full sync has successfully completed (so we know what files exist)
86
- // and we have a snapshot, let's use the snapshot
87
- if (
88
- client.fullSyncCompleted &&
89
- !client.eventedSpacePrimitives.isSnapshotEmpty()
90
- ) {
91
- return !!client.eventedSpacePrimitives.getSnapshot()[name];
92
- }
93
- try {
94
- await client.space.spacePrimitives.getFileMeta(name);
95
- // If this returned the file exists
96
- return true;
97
- } catch {
98
- // Assumption: any error means the file does not exist
99
- return false;
100
- }
186
+ "space.listAttachments": {
187
+ callback: async (): Promise<DocumentMeta[]> =>
188
+ await client.space.fetchDocumentList(),
189
+ description: "Deprecated alias for space.listDocuments.",
190
+ deprecated: "Use space.listDocuments instead.",
191
+ },
192
+ "space.readAttachment": {
193
+ callback: async (_ctx, name: string): Promise<Uint8Array> =>
194
+ (await client.space.readDocument(name)).data,
195
+ description: "Deprecated alias for space.readDocument.",
196
+ deprecated: "Use space.readDocument instead.",
197
+ signatures: ["space.readAttachment(name)"],
198
+ },
199
+ "space.getAttachmentMeta": {
200
+ callback: async (_ctx, name: string): Promise<DocumentMeta> =>
201
+ await client.space.getDocumentMeta(name),
202
+ description: "Deprecated alias for space.getDocumentMeta.",
203
+ deprecated: "Use space.getDocumentMeta instead.",
204
+ signatures: ["space.getAttachmentMeta(name)"],
205
+ },
206
+ "space.listFiles": {
207
+ callback: (): Promise<FileMeta[]> =>
208
+ client.space.spacePrimitives.fetchFileList(),
209
+ description: "Lists every file in the space.",
210
+ },
211
+ "space.getFileMeta": {
212
+ callback: (_ctx, name: string): Promise<FileMeta> =>
213
+ client.space.spacePrimitives.getFileMeta(name),
214
+ description: "Returns metadata for an arbitrary space file.",
215
+ signatures: ["space.getFileMeta(name)"],
216
+ },
217
+ "space.readFile": {
218
+ callback: async (_ctx, name: string): Promise<Uint8Array> =>
219
+ (await client.space.spacePrimitives.readFile(name)).data,
220
+ description: "Reads an arbitrary space file as binary data.",
221
+ signatures: ["space.readFile(name)"],
222
+ },
223
+ "space.readFileWithMeta": {
224
+ callback: async (
225
+ _ctx,
226
+ name: string,
227
+ ): Promise<{ data: Uint8Array; meta: FileMeta }> =>
228
+ await client.space.spacePrimitives.readFile(name),
229
+ description: "Reads an arbitrary space file together with its metadata.",
230
+ signatures: ["space.readFileWithMeta(name)"],
231
+ },
232
+ "space.collidingBasenames": {
233
+ callback: async (): Promise<Record<string, Path[]>> =>
234
+ (await resolutionIndex(client)).collidingBuckets(),
235
+ description:
236
+ "Returns every basename carried by more than one file, with the files carrying it.",
237
+ signatures: ["space.collidingBasenames()"],
238
+ },
239
+ "space.lookupPaths": {
240
+ callback: async (
241
+ _ctx,
242
+ paths: string[],
243
+ ): Promise<Record<string, PathLookup>> => {
244
+ const index = await resolutionIndex(client);
245
+
246
+ const result: Record<string, PathLookup> = {};
247
+ for (const path of paths) {
248
+ result[path] = {
249
+ exact: index.has(path),
250
+ candidates: index.candidates(fileName(path)),
251
+ };
252
+ }
253
+ return result;
254
+ },
255
+ description:
256
+ "Looks up, for each path, whether it exists exactly and which files share its basename.",
257
+ signatures: ["space.lookupPaths(paths)"],
258
+ },
259
+ "space.fileExists": {
260
+ callback: async (_ctx, name: string): Promise<boolean> => {
261
+ // If a full sync has successfully completed (so we know what files exist)
262
+ // and we have a snapshot, let's use the snapshot
263
+ if (
264
+ client.fullSyncCompleted &&
265
+ !client.eventedSpacePrimitives.isSnapshotEmpty()
266
+ ) {
267
+ return !!client.eventedSpacePrimitives.getSnapshot()[name];
268
+ }
269
+ try {
270
+ await client.space.spacePrimitives.getFileMeta(name);
271
+ return true;
272
+ } catch {
273
+ // Assumption: any error means the file does not exist
274
+ return false;
275
+ }
276
+ },
277
+ description: "Checks whether an arbitrary file exists in the space.",
278
+ signatures: ["space.fileExists(name)"],
279
+ },
280
+ "space.listRevisions": {
281
+ callback: (_ctx, path: string, before?: string): Promise<FileRevisions> =>
282
+ fetchRevisionsJson(
283
+ client,
284
+ `${encodePageURI(path)}${before ? `?before=${before}` : ""}`,
285
+ ),
286
+ description: "Lists the revision history of a file.",
287
+ signatures: ["space.listRevisions(path, before?)"],
288
+ },
289
+ "space.getRevision": {
290
+ callback: async (
291
+ _ctx,
292
+ path: string,
293
+ rev: string,
294
+ parent?: boolean,
295
+ ): Promise<string> => {
296
+ const resp = await client.httpSpacePrimitives.authenticatedFetch(
297
+ revisionsUrl(
298
+ client,
299
+ `${encodePageURI(path)}?rev=${rev}${parent ? "&parent=1" : ""}`,
300
+ ),
301
+ { method: "GET" },
302
+ );
303
+ if (!resp.ok) {
304
+ throw Object.assign(
305
+ new Error(`Could not load revision: ${resp.status}`),
306
+ { status: resp.status },
307
+ );
308
+ }
309
+ return resp.text();
310
+ },
311
+ description:
312
+ "Reads the text of a file as it was at a given revision, or at that revision's parent.",
313
+ signatures: ["space.getRevision(path, rev, parent?)"],
314
+ },
315
+ "space.getRevisionDiff": {
316
+ callback: async (_ctx, path: string, rev?: string): Promise<string> => {
317
+ const resp = await client.httpSpacePrimitives.authenticatedFetch(
318
+ revisionsUrl(
319
+ client,
320
+ `${encodePageURI(path)}?${rev ? `rev=${rev}&` : ""}format=diff`,
321
+ ),
322
+ { method: "GET" },
323
+ );
324
+ if (!resp.ok) {
325
+ throw Object.assign(
326
+ new Error(`Could not load revision diff: ${resp.status}`),
327
+ { status: resp.status },
328
+ );
329
+ }
330
+ return resp.text();
331
+ },
332
+ description:
333
+ "Reads a unified diff of a revision's own change (vs its parent), or of the uncommitted change when no revision is given.",
334
+ signatures: ["space.getRevisionDiff(path, rev?)"],
335
+ },
336
+ "space.getSpaceLog": {
337
+ callback: (_ctx, before?: string, q?: string): Promise<SpaceLog> => {
338
+ const params: string[] = [];
339
+ if (before) params.push(`before=${before}`);
340
+ if (q) params.push(`q=${encodeURIComponent(q)}`);
341
+ return fetchRevisionsJson(
342
+ client,
343
+ params.length ? `?${params.join("&")}` : "",
344
+ );
345
+ },
346
+ description: "Lists the space-wide commit log.",
347
+ signatures: ["space.getSpaceLog(before?, q?)"],
101
348
  },
102
349
  };
103
350
  }
104
351
 
105
352
  export function spaceWriteSyscalls(editor: Client): SysCallMapping {
106
353
  return {
107
- "space.writePage": (
108
- _ctx,
109
- name: string,
110
- text: string,
111
- ): Promise<PageMeta> => {
112
- return editor.space.writePage(name, text);
113
- },
114
- "space.deletePage": async (_ctx, name: string) => {
115
- console.log("Deleting page");
116
- await editor.space.deletePage(name);
117
- },
118
- "space.writeDocument": (
119
- _ctx,
120
- name: string,
121
- data: Uint8Array,
122
- ): Promise<DocumentMeta> => {
123
- return editor.space.writeDocument(name, data);
124
- },
125
- "space.deleteDocument": async (_ctx, name: string) => {
126
- await editor.space.deleteDocument(name);
127
- },
128
- "space.writeFile": (
129
- _ctx,
130
- name: string,
131
- data: Uint8Array,
132
- ): Promise<FileMeta> => {
133
- return editor.space.spacePrimitives.writeFile(name, data);
134
- },
135
- "space.deleteFile": (_ctx, name: string) => {
136
- return editor.space.spacePrimitives.deleteFile(name);
354
+ "space.syncGitNow": {
355
+ callback: () => fetchRevisionsJson(editor, "_sync", {}),
356
+ description: "Requests sync on the existing Git connection.",
357
+ },
358
+ "space.resolveGitConflict": {
359
+ callback: (
360
+ _ctx,
361
+ id: string,
362
+ generation: string,
363
+ contentRevision: string,
364
+ action: GitConflictAction,
365
+ ): Promise<GitConflicts> =>
366
+ fetchRevisionsJson(editor, `_conflicts/${encodeURIComponent(id)}`, {
367
+ generation,
368
+ contentRevision,
369
+ action,
370
+ }),
371
+ description:
372
+ "Resolves a Git conflict if the merge and saved content still match.",
373
+ signatures: [
374
+ "space.resolveGitConflict(id, generation, contentRevision, action)",
375
+ ],
376
+ },
377
+ "space.writePage": {
378
+ callback: (_ctx, name: string, text: string): Promise<PageMeta> =>
379
+ editor.space.writePage(name, text),
380
+ description: "Writes Markdown text to a page and returns its metadata.",
381
+ signatures: ["space.writePage(name, text)"],
382
+ },
383
+ "space.deletePage": {
384
+ callback: async (_ctx, name: string) => {
385
+ console.log("Deleting page");
386
+ await editor.space.deletePage(name);
387
+ },
388
+ description: "Deletes a page from the space.",
389
+ signatures: ["space.deletePage(name)"],
390
+ },
391
+ "space.writeDocument": {
392
+ callback: (_ctx, name: string, data: Uint8Array): Promise<DocumentMeta> =>
393
+ editor.space.writeDocument(name, data),
394
+ description: "Writes binary document data and returns its metadata.",
395
+ signatures: ["space.writeDocument(name, data)"],
396
+ },
397
+ "space.deleteDocument": {
398
+ callback: async (_ctx, name: string) => {
399
+ await editor.space.deleteDocument(name);
400
+ },
401
+ description: "Deletes a document from the space.",
402
+ signatures: ["space.deleteDocument(name)"],
403
+ },
404
+ "space.writeFile": {
405
+ callback: (_ctx, name: string, data: Uint8Array): Promise<FileMeta> =>
406
+ editor.space.spacePrimitives.writeFile(name, data),
407
+ description: "Writes an arbitrary binary file and returns its metadata.",
408
+ signatures: ["space.writeFile(name, data)"],
409
+ },
410
+ "space.deleteFile": {
411
+ callback: (_ctx, name: string) =>
412
+ editor.space.spacePrimitives.deleteFile(name),
413
+ description: "Deletes an arbitrary file from the space.",
414
+ signatures: ["space.deleteFile(name)"],
415
+ },
416
+ "space.createRevisionSnapshot": {
417
+ callback: async (): Promise<boolean> => {
418
+ const resp = await editor.httpSpacePrimitives.authenticatedFetch(
419
+ revisionsUrl(editor, ""),
420
+ { method: "POST", headers: { Accept: "application/json" } },
421
+ );
422
+ if (resp.status === 404) {
423
+ throw new Error("Revisions are not available for this space");
424
+ }
425
+ if (resp.status === 409) {
426
+ throw new Error("Revisions are not managed for this space");
427
+ }
428
+ if (!resp.ok) {
429
+ throw new Error(`Snapshot request failed: ${resp.status}`);
430
+ }
431
+ return (await resp.json()).committed === true;
432
+ },
433
+ description:
434
+ "Commits everything outstanding as a revision now, rather than waiting for the automatic commit. False if there was nothing to commit.",
435
+ signatures: ["space.createRevisionSnapshot()"],
137
436
  },
138
437
  };
139
438
  }