rotion 3.6.3 → 4.0.1

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 +20 -12
  2. package/README.md +26 -17
  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 -322
  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 +32 -25
@@ -1,375 +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
- // Editing a nested block does not update its parent block's last_edited_time,
66
- // only the page's. So pass the page's last_edited_time down to nested blocks.
67
- for (const block of allres.results) {
68
- try {
69
- const { type } = block;
70
- switch (type) {
71
- case 'bookmark':
72
- block.bookmark.site = await getHtmlMeta(block.bookmark.url);
73
- break;
74
- case 'bulleted_list_item':
75
- if (block.has_children) {
76
- block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
77
- }
78
- break;
79
- case 'breadcrumb':
80
- block.list = await FetchBreadcrumbs({ id: block_id, type: 'page_id' });
81
- break;
82
- case 'callout':
83
- if (block.callout.icon?.type === 'external') {
84
- const iconUrl = block.callout.icon.external.url;
85
- try {
86
- const ipws = await saveImage(iconUrl, `block-${block.id}`);
87
- block.callout.icon.src = ipws.path;
88
- }
89
- catch (e) {
90
- if (debug) {
91
- console.log(`Failed to save callout icon: ${e}`);
92
- }
93
- }
94
- }
95
- else if (block.callout.icon?.type === 'icon') {
96
- const url = getNotionIconUrl(block.callout.icon.icon);
97
- try {
98
- const ipws = await saveImage(url, `block-${block.id}`);
99
- block.callout.icon = { type: 'external', external: { url }, src: ipws.path };
100
- }
101
- catch (e) {
102
- if (debug) {
103
- console.log(`Failed to save callout icon: ${e}`);
104
- }
105
- }
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;
106
60
  }
107
- if (block.has_children) {
108
- block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
109
- }
110
- break;
111
- case 'child_database':
112
- // if (block.has_children) {
113
- const database_id = block.id;
114
- block.database = await reqAPIWithBackoffAndCache({
115
- name: 'notion.databases.retrieve',
116
- func: notion.databases.retrieve,
117
- args: { database_id },
118
- count: 3,
61
+ res = await reqAPIWithBackoff({
62
+ func: notion().blocks.children.list,
63
+ args: params,
64
+ count: 3
119
65
  });
120
- // }
121
- break;
122
- case 'child_page':
123
- block.page = await FetchPage({ page_id: block.id, last_edited_time: block.last_edited_time });
124
- // Unnecessary?
125
- // block.children = await FetchBlocks(block.id, block.last_edited_time)
126
- break;
127
- case 'column_list':
128
- block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
129
- block.columns = [];
130
- for (const b of block.children.results) {
131
- block.columns.push(await FetchBlocks({ block_id: b.id, last_edited_time: last_edited_time ?? b.last_edited_time }));
66
+ if (allres === undefined) {
67
+ allres = res;
132
68
  }
133
- break;
134
- case 'embed':
135
- block.embed.html = await getEmbedHtml(block);
136
- break;
137
- case 'image':
138
- const { id, image } = block;
139
- if (image !== undefined) {
140
- const imageUrl = image.type === 'file' ? image.file.url : image.external.url;
141
- try {
142
- const ipws = await saveImage(imageUrl, `block-${id}`);
143
- block.image.src = ipws.path;
144
- if (ipws.width) {
145
- block.image.width = ipws.width;
146
- }
147
- if (ipws.height) {
148
- block.image.height = ipws.height;
149
- }
150
- }
151
- catch (e) {
152
- if (debug) {
153
- console.log(`Failed to save image: ${e}`);
154
- }
155
- }
69
+ else {
70
+ allres.results.push(...res.results);
156
71
  }
157
- break;
158
- case 'numbered_list_item':
159
- if (block.has_children) {
160
- block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
72
+ if (res.next_cursor === null) {
73
+ break;
161
74
  }
162
- break;
163
- case 'paragraph':
164
- for (const richText of block.paragraph.rich_text) {
165
- if (richText.type !== 'mention') {
166
- continue;
167
- }
168
- const mention = richText.mention;
169
- switch (mention.type) {
170
- case 'database':
171
- try {
172
- const database_id = mention.database.id;
173
- const db = await reqAPIWithBackoffAndCache({
174
- name: 'notion.databases.retrieve',
175
- func: notion.databases.retrieve,
176
- args: { database_id },
177
- count: 3,
178
- });
179
- mention.database.name = db.title.map(v => v.plain_text).join('');
180
- if (db.icon?.type === 'emoji') {
181
- 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;
182
104
  }
183
- else if (db.icon?.type === 'external' || db.icon?.type === 'file') {
184
- const iconUrl = (db.icon.type === 'external') ? db.icon.external.url : db.icon.file.url;
185
- try {
186
- const ipws = await saveImage(iconUrl, `block-${block.id}`);
187
- 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;
188
165
  }
189
- catch (e) {
190
- if (debug) {
191
- console.log(`Failed to save database icon: ${e}`);
192
- }
166
+ if (ipws.height) {
167
+ block.image.height = ipws.height;
193
168
  }
194
169
  }
170
+ catch (e) {
171
+ reportFailure(`image of block ${id}`, e);
172
+ }
195
173
  }
196
- catch (e) {
197
- if (debug) {
198
- 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;
199
260
  }
200
- mention.database.name = '--';
201
- mention.database.icon = { type: 'emoji', emoji: '@' };
202
261
  }
203
262
  break;
204
- case 'page':
263
+ case 'file': {
264
+ const url = block.file.type === 'external' ? block.file.external.url : block.file.file.url;
205
265
  try {
206
- const page_id = mention.page.id;
207
- const page = await reqAPIWithBackoff({
208
- func: notion.pages.retrieve,
209
- args: { page_id },
210
- count: 3,
211
- });
212
- for (const prop of Object.values(page.properties)) {
213
- if (prop.type === 'title') {
214
- mention.page.name = prop.title.map(v => v.plain_text).join('');
215
- 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 });
216
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);
217
319
  }
218
- if (page.icon?.type === 'emoji') {
219
- mention.page.icon = {
220
- type: page.icon.type,
221
- emoji: page.icon.emoji,
222
- };
320
+ catch (e) {
321
+ reportFailure(`video of block ${block.id}`, e);
223
322
  }
224
- else if (page.icon?.type === 'external' || page.icon?.type === 'file') {
225
- const iconUrl = (page.icon.type === 'external') ? page.icon.external.url : page.icon.file.url;
226
- try {
227
- const ipws = await saveImage(iconUrl, `block-${block.id}`);
228
- mention.page.icon = {
229
- type: page.icon.type,
230
- src: ipws.path,
231
- url: ipws.path,
232
- };
233
- }
234
- catch (e) {
235
- if (debug) {
236
- 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
+ };
237
357
  }
238
358
  }
239
359
  }
240
- }
241
- catch (e) {
242
- if (debug) {
243
- console.log(`page mention is unsupported ${block.type}`, block, e);
360
+ catch (e) {
361
+ reportExtraFailure(`GitHub preview of block ${block.id}`, e);
244
362
  }
245
- mention.page.name = '--';
246
- 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);
247
384
  }
248
385
  break;
249
386
  }
250
387
  }
251
- break;
252
- case 'file': {
253
- const url = block.file.type === 'external' ? block.file.external.url : block.file.file.url;
254
- try {
255
- const { src, size } = await saveFile(url, `block-${block.id}`);
256
- block.file.src = src;
257
- block.file.size = size;
258
- }
259
- catch (e) {
260
- if (debug) {
261
- console.log(`Failed to save file: ${e}`);
262
- }
263
- }
264
- break;
265
- }
266
- case 'pdf': {
267
- const url = block.pdf.type === 'external' ? block.pdf.external.url : block.pdf.file.url;
268
- try {
269
- const { src, size } = await saveFile(url, `block-${block.id}`);
270
- block.pdf.src = src;
271
- block.pdf.size = size;
272
- }
273
388
  catch (e) {
274
- if (debug) {
275
- console.log(`Failed to save PDF: ${e}`);
276
- }
389
+ reportFailure(`contents of ${block.type} block ${block.id}`, e);
277
390
  }
278
- break;
279
391
  }
280
- case 'synced_block':
281
- if (block.has_children) {
282
- if (block.synced_block.synced_from === null) {
283
- block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
284
- }
285
- else if (block.synced_block.synced_from.type === 'block_id') {
286
- const block_id = block.synced_block.synced_from.block_id;
287
- block.children = await FetchBlocks({ block_id, last_edited_time: last_edited_time ?? block.last_edited_time });
288
- }
289
- }
290
- break;
291
- case 'table':
292
- block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
293
- break;
294
- case 'toggle':
295
- block.children = await FetchBlocks({ block_id: block.id, last_edited_time: last_edited_time ?? block.last_edited_time });
296
- break;
297
- case 'video':
298
- if (block.video.type === 'file') {
299
- try {
300
- const { src } = await saveFile(block.video.file.url, `block-${block.id}`);
301
- block.video.src = src;
302
- block.video.videoType = getVideoType(src);
303
- }
304
- catch (e) {
305
- if (debug) {
306
- console.log(`Failed to save video: ${e}`);
307
- }
308
- }
309
- }
310
- else if (block.video.type === 'external') {
311
- block.video.html = await getVideoHtml(block);
312
- }
313
- break;
314
- case 'link_preview':
315
- const { url } = block.link_preview;
316
- if (url.includes('figma.com')) {
317
- // https://www.figma.com/file/GfHZFRpWGd0QXsRg9igdw8/Google-Material-Design?node-id=0%3A1
318
- block.link_preview.figma = {
319
- 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>`
320
- };
321
- }
322
- else if (url.includes('github.com')) {
323
- const u = new URL(url).pathname;
324
- if (u.split('/').length > 1) {
325
- const [owner, repo, type, num] = u.split('/').slice(1);
326
- if (owner && repo && (type === 'issues' || type === 'pull') && num) {
327
- const res = await getIssueForLinkPreview({ owner, repo, number: num });
328
- block.link_preview.github = {
329
- type: 'issue',
330
- issue: res,
331
- };
332
- }
333
- else if (owner && repo) {
334
- const res = await getRepoForLinkPreview({ owner, repo });
335
- block.link_preview.github = {
336
- type: 'repo',
337
- repo: res,
338
- };
339
- }
340
- }
341
- }
342
- break;
343
- case 'audio':
344
- case 'code':
345
- case 'column':
346
- case 'divider':
347
- case 'equation':
348
- case 'heading_1':
349
- case 'heading_2':
350
- case 'heading_3':
351
- case 'link_to_page':
352
- case 'quote':
353
- case 'table_of_contents':
354
- case 'table_row':
355
- case 'template':
356
- case 'to_do':
357
- case 'unsupported':
358
- break;
359
- default:
360
- if (debug) {
361
- console.log(`unknown block error`, block);
362
- }
363
- break;
392
+ return allres;
393
+ });
394
+ if (dependencies.length > 0) {
395
+ allres.dependencies = dependencies;
364
396
  }
397
+ return allres;
398
+ });
399
+ if (complete) {
400
+ await writeCache(cacheFile, allres);
365
401
  }
366
- catch (e) {
367
- if (debug) {
368
- console.log(`error for ${block.type} contents get`, block, e);
369
- }
402
+ else {
403
+ warn(`not caching the blocks of ${block_id} because of a transient failure; they are fetched again on the next call`);
370
404
  }
371
- }
372
- await writeCache(cacheFile, allres);
373
- return allres;
405
+ return allres;
406
+ });
374
407
  };
375
408
  //# sourceMappingURL=blocks.js.map