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.
Files changed (56) hide show
  1. package/README.ja.md +39 -13
  2. package/README.md +45 -18
  3. package/dist/exporter/api.d.ts +11 -1
  4. package/dist/exporter/api.js +35 -30
  5. package/dist/exporter/api.js.map +1 -1
  6. package/dist/exporter/bin.d.ts +2 -0
  7. package/dist/exporter/bin.js +4 -0
  8. package/dist/exporter/bin.js.map +1 -0
  9. package/dist/exporter/blocks.js +355 -320
  10. package/dist/exporter/blocks.js.map +1 -1
  11. package/dist/exporter/breadcrumbs.js +3 -3
  12. package/dist/exporter/breadcrumbs.js.map +1 -1
  13. package/dist/exporter/cli.d.ts +16 -0
  14. package/dist/exporter/cli.js +126 -0
  15. package/dist/exporter/cli.js.map +1 -0
  16. package/dist/exporter/database.js +82 -75
  17. package/dist/exporter/database.js.map +1 -1
  18. package/dist/exporter/dependencies.d.ts +38 -0
  19. package/dist/exporter/dependencies.js +102 -0
  20. package/dist/exporter/dependencies.js.map +1 -0
  21. package/dist/exporter/failures.d.ts +52 -0
  22. package/dist/exporter/failures.js +105 -0
  23. package/dist/exporter/failures.js.map +1 -0
  24. package/dist/exporter/files.d.ts +21 -0
  25. package/dist/exporter/files.js +164 -94
  26. package/dist/exporter/files.js.map +1 -1
  27. package/dist/exporter/github.d.ts +8 -0
  28. package/dist/exporter/github.js +22 -5
  29. package/dist/exporter/github.js.map +1 -1
  30. package/dist/exporter/index.d.ts +4 -0
  31. package/dist/exporter/index.js +2 -0
  32. package/dist/exporter/index.js.map +1 -1
  33. package/dist/exporter/log.d.ts +6 -0
  34. package/dist/exporter/log.js +15 -0
  35. package/dist/exporter/log.js.map +1 -0
  36. package/dist/exporter/mutex.js +55 -16
  37. package/dist/exporter/mutex.js.map +1 -1
  38. package/dist/exporter/page.js +51 -35
  39. package/dist/exporter/page.js.map +1 -1
  40. package/dist/exporter/prune.d.ts +27 -0
  41. package/dist/exporter/prune.js +95 -0
  42. package/dist/exporter/prune.js.map +1 -0
  43. package/dist/exporter/types.d.ts +13 -0
  44. package/dist/exporter/variables.d.ts +61 -17
  45. package/dist/exporter/variables.js +86 -23
  46. package/dist/exporter/variables.js.map +1 -1
  47. package/dist/ui/components/Gallery/GalleryCard/options.d.ts +11 -0
  48. package/dist/ui/esm/components/Gallery/GalleryCard/GalleryCard.js +2 -17
  49. package/dist/ui/esm/components/Gallery/GalleryCard/GalleryCard.js.map +1 -1
  50. package/dist/ui/esm/components/Gallery/GalleryCard/options.js +27 -0
  51. package/dist/ui/esm/components/Gallery/GalleryCard/options.js.map +1 -0
  52. package/dist/ui/esm/components/Page/CodeBlock/Code/Code.js +5 -0
  53. package/dist/ui/esm/components/Page/CodeBlock/Code/Code.js.map +1 -1
  54. package/dist/ui/index-without-dark.css +1 -1
  55. package/dist/ui/index.css +1 -1
  56. package/package.json +30 -24
@@ -1,373 +1,408 @@
1
1
  import { reqAPIWithBackoff, reqAPIWithBackoffAndCache, notion, } from './api.js';
2
- import { cacheDir, incrementalCache, debug, } from './variables.js';
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
- try {
16
- const list = await readCache(cacheFile);
17
- if (!isEmpty(list)) {
18
- if (incrementalCache && last_edited_time === undefined) {
19
- if (debug) {
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
- return list;
23
- }
24
- if (!incrementalCache || list.last_edited_time === last_edited_time) {
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
- console.log(`use cache so same last-edited-time in FetchBlocks(): ${cacheFile}`);
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
- catch (_) {
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
- res = await reqAPIWithBackoff({
46
- func: notion.blocks.children.list,
47
- args: params,
48
- count: 3
49
- });
50
- if (allres === undefined) {
51
- allres = res;
52
- }
53
- else {
54
- allres.results.push(...res.results);
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
- if (block.has_children) {
106
- block.children = await FetchBlocks({ block_id: block.id, last_edited_time: block.last_edited_time });
107
- }
108
- break;
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
- break;
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
- break;
132
- case 'embed':
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
- break;
156
- case 'numbered_list_item':
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
- break;
161
- case 'paragraph':
162
- for (const richText of block.paragraph.rich_text) {
163
- if (richText.type !== 'mention') {
164
- continue;
165
- }
166
- const mention = richText.mention;
167
- switch (mention.type) {
168
- case 'database':
169
- try {
170
- const database_id = mention.database.id;
171
- const db = await reqAPIWithBackoffAndCache({
172
- name: 'notion.databases.retrieve',
173
- func: notion.databases.retrieve,
174
- args: { database_id },
175
- count: 3,
176
- });
177
- mention.database.name = db.title.map(v => v.plain_text).join('');
178
- if (db.icon?.type === 'emoji') {
179
- mention.database.icon = { type: 'emoji', emoji: db.icon.emoji };
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
- else if (db.icon?.type === 'external' || db.icon?.type === 'file') {
182
- const iconUrl = (db.icon.type === 'external') ? db.icon.external.url : db.icon.file.url;
183
- try {
184
- const ipws = await saveImage(iconUrl, `block-${block.id}`);
185
- mention.database.icon = { type: db.icon.type, src: ipws.path, url: ipws.path };
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
- catch (e) {
188
- if (debug) {
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
- catch (e) {
195
- if (debug) {
196
- console.log(`database view mention is unsupported ${block.type}`, block, e);
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 'page':
263
+ case 'file': {
264
+ const url = block.file.type === 'external' ? block.file.external.url : block.file.file.url;
203
265
  try {
204
- const page_id = mention.page.id;
205
- const page = await reqAPIWithBackoff({
206
- func: notion.pages.retrieve,
207
- args: { page_id },
208
- count: 3,
209
- });
210
- for (const prop of Object.values(page.properties)) {
211
- if (prop.type === 'title') {
212
- mention.page.name = prop.title.map(v => v.plain_text).join('');
213
- break;
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
- if (page.icon?.type === 'emoji') {
217
- mention.page.icon = {
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
- else if (page.icon?.type === 'external' || page.icon?.type === 'file') {
223
- const iconUrl = (page.icon.type === 'external') ? page.icon.external.url : page.icon.file.url;
224
- try {
225
- const ipws = await saveImage(iconUrl, `block-${block.id}`);
226
- mention.page.icon = {
227
- type: page.icon.type,
228
- src: ipws.path,
229
- url: ipws.path,
230
- };
231
- }
232
- catch (e) {
233
- if (debug) {
234
- console.log(`Failed to save page icon: ${e}`);
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
- catch (e) {
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
- mention.page.name = '--';
244
- mention.page.icon = { type: 'emoji', emoji: '@' };
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
- if (debug) {
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
- case 'synced_block':
279
- if (block.has_children) {
280
- if (block.synced_block.synced_from === null) {
281
- block.children = await FetchBlocks({ block_id: block.id, last_edited_time: block.last_edited_time });
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
- catch (e) {
365
- if (debug) {
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
- await writeCache(cacheFile, allres);
371
- return allres;
405
+ return allres;
406
+ });
372
407
  };
373
408
  //# sourceMappingURL=blocks.js.map