@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.
- package/LICENSE +21 -0
- package/README.md +23 -0
- package/dist/account.d.ts +57 -0
- package/dist/account.js +104 -0
- package/dist/account.js.map +1 -0
- package/dist/ai.d.ts +264 -0
- package/dist/ai.js +36 -0
- package/dist/ai.js.map +1 -0
- package/dist/backup.d.ts +52 -0
- package/dist/backup.js +35 -0
- package/dist/backup.js.map +1 -0
- package/dist/bookFolder.d.ts +44 -0
- package/dist/bookFolder.js +98 -0
- package/dist/bookFolder.js.map +1 -0
- package/dist/bookfile.d.ts +42 -0
- package/dist/bookfile.js +159 -0
- package/dist/bookfile.js.map +1 -0
- package/dist/client.d.ts +279 -0
- package/dist/client.js +502 -0
- package/dist/client.js.map +1 -0
- package/dist/connection.d.ts +20 -0
- package/dist/connection.js +77 -0
- package/dist/connection.js.map +1 -0
- package/dist/content.d.ts +38 -0
- package/dist/content.js +107 -0
- package/dist/content.js.map +1 -0
- package/dist/database.d.ts +687 -0
- package/dist/database.js +1082 -0
- package/dist/database.js.map +1 -0
- package/dist/formula.d.ts +41 -0
- package/dist/formula.js +469 -0
- package/dist/formula.js.map +1 -0
- package/dist/forwarding/challenge.d.ts +35 -0
- package/dist/forwarding/challenge.js +45 -0
- package/dist/forwarding/challenge.js.map +1 -0
- package/dist/forwarding/encoding.d.ts +14 -0
- package/dist/forwarding/encoding.js +54 -0
- package/dist/forwarding/encoding.js.map +1 -0
- package/dist/forwarding/forwardingClient.d.ts +76 -0
- package/dist/forwarding/forwardingClient.js +128 -0
- package/dist/forwarding/forwardingClient.js.map +1 -0
- package/dist/forwarding/index.d.ts +5 -0
- package/dist/forwarding/index.js +14 -0
- package/dist/forwarding/index.js.map +1 -0
- package/dist/forwarding/siteKey.d.ts +12 -0
- package/dist/forwarding/siteKey.js +36 -0
- package/dist/forwarding/siteKey.js.map +1 -0
- package/dist/forwarding/tunnelClient.d.ts +56 -0
- package/dist/forwarding/tunnelClient.js +206 -0
- package/dist/forwarding/tunnelClient.js.map +1 -0
- package/dist/forwarding/tunnelProtocol.d.ts +45 -0
- package/dist/forwarding/tunnelProtocol.js +29 -0
- package/dist/forwarding/tunnelProtocol.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/mtime.d.ts +51 -0
- package/dist/mtime.js +84 -0
- package/dist/mtime.js.map +1 -0
- package/dist/pageProperties.d.ts +80 -0
- package/dist/pageProperties.js +97 -0
- package/dist/pageProperties.js.map +1 -0
- package/dist/plugins.d.ts +110 -0
- package/dist/plugins.js +113 -0
- package/dist/plugins.js.map +1 -0
- package/dist/routes.d.ts +89 -0
- package/dist/routes.js +89 -0
- package/dist/routes.js.map +1 -0
- package/dist/sampleDocument.d.ts +25 -0
- package/dist/sampleDocument.js +67 -0
- package/dist/sampleDocument.js.map +1 -0
- package/dist/suggestions.d.ts +126 -0
- package/dist/suggestions.js +21 -0
- package/dist/suggestions.js.map +1 -0
- package/dist/templates.d.ts +35 -0
- package/dist/templates.js +555 -0
- package/dist/templates.js.map +1 -0
- package/dist/types.d.ts +146 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// The reusable tunnel client the desktop embeds (Tauri webview/sidecar, Node, or
|
|
2
|
+
// any runtime with global `WebSocket` + `fetch`). It dials OUT to the relay,
|
|
3
|
+
// completes the double-gated attach (signs the relay's nonce with the site key +
|
|
4
|
+
// presents the account ticket), then serves inbound requests against the local
|
|
5
|
+
// OpenBook data server, streaming responses — including SSE — back up the tunnel.
|
|
6
|
+
//
|
|
7
|
+
// No Node-only APIs, so it also runs inside the Tauri webview. On the desktop the
|
|
8
|
+
// `fetchImpl` is the IPC transport, so forwarded traffic reaches the portless
|
|
9
|
+
// local server without opening a TCP port.
|
|
10
|
+
import { globalFetch } from '../client';
|
|
11
|
+
import { buildRelayAttachMessage } from './challenge';
|
|
12
|
+
import { signWithSiteKey } from './siteKey';
|
|
13
|
+
import { decodeBody, decodeControl, encodeBody, encodeControl } from './tunnelProtocol';
|
|
14
|
+
export class TunnelClient {
|
|
15
|
+
constructor(opts) {
|
|
16
|
+
this.opts = opts;
|
|
17
|
+
this.status = 'offline';
|
|
18
|
+
this.backoff = 500;
|
|
19
|
+
this.stopped = false;
|
|
20
|
+
this.inflight = new Map();
|
|
21
|
+
this.fetchImpl = opts.fetchImpl ?? globalFetch;
|
|
22
|
+
this.WS = opts.webSocketImpl ?? WebSocket;
|
|
23
|
+
}
|
|
24
|
+
start() {
|
|
25
|
+
this.stopped = false;
|
|
26
|
+
this.connect();
|
|
27
|
+
}
|
|
28
|
+
stop() {
|
|
29
|
+
this.stopped = true;
|
|
30
|
+
this.setStatus('offline');
|
|
31
|
+
this.ws?.close();
|
|
32
|
+
}
|
|
33
|
+
get currentStatus() {
|
|
34
|
+
return this.status;
|
|
35
|
+
}
|
|
36
|
+
setStatus(next) {
|
|
37
|
+
if (next !== this.status) {
|
|
38
|
+
this.status = next;
|
|
39
|
+
this.opts.onStatus?.(next);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
connect() {
|
|
43
|
+
this.setStatus(this.backoff > 500 ? 'reconnecting' : 'connecting');
|
|
44
|
+
void this.dial();
|
|
45
|
+
}
|
|
46
|
+
/** Mint a fresh ticket, then open the relay socket with it. Minting per dial is
|
|
47
|
+
* what keeps reconnects working — a reused ticket expires and attach fails. */
|
|
48
|
+
async dial() {
|
|
49
|
+
let info;
|
|
50
|
+
try {
|
|
51
|
+
info = await this.opts.ticketProvider();
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Couldn't mint (account unreachable / token expired) — back off and retry.
|
|
55
|
+
this.scheduleReconnect();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (this.stopped)
|
|
59
|
+
return;
|
|
60
|
+
this.ticket = info.ticket;
|
|
61
|
+
const ws = new this.WS(info.relayWsUrl);
|
|
62
|
+
ws.binaryType = 'arraybuffer';
|
|
63
|
+
this.ws = ws;
|
|
64
|
+
ws.onmessage = (ev) => void this.onMessage(ev.data);
|
|
65
|
+
ws.onclose = () => this.onClose();
|
|
66
|
+
ws.onerror = () => ws.close();
|
|
67
|
+
}
|
|
68
|
+
onClose() {
|
|
69
|
+
for (const f of this.inflight.values())
|
|
70
|
+
f.controller.abort();
|
|
71
|
+
this.inflight.clear();
|
|
72
|
+
this.scheduleReconnect();
|
|
73
|
+
}
|
|
74
|
+
scheduleReconnect() {
|
|
75
|
+
if (this.stopped) {
|
|
76
|
+
this.setStatus('offline');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.setStatus('reconnecting');
|
|
80
|
+
const delay = this.backoff;
|
|
81
|
+
this.backoff = Math.min(this.backoff * 2, this.opts.maxBackoffMs ?? 30000);
|
|
82
|
+
setTimeout(() => {
|
|
83
|
+
if (!this.stopped)
|
|
84
|
+
this.connect();
|
|
85
|
+
}, delay);
|
|
86
|
+
}
|
|
87
|
+
sendControl(frame) {
|
|
88
|
+
this.ws?.send(encodeControl(frame));
|
|
89
|
+
}
|
|
90
|
+
/** Pause while the socket's send buffer is backed up, so streaming a large
|
|
91
|
+
* response to a slow viewer can't grow memory without bound. */
|
|
92
|
+
async drain() {
|
|
93
|
+
const ws = this.ws;
|
|
94
|
+
if (!ws)
|
|
95
|
+
return;
|
|
96
|
+
const HIGH_WATER = 4 * 1024 * 1024; // 4 MB
|
|
97
|
+
while (ws.readyState === ws.OPEN && ws.bufferedAmount > HIGH_WATER) {
|
|
98
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async onMessage(data) {
|
|
102
|
+
if (typeof data === 'string') {
|
|
103
|
+
const frame = decodeControl(data);
|
|
104
|
+
if (frame)
|
|
105
|
+
await this.onControl(frame);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const { id, chunk } = decodeBody(new Uint8Array(data));
|
|
109
|
+
this.inflight.get(id)?.bodyController?.enqueue(chunk);
|
|
110
|
+
}
|
|
111
|
+
async onControl(frame) {
|
|
112
|
+
switch (frame.t) {
|
|
113
|
+
case 'challenge': {
|
|
114
|
+
if (!this.ticket) {
|
|
115
|
+
this.ws?.close();
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
const signature = await signWithSiteKey(this.opts.privateKey, buildRelayAttachMessage(frame.nonce));
|
|
119
|
+
this.sendControl({ t: 'attach', ticket: this.ticket, signature });
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case 'ready':
|
|
123
|
+
this.backoff = 500;
|
|
124
|
+
this.setStatus('online');
|
|
125
|
+
break;
|
|
126
|
+
case 'error':
|
|
127
|
+
this.ws?.close();
|
|
128
|
+
break;
|
|
129
|
+
case 'ping':
|
|
130
|
+
this.sendControl({ t: 'pong' });
|
|
131
|
+
break;
|
|
132
|
+
case 'req':
|
|
133
|
+
void this.handleRequest(frame);
|
|
134
|
+
break;
|
|
135
|
+
case 'end': {
|
|
136
|
+
const f = this.inflight.get(frame.id);
|
|
137
|
+
f?.bodyController?.close();
|
|
138
|
+
if (f)
|
|
139
|
+
f.bodyController = undefined;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case 'abort': {
|
|
143
|
+
this.inflight.get(frame.id)?.controller.abort();
|
|
144
|
+
this.inflight.delete(frame.id);
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
default:
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async handleRequest(frame) {
|
|
152
|
+
const { id } = frame;
|
|
153
|
+
const controller = new AbortController();
|
|
154
|
+
const inflight = { controller };
|
|
155
|
+
this.inflight.set(id, inflight);
|
|
156
|
+
const hasBody = frame.method !== 'GET' && frame.method !== 'HEAD';
|
|
157
|
+
let body;
|
|
158
|
+
if (hasBody) {
|
|
159
|
+
body = new ReadableStream({
|
|
160
|
+
start: (c) => {
|
|
161
|
+
inflight.bodyController = c;
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const url = `${this.opts.localOrigin.replace(/\/$/, '')}${frame.path}`;
|
|
166
|
+
const headers = new Headers();
|
|
167
|
+
for (const [k, v] of frame.headers) {
|
|
168
|
+
const lk = k.toLowerCase();
|
|
169
|
+
if (lk === 'host' || lk === 'connection' || lk === 'content-length')
|
|
170
|
+
continue;
|
|
171
|
+
headers.append(k, v);
|
|
172
|
+
}
|
|
173
|
+
const init = { method: frame.method, headers, signal: controller.signal };
|
|
174
|
+
if (hasBody) {
|
|
175
|
+
init.body = body;
|
|
176
|
+
init.duplex = 'half';
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const res = await this.fetchImpl(url, init);
|
|
180
|
+
const resHeaders = [];
|
|
181
|
+
res.headers.forEach((value, key) => resHeaders.push([key, value]));
|
|
182
|
+
this.sendControl({ t: 'res', id, status: res.status, headers: resHeaders });
|
|
183
|
+
if (res.body) {
|
|
184
|
+
const reader = res.body.getReader();
|
|
185
|
+
for (;;) {
|
|
186
|
+
const { done, value } = await reader.read();
|
|
187
|
+
if (done)
|
|
188
|
+
break;
|
|
189
|
+
if (value && value.byteLength) {
|
|
190
|
+
this.ws?.send(encodeBody(id, value));
|
|
191
|
+
await this.drain(); // backpressure: don't outrun a slow consumer
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
this.sendControl({ t: 'end', id });
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
if (!controller.signal.aborted)
|
|
199
|
+
this.sendControl({ t: 'abort', id, reason: 'local fetch failed' });
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
this.inflight.delete(id);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=tunnelClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnelClient.js","sourceRoot":"","sources":["../../src/forwarding/tunnelClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type ControlFrame = {
|
|
2
|
+
t: 'challenge';
|
|
3
|
+
nonce: string;
|
|
4
|
+
} | {
|
|
5
|
+
t: 'attach';
|
|
6
|
+
ticket: string;
|
|
7
|
+
signature: string;
|
|
8
|
+
clientInfo?: string;
|
|
9
|
+
} | {
|
|
10
|
+
t: 'ready';
|
|
11
|
+
siteId: string;
|
|
12
|
+
} | {
|
|
13
|
+
t: 'error';
|
|
14
|
+
message: string;
|
|
15
|
+
} | {
|
|
16
|
+
t: 'req';
|
|
17
|
+
id: number;
|
|
18
|
+
method: string;
|
|
19
|
+
path: string;
|
|
20
|
+
headers: [string, string][];
|
|
21
|
+
} | {
|
|
22
|
+
t: 'res';
|
|
23
|
+
id: number;
|
|
24
|
+
status: number;
|
|
25
|
+
headers: [string, string][];
|
|
26
|
+
} | {
|
|
27
|
+
t: 'end';
|
|
28
|
+
id: number;
|
|
29
|
+
} | {
|
|
30
|
+
t: 'abort';
|
|
31
|
+
id: number;
|
|
32
|
+
reason?: string;
|
|
33
|
+
} | {
|
|
34
|
+
t: 'ping';
|
|
35
|
+
} | {
|
|
36
|
+
t: 'pong';
|
|
37
|
+
};
|
|
38
|
+
export declare function encodeControl(frame: ControlFrame): string;
|
|
39
|
+
export declare function decodeControl(data: string): ControlFrame | null;
|
|
40
|
+
/** Binary body frame: [id: uint32 BE][...bytes]. */
|
|
41
|
+
export declare function encodeBody(id: number, chunk: Uint8Array): Uint8Array<ArrayBuffer>;
|
|
42
|
+
export declare function decodeBody(buf: Uint8Array): {
|
|
43
|
+
id: number;
|
|
44
|
+
chunk: Uint8Array;
|
|
45
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// The wire protocol for the reverse tunnel between a desktop and its relay. One
|
|
2
|
+
// WebSocket carries many concurrent HTTP exchanges, multiplexed by a numeric
|
|
3
|
+
// request id. Control frames are JSON (WS text); body chunks are binary (WS
|
|
4
|
+
// binary) prefixed with the id, so large payloads and SSE stream without base64
|
|
5
|
+
// overhead. Shared by the relay (open.book.pub) and the tunnel client below.
|
|
6
|
+
export function encodeControl(frame) {
|
|
7
|
+
return JSON.stringify(frame);
|
|
8
|
+
}
|
|
9
|
+
export function decodeControl(data) {
|
|
10
|
+
try {
|
|
11
|
+
const f = JSON.parse(data);
|
|
12
|
+
return f && typeof f.t === 'string' ? f : null;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Binary body frame: [id: uint32 BE][...bytes]. */
|
|
19
|
+
export function encodeBody(id, chunk) {
|
|
20
|
+
const out = new Uint8Array(4 + chunk.byteLength);
|
|
21
|
+
new DataView(out.buffer).setUint32(0, id >>> 0, false);
|
|
22
|
+
out.set(chunk, 4);
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
export function decodeBody(buf) {
|
|
26
|
+
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
27
|
+
return { id: view.getUint32(0, false), chunk: buf.subarray(4) };
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=tunnelProtocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnelProtocol.js","sourceRoot":"","sources":["../../src/forwarding/tunnelProtocol.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,6EAA6E;AAC7E,4EAA4E;AAC5E,gFAAgF;AAChF,6EAA6E;AAiB7E,MAAM,UAAU,aAAa,CAAC,KAAmB;IAC/C,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,CAAC;QAC3C,OAAO,CAAC,IAAI,OAAQ,CAAmB,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,UAAU,CAAC,EAAU,EAAE,KAAiB;IACtD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;IACjD,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IACvD,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAClB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAe;IACxC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IACtE,OAAO,EAAC,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAC,CAAC;AAChE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type { PageSnapshot, PageMeta, StoredPage, PageInput, ServerInfo, ServerControls } from './types';
|
|
2
|
+
export { emptyPageSnapshot } from './types';
|
|
3
|
+
export { API, type ApiError } from './routes';
|
|
4
|
+
export type { DataClient, PageSubscription, FetchLike, LiveSourceLike, HttpDataClientOptions } from './client';
|
|
5
|
+
export { HttpDataClient } from './client';
|
|
6
|
+
export { ForwardingClient, TunnelClient, MemoryKeyStore, mintSiteKeypair, signWithSiteKey, type KeyStore, type SiteIdentity, type ForwardingClientOptions, type TunnelStatus, } from './forwarding';
|
|
7
|
+
export type { DatabasePropertyType, NumberFormat, NumberDisplay, StatusGroup, RollupFunction, RollupConfig, RelationCardinality, DatabaseSelectOption, DatabaseProperty, DateRange, LocationValue, PropertyGroup, RowTemplate, DatabaseViewType, ChartAggregate, SummaryType, FilterOperator, FilterConjunction, DatabaseFilter, DatabaseFilterGroup, FilterNode, SortDirection, DatabaseSort, DatabaseView, DatabaseMetric, ColorRule, DatabaseSchema, StoredDatabase, DatabaseInput, DatabaseUpdate, DatabaseRow, RowInput, RowUpdate, RowGroup, ChartDatum, ChartSeries, ChartSegment, ChartGroup, ChartMatrix, DateSpan, GraphNode, GraphEdge, DependencyGraph, InverseUpdate, RowTreeNode, } from './database';
|
|
8
|
+
export { TITLE_PROPERTY_ID, SELECT_COLORS, NO_VALUE_GROUP, STATUS_GROUPS, RELATIVE_DATE_OPS, projectExports, rowValue, matchesFilter, rowMatchesCondition, applyView, viewFilterRoot, isFilterGroup, shortId, defaultDatabaseSchema, defaultView, defaultStatusOptions, removeProperty, formatNumber, formatUniqueId, numberProgress, groupRows, groupRowsBy, groupRowsByParent, PARENT_GROUP_ID, NO_PARENT_GROUP, aggregateRows, aggregateMatrix, CHART_TOTAL_SERIES, summarizeColumn, dateStart, dateEnd, parseDay, rowDateSpan, rowLocation, asLocation, dependencyGraph, syncInverseUpdates, relationSides, buildRowTree, flattenRowTree, isImageUrl, firstImageUrl, coverImageUrl, } from './database';
|
|
9
|
+
export { evaluateFormula, formulaReferences, FormulaError, type FormulaValue, type FormulaResolver, } from './formula';
|
|
10
|
+
export { OWNER_PROPERTY_ID, VERIFICATION_PROPERTY_ID, BACKLINKS_PROPERTY_ID, THEME_PROPERTY_ID, COVER_PROPERTY_ID, FONTS_PROPERTY_ID, ICON_PROPERTY_ID, FULLWIDTH_PROPERTY_ID, SYSTEM_PAGE_PROPERTIES, isVerified, verificationExpired, isVerificationActive, makeVerification, extractMentionIds, propertiesReferencePage, type VerificationValue, } from './pageProperties';
|
|
11
|
+
export { getServerUrlOverride, setServerUrlOverride, getServerTokenOverride, setServerTokenOverride, isMixedContentBlocked } from './connection';
|
|
12
|
+
export { AccountClient, AccountError, DEFAULT_ACCOUNT_URL, getAccountUrlOverride, setAccountUrlOverride, resolveAccountUrl, type AccountSettings, } from './account';
|
|
13
|
+
export { snapshotText, paragraphBlocks, textSnapshot, appendTextToSnapshot, appendBlocksToSnapshot, type AppendBlock } from './content';
|
|
14
|
+
export { snapshotBlocks, contentHash, computeBlockMtimes, stampSnapshotMtimes, latestBlockMtime, type BlockDigest, } from './mtime';
|
|
15
|
+
export { pageToBookHtml, bookHtmlToPage, readBookHtmlMeta, slugify, type BookPageRecord, } from './bookfile';
|
|
16
|
+
export { spaceToBookFiles, parseBookFolder, SPACE_BUNDLE_FILE, type BookFolderFile, type SpaceSnapshot, } from './bookFolder';
|
|
17
|
+
export { canonicalDigest, generateRegistryKeys, signPlugin, verifyPlugin, validateManifest, OPENBOOK_REGISTRY, type PluginManifest, type PluginAgentTool, type PluginPackage, type PluginSignature, type StoredPlugin, } from './plugins';
|
|
18
|
+
export { buildSampleDocument, seedSampleDocument, SAMPLE_DOCUMENT_NAME } from './sampleDocument';
|
|
19
|
+
export type { CommentRun, SuggestionAuthorKind, SuggestionKind, SuggestionStatus, SuggestionTarget, StoredSuggestion, SuggestionInput, SuggestionUpdate, StoredComment, CommentInput, } from './suggestions';
|
|
20
|
+
export { PAGE_TEMPLATES, instantiateTemplate, type PageTemplate } from './templates';
|
|
21
|
+
export { BACKUP_VERSION, remapBundle, type SpaceBackup, type ImportMode, type ImportRequest, type ImportResult, } from './backup';
|
|
22
|
+
export type { AgentChatEvent, AgentChatMessage, AgentChatOptions, AgentProposal, InterviewStep, AiProvider, AiConfig, AiProviderSettings, AiEffort, AiSkill, AiStatus, AiSearchResult, AiSearchResponse, AiTasksResponse, AiStreamEvent, } from './ai';
|
|
23
|
+
export { providerSettings } from './ai';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { emptyPageSnapshot } from './types';
|
|
2
|
+
export { API } from './routes';
|
|
3
|
+
export { HttpDataClient } from './client';
|
|
4
|
+
export { ForwardingClient, TunnelClient, MemoryKeyStore, mintSiteKeypair, signWithSiteKey, } from './forwarding';
|
|
5
|
+
export { TITLE_PROPERTY_ID, SELECT_COLORS, NO_VALUE_GROUP, STATUS_GROUPS, RELATIVE_DATE_OPS, projectExports, rowValue, matchesFilter, rowMatchesCondition, applyView, viewFilterRoot, isFilterGroup, shortId, defaultDatabaseSchema, defaultView, defaultStatusOptions, removeProperty, formatNumber, formatUniqueId, numberProgress, groupRows, groupRowsBy, groupRowsByParent, PARENT_GROUP_ID, NO_PARENT_GROUP, aggregateRows, aggregateMatrix, CHART_TOTAL_SERIES, summarizeColumn, dateStart, dateEnd, parseDay, rowDateSpan, rowLocation, asLocation, dependencyGraph, syncInverseUpdates, relationSides, buildRowTree, flattenRowTree, isImageUrl, firstImageUrl, coverImageUrl, } from './database';
|
|
6
|
+
export { evaluateFormula, formulaReferences, FormulaError, } from './formula';
|
|
7
|
+
export { OWNER_PROPERTY_ID, VERIFICATION_PROPERTY_ID, BACKLINKS_PROPERTY_ID, THEME_PROPERTY_ID, COVER_PROPERTY_ID, FONTS_PROPERTY_ID, ICON_PROPERTY_ID, FULLWIDTH_PROPERTY_ID, SYSTEM_PAGE_PROPERTIES, isVerified, verificationExpired, isVerificationActive, makeVerification, extractMentionIds, propertiesReferencePage, } from './pageProperties';
|
|
8
|
+
export { getServerUrlOverride, setServerUrlOverride, getServerTokenOverride, setServerTokenOverride, isMixedContentBlocked } from './connection';
|
|
9
|
+
export { AccountClient, AccountError, DEFAULT_ACCOUNT_URL, getAccountUrlOverride, setAccountUrlOverride, resolveAccountUrl, } from './account';
|
|
10
|
+
export { snapshotText, paragraphBlocks, textSnapshot, appendTextToSnapshot, appendBlocksToSnapshot } from './content';
|
|
11
|
+
export { snapshotBlocks, contentHash, computeBlockMtimes, stampSnapshotMtimes, latestBlockMtime, } from './mtime';
|
|
12
|
+
export { pageToBookHtml, bookHtmlToPage, readBookHtmlMeta, slugify, } from './bookfile';
|
|
13
|
+
export { spaceToBookFiles, parseBookFolder, SPACE_BUNDLE_FILE, } from './bookFolder';
|
|
14
|
+
export { canonicalDigest, generateRegistryKeys, signPlugin, verifyPlugin, validateManifest, OPENBOOK_REGISTRY, } from './plugins';
|
|
15
|
+
export { buildSampleDocument, seedSampleDocument, SAMPLE_DOCUMENT_NAME } from './sampleDocument';
|
|
16
|
+
export { PAGE_TEMPLATES, instantiateTemplate } from './templates';
|
|
17
|
+
export { BACKUP_VERSION, remapBundle, } from './backup';
|
|
18
|
+
export { providerSettings } from './ai';
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,iBAAiB,EAAC,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAC,GAAG,EAAgB,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAC,cAAc,EAAC,MAAM,UAAU,CAAC;AACxC,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,eAAe,GAKhB,MAAM,cAAc,CAAC;AAgDtB,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,cAAc,EACd,aAAa,EACb,OAAO,EACP,qBAAqB,EACrB,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,YAAY,EACZ,cAAc,EACd,cAAc,EACd,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,OAAO,EACP,QAAQ,EACR,WAAW,EACX,WAAW,EACX,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,UAAU,EACV,aAAa,EACb,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,YAAY,GAGb,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,UAAU,EACV,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,GAExB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAC,oBAAoB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,qBAAqB,EAAC,MAAM,cAAc,CAAC;AAC/I,OAAO,EACL,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,GAElB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAC,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,oBAAoB,EAAE,sBAAsB,EAAmB,MAAM,WAAW,CAAC;AACtI,OAAO,EACL,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,GAEjB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,OAAO,GAER,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,GAGlB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAMlB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAC,mBAAmB,EAAE,kBAAkB,EAAE,oBAAoB,EAAC,MAAM,kBAAkB,CAAC;AAa/F,OAAO,EAAC,cAAc,EAAE,mBAAmB,EAAoB,MAAM,aAAa,CAAC;AACnF,OAAO,EACL,cAAc,EACd,WAAW,GAKZ,MAAM,UAAU,CAAC;AAkBlB,OAAO,EAAC,gBAAgB,EAAC,MAAM,MAAM,CAAC"}
|
package/dist/mtime.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-block modification tracking — the change/version signal the on-disk book
|
|
3
|
+
* mirror, the external-change watcher, and conflict detection all rely on
|
|
4
|
+
* (OB-133). A page snapshot can carry an `mtimes` array of `[blockId, ISO]`
|
|
5
|
+
* pairs; the server stamps it on every write so an unchanged block keeps its old
|
|
6
|
+
* timestamp while a changed or newly-inserted one is restamped to "now".
|
|
7
|
+
*
|
|
8
|
+
* Pure and isomorphic (no DOM, no Node): it reads whichever block projection a
|
|
9
|
+
* page uses — the CRDT block editor's JSON projection (`blockdoc.blocks`) or the
|
|
10
|
+
* legacy EditorJS blocks (`editorjs.blocks`) — so the same logic runs on the
|
|
11
|
+
* server write path and in unit tests.
|
|
12
|
+
*/
|
|
13
|
+
import type { PageSnapshot } from './types';
|
|
14
|
+
/** A top-level block reduced to the bits that define its identity + content. */
|
|
15
|
+
export interface BlockDigest {
|
|
16
|
+
/** Stable block id (used to match the same block across two snapshot versions). */
|
|
17
|
+
id: string;
|
|
18
|
+
/** Block type (`paragraph`, `heading`, …). */
|
|
19
|
+
type: string;
|
|
20
|
+
/** A short, deterministic hash of the block's full content (incl. children). */
|
|
21
|
+
hash: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* FNV-1a (32-bit) over a string, returned as 8 hex chars. Fast, dependency-free,
|
|
25
|
+
* and stable across platforms — exactly what change-detection needs (a rare
|
|
26
|
+
* collision only means a missed restamp, never corruption).
|
|
27
|
+
*/
|
|
28
|
+
export declare function contentHash(input: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* The ordered list of a snapshot's top-level blocks, each as a {@link BlockDigest}.
|
|
31
|
+
* Reads the block-editor projection for `editor: 'blocks'` pages and the EditorJS
|
|
32
|
+
* blocks otherwise. A block with no explicit id falls back to a positional id so
|
|
33
|
+
* an unchanged document still matches itself across versions.
|
|
34
|
+
*/
|
|
35
|
+
export declare function snapshotBlocks(data: PageSnapshot | null | undefined): BlockDigest[];
|
|
36
|
+
/**
|
|
37
|
+
* Compute the `[blockId, ISO]` mtimes for `next`, carrying forward each block's
|
|
38
|
+
* timestamp from `prev` when its content is unchanged and stamping `nowIso` for
|
|
39
|
+
* new or changed blocks. The result preserves `next`'s block order. Pages with
|
|
40
|
+
* no `prev` (a fresh insert) get every block stamped `nowIso`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function computeBlockMtimes(prev: PageSnapshot | null | undefined, next: PageSnapshot, nowIso: string): Array<[string, string]>;
|
|
43
|
+
/**
|
|
44
|
+
* Return `next` with its `mtimes` stamped relative to `prev`. The server calls
|
|
45
|
+
* this on every content write so a page snapshot always describes when each of
|
|
46
|
+
* its blocks last changed. Idempotent when the document is unchanged: re-stamping
|
|
47
|
+
* an identical snapshot keeps every timestamp.
|
|
48
|
+
*/
|
|
49
|
+
export declare function stampSnapshotMtimes(prev: PageSnapshot | null | undefined, next: PageSnapshot, nowIso?: string): PageSnapshot;
|
|
50
|
+
/** The most recent block mtime in a snapshot, or `null` if none are stamped. */
|
|
51
|
+
export declare function latestBlockMtime(data: PageSnapshot | null | undefined): string | null;
|
package/dist/mtime.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FNV-1a (32-bit) over a string, returned as 8 hex chars. Fast, dependency-free,
|
|
3
|
+
* and stable across platforms — exactly what change-detection needs (a rare
|
|
4
|
+
* collision only means a missed restamp, never corruption).
|
|
5
|
+
*/
|
|
6
|
+
export function contentHash(input) {
|
|
7
|
+
let h = 0x811c9dc5;
|
|
8
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
9
|
+
h ^= input.charCodeAt(i);
|
|
10
|
+
// h *= 16777619, kept in 32-bit range via the >>> 0 below.
|
|
11
|
+
h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;
|
|
12
|
+
}
|
|
13
|
+
return h.toString(16).padStart(8, '0');
|
|
14
|
+
}
|
|
15
|
+
/** Stable JSON: object keys sorted so `{a,b}` and `{b,a}` hash identically. */
|
|
16
|
+
function stableStringify(value) {
|
|
17
|
+
if (value === null || typeof value !== 'object')
|
|
18
|
+
return JSON.stringify(value) ?? 'null';
|
|
19
|
+
if (Array.isArray(value))
|
|
20
|
+
return `[${value.map(stableStringify).join(',')}]`;
|
|
21
|
+
const obj = value;
|
|
22
|
+
const keys = Object.keys(obj).sort();
|
|
23
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(',')}}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The ordered list of a snapshot's top-level blocks, each as a {@link BlockDigest}.
|
|
27
|
+
* Reads the block-editor projection for `editor: 'blocks'` pages and the EditorJS
|
|
28
|
+
* blocks otherwise. A block with no explicit id falls back to a positional id so
|
|
29
|
+
* an unchanged document still matches itself across versions.
|
|
30
|
+
*/
|
|
31
|
+
export function snapshotBlocks(data) {
|
|
32
|
+
if (!data)
|
|
33
|
+
return [];
|
|
34
|
+
const blockdoc = data.blockdoc;
|
|
35
|
+
if (data.editor === 'blocks' && Array.isArray(blockdoc?.blocks)) {
|
|
36
|
+
return blockdoc.blocks.map((b, i) => ({
|
|
37
|
+
id: typeof b.id === 'string' && b.id ? b.id : `b${i}`,
|
|
38
|
+
type: typeof b.type === 'string' ? b.type : 'unknown',
|
|
39
|
+
hash: contentHash(stableStringify({ type: b.type, text: b.text, props: b.props, children: b.children })),
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
const blocks = data.editorjs?.blocks ?? [];
|
|
43
|
+
return blocks.map((b, i) => ({
|
|
44
|
+
id: typeof b.id === 'string' && b.id ? b.id : `b${i}`,
|
|
45
|
+
type: typeof b.type === 'string' ? b.type : 'unknown',
|
|
46
|
+
hash: contentHash(stableStringify({ type: b.type, data: b.data })),
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Compute the `[blockId, ISO]` mtimes for `next`, carrying forward each block's
|
|
51
|
+
* timestamp from `prev` when its content is unchanged and stamping `nowIso` for
|
|
52
|
+
* new or changed blocks. The result preserves `next`'s block order. Pages with
|
|
53
|
+
* no `prev` (a fresh insert) get every block stamped `nowIso`.
|
|
54
|
+
*/
|
|
55
|
+
export function computeBlockMtimes(prev, next, nowIso) {
|
|
56
|
+
const prevBlocks = snapshotBlocks(prev);
|
|
57
|
+
const prevHash = new Map();
|
|
58
|
+
for (const b of prevBlocks)
|
|
59
|
+
prevHash.set(b.id, b.hash);
|
|
60
|
+
const prevMtime = new Map(prev?.mtimes ?? []);
|
|
61
|
+
return snapshotBlocks(next).map((b) => {
|
|
62
|
+
const unchanged = prevHash.get(b.id) === b.hash && prevMtime.has(b.id);
|
|
63
|
+
return [b.id, unchanged ? prevMtime.get(b.id) : nowIso];
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Return `next` with its `mtimes` stamped relative to `prev`. The server calls
|
|
68
|
+
* this on every content write so a page snapshot always describes when each of
|
|
69
|
+
* its blocks last changed. Idempotent when the document is unchanged: re-stamping
|
|
70
|
+
* an identical snapshot keeps every timestamp.
|
|
71
|
+
*/
|
|
72
|
+
export function stampSnapshotMtimes(prev, next, nowIso = new Date().toISOString()) {
|
|
73
|
+
return { ...next, mtimes: computeBlockMtimes(prev, next, nowIso) };
|
|
74
|
+
}
|
|
75
|
+
/** The most recent block mtime in a snapshot, or `null` if none are stamped. */
|
|
76
|
+
export function latestBlockMtime(data) {
|
|
77
|
+
let latest = null;
|
|
78
|
+
for (const [, iso] of data?.mtimes ?? []) {
|
|
79
|
+
if (latest === null || iso > latest)
|
|
80
|
+
latest = iso;
|
|
81
|
+
}
|
|
82
|
+
return latest;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=mtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mtime.js","sourceRoot":"","sources":["../src/mtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in **page properties** — structured metadata every page carries,
|
|
3
|
+
* modelled with the same {@link DatabaseProperty} vocabulary as databases so
|
|
4
|
+
* the two presentations are duals:
|
|
5
|
+
*
|
|
6
|
+
* - A standalone page shows them in a wiki-style *properties panel* under its
|
|
7
|
+
* title (Owner, Verification, Backlinks).
|
|
8
|
+
* - A collection of pages gathered into a database shows the very same values
|
|
9
|
+
* as *columns*, because the property ids below are stable and reserved — a
|
|
10
|
+
* column whose id is {@link OWNER_PROPERTY_ID} reads the identical
|
|
11
|
+
* `page.properties[OWNER_PROPERTY_ID]` the panel edits.
|
|
12
|
+
*
|
|
13
|
+
* Owner and Verification are stored on the page (`page.properties`). Backlinks
|
|
14
|
+
* are *computed* from the link graph (which pages mention this one), so they
|
|
15
|
+
* are never stored — see {@link extractMentionIds}.
|
|
16
|
+
*/
|
|
17
|
+
import type { PageSnapshot } from './types';
|
|
18
|
+
import type { DatabaseProperty } from './database';
|
|
19
|
+
/** Reserved, stable id for the Owner (person) property. */
|
|
20
|
+
export declare const OWNER_PROPERTY_ID = "sys_owner";
|
|
21
|
+
/** Reserved, stable id for the Verification property. */
|
|
22
|
+
export declare const VERIFICATION_PROPERTY_ID = "sys_verification";
|
|
23
|
+
/** Reserved, stable id for the Backlinks (computed) property. */
|
|
24
|
+
export declare const BACKLINKS_PROPERTY_ID = "sys_backlinks";
|
|
25
|
+
/**
|
|
26
|
+
* Reserved ids for a page's **appearance**, stored on `page.properties` so a
|
|
27
|
+
* page's theme / cover / typefaces travel with the document (sync across
|
|
28
|
+
* devices) rather than living only in the local browser. They are appearance
|
|
29
|
+
* metadata — not panel/column properties — so they are intentionally NOT part
|
|
30
|
+
* of {@link SYSTEM_PAGE_PROPERTIES}.
|
|
31
|
+
*/
|
|
32
|
+
export declare const THEME_PROPERTY_ID = "sys_theme";
|
|
33
|
+
export declare const COVER_PROPERTY_ID = "sys_cover";
|
|
34
|
+
export declare const FONTS_PROPERTY_ID = "sys_fonts";
|
|
35
|
+
/** The page's emoji icon (a string). Projected into {@link PageMeta} so the
|
|
36
|
+
* sidebar/tabs/mentions resolve it from the page list, not a per-page fetch. */
|
|
37
|
+
export declare const ICON_PROPERTY_ID = "sys_icon";
|
|
38
|
+
/** The page's full-width layout flag (a boolean; absent = centered column). */
|
|
39
|
+
export declare const FULLWIDTH_PROPERTY_ID = "sys_fullwidth";
|
|
40
|
+
/** The stored shape of a Verification property value. */
|
|
41
|
+
export interface VerificationValue {
|
|
42
|
+
verified: boolean;
|
|
43
|
+
/** Display name of who verified it; `null` when unknown. */
|
|
44
|
+
by?: string | null;
|
|
45
|
+
/** ISO timestamp the page was verified. */
|
|
46
|
+
at?: string | null;
|
|
47
|
+
/** ISO timestamp the verification lapses; absent/`null` = never expires. */
|
|
48
|
+
expiresAt?: string | null;
|
|
49
|
+
}
|
|
50
|
+
/** The built-in properties every page carries, in panel order. */
|
|
51
|
+
export declare const SYSTEM_PAGE_PROPERTIES: DatabaseProperty[];
|
|
52
|
+
/** True when a stored value represents a verified page (ignoring expiry). */
|
|
53
|
+
export declare function isVerified(value: unknown): boolean;
|
|
54
|
+
/** True when a verified page's verification has lapsed (expiry is in the past). */
|
|
55
|
+
export declare function verificationExpired(value: unknown, nowMs?: number): boolean;
|
|
56
|
+
/** True when a page is verified AND its verification has not lapsed. */
|
|
57
|
+
export declare function isVerificationActive(value: unknown, nowMs?: number): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* A verification value stamped as verified now (the caller supplies the time).
|
|
60
|
+
* `expiresAtIso` sets an expiry; omit/`null` for a verification that never
|
|
61
|
+
* lapses.
|
|
62
|
+
*/
|
|
63
|
+
export declare function makeVerification(by: string | null, atIso: string, expiresAtIso?: string | null): VerificationValue;
|
|
64
|
+
/**
|
|
65
|
+
* Pure: the set of page ids this snapshot links to, by scanning the EditorJS
|
|
66
|
+
* blocks for inline mention anchors (`<a … data-page-id="…">`). Order-preserving
|
|
67
|
+
* and de-duplicated — the out-edges of one page in the backlink graph.
|
|
68
|
+
*
|
|
69
|
+
* Works on both the raw HTML form (`data-page-id="id"`) and the JSON-escaped
|
|
70
|
+
* form (`data-page-id=\"id\"`) that appears once the document is serialised, so
|
|
71
|
+
* it can run over a parsed snapshot or a stringified one alike.
|
|
72
|
+
*/
|
|
73
|
+
/**
|
|
74
|
+
* Pure: does a page's stored `properties` reference `id`? True when any property
|
|
75
|
+
* value equals `id`, or `id` is an element of an array-valued property (a
|
|
76
|
+
* `relation`). This lets the backlink graph count relation links — set on a
|
|
77
|
+
* row's properties — alongside inline `@`-mentions in the document.
|
|
78
|
+
*/
|
|
79
|
+
export declare function propertiesReferencePage(properties: Record<string, unknown> | null | undefined, id: string): boolean;
|
|
80
|
+
export declare function extractMentionIds(snapshot: Pick<PageSnapshot, 'editorjs'>): string[];
|