rotion 3.6.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +39 -13
- package/README.md +45 -18
- package/dist/exporter/api.d.ts +11 -1
- package/dist/exporter/api.js +35 -30
- package/dist/exporter/api.js.map +1 -1
- package/dist/exporter/bin.d.ts +2 -0
- package/dist/exporter/bin.js +4 -0
- package/dist/exporter/bin.js.map +1 -0
- package/dist/exporter/blocks.js +355 -320
- package/dist/exporter/blocks.js.map +1 -1
- package/dist/exporter/breadcrumbs.js +3 -3
- package/dist/exporter/breadcrumbs.js.map +1 -1
- package/dist/exporter/cli.d.ts +16 -0
- package/dist/exporter/cli.js +126 -0
- package/dist/exporter/cli.js.map +1 -0
- package/dist/exporter/database.js +82 -75
- package/dist/exporter/database.js.map +1 -1
- package/dist/exporter/dependencies.d.ts +38 -0
- package/dist/exporter/dependencies.js +102 -0
- package/dist/exporter/dependencies.js.map +1 -0
- package/dist/exporter/failures.d.ts +52 -0
- package/dist/exporter/failures.js +105 -0
- package/dist/exporter/failures.js.map +1 -0
- package/dist/exporter/files.d.ts +21 -0
- package/dist/exporter/files.js +164 -94
- package/dist/exporter/files.js.map +1 -1
- package/dist/exporter/github.d.ts +8 -0
- package/dist/exporter/github.js +22 -5
- package/dist/exporter/github.js.map +1 -1
- package/dist/exporter/index.d.ts +4 -0
- package/dist/exporter/index.js +2 -0
- package/dist/exporter/index.js.map +1 -1
- package/dist/exporter/log.d.ts +6 -0
- package/dist/exporter/log.js +15 -0
- package/dist/exporter/log.js.map +1 -0
- package/dist/exporter/mutex.js +55 -16
- package/dist/exporter/mutex.js.map +1 -1
- package/dist/exporter/page.js +51 -35
- package/dist/exporter/page.js.map +1 -1
- package/dist/exporter/prune.d.ts +27 -0
- package/dist/exporter/prune.js +95 -0
- package/dist/exporter/prune.js.map +1 -0
- package/dist/exporter/types.d.ts +13 -0
- package/dist/exporter/variables.d.ts +61 -17
- package/dist/exporter/variables.js +86 -23
- package/dist/exporter/variables.js.map +1 -1
- package/dist/ui/components/Gallery/GalleryCard/options.d.ts +11 -0
- package/dist/ui/esm/components/Gallery/GalleryCard/GalleryCard.js +2 -17
- package/dist/ui/esm/components/Gallery/GalleryCard/GalleryCard.js.map +1 -1
- package/dist/ui/esm/components/Gallery/GalleryCard/options.js +27 -0
- package/dist/ui/esm/components/Gallery/GalleryCard/options.js.map +1 -0
- package/dist/ui/esm/components/Page/CodeBlock/Code/Code.js +5 -0
- package/dist/ui/esm/components/Page/CodeBlock/Code/Code.js.map +1 -1
- package/dist/ui/index-without-dark.css +1 -1
- package/dist/ui/index.css +1 -1
- package/package.json +30 -24
package/dist/exporter/blocks.js
CHANGED
|
@@ -1,373 +1,408 @@
|
|
|
1
1
|
import { reqAPIWithBackoff, reqAPIWithBackoffAndCache, notion, } from './api.js';
|
|
2
|
-
import {
|
|
2
|
+
import { config } from './variables.js';
|
|
3
3
|
import { FetchPage, getNotionIconUrl } from './page.js';
|
|
4
4
|
import { createDirWhenNotfound, saveImage, saveFile, readCache, writeCache, getHtmlMeta, getVideoHtml, getEmbedHtml, isEmpty, getVideoType } from './files.js';
|
|
5
5
|
import { FetchBreadcrumbs } from './breadcrumbs.js';
|
|
6
6
|
import { getIssueForLinkPreview, getRepoForLinkPreview } from './github.js';
|
|
7
|
+
import { collectFailures, reportFailure, reportExtraFailure } from './failures.js';
|
|
8
|
+
import { warn } from './log.js';
|
|
9
|
+
import { withFileLock } from './mutex.js';
|
|
10
|
+
import { collectDependencies, recordDependency, recordDependencies, isAnyDependencyChanged, editedTimeOf, pageOfBlock, } from './dependencies.js';
|
|
7
11
|
/**
|
|
8
12
|
* FetchBlocks retrieves page blocks and download images in from blocks.
|
|
9
13
|
* And create cache that includes filepath of downloaded images.
|
|
10
14
|
* The last_edited_time of 2nd args is for ROTION_INCREMENTAL_CACHE.
|
|
11
15
|
*/
|
|
12
16
|
export const FetchBlocks = async ({ block_id, last_edited_time }) => {
|
|
17
|
+
const { cacheDir, incrementalCache, debug } = config();
|
|
13
18
|
await createDirWhenNotfound(cacheDir);
|
|
14
19
|
const cacheFile = `${cacheDir}/notion.blocks.children.list-${block_id}`;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
const lockKey = `blocks-${block_id}`;
|
|
21
|
+
// Parallel build workers, or concurrent requests to a server, wait here
|
|
22
|
+
// for the one that fetches the blocks, and then read its cache.
|
|
23
|
+
return withFileLock(lockKey, async () => {
|
|
24
|
+
try {
|
|
25
|
+
const list = await readCache(cacheFile);
|
|
26
|
+
if (!isEmpty(list)) {
|
|
27
|
+
if (incrementalCache && last_edited_time === undefined && debug) {
|
|
20
28
|
console.log(`use cache in FetchBlocks(): ${cacheFile}, last_edited_time is required as a FetchBlocks() args when incremental cache`);
|
|
21
29
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
const sameEditedTime = !incrementalCache || last_edited_time === undefined || list.last_edited_time === last_edited_time;
|
|
31
|
+
// A page or database the blocks show may have been edited even though
|
|
32
|
+
// the page itself was not
|
|
33
|
+
if (sameEditedTime && !(incrementalCache && await isAnyDependencyChanged(list.dependencies))) {
|
|
34
|
+
if (debug) {
|
|
35
|
+
console.log(`use cache so same last-edited-time in FetchBlocks(): ${cacheFile}`);
|
|
36
|
+
}
|
|
37
|
+
recordDependencies(list.dependencies);
|
|
38
|
+
return list;
|
|
39
|
+
}
|
|
25
40
|
if (debug) {
|
|
26
|
-
|
|
41
|
+
const reason = sameEditedTime ? 'a page or database it shows was edited' : 'an old cache file was found';
|
|
42
|
+
console.log(`requesting to API because ${reason} in FetchBlocks(): ${cacheFile}`);
|
|
27
43
|
}
|
|
28
|
-
return list;
|
|
29
|
-
}
|
|
30
|
-
if (debug) {
|
|
31
|
-
console.log(`requesting to API because an old cache file was found in FetchBlocks(): ${cacheFile}`);
|
|
32
44
|
}
|
|
33
45
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/* not fatal */
|
|
37
|
-
}
|
|
38
|
-
let allres;
|
|
39
|
-
let res;
|
|
40
|
-
let params = { block_id, start_cursor: undefined };
|
|
41
|
-
while (true) {
|
|
42
|
-
if (res && res.next_cursor) {
|
|
43
|
-
params.start_cursor = res.next_cursor;
|
|
46
|
+
catch (_) {
|
|
47
|
+
/* not fatal */
|
|
44
48
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (res.next_cursor === null) {
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
// With the blocks api, you can get the last modified date of a block,
|
|
61
|
-
// but not the last modified date of all blocks. So extend the type and add it.
|
|
62
|
-
if (last_edited_time) {
|
|
63
|
-
allres.last_edited_time = last_edited_time;
|
|
64
|
-
}
|
|
65
|
-
for (const block of allres.results) {
|
|
66
|
-
try {
|
|
67
|
-
const { type } = block;
|
|
68
|
-
switch (type) {
|
|
69
|
-
case 'bookmark':
|
|
70
|
-
block.bookmark.site = await getHtmlMeta(block.bookmark.url);
|
|
71
|
-
break;
|
|
72
|
-
case 'bulleted_list_item':
|
|
73
|
-
if (block.has_children) {
|
|
74
|
-
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: block.last_edited_time });
|
|
75
|
-
}
|
|
76
|
-
break;
|
|
77
|
-
case 'breadcrumb':
|
|
78
|
-
block.list = await FetchBreadcrumbs({ id: block_id, type: 'page_id' });
|
|
79
|
-
break;
|
|
80
|
-
case 'callout':
|
|
81
|
-
if (block.callout.icon?.type === 'external') {
|
|
82
|
-
const iconUrl = block.callout.icon.external.url;
|
|
83
|
-
try {
|
|
84
|
-
const ipws = await saveImage(iconUrl, `block-${block.id}`);
|
|
85
|
-
block.callout.icon.src = ipws.path;
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
88
|
-
if (debug) {
|
|
89
|
-
console.log(`Failed to save callout icon: ${e}`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
else if (block.callout.icon?.type === 'icon') {
|
|
94
|
-
const url = getNotionIconUrl(block.callout.icon.icon);
|
|
95
|
-
try {
|
|
96
|
-
const ipws = await saveImage(url, `block-${block.id}`);
|
|
97
|
-
block.callout.icon = { type: 'external', external: { url }, src: ipws.path };
|
|
98
|
-
}
|
|
99
|
-
catch (e) {
|
|
100
|
-
if (debug) {
|
|
101
|
-
console.log(`Failed to save callout icon: ${e}`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
49
|
+
// A result that misses something because of a transient failure, such as
|
|
50
|
+
// a rate limit or a download that timed out, is not cached, so that the
|
|
51
|
+
// next call fetches it again instead of keeping the gap.
|
|
52
|
+
const { value: allres, complete } = await collectFailures(async () => {
|
|
53
|
+
const { value: allres, dependencies } = await collectDependencies(async () => {
|
|
54
|
+
let allres;
|
|
55
|
+
let res;
|
|
56
|
+
let params = { block_id, start_cursor: undefined };
|
|
57
|
+
while (true) {
|
|
58
|
+
if (res && res.next_cursor) {
|
|
59
|
+
params.start_cursor = res.next_cursor;
|
|
104
60
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
case 'child_database':
|
|
110
|
-
// if (block.has_children) {
|
|
111
|
-
const database_id = block.id;
|
|
112
|
-
block.database = await reqAPIWithBackoffAndCache({
|
|
113
|
-
name: 'notion.databases.retrieve',
|
|
114
|
-
func: notion.databases.retrieve,
|
|
115
|
-
args: { database_id },
|
|
116
|
-
count: 3,
|
|
61
|
+
res = await reqAPIWithBackoff({
|
|
62
|
+
func: notion().blocks.children.list,
|
|
63
|
+
args: params,
|
|
64
|
+
count: 3
|
|
117
65
|
});
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
case 'child_page':
|
|
121
|
-
block.page = await FetchPage({ page_id: block.id, last_edited_time: block.last_edited_time });
|
|
122
|
-
// Unnecessary?
|
|
123
|
-
// block.children = await FetchBlocks(block.id, block.last_edited_time)
|
|
124
|
-
break;
|
|
125
|
-
case 'column_list':
|
|
126
|
-
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: block.last_edited_time });
|
|
127
|
-
block.columns = [];
|
|
128
|
-
for (const b of block.children.results) {
|
|
129
|
-
block.columns.push(await FetchBlocks({ block_id: b.id, last_edited_time: block.last_edited_time }));
|
|
66
|
+
if (allres === undefined) {
|
|
67
|
+
allres = res;
|
|
130
68
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
block.embed.html = await getEmbedHtml(block);
|
|
134
|
-
break;
|
|
135
|
-
case 'image':
|
|
136
|
-
const { id, image } = block;
|
|
137
|
-
if (image !== undefined) {
|
|
138
|
-
const imageUrl = image.type === 'file' ? image.file.url : image.external.url;
|
|
139
|
-
try {
|
|
140
|
-
const ipws = await saveImage(imageUrl, `block-${id}`);
|
|
141
|
-
block.image.src = ipws.path;
|
|
142
|
-
if (ipws.width) {
|
|
143
|
-
block.image.width = ipws.width;
|
|
144
|
-
}
|
|
145
|
-
if (ipws.height) {
|
|
146
|
-
block.image.height = ipws.height;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
catch (e) {
|
|
150
|
-
if (debug) {
|
|
151
|
-
console.log(`Failed to save image: ${e}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
69
|
+
else {
|
|
70
|
+
allres.results.push(...res.results);
|
|
154
71
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (block.has_children) {
|
|
158
|
-
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: block.last_edited_time });
|
|
72
|
+
if (res.next_cursor === null) {
|
|
73
|
+
break;
|
|
159
74
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
75
|
+
}
|
|
76
|
+
// With the blocks api, you can get the last modified date of a block,
|
|
77
|
+
// but not the last modified date of all blocks. So extend the type and add it.
|
|
78
|
+
if (last_edited_time) {
|
|
79
|
+
allres.last_edited_time = last_edited_time;
|
|
80
|
+
}
|
|
81
|
+
// Editing a nested block does not update its parent block's last_edited_time,
|
|
82
|
+
// only the page's. So pass the page's last_edited_time down to nested blocks.
|
|
83
|
+
for (const block of allres.results) {
|
|
84
|
+
try {
|
|
85
|
+
const { type } = block;
|
|
86
|
+
switch (type) {
|
|
87
|
+
case 'bookmark':
|
|
88
|
+
block.bookmark.site = await getHtmlMeta(block.bookmark.url);
|
|
89
|
+
break;
|
|
90
|
+
case 'bulleted_list_item':
|
|
91
|
+
if (block.has_children) {
|
|
92
|
+
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
case 'breadcrumb':
|
|
96
|
+
block.list = await FetchBreadcrumbs({ id: block_id, type: 'page_id' });
|
|
97
|
+
break;
|
|
98
|
+
case 'callout':
|
|
99
|
+
if (block.callout.icon?.type === 'external') {
|
|
100
|
+
const iconUrl = block.callout.icon.external.url;
|
|
101
|
+
try {
|
|
102
|
+
const ipws = await saveImage(iconUrl, `block-${block.id}`);
|
|
103
|
+
block.callout.icon.src = ipws.path;
|
|
180
104
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
105
|
+
catch (e) {
|
|
106
|
+
reportFailure(`icon of callout block ${block.id}`, e);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (block.callout.icon?.type === 'icon') {
|
|
110
|
+
const url = getNotionIconUrl(block.callout.icon.icon);
|
|
111
|
+
try {
|
|
112
|
+
const ipws = await saveImage(url, `block-${block.id}`);
|
|
113
|
+
block.callout.icon = { type: 'external', external: { url }, src: ipws.path };
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
reportFailure(`icon of callout block ${block.id}`, e);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (block.has_children) {
|
|
120
|
+
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
case 'child_database':
|
|
124
|
+
// if (block.has_children) {
|
|
125
|
+
const database_id = block.id;
|
|
126
|
+
block.database = await reqAPIWithBackoffAndCache({
|
|
127
|
+
name: 'notion.databases.retrieve',
|
|
128
|
+
func: notion().databases.retrieve,
|
|
129
|
+
args: { database_id },
|
|
130
|
+
count: 3,
|
|
131
|
+
});
|
|
132
|
+
recordDependency({ type: 'database', id: database_id, last_edited_time: block.database.last_edited_time });
|
|
133
|
+
// }
|
|
134
|
+
break;
|
|
135
|
+
case 'child_page':
|
|
136
|
+
{
|
|
137
|
+
// The title and icon come from the child page; its own
|
|
138
|
+
// last_edited_time tells whether they changed
|
|
139
|
+
const edited = await editedTimeOf('page', block.id);
|
|
140
|
+
recordDependency({ type: 'page', id: block.id, last_edited_time: edited });
|
|
141
|
+
block.page = await FetchPage({ page_id: block.id, last_edited_time: edited });
|
|
142
|
+
}
|
|
143
|
+
// Unnecessary?
|
|
144
|
+
// block.children = await FetchBlocks(block.id, block.last_edited_time)
|
|
145
|
+
break;
|
|
146
|
+
case 'column_list':
|
|
147
|
+
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
|
|
148
|
+
block.columns = [];
|
|
149
|
+
for (const b of block.children.results) {
|
|
150
|
+
block.columns.push(await FetchBlocks({ block_id: b.id, last_edited_time: last_edited_time ?? b.last_edited_time }));
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
case 'embed':
|
|
154
|
+
block.embed.html = await getEmbedHtml(block);
|
|
155
|
+
break;
|
|
156
|
+
case 'image':
|
|
157
|
+
const { id, image } = block;
|
|
158
|
+
if (image !== undefined) {
|
|
159
|
+
const imageUrl = image.type === 'file' ? image.file.url : image.external.url;
|
|
160
|
+
try {
|
|
161
|
+
const ipws = await saveImage(imageUrl, `block-${id}`);
|
|
162
|
+
block.image.src = ipws.path;
|
|
163
|
+
if (ipws.width) {
|
|
164
|
+
block.image.width = ipws.width;
|
|
186
165
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
console.log(`Failed to save database icon: ${e}`);
|
|
190
|
-
}
|
|
166
|
+
if (ipws.height) {
|
|
167
|
+
block.image.height = ipws.height;
|
|
191
168
|
}
|
|
192
169
|
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
reportFailure(`image of block ${id}`, e);
|
|
172
|
+
}
|
|
193
173
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
174
|
+
break;
|
|
175
|
+
case 'numbered_list_item':
|
|
176
|
+
if (block.has_children) {
|
|
177
|
+
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
case 'paragraph':
|
|
181
|
+
for (const richText of block.paragraph.rich_text) {
|
|
182
|
+
if (richText.type !== 'mention') {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const mention = richText.mention;
|
|
186
|
+
switch (mention.type) {
|
|
187
|
+
case 'database':
|
|
188
|
+
try {
|
|
189
|
+
const database_id = mention.database.id;
|
|
190
|
+
const db = await reqAPIWithBackoffAndCache({
|
|
191
|
+
name: 'notion.databases.retrieve',
|
|
192
|
+
func: notion().databases.retrieve,
|
|
193
|
+
args: { database_id },
|
|
194
|
+
count: 3,
|
|
195
|
+
});
|
|
196
|
+
recordDependency({ type: 'database', id: database_id, last_edited_time: db.last_edited_time });
|
|
197
|
+
mention.database.name = db.title.map(v => v.plain_text).join('');
|
|
198
|
+
if (db.icon?.type === 'emoji') {
|
|
199
|
+
mention.database.icon = { type: 'emoji', emoji: db.icon.emoji };
|
|
200
|
+
}
|
|
201
|
+
else if (db.icon?.type === 'external' || db.icon?.type === 'file') {
|
|
202
|
+
const iconUrl = (db.icon.type === 'external') ? db.icon.external.url : db.icon.file.url;
|
|
203
|
+
try {
|
|
204
|
+
const ipws = await saveImage(iconUrl, `block-${block.id}`);
|
|
205
|
+
mention.database.icon = { type: db.icon.type, src: ipws.path, url: ipws.path };
|
|
206
|
+
}
|
|
207
|
+
catch (e) {
|
|
208
|
+
reportFailure(`icon of the database mentioned in block ${block.id}`, e);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (e) {
|
|
213
|
+
reportFailure(`database mentioned in block ${block.id}`, e);
|
|
214
|
+
mention.database.name = '--';
|
|
215
|
+
mention.database.icon = { type: 'emoji', emoji: '@' };
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
case 'page':
|
|
219
|
+
try {
|
|
220
|
+
const page_id = mention.page.id;
|
|
221
|
+
const page = await reqAPIWithBackoff({
|
|
222
|
+
func: notion().pages.retrieve,
|
|
223
|
+
args: { page_id },
|
|
224
|
+
count: 3,
|
|
225
|
+
});
|
|
226
|
+
recordDependency({ type: 'page', id: page_id, last_edited_time: page.last_edited_time });
|
|
227
|
+
for (const prop of Object.values(page.properties)) {
|
|
228
|
+
if (prop.type === 'title') {
|
|
229
|
+
mention.page.name = prop.title.map(v => v.plain_text).join('');
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (page.icon?.type === 'emoji') {
|
|
234
|
+
mention.page.icon = {
|
|
235
|
+
type: page.icon.type,
|
|
236
|
+
emoji: page.icon.emoji,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
else if (page.icon?.type === 'external' || page.icon?.type === 'file') {
|
|
240
|
+
const iconUrl = (page.icon.type === 'external') ? page.icon.external.url : page.icon.file.url;
|
|
241
|
+
try {
|
|
242
|
+
const ipws = await saveImage(iconUrl, `block-${block.id}`);
|
|
243
|
+
mention.page.icon = {
|
|
244
|
+
type: page.icon.type,
|
|
245
|
+
src: ipws.path,
|
|
246
|
+
url: ipws.path,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
catch (e) {
|
|
250
|
+
reportFailure(`icon of the page mentioned in block ${block.id}`, e);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch (e) {
|
|
255
|
+
reportFailure(`page mentioned in block ${block.id}`, e);
|
|
256
|
+
mention.page.name = '--';
|
|
257
|
+
mention.page.icon = { type: 'emoji', emoji: '@' };
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
197
260
|
}
|
|
198
|
-
mention.database.name = '--';
|
|
199
|
-
mention.database.icon = { type: 'emoji', emoji: '@' };
|
|
200
261
|
}
|
|
201
262
|
break;
|
|
202
|
-
case '
|
|
263
|
+
case 'file': {
|
|
264
|
+
const url = block.file.type === 'external' ? block.file.external.url : block.file.file.url;
|
|
203
265
|
try {
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
266
|
+
const { src, size } = await saveFile(url, `block-${block.id}`);
|
|
267
|
+
block.file.src = src;
|
|
268
|
+
block.file.size = size;
|
|
269
|
+
}
|
|
270
|
+
catch (e) {
|
|
271
|
+
reportFailure(`file of block ${block.id}`, e);
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case 'pdf': {
|
|
276
|
+
const url = block.pdf.type === 'external' ? block.pdf.external.url : block.pdf.file.url;
|
|
277
|
+
try {
|
|
278
|
+
const { src, size } = await saveFile(url, `block-${block.id}`);
|
|
279
|
+
block.pdf.src = src;
|
|
280
|
+
block.pdf.size = size;
|
|
281
|
+
}
|
|
282
|
+
catch (e) {
|
|
283
|
+
reportFailure(`PDF of block ${block.id}`, e);
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
case 'synced_block':
|
|
288
|
+
if (block.has_children) {
|
|
289
|
+
if (block.synced_block.synced_from === null) {
|
|
290
|
+
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
|
|
291
|
+
}
|
|
292
|
+
else if (block.synced_block.synced_from.type === 'block_id') {
|
|
293
|
+
const block_id = block.synced_block.synced_from.block_id;
|
|
294
|
+
// The source is edited in its own page, which does not change
|
|
295
|
+
// the last_edited_time of this one. Cache its blocks with the
|
|
296
|
+
// time of that page, the same key the page itself uses.
|
|
297
|
+
const sourcePage = await pageOfBlock(block_id);
|
|
298
|
+
let sourceEdited;
|
|
299
|
+
if (sourcePage !== undefined) {
|
|
300
|
+
sourceEdited = await editedTimeOf('page', sourcePage);
|
|
301
|
+
recordDependency({ type: 'page', id: sourcePage, last_edited_time: sourceEdited });
|
|
214
302
|
}
|
|
303
|
+
block.children = await FetchBlocks({ block_id, last_edited_time: sourceEdited ?? last_edited_time ?? block.last_edited_time });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
break;
|
|
307
|
+
case 'table':
|
|
308
|
+
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
|
|
309
|
+
break;
|
|
310
|
+
case 'toggle':
|
|
311
|
+
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
|
|
312
|
+
break;
|
|
313
|
+
case 'video':
|
|
314
|
+
if (block.video.type === 'file') {
|
|
315
|
+
try {
|
|
316
|
+
const { src } = await saveFile(block.video.file.url, `block-${block.id}`);
|
|
317
|
+
block.video.src = src;
|
|
318
|
+
block.video.videoType = getVideoType(src);
|
|
215
319
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
type: page.icon.type,
|
|
219
|
-
emoji: page.icon.emoji,
|
|
220
|
-
};
|
|
320
|
+
catch (e) {
|
|
321
|
+
reportFailure(`video of block ${block.id}`, e);
|
|
221
322
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
323
|
+
}
|
|
324
|
+
else if (block.video.type === 'external') {
|
|
325
|
+
block.video.html = await getVideoHtml(block);
|
|
326
|
+
}
|
|
327
|
+
break;
|
|
328
|
+
case 'link_preview':
|
|
329
|
+
const { url } = block.link_preview;
|
|
330
|
+
if (url.includes('figma.com')) {
|
|
331
|
+
// https://www.figma.com/file/GfHZFRpWGd0QXsRg9igdw8/Google-Material-Design?node-id=0%3A1
|
|
332
|
+
block.link_preview.figma = {
|
|
333
|
+
html: `<iframe style="border: 1px solid rgba(0, 0, 0, 0.1);" width="100%" height="450" src="https://www.figma.com/embed?embed_host=share&url=${encodeURI(url)}" allowfullscreen></iframe>`
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
else if (url.includes('github.com')) {
|
|
337
|
+
// Like bookmarks and embeds, the preview comes from a third party,
|
|
338
|
+
// so GitHub being unavailable does not keep the whole page out of
|
|
339
|
+
// the cache.
|
|
340
|
+
try {
|
|
341
|
+
const u = new URL(url).pathname;
|
|
342
|
+
if (u.split('/').length > 1) {
|
|
343
|
+
const [owner, repo, type, num] = u.split('/').slice(1);
|
|
344
|
+
if (owner && repo && (type === 'issues' || type === 'pull') && num) {
|
|
345
|
+
const res = await getIssueForLinkPreview({ owner, repo, number: num });
|
|
346
|
+
block.link_preview.github = {
|
|
347
|
+
type: 'issue',
|
|
348
|
+
issue: res,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
else if (owner && repo) {
|
|
352
|
+
const res = await getRepoForLinkPreview({ owner, repo });
|
|
353
|
+
block.link_preview.github = {
|
|
354
|
+
type: 'repo',
|
|
355
|
+
repo: res,
|
|
356
|
+
};
|
|
235
357
|
}
|
|
236
358
|
}
|
|
237
359
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (debug) {
|
|
241
|
-
console.log(`page mention is unsupported ${block.type}`, block, e);
|
|
360
|
+
catch (e) {
|
|
361
|
+
reportExtraFailure(`GitHub preview of block ${block.id}`, e);
|
|
242
362
|
}
|
|
243
|
-
|
|
244
|
-
|
|
363
|
+
}
|
|
364
|
+
break;
|
|
365
|
+
case 'audio':
|
|
366
|
+
case 'code':
|
|
367
|
+
case 'column':
|
|
368
|
+
case 'divider':
|
|
369
|
+
case 'equation':
|
|
370
|
+
case 'heading_1':
|
|
371
|
+
case 'heading_2':
|
|
372
|
+
case 'heading_3':
|
|
373
|
+
case 'link_to_page':
|
|
374
|
+
case 'quote':
|
|
375
|
+
case 'table_of_contents':
|
|
376
|
+
case 'table_row':
|
|
377
|
+
case 'template':
|
|
378
|
+
case 'to_do':
|
|
379
|
+
case 'unsupported':
|
|
380
|
+
break;
|
|
381
|
+
default:
|
|
382
|
+
if (debug) {
|
|
383
|
+
console.log(`unknown block error`, block);
|
|
245
384
|
}
|
|
246
385
|
break;
|
|
247
386
|
}
|
|
248
387
|
}
|
|
249
|
-
break;
|
|
250
|
-
case 'file': {
|
|
251
|
-
const url = block.file.type === 'external' ? block.file.external.url : block.file.file.url;
|
|
252
|
-
try {
|
|
253
|
-
const { src, size } = await saveFile(url, `block-${block.id}`);
|
|
254
|
-
block.file.src = src;
|
|
255
|
-
block.file.size = size;
|
|
256
|
-
}
|
|
257
|
-
catch (e) {
|
|
258
|
-
if (debug) {
|
|
259
|
-
console.log(`Failed to save file: ${e}`);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
break;
|
|
263
|
-
}
|
|
264
|
-
case 'pdf': {
|
|
265
|
-
const url = block.pdf.type === 'external' ? block.pdf.external.url : block.pdf.file.url;
|
|
266
|
-
try {
|
|
267
|
-
const { src, size } = await saveFile(url, `block-${block.id}`);
|
|
268
|
-
block.pdf.src = src;
|
|
269
|
-
block.pdf.size = size;
|
|
270
|
-
}
|
|
271
388
|
catch (e) {
|
|
272
|
-
|
|
273
|
-
console.log(`Failed to save PDF: ${e}`);
|
|
274
|
-
}
|
|
389
|
+
reportFailure(`contents of ${block.type} block ${block.id}`, e);
|
|
275
390
|
}
|
|
276
|
-
break;
|
|
277
391
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
else if (block.synced_block.synced_from.type === 'block_id') {
|
|
284
|
-
const block_id = block.synced_block.synced_from.block_id;
|
|
285
|
-
block.children = await FetchBlocks({ block_id, last_edited_time: block.last_edited_time });
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
break;
|
|
289
|
-
case 'table':
|
|
290
|
-
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: block.last_edited_time });
|
|
291
|
-
break;
|
|
292
|
-
case 'toggle':
|
|
293
|
-
block.children = await FetchBlocks({ block_id: block.id, last_edited_time: block.last_edited_time });
|
|
294
|
-
break;
|
|
295
|
-
case 'video':
|
|
296
|
-
if (block.video.type === 'file') {
|
|
297
|
-
try {
|
|
298
|
-
const { src } = await saveFile(block.video.file.url, `block-${block.id}`);
|
|
299
|
-
block.video.src = src;
|
|
300
|
-
block.video.videoType = getVideoType(src);
|
|
301
|
-
}
|
|
302
|
-
catch (e) {
|
|
303
|
-
if (debug) {
|
|
304
|
-
console.log(`Failed to save video: ${e}`);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
else if (block.video.type === 'external') {
|
|
309
|
-
block.video.html = await getVideoHtml(block);
|
|
310
|
-
}
|
|
311
|
-
break;
|
|
312
|
-
case 'link_preview':
|
|
313
|
-
const { url } = block.link_preview;
|
|
314
|
-
if (url.includes('figma.com')) {
|
|
315
|
-
// https://www.figma.com/file/GfHZFRpWGd0QXsRg9igdw8/Google-Material-Design?node-id=0%3A1
|
|
316
|
-
block.link_preview.figma = {
|
|
317
|
-
html: `<iframe style="border: 1px solid rgba(0, 0, 0, 0.1);" width="100%" height="450" src="https://www.figma.com/embed?embed_host=share&url=${encodeURI(url)}" allowfullscreen></iframe>`
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
else if (url.includes('github.com')) {
|
|
321
|
-
const u = new URL(url).pathname;
|
|
322
|
-
if (u.split('/').length > 1) {
|
|
323
|
-
const [owner, repo, type, num] = u.split('/').slice(1);
|
|
324
|
-
if (owner && repo && (type === 'issues' || type === 'pull') && num) {
|
|
325
|
-
const res = await getIssueForLinkPreview({ owner, repo, number: num });
|
|
326
|
-
block.link_preview.github = {
|
|
327
|
-
type: 'issue',
|
|
328
|
-
issue: res,
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
else if (owner && repo) {
|
|
332
|
-
const res = await getRepoForLinkPreview({ owner, repo });
|
|
333
|
-
block.link_preview.github = {
|
|
334
|
-
type: 'repo',
|
|
335
|
-
repo: res,
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
break;
|
|
341
|
-
case 'audio':
|
|
342
|
-
case 'code':
|
|
343
|
-
case 'column':
|
|
344
|
-
case 'divider':
|
|
345
|
-
case 'equation':
|
|
346
|
-
case 'heading_1':
|
|
347
|
-
case 'heading_2':
|
|
348
|
-
case 'heading_3':
|
|
349
|
-
case 'link_to_page':
|
|
350
|
-
case 'quote':
|
|
351
|
-
case 'table_of_contents':
|
|
352
|
-
case 'table_row':
|
|
353
|
-
case 'template':
|
|
354
|
-
case 'to_do':
|
|
355
|
-
case 'unsupported':
|
|
356
|
-
break;
|
|
357
|
-
default:
|
|
358
|
-
if (debug) {
|
|
359
|
-
console.log(`unknown block error`, block);
|
|
360
|
-
}
|
|
361
|
-
break;
|
|
392
|
+
return allres;
|
|
393
|
+
});
|
|
394
|
+
if (dependencies.length > 0) {
|
|
395
|
+
allres.dependencies = dependencies;
|
|
362
396
|
}
|
|
397
|
+
return allres;
|
|
398
|
+
});
|
|
399
|
+
if (complete) {
|
|
400
|
+
await writeCache(cacheFile, allres);
|
|
363
401
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
console.log(`error for ${block.type} contents get`, block, e);
|
|
367
|
-
}
|
|
402
|
+
else {
|
|
403
|
+
warn(`not caching the blocks of ${block_id} because of a transient failure; they are fetched again on the next call`);
|
|
368
404
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
return allres;
|
|
405
|
+
return allres;
|
|
406
|
+
});
|
|
372
407
|
};
|
|
373
408
|
//# sourceMappingURL=blocks.js.map
|