@@ -53,6 +53,7 @@ export class NotionAPI {
53
53
chunkNumber = 0 ,
54
54
throwOnCollectionErrors = false ,
55
55
collectionReducerLimit = 999 ,
56
+ fetchRelationPages = false ,
56
57
kyOptions
57
58
} : {
58
59
concurrency ?: number
@@ -63,6 +64,7 @@ export class NotionAPI {
63
64
chunkNumber ?: number
64
65
throwOnCollectionErrors ?: boolean
65
66
collectionReducerLimit ?: number
67
+ fetchRelationPages ?: boolean
66
68
kyOptions ?: KyOptions
67
69
} = { }
68
70
) : Promise < notion . ExtendedRecordMap > {
@@ -220,9 +222,97 @@ export class NotionAPI {
220
222
await this . addSignedUrls ( { recordMap, contentBlockIds, kyOptions } )
221
223
}
222
224
225
+ if ( fetchRelationPages ) {
226
+ const newBlocks = await this . fetchRelationPages ( recordMap , kyOptions )
227
+ recordMap . block = { ...recordMap . block , ...newBlocks }
228
+ }
229
+
223
230
return recordMap
224
231
}
225
232
233
+ fetchRelationPages = async (
234
+ recordMap : notion . ExtendedRecordMap ,
235
+ kyOptions : KyOptions | undefined
236
+ ) : Promise < notion . BlockMap > => {
237
+ const maxIterations = 10
238
+
239
+ for ( let i = 0 ; i < maxIterations ; ++ i ) {
240
+ const relationPageIdsThisIteration = new Set < string > ( )
241
+
242
+ for ( const blockId of Object . keys ( recordMap . block ) ) {
243
+ const blockValue = recordMap . block [ blockId ] ?. value
244
+ if (
245
+ blockValue ?. parent_table === 'collection' &&
246
+ blockValue ?. parent_id
247
+ ) {
248
+ const collection = recordMap . collection [ blockValue . parent_id ] ?. value
249
+ if ( collection ?. schema ) {
250
+ const ids = this . extractRelationPageIdsFromBlock (
251
+ blockValue ,
252
+ collection . schema
253
+ )
254
+ for ( const id of ids ) relationPageIdsThisIteration . add ( id )
255
+ }
256
+ }
257
+ }
258
+
259
+ const missingRelationPageIds = Array . from (
260
+ relationPageIdsThisIteration
261
+ ) . filter ( ( id ) => ! recordMap . block [ id ] ?. value )
262
+
263
+ if ( ! missingRelationPageIds . length ) break
264
+
265
+ try {
266
+ const newBlocks = await this . getBlocks (
267
+ missingRelationPageIds ,
268
+ kyOptions
269
+ ) . then ( ( res ) => res . recordMap . block )
270
+ recordMap . block = { ...recordMap . block , ...newBlocks }
271
+ } catch ( err : any ) {
272
+ console . warn (
273
+ 'NotionAPI getBlocks error during fetchRelationPages:' ,
274
+ err
275
+ )
276
+ // consider break or delay/retry here
277
+ }
278
+ }
279
+
280
+ return recordMap . block
281
+ }
282
+
283
+ extractRelationPageIdsFromBlock = (
284
+ blockValue : any ,
285
+ collectionSchema : any
286
+ ) : Set < string > => {
287
+ const pageIds = new Set < string > ( )
288
+
289
+ for ( const propertyId of Object . keys ( blockValue . properties || { } ) ) {
290
+ const schema = collectionSchema [ propertyId ]
291
+ if ( schema ?. type === 'relation' ) {
292
+ const decorations = blockValue . properties [ propertyId ]
293
+ if ( Array . isArray ( decorations ) ) {
294
+ for ( const decoration of decorations ) {
295
+ if (
296
+ Array . isArray ( decoration ) &&
297
+ decoration . length > 1 &&
298
+ decoration [ 0 ] === '‣'
299
+ ) {
300
+ const pagePointer = decoration [ 1 ] ?. [ 0 ]
301
+ if (
302
+ Array . isArray ( pagePointer ) &&
303
+ pagePointer . length > 1 &&
304
+ pagePointer [ 0 ] === 'p'
305
+ ) {
306
+ pageIds . add ( pagePointer [ 1 ] )
307
+ }
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+ return pageIds
314
+ }
315
+
226
316
public async addSignedUrls ( {
227
317
recordMap,
228
318
contentBlockIds,
0 commit comments