@book.dev/sdk 1.66.0 → 1.68.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/dist/authors.d.ts +31 -0
- package/dist/authors.js +49 -19
- package/dist/authors.js.map +1 -1
- package/dist/backup.d.ts +5 -3
- package/dist/backup.js +11 -3
- package/dist/backup.js.map +1 -1
- package/dist/client.d.ts +149 -0
- package/dist/client.js +351 -21
- package/dist/client.js.map +1 -1
- package/dist/forwarding/tunnelClient.js +9 -0
- package/dist/forwarding/tunnelClient.js.map +1 -1
- package/dist/identity.d.ts +27 -0
- package/dist/identity.js +36 -0
- package/dist/identity.js.map +1 -1
- package/dist/import.d.ts +265 -0
- package/dist/import.js +312 -0
- package/dist/import.js.map +1 -0
- package/dist/importAssets.d.ts +167 -0
- package/dist/importAssets.js +332 -0
- package/dist/importAssets.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/markdownImport.d.ts +79 -0
- package/dist/markdownImport.js +348 -0
- package/dist/markdownImport.js.map +1 -0
- package/dist/notionImport.d.ts +58 -0
- package/dist/notionImport.js +539 -0
- package/dist/notionImport.js.map +1 -0
- package/dist/routes.d.ts +47 -0
- package/dist/routes.js +48 -0
- package/dist/routes.js.map +1 -1
- package/dist/sampleDocument.d.ts +6 -4
- package/dist/sampleDocument.js +12 -6
- package/dist/sampleDocument.js.map +1 -1
- package/dist/templates.d.ts +3 -2
- package/dist/templates.js +8 -6
- package/dist/templates.js.map +1 -1
- package/dist/types.d.ts +5 -4
- package/dist/types.js +4 -3
- package/dist/types.js.map +1 -1
- package/package.json +11 -5
package/dist/import.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The **format-agnostic import core** — the spine every importer (Markdown,
|
|
3
|
+
* HTML, Notion, …) targets. A parser's only job is to turn its source format
|
|
4
|
+
* into an {@link ImportedDoc}; this module then writes that IR into the
|
|
5
|
+
* workspace through the *existing* data paths, so importers never touch the
|
|
6
|
+
* store, the CRDT, or the wire protocol directly.
|
|
7
|
+
*
|
|
8
|
+
* Two writers, picked by the document's size/shape (see {@link chooseStrategy}):
|
|
9
|
+
*
|
|
10
|
+
* - **Strategy A — {@link writeViaCreateApis}** drives `savePage` /
|
|
11
|
+
* `createDatabase` / `createRow` directly, exactly like the template gallery
|
|
12
|
+
* (see `templates.ts`). Best for a single page / small tree: it streams
|
|
13
|
+
* creates and resolves parent links as it goes, with no whole-bundle staging.
|
|
14
|
+
*
|
|
15
|
+
* - **Strategy B — {@link writeViaBundle}** maps the whole IR into a backup
|
|
16
|
+
* bundle (`{pages, databases}` native records) and hands it to
|
|
17
|
+
* `importSpace(…, mode:'copy')`. That inherits — for free — the server's
|
|
18
|
+
* id-remap, `@`-mention link-rewrite, name de-dup, and idempotent-replay
|
|
19
|
+
* machinery (`remapBundle` / `importBundle`), plus the Restore dialog on the
|
|
20
|
+
* UI side. Best for a multi-page / database tree.
|
|
21
|
+
*
|
|
22
|
+
* **Assets (v1 decision).** A real asset store is a separate epic; until then an
|
|
23
|
+
* image becomes a *placeholder* that preserves the original `ref` + `alt` rather
|
|
24
|
+
* than being silently dropped — {@link imagePlaceholderBlock} for body content,
|
|
25
|
+
* {@link imagePlaceholderCell} for a database cell. The "never silently drop
|
|
26
|
+
* content" guarantee that the inbound HTML/EditorJS converters follow (see
|
|
27
|
+
* `ui/blockeditor/model.ts`) holds end-to-end through import.
|
|
28
|
+
*
|
|
29
|
+
* **Why the SDK.** The writers speak `DataClient` and the backup bundle, both
|
|
30
|
+
* SDK-owned; the block payload is the editor's plain JSON projection, which the
|
|
31
|
+
* SDK already handles structurally (`content.ts`, `mtime.ts`). {@link
|
|
32
|
+
* ImportedBlock} is the structural mirror of `@book.dev/ui`'s `BlockJSON`: a
|
|
33
|
+
* parser's real `BlockJSON` (built with the UI's `htmlToBlocks` / `htmlToRuns`
|
|
34
|
+
* helpers) is assignable straight into this IR — without the SDK taking a UI
|
|
35
|
+
* dependency. The assignment is one-way *by design*: `ImportedBlock.id` is
|
|
36
|
+
* OPTIONAL (import omits ids; the editor mints them on load via its
|
|
37
|
+
* `jsonToNewBlock`), so the reverse — treating an `ImportedBlock` as a
|
|
38
|
+
* `BlockJSON` — intentionally does not hold.
|
|
39
|
+
*/
|
|
40
|
+
import { emptyPageSnapshot } from './types';
|
|
41
|
+
import { ICON_PROPERTY_ID } from './pageProperties';
|
|
42
|
+
import { contentHash } from './mtime';
|
|
43
|
+
// ── Image-placeholder shim (v1 assets decision) ──────────────────────────────
|
|
44
|
+
/**
|
|
45
|
+
* The `props.kind` marker on a placeholder block, and the `props` key the
|
|
46
|
+
* structured {@link ImportedAsset} is stashed under. A future real-asset epic
|
|
47
|
+
* can query for `props[IMAGE_PLACEHOLDER_PROP]` to find and rehydrate every
|
|
48
|
+
* placeholder a past import left behind.
|
|
49
|
+
*/
|
|
50
|
+
export const IMAGE_PLACEHOLDER_KIND = 'import-image-placeholder';
|
|
51
|
+
export const IMAGE_PLACEHOLDER_PROP = 'importedAsset';
|
|
52
|
+
/**
|
|
53
|
+
* Turn an image asset into a real, visible **callout** block that preserves the
|
|
54
|
+
* original ref (as a clickable link run) and the alt/title text — never a
|
|
55
|
+
* silent drop. The structured {@link ImportedAsset} is stashed in `props` so the
|
|
56
|
+
* asset can be rehydrated later. Mirrors the "honest visible marker" pattern the
|
|
57
|
+
* EditorJS migration uses for not-yet-supported blocks (`model.ts`). Pure: the
|
|
58
|
+
* id is derived from the asset, so the same image always yields the same block
|
|
59
|
+
* id (deterministic re-imports, no module-global counter).
|
|
60
|
+
*/
|
|
61
|
+
export function imagePlaceholderBlock(asset) {
|
|
62
|
+
const label = asset.alt?.trim() || asset.title?.trim() || asset.ref;
|
|
63
|
+
return {
|
|
64
|
+
id: `imp_img_${contentHash(`${asset.ref}|${asset.alt ?? ''}`)}`,
|
|
65
|
+
type: 'callout',
|
|
66
|
+
text: [{ t: '🖼 ' }, { t: label, a: { a: asset.ref } }],
|
|
67
|
+
props: {
|
|
68
|
+
variant: 'info',
|
|
69
|
+
[IMAGE_PLACEHOLDER_PROP]: {
|
|
70
|
+
kind: IMAGE_PLACEHOLDER_KIND,
|
|
71
|
+
assetKind: asset.kind,
|
|
72
|
+
ref: asset.ref,
|
|
73
|
+
...(asset.alt ? { alt: asset.alt } : {}),
|
|
74
|
+
...(asset.title ? { title: asset.title } : {}),
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The placeholder *cell* value for a `files` / `url` database property: the
|
|
81
|
+
* original ref, so the cell still renders and resolves the image (`firstImageUrl`
|
|
82
|
+
* / `coverImageUrl` read it). Alt, when present, is preserved at block
|
|
83
|
+
* granularity via {@link imagePlaceholderBlock} on the row's body.
|
|
84
|
+
*/
|
|
85
|
+
export function imagePlaceholderCell(asset) {
|
|
86
|
+
return asset.ref;
|
|
87
|
+
}
|
|
88
|
+
// ── Snapshot construction ────────────────────────────────────────────────────
|
|
89
|
+
/**
|
|
90
|
+
* Wrap a block list in a page snapshot the block editor reads — the same shape
|
|
91
|
+
* `templates.ts` ships (`editor:'blocks'`, blocks in `blockdoc.blocks`, NO CRDT
|
|
92
|
+
* `update`). The editor rebuilds the Y.Doc from the JSON projection on load
|
|
93
|
+
* (`decodeSnapshot` falls back to it), so importers never touch yjs.
|
|
94
|
+
*/
|
|
95
|
+
export function importedBlocksToSnapshot(blocks) {
|
|
96
|
+
return {
|
|
97
|
+
editorjs: { blocks: [] },
|
|
98
|
+
values: [],
|
|
99
|
+
names: [],
|
|
100
|
+
editor: 'blocks',
|
|
101
|
+
blockdoc: { blocks },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const rowSnapshot = (row) => row.blocks && row.blocks.length > 0 ? importedBlocksToSnapshot(row.blocks) : emptyPageSnapshot();
|
|
105
|
+
const pageIsSimple = (page) => !page.database && !(page.children && page.children.length > 0);
|
|
106
|
+
/**
|
|
107
|
+
* Pick a writer by the document's shape: a lone, childless, database-less page
|
|
108
|
+
* → **Strategy A** (stream creates); anything bigger — multiple pages, nested
|
|
109
|
+
* children, or a hosted database — → **Strategy B** (stage a bundle and let the
|
|
110
|
+
* server remap/rewrite/dedup the whole tree at once).
|
|
111
|
+
*/
|
|
112
|
+
export function chooseStrategy(doc) {
|
|
113
|
+
return doc.pages.length === 1 && pageIsSimple(doc.pages[0]) ? 'create' : 'bundle';
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Does this block tree hold an image placeholder (a callout carrying
|
|
117
|
+
* {@link IMAGE_PLACEHOLDER_PROP})? Used by the writers to record which landed
|
|
118
|
+
* pages the post-import rehydration pass must revisit (see
|
|
119
|
+
* {@link ImportWriteResult.placeholderPageIds}). Recurses container children.
|
|
120
|
+
*/
|
|
121
|
+
export function blocksHaveImagePlaceholder(blocks) {
|
|
122
|
+
for (const b of blocks ?? []) {
|
|
123
|
+
if (b.props?.[IMAGE_PLACEHOLDER_PROP])
|
|
124
|
+
return true;
|
|
125
|
+
if (b.children && blocksHaveImagePlaceholder(b.children))
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
/** How many `(imported)`-suffixed names to try before falling back to untitled. */
|
|
131
|
+
const NAME_SUFFIX_ATTEMPTS = 5;
|
|
132
|
+
/**
|
|
133
|
+
* Run a name-bearing create (page or row) with a retry ladder so an import
|
|
134
|
+
* **never hard-fails on a single record**. Names are not unique (server
|
|
135
|
+
* migration 0015), so the plain title normally lands on the first attempt; the
|
|
136
|
+
* `"<title> (imported)"` / numbered candidates remain as a retry path for
|
|
137
|
+
* transient failures — consistent with the server's copy-mode ` (imported)`
|
|
138
|
+
* suffix so Strategy A and B stay convergent — with an untitled record as the
|
|
139
|
+
* last resort. A genuine persistent failure surfaces once the untitled
|
|
140
|
+
* fallback also throws.
|
|
141
|
+
*/
|
|
142
|
+
async function createDeduped(title, create) {
|
|
143
|
+
const candidates = [];
|
|
144
|
+
if (title) {
|
|
145
|
+
candidates.push(title, `${title} (imported)`);
|
|
146
|
+
for (let n = 2; n <= NAME_SUFFIX_ATTEMPTS; n += 1)
|
|
147
|
+
candidates.push(`${title} (imported) ${n}`);
|
|
148
|
+
}
|
|
149
|
+
for (const name of candidates) {
|
|
150
|
+
try {
|
|
151
|
+
return await create(name);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// Name taken (or transient) — step the suffix and retry the next candidate.
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Empty title, or every suffix exhausted: land it untitled rather than abort.
|
|
158
|
+
return create(null);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* **Strategy A.** Write the tree by driving the create APIs directly, the way
|
|
162
|
+
* the template gallery does: each page is `savePage`d (parent resolved from the
|
|
163
|
+
* already-created ancestor), a hosted database is `createDatabase`d on it, and
|
|
164
|
+
* every row — including nested sub-items — is `createRow`d. Each create runs
|
|
165
|
+
* through the {@link createDeduped} retry ladder so one failing record degrades
|
|
166
|
+
* (suffixed, then untitled) instead of aborting the import. Returns the ids it
|
|
167
|
+
* minted, in creation order.
|
|
168
|
+
*/
|
|
169
|
+
export async function writeViaCreateApis(client, doc, opts = {}) {
|
|
170
|
+
const result = { strategy: 'create', pageIds: [], databaseIds: [], rowIds: [], placeholderPageIds: [] };
|
|
171
|
+
const writeRows = async (databaseId, rows, parentRowId) => {
|
|
172
|
+
for (const row of rows) {
|
|
173
|
+
const data = row.blocks && row.blocks.length > 0 ? importedBlocksToSnapshot(row.blocks) : undefined;
|
|
174
|
+
const stored = await createDeduped(row.title, (name) => client.createRow(databaseId, { name, properties: row.properties, data, parentId: parentRowId }));
|
|
175
|
+
result.rowIds.push(stored.id);
|
|
176
|
+
if (blocksHaveImagePlaceholder(row.blocks))
|
|
177
|
+
result.placeholderPageIds.push(stored.id);
|
|
178
|
+
if (row.children && row.children.length > 0)
|
|
179
|
+
await writeRows(databaseId, row.children, stored.id);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
const writePage = async (page, parentId) => {
|
|
183
|
+
const stored = await createDeduped(page.title, (name) => client.savePage({ name, data: importedBlocksToSnapshot(page.blocks), parentId }));
|
|
184
|
+
result.pageIds.push(stored.id);
|
|
185
|
+
if (blocksHaveImagePlaceholder(page.blocks))
|
|
186
|
+
result.placeholderPageIds.push(stored.id);
|
|
187
|
+
if (page.icon)
|
|
188
|
+
await client.setPageProperties(stored.id, { [ICON_PROPERTY_ID]: page.icon });
|
|
189
|
+
if (page.database) {
|
|
190
|
+
const db = await client.createDatabase({
|
|
191
|
+
pageId: stored.id,
|
|
192
|
+
name: page.database.name ?? page.title ?? null,
|
|
193
|
+
schema: page.database.schema,
|
|
194
|
+
});
|
|
195
|
+
result.databaseIds.push(db.id);
|
|
196
|
+
await writeRows(db.id, page.database.rows, null);
|
|
197
|
+
}
|
|
198
|
+
for (const child of page.children ?? [])
|
|
199
|
+
await writePage(child, stored.id);
|
|
200
|
+
};
|
|
201
|
+
for (const page of doc.pages)
|
|
202
|
+
await writePage(page, opts.parentId ?? null);
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Map an {@link ImportedDoc} to a copy-mode backup bundle: native `StoredPage` /
|
|
207
|
+
* `StoredDatabase` records with internally-consistent **synthetic** ids (the
|
|
208
|
+
* server re-keys every id on import). The host page's `hostedDatabaseId`, the
|
|
209
|
+
* database's `pageId`, each row's `databaseId`, and sub-item / child `parentId`
|
|
210
|
+
* links are all wired up so `remapBundle` can rewrite them as one graph. Pure —
|
|
211
|
+
* no client, no I/O — so it is directly unit-testable.
|
|
212
|
+
*/
|
|
213
|
+
export function buildImportBundle(doc, opts = {}) {
|
|
214
|
+
const now = opts.now ?? new Date().toISOString();
|
|
215
|
+
let counter = 0;
|
|
216
|
+
const newId = opts.newId ?? (() => `imp_${++counter}`);
|
|
217
|
+
const pages = [];
|
|
218
|
+
const databases = [];
|
|
219
|
+
const emitRows = (rows, databaseId, parentRowId) => {
|
|
220
|
+
for (const row of rows) {
|
|
221
|
+
// A parser may pin a stable id (so cross-page mentions resolve); else mint one.
|
|
222
|
+
const id = row.id ?? newId();
|
|
223
|
+
pages.push({
|
|
224
|
+
id,
|
|
225
|
+
name: row.title || null,
|
|
226
|
+
data: rowSnapshot(row),
|
|
227
|
+
hostedDatabaseId: null,
|
|
228
|
+
databaseId,
|
|
229
|
+
parentId: parentRowId,
|
|
230
|
+
properties: row.properties ?? {},
|
|
231
|
+
deletedAt: null,
|
|
232
|
+
createdAt: now,
|
|
233
|
+
updatedAt: now,
|
|
234
|
+
});
|
|
235
|
+
if (row.children && row.children.length > 0)
|
|
236
|
+
emitRows(row.children, databaseId, id);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
const emitPage = (page, parentId) => {
|
|
240
|
+
// A parser may pin a stable id (so cross-page mentions resolve); else mint one.
|
|
241
|
+
const id = page.id ?? newId();
|
|
242
|
+
const hostedDatabaseId = page.database ? newId() : null;
|
|
243
|
+
pages.push({
|
|
244
|
+
id,
|
|
245
|
+
name: page.title || null,
|
|
246
|
+
data: importedBlocksToSnapshot(page.blocks),
|
|
247
|
+
hostedDatabaseId,
|
|
248
|
+
databaseId: null,
|
|
249
|
+
parentId,
|
|
250
|
+
properties: page.icon ? { [ICON_PROPERTY_ID]: page.icon } : {},
|
|
251
|
+
deletedAt: null,
|
|
252
|
+
createdAt: now,
|
|
253
|
+
updatedAt: now,
|
|
254
|
+
});
|
|
255
|
+
if (page.database && hostedDatabaseId) {
|
|
256
|
+
databases.push({
|
|
257
|
+
id: hostedDatabaseId,
|
|
258
|
+
pageId: id,
|
|
259
|
+
name: page.database.name ?? page.title ?? null,
|
|
260
|
+
schema: page.database.schema,
|
|
261
|
+
createdAt: now,
|
|
262
|
+
updatedAt: now,
|
|
263
|
+
});
|
|
264
|
+
emitRows(page.database.rows, hostedDatabaseId, null);
|
|
265
|
+
}
|
|
266
|
+
for (const child of page.children ?? [])
|
|
267
|
+
emitPage(child, id);
|
|
268
|
+
};
|
|
269
|
+
// Bundle roots land at the top level: copy-mode remap nulls any parent not in
|
|
270
|
+
// the bundle, so an external `parentId` would be dropped anyway — don't set it.
|
|
271
|
+
for (const page of doc.pages)
|
|
272
|
+
emitPage(page, null);
|
|
273
|
+
return { pages, databases };
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* **Strategy B.** Build the copy-mode bundle ({@link buildImportBundle}) and
|
|
277
|
+
* hand it to `importSpace`, inheriting the server's id-remap, link-rewrite,
|
|
278
|
+
* name-dedup, and idempotent-replay handling. Surfaces the server's
|
|
279
|
+
* {@link ImportResult} (and its `idMap` as the new page ids).
|
|
280
|
+
*/
|
|
281
|
+
export async function writeViaBundle(client, doc, opts = {}) {
|
|
282
|
+
const { pages, databases } = buildImportBundle(doc, opts);
|
|
283
|
+
const req = { pages, databases, mode: 'copy' };
|
|
284
|
+
const importResult = await client.importSpace(req);
|
|
285
|
+
// Map the bundle pages that carry an image placeholder to their server-assigned
|
|
286
|
+
// ids (via the id-map) so the post-import rehydration pass revisits only those.
|
|
287
|
+
const placeholderPageIds = [];
|
|
288
|
+
for (const page of pages) {
|
|
289
|
+
const blocks = page.data.blockdoc?.blocks;
|
|
290
|
+
if (!blocksHaveImagePlaceholder(blocks))
|
|
291
|
+
continue;
|
|
292
|
+
const landed = importResult.idMap[page.id];
|
|
293
|
+
if (landed)
|
|
294
|
+
placeholderPageIds.push(landed);
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
strategy: 'bundle',
|
|
298
|
+
pageIds: Object.values(importResult.idMap),
|
|
299
|
+
databaseIds: [],
|
|
300
|
+
rowIds: [],
|
|
301
|
+
importResult,
|
|
302
|
+
placeholderPageIds,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Import a document, picking the writer by {@link chooseStrategy}. The single
|
|
307
|
+
* entry point a parser calls once it has built its {@link ImportedDoc}.
|
|
308
|
+
*/
|
|
309
|
+
export function importDoc(client, doc, opts = {}) {
|
|
310
|
+
return chooseStrategy(doc) === 'create' ? writeViaCreateApis(client, doc, opts) : writeViaBundle(client, doc, opts);
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../src/import.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **Import asset rehydration (Assets A4).** Turns the image *placeholders* the
|
|
3
|
+
* importers emit (see `import.ts` {@link imagePlaceholderBlock}) into real,
|
|
4
|
+
* rendering `image` blocks — the payoff of OB-ASSETS: an imported picture comes
|
|
5
|
+
* through for real, not as a callout.
|
|
6
|
+
*
|
|
7
|
+
* Two seams, because the bytes arrive two very different ways:
|
|
8
|
+
*
|
|
9
|
+
* - **URL / `data:` images (Markdown · HTML).** These already carry a loadable
|
|
10
|
+
* source, so no upload is needed — {@link rehydrateImageUrls} rewrites the
|
|
11
|
+
* placeholder to an `image` block whose `src` is the original URL (kept as a
|
|
12
|
+
* link) or the inline `data:` URL (which the editor's A2 on-load migration
|
|
13
|
+
* then stores). Pure, DOM-free, no client — runnable *before* the doc is
|
|
14
|
+
* landed, so the page lands already-correct.
|
|
15
|
+
*
|
|
16
|
+
* - **Embedded bytes (Notion zip · opt-in URL download).** A Notion export holds
|
|
17
|
+
* every image's bytes inside the zip; there is no URL to render. So — *after*
|
|
18
|
+
* the page is created (we need its id to ref the asset's read-gate) —
|
|
19
|
+
* {@link rehydrateStoredImages} resolves each placeholder's `ref` to bytes,
|
|
20
|
+
* `putAsset`s them, and rewrites the placeholder to an `image` block holding
|
|
21
|
+
* the returned `assetId`. Content-addressed dedup makes a re-import idempotent.
|
|
22
|
+
*
|
|
23
|
+
* **Never lose the reference.** If the bytes are missing, over the 10 MiB cap, or
|
|
24
|
+
* the upload fails, the block degrades rather than dropping: a loadable URL is
|
|
25
|
+
* preserved as a URL `image` block; anything else keeps its original placeholder
|
|
26
|
+
* (ref + alt intact). A big import's uploads are processed with bounded
|
|
27
|
+
* concurrency so the UI is never blocked, and only pages that actually contain a
|
|
28
|
+
* placeholder are re-read/re-saved (the writers report them in
|
|
29
|
+
* `ImportWriteResult.placeholderPageIds`).
|
|
30
|
+
*/
|
|
31
|
+
import { type ImportedBlock, type ImportedDoc } from './import';
|
|
32
|
+
import type { PageInput, StoredPage } from './types';
|
|
33
|
+
/** The `image` block type — the native picture block the editor ships (A0/A2). */
|
|
34
|
+
export declare const IMAGE_BLOCK_TYPE = "image";
|
|
35
|
+
/**
|
|
36
|
+
* Hard cap on an uploaded asset (10 MiB) — matches the server's asset bodyLimit
|
|
37
|
+
* (`app.ts` `ASSET_MAX_BYTES`). An import image over this stays a placeholder
|
|
38
|
+
* rather than failing the whole import.
|
|
39
|
+
*/
|
|
40
|
+
export declare const DEFAULT_MAX_ASSET_BYTES: number;
|
|
41
|
+
/** Is this an `http(s)` URL — a picture the `image` block can render from `src`? */
|
|
42
|
+
export declare const isHttpUrl: (s: string) => boolean;
|
|
43
|
+
/** Is this a `data:` URL — inline bytes the editor can render (and A2 migrates)? */
|
|
44
|
+
export declare const isDataUrlRef: (s: string) => boolean;
|
|
45
|
+
/** Guess an image mime from a ref's file extension (defaults to octet-stream). */
|
|
46
|
+
export declare function mimeFromRef(ref: string): string;
|
|
47
|
+
/** The structured asset preserved on a placeholder block (see `imagePlaceholderBlock`). */
|
|
48
|
+
interface PlaceholderMeta {
|
|
49
|
+
ref?: string;
|
|
50
|
+
alt?: string;
|
|
51
|
+
title?: string;
|
|
52
|
+
}
|
|
53
|
+
/** Read the {@link PlaceholderMeta} off a block, or `undefined` if it isn't a placeholder. */
|
|
54
|
+
export declare function imagePlaceholderMeta(block: ImportedBlock): PlaceholderMeta | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Rewrite an image *placeholder* into a real `image` block, carrying either the
|
|
57
|
+
* uploaded `assetId` or a renderable `src`. The placeholder's `alt` becomes the
|
|
58
|
+
* image alt, its `title` the caption; the deterministic block id is kept so the
|
|
59
|
+
* rewrite is stable across re-imports.
|
|
60
|
+
*/
|
|
61
|
+
export declare function importedImageBlock(placeholder: ImportedBlock, target: {
|
|
62
|
+
assetId: string;
|
|
63
|
+
} | {
|
|
64
|
+
src: string;
|
|
65
|
+
}): ImportedBlock;
|
|
66
|
+
/** Options for {@link rehydrateImageUrls}. */
|
|
67
|
+
export interface RehydrateUrlOptions {
|
|
68
|
+
/**
|
|
69
|
+
* Rewrite an `http(s)` image placeholder into a URL `image` block (default
|
|
70
|
+
* `true`). Set `false` to *keep* it a placeholder so a later
|
|
71
|
+
* {@link rehydrateStoredImages} pass can download the bytes into the store
|
|
72
|
+
* (the opt-in "download into workspace" flow).
|
|
73
|
+
*/
|
|
74
|
+
preserveHttpUrls?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Rewrite a `data:` placeholder into an inline `image` block when its decoded
|
|
77
|
+
* size is within this many bytes (default 10 MiB). An over-cap `data:` image
|
|
78
|
+
* stays a placeholder so the CRDT never carries a huge base64 blob.
|
|
79
|
+
*/
|
|
80
|
+
maxDataBytes?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* **Pass 1 (pure).** Rewrite every image placeholder whose ref is a directly
|
|
84
|
+
* loadable source — an `http(s)` URL (preserved as a link) or an in-cap `data:`
|
|
85
|
+
* URL — into a real `image` block, throughout the doc (pages, children, database
|
|
86
|
+
* rows). No client, no I/O: run this on the IR *before* landing it so the page
|
|
87
|
+
* arrives already carrying real images. Idempotent (an `image` block isn't a
|
|
88
|
+
* placeholder, so a re-run is a no-op).
|
|
89
|
+
*/
|
|
90
|
+
export declare function rehydrateImageUrls(doc: ImportedDoc, opts?: RehydrateUrlOptions): ImportedDoc;
|
|
91
|
+
/** An asset's raw bytes plus its mime, as resolved for upload. */
|
|
92
|
+
export interface AssetBytes {
|
|
93
|
+
bytes: Uint8Array;
|
|
94
|
+
mime: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Resolve a placeholder's `ref` to its bytes for upload, or `null` when the
|
|
98
|
+
* bytes are unavailable (missing / not fetchable). May be async (a network
|
|
99
|
+
* fetch). {@link notionAssetResolver} and {@link urlAssetResolver} build one.
|
|
100
|
+
*/
|
|
101
|
+
export type ImportAssetResolver = (ref: string) => AssetBytes | null | Promise<AssetBytes | null>;
|
|
102
|
+
/** The slice of the data client {@link rehydrateStoredImages} drives. */
|
|
103
|
+
export interface RehydrateStoredClient {
|
|
104
|
+
getPage(id: string): Promise<StoredPage | null>;
|
|
105
|
+
savePage(input: PageInput): Promise<StoredPage>;
|
|
106
|
+
putAsset(bytes: Uint8Array, mime: string, pageId: string): Promise<{
|
|
107
|
+
id: string;
|
|
108
|
+
}>;
|
|
109
|
+
}
|
|
110
|
+
/** Options for {@link rehydrateStoredImages}. */
|
|
111
|
+
export interface RehydrateStoredOptions {
|
|
112
|
+
/** Skip (keep as placeholder) any image whose bytes exceed this (default 10 MiB). */
|
|
113
|
+
maxAssetBytes?: number;
|
|
114
|
+
/**
|
|
115
|
+
* Max total in-flight uploads at once (default 4) — a single shared budget
|
|
116
|
+
* across every page, so an image-dense page can't fan out N uploads and flood
|
|
117
|
+
* the socket. Also caps how many pages are walked concurrently.
|
|
118
|
+
*/
|
|
119
|
+
concurrency?: number;
|
|
120
|
+
/** Progress callback: pages processed / total (so the UI can show a live count). */
|
|
121
|
+
onProgress?: (done: number, total: number) => void;
|
|
122
|
+
}
|
|
123
|
+
/** What a stored-image pass did, for honest reporting. */
|
|
124
|
+
export interface RehydrateStoredResult {
|
|
125
|
+
/** Placeholders uploaded to the store and rewritten to an `assetId` image block. */
|
|
126
|
+
uploaded: number;
|
|
127
|
+
/** Placeholders degraded to a URL `image` block (bytes unavailable but ref loadable). */
|
|
128
|
+
preservedUrls: number;
|
|
129
|
+
/** Placeholders left untouched (over-cap / no bytes / not loadable) — ref never lost. */
|
|
130
|
+
keptPlaceholders: number;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* **Pass 2 (post-import).** For each page id carrying an image placeholder (the
|
|
134
|
+
* writers report these in `ImportWriteResult.placeholderPageIds`), resolve every
|
|
135
|
+
* placeholder's bytes and — within the size cap — `putAsset` + rewrite it to an
|
|
136
|
+
* `assetId` `image` block, then re-save the page. A single shared limiter caps
|
|
137
|
+
* total in-flight uploads (across all pages and all images) so a large,
|
|
138
|
+
* image-dense import streams rather than flooding the socket. Degrades safely
|
|
139
|
+
* (never loses a ref); idempotent (a re-run finds real images, not placeholders,
|
|
140
|
+
* and content-addressed dedup returns the same `assetId`).
|
|
141
|
+
*/
|
|
142
|
+
export declare function rehydrateStoredImages(client: RehydrateStoredClient, pageIds: string[], resolve: ImportAssetResolver, opts?: RehydrateStoredOptions): Promise<RehydrateStoredResult>;
|
|
143
|
+
/**
|
|
144
|
+
* Build a resolver over a Notion export zip: a placeholder `ref` (rewritten by
|
|
145
|
+
* {@link notionExportToImportedDoc} to the absolute in-zip path) → its bytes +
|
|
146
|
+
* a mime guessed from the extension. Unzips lazily on first use (the import may
|
|
147
|
+
* land no images), and answers `null` for a ref with no matching entry.
|
|
148
|
+
*/
|
|
149
|
+
export declare function notionAssetResolver(zipBytes: Uint8Array): ImportAssetResolver;
|
|
150
|
+
/** The `fetch`-shaped surface {@link urlAssetResolver} needs (so it's testable with a fake). */
|
|
151
|
+
export type FetchLike = (url: string) => Promise<{
|
|
152
|
+
ok: boolean;
|
|
153
|
+
status: number;
|
|
154
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
155
|
+
headers: {
|
|
156
|
+
get(name: string): string | null;
|
|
157
|
+
};
|
|
158
|
+
}>;
|
|
159
|
+
/**
|
|
160
|
+
* Build a resolver that downloads an `http(s)` image `ref` into bytes — the
|
|
161
|
+
* opt-in "store a copy of each linked image" path. Uses the ambient `fetch` when
|
|
162
|
+
* no impl is passed. Answers `null` (→ the URL is preserved as a link) for a
|
|
163
|
+
* non-http ref, a non-OK response, an empty body, or any fetch error — a slow /
|
|
164
|
+
* failing / cross-origin link degrades rather than breaking the import.
|
|
165
|
+
*/
|
|
166
|
+
export declare function urlAssetResolver(fetchImpl?: FetchLike): ImportAssetResolver;
|
|
167
|
+
export {};
|