@book.dev/sdk 1.60.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 (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -0
  3. package/dist/account.d.ts +57 -0
  4. package/dist/account.js +104 -0
  5. package/dist/account.js.map +1 -0
  6. package/dist/ai.d.ts +264 -0
  7. package/dist/ai.js +36 -0
  8. package/dist/ai.js.map +1 -0
  9. package/dist/backup.d.ts +52 -0
  10. package/dist/backup.js +35 -0
  11. package/dist/backup.js.map +1 -0
  12. package/dist/bookFolder.d.ts +44 -0
  13. package/dist/bookFolder.js +98 -0
  14. package/dist/bookFolder.js.map +1 -0
  15. package/dist/bookfile.d.ts +42 -0
  16. package/dist/bookfile.js +159 -0
  17. package/dist/bookfile.js.map +1 -0
  18. package/dist/client.d.ts +279 -0
  19. package/dist/client.js +502 -0
  20. package/dist/client.js.map +1 -0
  21. package/dist/connection.d.ts +20 -0
  22. package/dist/connection.js +77 -0
  23. package/dist/connection.js.map +1 -0
  24. package/dist/content.d.ts +38 -0
  25. package/dist/content.js +107 -0
  26. package/dist/content.js.map +1 -0
  27. package/dist/database.d.ts +687 -0
  28. package/dist/database.js +1082 -0
  29. package/dist/database.js.map +1 -0
  30. package/dist/formula.d.ts +41 -0
  31. package/dist/formula.js +469 -0
  32. package/dist/formula.js.map +1 -0
  33. package/dist/forwarding/challenge.d.ts +35 -0
  34. package/dist/forwarding/challenge.js +45 -0
  35. package/dist/forwarding/challenge.js.map +1 -0
  36. package/dist/forwarding/encoding.d.ts +14 -0
  37. package/dist/forwarding/encoding.js +54 -0
  38. package/dist/forwarding/encoding.js.map +1 -0
  39. package/dist/forwarding/forwardingClient.d.ts +76 -0
  40. package/dist/forwarding/forwardingClient.js +128 -0
  41. package/dist/forwarding/forwardingClient.js.map +1 -0
  42. package/dist/forwarding/index.d.ts +5 -0
  43. package/dist/forwarding/index.js +14 -0
  44. package/dist/forwarding/index.js.map +1 -0
  45. package/dist/forwarding/siteKey.d.ts +12 -0
  46. package/dist/forwarding/siteKey.js +36 -0
  47. package/dist/forwarding/siteKey.js.map +1 -0
  48. package/dist/forwarding/tunnelClient.d.ts +56 -0
  49. package/dist/forwarding/tunnelClient.js +206 -0
  50. package/dist/forwarding/tunnelClient.js.map +1 -0
  51. package/dist/forwarding/tunnelProtocol.d.ts +45 -0
  52. package/dist/forwarding/tunnelProtocol.js +29 -0
  53. package/dist/forwarding/tunnelProtocol.js.map +1 -0
  54. package/dist/index.d.ts +23 -0
  55. package/dist/index.js +19 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/mtime.d.ts +51 -0
  58. package/dist/mtime.js +84 -0
  59. package/dist/mtime.js.map +1 -0
  60. package/dist/pageProperties.d.ts +80 -0
  61. package/dist/pageProperties.js +97 -0
  62. package/dist/pageProperties.js.map +1 -0
  63. package/dist/plugins.d.ts +110 -0
  64. package/dist/plugins.js +113 -0
  65. package/dist/plugins.js.map +1 -0
  66. package/dist/routes.d.ts +89 -0
  67. package/dist/routes.js +89 -0
  68. package/dist/routes.js.map +1 -0
  69. package/dist/sampleDocument.d.ts +25 -0
  70. package/dist/sampleDocument.js +67 -0
  71. package/dist/sampleDocument.js.map +1 -0
  72. package/dist/suggestions.d.ts +126 -0
  73. package/dist/suggestions.js +21 -0
  74. package/dist/suggestions.js.map +1 -0
  75. package/dist/templates.d.ts +35 -0
  76. package/dist/templates.js +555 -0
  77. package/dist/templates.js.map +1 -0
  78. package/dist/types.d.ts +146 -0
  79. package/dist/types.js +16 -0
  80. package/dist/types.js.map +1 -0
  81. package/package.json +33 -0
@@ -0,0 +1,98 @@
1
+ import { pageToBookHtml, bookHtmlToPage, slugify } from './bookfile';
2
+ /** Lossless structured sidecar, parsed back by {@link parseBookFolder}. */
3
+ export const SPACE_BUNDLE_FILE = 'openbook.space.json';
4
+ const MAX_DEPTH = 64;
5
+ const pageIcon = (page) => {
6
+ const icon = page.properties?.sys_icon;
7
+ return typeof icon === 'string' ? icon : null;
8
+ };
9
+ const folderName = (root) => `${slugify(root.name ?? 'untitled')}--${root.id.slice(0, 8)}`;
10
+ const fileName = (page) => `${slugify(page.name ?? 'untitled')}--${page.id.slice(0, 8)}.html`;
11
+ /** Topmost ancestor: walk `parentId`, or a row's database-host page, to the root. */
12
+ function rootOf(page, byId, dbHost) {
13
+ let root = page;
14
+ for (let depth = 0; depth < MAX_DEPTH; depth += 1) {
15
+ let parentId = null;
16
+ if (root.parentId)
17
+ parentId = root.parentId;
18
+ else if (root.databaseId)
19
+ parentId = dbHost.get(root.databaseId) ?? null;
20
+ const parent = parentId ? byId.get(parentId) : null;
21
+ if (!parent)
22
+ break;
23
+ root = parent;
24
+ }
25
+ return root;
26
+ }
27
+ /**
28
+ * Serialise a space to its on-disk files. By default includes the lossless
29
+ * {@link SPACE_BUNDLE_FILE}; pass `includeBundle: false` for the human-readable
30
+ * HTML files only.
31
+ */
32
+ export function spaceToBookFiles(snapshot, opts = {}) {
33
+ const { pages, databases } = snapshot;
34
+ const byId = new Map(pages.map((p) => [p.id, p]));
35
+ const dbHost = new Map(databases.map((d) => [d.id, d.pageId]));
36
+ const files = [];
37
+ for (const page of pages) {
38
+ const root = rootOf(page, byId, dbHost);
39
+ const html = pageToBookHtml({
40
+ id: page.id,
41
+ name: page.name,
42
+ icon: pageIcon(page),
43
+ updatedAt: page.updatedAt,
44
+ data: page.data,
45
+ });
46
+ files.push({ path: `${folderName(root)}/${fileName(page)}`, contents: html });
47
+ }
48
+ if (opts.includeBundle !== false) {
49
+ files.push({ path: SPACE_BUNDLE_FILE, contents: JSON.stringify(snapshot, null, 2) });
50
+ }
51
+ return files;
52
+ }
53
+ /**
54
+ * Reconstruct a space from a folder's files. Prefers the lossless
55
+ * {@link SPACE_BUNDLE_FILE} when present; otherwise falls back to parsing the
56
+ * `.html` files into a flat list of pages (no databases, no nesting — the most
57
+ * a human-readable folder can recover). Returns `null` if nothing parseable was
58
+ * found, so the caller can surface "not an OpenBook folder".
59
+ */
60
+ export function parseBookFolder(files) {
61
+ const bundle = files.find((f) => f.path === SPACE_BUNDLE_FILE || f.path.endsWith(`/${SPACE_BUNDLE_FILE}`));
62
+ if (bundle) {
63
+ try {
64
+ const parsed = JSON.parse(bundle.contents);
65
+ if (Array.isArray(parsed.pages)) {
66
+ return { pages: parsed.pages, databases: Array.isArray(parsed.databases) ? parsed.databases : [] };
67
+ }
68
+ }
69
+ catch {
70
+ // Corrupt bundle — fall through to the HTML files.
71
+ }
72
+ }
73
+ const pages = [];
74
+ for (const file of files) {
75
+ if (!file.path.endsWith('.html'))
76
+ continue;
77
+ const record = bookHtmlToPage(file.contents);
78
+ if (record)
79
+ pages.push(recordToPage(record));
80
+ }
81
+ return pages.length ? { pages, databases: [] } : null;
82
+ }
83
+ /** Inflate a parsed `.book.html` record into a minimal StoredPage for import. */
84
+ function recordToPage(record) {
85
+ return {
86
+ id: record.id,
87
+ name: record.name,
88
+ data: record.data,
89
+ hostedDatabaseId: null,
90
+ databaseId: null,
91
+ parentId: null,
92
+ properties: record.icon ? { sys_icon: record.icon } : {},
93
+ deletedAt: null,
94
+ createdAt: record.updatedAt,
95
+ updatedAt: record.updatedAt,
96
+ };
97
+ }
98
+ //# sourceMappingURL=bookFolder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bookFolder.js","sourceRoot":"","sources":["../src/bookFolder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The on-disk **book file** format (OB-134): one HTML file per page. Each file
3
+ * is human-readable (so external sync/backup tools and editors see real content)
4
+ * yet round-trips losslessly back into pglite, because it embeds the canonical
5
+ * {@link PageSnapshot} as a JSON island alongside the rendered body.
6
+ *
7
+ * Every rendered block carries a stable `data-block-id` and a `data-block-mtime`,
8
+ * and the page itself carries `data-page-id` / `data-page-updated` (the DB
9
+ * `updatedAt` the file was rendered from — the conflict base) / `data-page-mtime`.
10
+ * Those attributes are the change signal the watcher and conflict resolver read.
11
+ *
12
+ * Pure and isomorphic: rendering and the island parse use no DOM, so the server
13
+ * mirror can emit and re-read files directly. The readable body mirrors the
14
+ * `export/` look (the `ob-*` class names) without depending on the ui package.
15
+ */
16
+ import type { PageSnapshot } from './types';
17
+ /** A page reduced to what a book file needs to carry. */
18
+ export interface BookPageRecord {
19
+ id: string;
20
+ name: string | null;
21
+ icon: string | null;
22
+ /** The DB `updatedAt` this file was rendered from — the conflict base. */
23
+ updatedAt: string;
24
+ data: PageSnapshot;
25
+ }
26
+ /** Lower-case, dash-separated, filesystem-safe slug (for the on-disk filename). */
27
+ export declare function slugify(input: string, fallback?: string): string;
28
+ /** Render a page to its on-disk book-file HTML (readable body + canonical island). */
29
+ export declare function pageToBookHtml(record: BookPageRecord): string;
30
+ /** Cheap metadata read from the `<html>` tag — id + conflict base + name. */
31
+ export declare function readBookHtmlMeta(html: string): {
32
+ id: string;
33
+ name: string;
34
+ updatedAt: string;
35
+ } | null;
36
+ /**
37
+ * Parse a book file back into its page record via the canonical JSON island.
38
+ * Returns `null` if the file isn't an OpenBook book page or the island is
39
+ * missing/corrupt. This is the lossless re-import path (sync clients, restored
40
+ * backups, moved files all preserve the island).
41
+ */
42
+ export declare function bookHtmlToPage(html: string): BookPageRecord | null;
@@ -0,0 +1,159 @@
1
+ import { latestBlockMtime } from './mtime';
2
+ const MARKER = 'application/openbook+json';
3
+ const esc = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
4
+ /** Lower-case, dash-separated, filesystem-safe slug (for the on-disk filename). */
5
+ export function slugify(input, fallback = 'untitled') {
6
+ const slug = (input || '')
7
+ .normalize('NFKD')
8
+ .replace(/[̀-ͯ]/g, '')
9
+ .toLowerCase()
10
+ .replace(/[^a-z0-9]+/g, '-')
11
+ .replace(/^-+|-+$/g, '')
12
+ .slice(0, 80);
13
+ return slug || fallback;
14
+ }
15
+ /** Ordered top-level blocks with resolved ids (matching sdk `snapshotBlocks`). */
16
+ function orderedBlocks(data) {
17
+ const blockdoc = data.blockdoc;
18
+ const raw = data.editor === 'blocks' && Array.isArray(blockdoc?.blocks)
19
+ ? blockdoc.blocks
20
+ : (data.editorjs?.blocks ?? []);
21
+ return raw.map((b, i) => ({ id: typeof b.id === 'string' && b.id ? b.id : `b${i}`, type: b.type ?? 'unknown', block: b }));
22
+ }
23
+ /** Inline HTML for a block-editor rich-text run. */
24
+ function runHtml(run) {
25
+ let out = esc(run.t).replace(/\n/g, '<br>');
26
+ const a = run.a ?? {};
27
+ if (a.c)
28
+ out = `<code>${out}</code>`;
29
+ if (a.b)
30
+ out = `<strong>${out}</strong>`;
31
+ if (a.i)
32
+ out = `<em>${out}</em>`;
33
+ if (a.u)
34
+ out = `<u>${out}</u>`;
35
+ if (a.s)
36
+ out = `<s>${out}</s>`;
37
+ if (typeof a.m === 'string')
38
+ out = `<a class="ob-mention" data-page-id="${esc(a.m)}">${out}</a>`;
39
+ else if (typeof a.a === 'string')
40
+ out = `<a href="${esc(a.a)}">${out}</a>`;
41
+ return out;
42
+ }
43
+ const runsHtml = (runs) => (runs ?? []).map(runHtml).join('');
44
+ /** Best-effort readable inner HTML for one block (the island stays authoritative). */
45
+ function blockInnerHtml(type, block) {
46
+ const d = block.data ?? {};
47
+ // Block-editor pages carry rich-text runs; EditorJS pages carry HTML strings.
48
+ const textHtml = block.text ? runsHtml(block.text) : typeof d.text === 'string' ? d.text : '';
49
+ switch (type) {
50
+ case 'heading':
51
+ case 'header': {
52
+ const level = Math.min(6, Math.max(1, Number(block.props?.level ?? d.level ?? 2)));
53
+ return { tag: `h${level}`, inner: textHtml };
54
+ }
55
+ case 'quote':
56
+ return { tag: 'blockquote', inner: textHtml };
57
+ case 'code':
58
+ return { tag: 'pre', inner: `<code>${esc(block.text ? (block.text.map((r) => r.t).join('')) : String(d.code ?? ''))}</code>` };
59
+ case 'list': {
60
+ const items = Array.isArray(d.items) ? d.items : [];
61
+ const ordered = d.style === 'ordered';
62
+ const lis = items
63
+ .map((it) => `<li>${typeof it === 'string' ? it : esc(String(it?.content ?? ''))}</li>`)
64
+ .join('');
65
+ return { tag: ordered ? 'ol' : 'ul', inner: lis };
66
+ }
67
+ case 'todo':
68
+ case 'checklist': {
69
+ const checked = block.props?.checked === true || d.checked === true;
70
+ return { tag: 'p', inner: `<input type="checkbox"${checked ? ' checked' : ''} disabled> ${textHtml}` };
71
+ }
72
+ case 'delimiter':
73
+ case 'divider':
74
+ return { tag: 'hr', inner: '' };
75
+ case 'paragraph':
76
+ return { tag: 'p', inner: textHtml };
77
+ default:
78
+ // Unknown / rich blocks (charts, tables, kit, …): readable text only — the
79
+ // JSON island carries their full data for a faithful re-import.
80
+ return { tag: 'div', inner: textHtml || `<em class="ob-raw">${esc(type)} block</em>` };
81
+ }
82
+ }
83
+ /** Render a page to its on-disk book-file HTML (readable body + canonical island). */
84
+ export function pageToBookHtml(record) {
85
+ const { id, name, icon, updatedAt, data } = record;
86
+ const title = (name ?? '').trim() || 'Untitled';
87
+ const mtime = new Map(data.mtimes ?? []);
88
+ const pageMtime = latestBlockMtime(data) ?? updatedAt;
89
+ const body = orderedBlocks(data)
90
+ .map(({ id: blockId, type, block }) => {
91
+ const { tag, inner } = blockInnerHtml(type, block);
92
+ const attrs = `data-block-id="${esc(blockId)}" data-block-type="${esc(type)}" data-block-mtime="${esc(mtime.get(blockId) ?? updatedAt)}"`;
93
+ return tag === 'hr' ? ` <hr ${attrs}>` : ` <${tag} ${attrs}>${inner}</${tag}>`;
94
+ })
95
+ .join('\n');
96
+ // The island JSON is escaped so a literal `</script>` in content can't close
97
+ // the tag early; `<\/` is still valid JSON and parses back to `</`.
98
+ const island = JSON.stringify({ version: 1, id, name, icon, updatedAt, data }).replace(/<\//g, '<\\/');
99
+ return `<!doctype html>
100
+ <html lang="en" data-openbook="book-page" data-page-id="${esc(id)}" data-page-name="${esc(title)}" data-page-updated="${esc(updatedAt)}" data-page-mtime="${esc(pageMtime)}">
101
+ <head>
102
+ <meta charset="utf-8">
103
+ <meta name="viewport" content="width=device-width, initial-scale=1">
104
+ <title>${esc(title)}</title>
105
+ </head>
106
+ <body class="ob-page">
107
+ <article>
108
+ <h1 class="ob-page-title">${icon ? `${esc(icon)} ` : ''}${esc(title)}</h1>
109
+ ${body}
110
+ </article>
111
+ <script type="${MARKER}" data-openbook-snapshot>
112
+ ${island}
113
+ </script>
114
+ </body>
115
+ </html>
116
+ `;
117
+ }
118
+ /** Cheap metadata read from the `<html>` tag — id + conflict base + name. */
119
+ export function readBookHtmlMeta(html) {
120
+ const tag = html.match(/<html\b[^>]*\bdata-openbook="book-page"[^>]*>/i)?.[0];
121
+ if (!tag)
122
+ return null;
123
+ const attr = (n) => {
124
+ const m = tag.match(new RegExp(`\\b${n}="([^"]*)"`, 'i'));
125
+ return m ? unesc(m[1]) : '';
126
+ };
127
+ const id = attr('data-page-id');
128
+ if (!id)
129
+ return null;
130
+ return { id, name: attr('data-page-name'), updatedAt: attr('data-page-updated') };
131
+ }
132
+ const unesc = (s) => s.replace(/&quot;/g, '"').replace(/&gt;/g, '>').replace(/&lt;/g, '<').replace(/&amp;/g, '&');
133
+ /**
134
+ * Parse a book file back into its page record via the canonical JSON island.
135
+ * Returns `null` if the file isn't an OpenBook book page or the island is
136
+ * missing/corrupt. This is the lossless re-import path (sync clients, restored
137
+ * backups, moved files all preserve the island).
138
+ */
139
+ export function bookHtmlToPage(html) {
140
+ const m = html.match(new RegExp(`<script[^>]*type="${MARKER.replace(/[/+]/g, '\\$&')}"[^>]*>([\\s\\S]*?)</script>`, 'i'));
141
+ if (!m)
142
+ return null;
143
+ try {
144
+ const parsed = JSON.parse(m[1].trim());
145
+ if (!parsed.id || !parsed.data)
146
+ return null;
147
+ return {
148
+ id: parsed.id,
149
+ name: parsed.name ?? null,
150
+ icon: parsed.icon ?? null,
151
+ updatedAt: parsed.updatedAt ?? '',
152
+ data: parsed.data,
153
+ };
154
+ }
155
+ catch {
156
+ return null;
157
+ }
158
+ }
159
+ //# sourceMappingURL=bookfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bookfile.js","sourceRoot":"","sources":["../src/bookfile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,279 @@
1
+ import type { PluginPackage, StoredPlugin } from './plugins';
2
+ import type { AgentChatEvent, AgentChatMessage, AgentChatOptions, AiConfig, AiSearchResponse, AiSkill, AiStatus, AiTasksResponse } from './ai';
3
+ import type { PageInput, PageMeta, StoredPage } from './types';
4
+ import type { ImportRequest, ImportResult } from './backup';
5
+ import type { DatabaseInput, DatabaseRow, DatabaseUpdate, RowInput, RowUpdate, StoredDatabase } from './database';
6
+ import type { CommentInput, StoredComment, StoredSuggestion, SuggestionInput, SuggestionStatus, SuggestionUpdate } from './suggestions';
7
+ /** Handlers for a single page's live update stream. */
8
+ export interface PageSubscription {
9
+ /** A newer version of the page was saved (by anyone). */
10
+ onPage?: (page: StoredPage) => void;
11
+ /** The page was deleted. */
12
+ onDeleted?: (id: string) => void;
13
+ }
14
+ /**
15
+ * Storage-agnostic data access used by the document UI.
16
+ *
17
+ * The desktop app and the web shell both talk to a server through the same
18
+ * {@link HttpDataClient} — the desktop just points it at its bundled local
19
+ * server, the web shell at a remote one. Swapping the target URL is how a user
20
+ * moves between "store locally" and "connect to an external server"; the
21
+ * document code never changes.
22
+ */
23
+ export interface DataClient {
24
+ /** List all pages' metadata, most-recently-updated first. */
25
+ listPages(): Promise<PageMeta[]>;
26
+ /** Fetch a page by id, or `null` if it does not exist. */
27
+ getPage(id: string): Promise<StoredPage | null>;
28
+ /** Create or update a page (upsert keyed on `input.id`). */
29
+ savePage(input: PageInput): Promise<StoredPage>;
30
+ /** Update only a page's name (leaves its document data untouched). */
31
+ renamePage(id: string, name: string | null): Promise<StoredPage>;
32
+ /**
33
+ * Shallow-merge structured property values into a page (owner, verification,
34
+ * …), leaving its document content and other properties untouched. Used by
35
+ * the page properties panel for non-row pages (database rows use {@link updateRow}).
36
+ */
37
+ setPageProperties(id: string, properties: Record<string, unknown>): Promise<StoredPage>;
38
+ /** List the live pages that link to `id` (via `@`-mentions), newest first. */
39
+ listBacklinks(id: string): Promise<PageMeta[]>;
40
+ /**
41
+ * Move a page within the sidebar tree: set its parent (`null` = top level) and
42
+ * the full ordered list of sibling ids under that parent (including this page).
43
+ * Used by drag-to-reorder / drag-to-nest. Rejects a move that would create a
44
+ * cycle (nesting a page under itself or a descendant).
45
+ */
46
+ movePage(id: string, move: {
47
+ parentId: string | null;
48
+ orderedIds: string[];
49
+ }): Promise<StoredPage>;
50
+ aiStatus(): Promise<AiStatus>;
51
+ aiSetConfig(config: AiConfig): Promise<AiConfig>;
52
+ aiIndex(): Promise<{
53
+ pages: number;
54
+ chunks: number;
55
+ }>;
56
+ aiSearch(query: string, limit?: number): Promise<AiSearchResponse>;
57
+ aiTasks(goal: string, context?: string): Promise<AiTasksResponse>;
58
+ aiDownloadModel(url?: string): Promise<AiStatus['download']>;
59
+ aiComplete(text: string, onToken: (token: string) => void, opts?: {
60
+ instruction?: string;
61
+ signal?: AbortSignal;
62
+ }): Promise<string>;
63
+ aiGenerate(prompt: string, onToken: (token: string) => void, opts?: {
64
+ system?: string;
65
+ maxTokens?: number;
66
+ signal?: AbortSignal;
67
+ }): Promise<string>;
68
+ /**
69
+ * Run the workspace agent on a conversation. `onEvent` fires once per step
70
+ * (tool call, tool result, reasoning, proposals, final answer, error);
71
+ * resolves when the run ends. `opts` carries effort/thinking/skills overrides.
72
+ */
73
+ agentChat(messages: AgentChatMessage[], onEvent: (event: AgentChatEvent) => void, opts?: AgentChatOptions): Promise<void>;
74
+ /** List the workspace's prompt/recipe skills. */
75
+ aiSkills(): Promise<AiSkill[]>;
76
+ /** Create or replace a prompt/recipe skill (keyed on its slug). */
77
+ aiSaveSkill(skill: AiSkill): Promise<AiSkill>;
78
+ /** Delete a prompt/recipe skill by name. */
79
+ aiDeleteSkill(name: string): Promise<boolean>;
80
+ listPlugins(): Promise<StoredPlugin[]>;
81
+ installPlugin(pkg: PluginPackage): Promise<StoredPlugin>;
82
+ setPluginEnabled(id: string, enabled: boolean): Promise<StoredPlugin>;
83
+ removePlugin(id: string): Promise<boolean>;
84
+ /**
85
+ * Move a page (and its nested subtree) to the trash. Soft delete: the page is
86
+ * recoverable via {@link restorePage} until the server's cleanup job purges
87
+ * it. Resolves `true` if a live page was trashed.
88
+ */
89
+ deletePage(id: string): Promise<boolean>;
90
+ /** Export the whole space: every live page (full data) + every database. */
91
+ exportSpace(): Promise<{
92
+ pages: StoredPage[];
93
+ databases: StoredDatabase[];
94
+ }>;
95
+ /** Restore a (client-selected) set of pages/databases; see {@link ImportRequest}. */
96
+ importSpace(req: ImportRequest): Promise<ImportResult>;
97
+ /** List the trash (most-recently-deleted first). */
98
+ listTrash(): Promise<PageMeta[]>;
99
+ /** Restore a trashed page, or `null` if it isn't in the trash. */
100
+ restorePage(id: string): Promise<StoredPage | null>;
101
+ /** Permanently delete one trashed page (and its subtree). `true` if removed. */
102
+ purgePage(id: string): Promise<boolean>;
103
+ /** Permanently empty the whole trash. Resolves the number of pages purged. */
104
+ emptyTrash(): Promise<number>;
105
+ /** Subscribe to a single page's live updates. Returns an unsubscribe fn. */
106
+ subscribePage(id: string, handlers: PageSubscription): () => void;
107
+ /** Subscribe to live page-list updates. Returns an unsubscribe fn. */
108
+ subscribePages(onList: (pages: PageMeta[]) => void): () => void;
109
+ /** Create a database for a host page. */
110
+ createDatabase(input: DatabaseInput): Promise<StoredDatabase>;
111
+ /** Fetch a database by id, or `null` if it does not exist. */
112
+ getDatabase(id: string): Promise<StoredDatabase | null>;
113
+ /** Fetch the database hosted by a page, or `null` if the page hosts none. */
114
+ getPageDatabase(pageId: string): Promise<StoredDatabase | null>;
115
+ /** Update a database's name and/or schema. */
116
+ updateDatabase(id: string, patch: DatabaseUpdate): Promise<StoredDatabase>;
117
+ /** Delete a database and all its row pages. Resolves `true` if removed. */
118
+ deleteDatabase(id: string): Promise<boolean>;
119
+ /** List a database's rows (projected: properties + exported cell values). */
120
+ listRows(databaseId: string): Promise<DatabaseRow[]>;
121
+ /** Create a row (a new page) inside a database. Returns the row page. */
122
+ createRow(databaseId: string, input?: RowInput): Promise<StoredPage>;
123
+ /** Update a row's title and/or manual property values. */
124
+ updateRow(databaseId: string, rowId: string, patch: RowUpdate): Promise<DatabaseRow>;
125
+ /** Set the manual order of a database's rows (full ordered id list). */
126
+ reorderRows(databaseId: string, orderedIds: string[]): Promise<void>;
127
+ /** Subscribe to a database's live row-list updates. Returns an unsubscribe fn. */
128
+ subscribeRows(databaseId: string, onRows: (rows: DatabaseRow[]) => void): () => void;
129
+ /** List a page's suggestions, newest first. `status` filters (e.g. only open). */
130
+ listSuggestions(pageId: string, status?: SuggestionStatus): Promise<StoredSuggestion[]>;
131
+ /** Persist a new suggestion (status defaults to `open`). */
132
+ createSuggestion(input: SuggestionInput): Promise<StoredSuggestion>;
133
+ /** Update a suggestion (today: its status). Returns the updated suggestion. */
134
+ updateSuggestion(id: string, patch: SuggestionUpdate): Promise<StoredSuggestion>;
135
+ /** Delete a suggestion (and its thread). Resolves `true` if removed. */
136
+ deleteSuggestion(id: string): Promise<boolean>;
137
+ /** List a page's comments, oldest first (so a thread reads top-to-bottom). */
138
+ listComments(pageId: string): Promise<StoredComment[]>;
139
+ /** Persist a new comment (on a suggestion or a block). */
140
+ createComment(input: CommentInput): Promise<StoredComment>;
141
+ /** Delete a comment. Resolves `true` if removed. */
142
+ deleteComment(id: string): Promise<boolean>;
143
+ }
144
+ /**
145
+ * The `fetch` surface {@link HttpDataClient} needs. Defaults to the global
146
+ * `fetch`; the desktop injects an implementation that tunnels requests over its
147
+ * host IPC bridge (a Unix-socket server with no TCP port) instead.
148
+ */
149
+ export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
150
+ /**
151
+ * The global `fetch`, wrapped so it's safe to store on an object and call as a
152
+ * property/method. WebKit (the desktop WKWebView) throws "Can only call
153
+ * Window.fetch on instances of Window" when `fetch` runs with a `this` that
154
+ * isn't the window — which is exactly what `obj.fetchImpl(...)` does once the
155
+ * bare global is assigned to a property. Calling the unqualified global here
156
+ * sidesteps that, so it's the right default wherever a {@link FetchLike} is
157
+ * stored and later invoked as a method.
158
+ */
159
+ export declare const globalFetch: FetchLike;
160
+ /**
161
+ * The slice of `EventSource` {@link LiveStream} uses (named events + open/error
162
+ * via `addEventListener`, plus `close`). Defaults to a real `EventSource`; the
163
+ * desktop injects a source backed by host IPC events, since its server speaks
164
+ * over a socket the webview can't open an `EventSource` to.
165
+ */
166
+ export interface LiveSourceLike {
167
+ addEventListener(type: string, handler: (event: {
168
+ data?: string;
169
+ }) => void): void;
170
+ close(): void;
171
+ }
172
+ /** Options for swapping {@link HttpDataClient}'s transport (desktop IPC). */
173
+ export interface HttpDataClientOptions {
174
+ /** Replacement for the global `fetch` (e.g. tunnel requests over host IPC). */
175
+ fetchImpl?: FetchLike;
176
+ /** Factory for the live-update source (e.g. an IPC-event-backed source). */
177
+ createLiveSource?: (url: string) => LiveSourceLike;
178
+ }
179
+ /**
180
+ * {@link DataClient} backed by an OpenBook server's HTTP API. Isomorphic, and
181
+ * transport-pluggable: by default it uses the global `fetch` + `EventSource`
182
+ * (web, remote), but the desktop injects a `fetchImpl`/`createLiveSource` that
183
+ * tunnel over its host IPC bridge to a portless Unix-socket server.
184
+ */
185
+ export declare class HttpDataClient implements DataClient {
186
+ private readonly baseUrl;
187
+ private readonly token?;
188
+ private readonly fetchImpl;
189
+ private readonly createLiveSource;
190
+ private live;
191
+ /**
192
+ * @param baseUrl Server base URL. May be empty when a `fetchImpl` resolves
193
+ * paths itself (the desktop IPC transport).
194
+ * @param token Optional access token required by a published (LAN) server;
195
+ * sent as `Authorization: Bearer` on requests and `?token=` on
196
+ * the SSE stream (EventSource can't set headers). Omit for a
197
+ * loopback/local server, which needs none.
198
+ * @param opts Optional transport overrides (desktop IPC).
199
+ */
200
+ constructor(baseUrl: string, token?: string, opts?: HttpDataClientOptions);
201
+ /** `fetch` (or the injected transport) with the access token attached (when set). */
202
+ private authFetch;
203
+ /** Lazily create the shared live connection (browser-only). */
204
+ private liveStream;
205
+ listPages(): Promise<PageMeta[]>;
206
+ getPage(id: string): Promise<StoredPage | null>;
207
+ savePage(input: PageInput): Promise<StoredPage>;
208
+ renamePage(id: string, name: string | null): Promise<StoredPage>;
209
+ setPageProperties(id: string, properties: Record<string, unknown>): Promise<StoredPage>;
210
+ listBacklinks(id: string): Promise<PageMeta[]>;
211
+ movePage(id: string, move: {
212
+ parentId: string | null;
213
+ orderedIds: string[];
214
+ }): Promise<StoredPage>;
215
+ deletePage(id: string): Promise<boolean>;
216
+ exportSpace(): Promise<{
217
+ pages: StoredPage[];
218
+ databases: StoredDatabase[];
219
+ }>;
220
+ importSpace(req: ImportRequest): Promise<ImportResult>;
221
+ listTrash(): Promise<PageMeta[]>;
222
+ restorePage(id: string): Promise<StoredPage | null>;
223
+ purgePage(id: string): Promise<boolean>;
224
+ emptyTrash(): Promise<number>;
225
+ subscribePage(id: string, handlers: PageSubscription): () => void;
226
+ subscribePages(onList: (pages: PageMeta[]) => void): () => void;
227
+ createDatabase(input: DatabaseInput): Promise<StoredDatabase>;
228
+ getDatabase(id: string): Promise<StoredDatabase | null>;
229
+ getPageDatabase(pageId: string): Promise<StoredDatabase | null>;
230
+ updateDatabase(id: string, patch: DatabaseUpdate): Promise<StoredDatabase>;
231
+ deleteDatabase(id: string): Promise<boolean>;
232
+ listRows(databaseId: string): Promise<DatabaseRow[]>;
233
+ createRow(databaseId: string, input?: RowInput): Promise<StoredPage>;
234
+ updateRow(databaseId: string, rowId: string, patch: RowUpdate): Promise<DatabaseRow>;
235
+ reorderRows(databaseId: string, orderedIds: string[]): Promise<void>;
236
+ subscribeRows(databaseId: string, onRows: (rows: DatabaseRow[]) => void): () => void;
237
+ listSuggestions(pageId: string, status?: SuggestionStatus): Promise<StoredSuggestion[]>;
238
+ createSuggestion(input: SuggestionInput): Promise<StoredSuggestion>;
239
+ updateSuggestion(id: string, patch: SuggestionUpdate): Promise<StoredSuggestion>;
240
+ deleteSuggestion(id: string): Promise<boolean>;
241
+ listComments(pageId: string): Promise<StoredComment[]>;
242
+ createComment(input: CommentInput): Promise<StoredComment>;
243
+ deleteComment(id: string): Promise<boolean>;
244
+ aiStatus(): Promise<AiStatus>;
245
+ aiSetConfig(config: AiConfig): Promise<AiConfig>;
246
+ aiIndex(): Promise<{
247
+ pages: number;
248
+ chunks: number;
249
+ }>;
250
+ aiSearch(query: string, limit?: number): Promise<AiSearchResponse>;
251
+ aiTasks(goal: string, context?: string): Promise<AiTasksResponse>;
252
+ aiDownloadModel(url?: string): Promise<AiStatus['download']>;
253
+ /**
254
+ * Stream a document completion. `onToken` fires per token; resolves with
255
+ * the full text. Abort via the optional signal.
256
+ */
257
+ aiComplete(text: string, onToken: (token: string) => void, opts?: {
258
+ instruction?: string;
259
+ signal?: AbortSignal;
260
+ }): Promise<string>;
261
+ /** Stream a raw generation (prompt + optional system). */
262
+ aiGenerate(prompt: string, onToken: (token: string) => void, opts?: {
263
+ system?: string;
264
+ maxTokens?: number;
265
+ signal?: AbortSignal;
266
+ }): Promise<string>;
267
+ listPlugins(): Promise<StoredPlugin[]>;
268
+ installPlugin(pkg: PluginPackage): Promise<StoredPlugin>;
269
+ setPluginEnabled(id: string, enabled: boolean): Promise<StoredPlugin>;
270
+ removePlugin(id: string): Promise<boolean>;
271
+ aiSkills(): Promise<AiSkill[]>;
272
+ aiSaveSkill(skill: AiSkill): Promise<AiSkill>;
273
+ aiDeleteSkill(name: string): Promise<boolean>;
274
+ /** Run the workspace agent, surfacing each streamed step via `onEvent`. */
275
+ agentChat(messages: AgentChatMessage[], onEvent: (event: AgentChatEvent) => void, opts?: AgentChatOptions): Promise<void>;
276
+ /** POST a body and consume the SSE token stream the AI endpoints emit. */
277
+ private aiStream;
278
+ private request;
279
+ }