@@ -53,6 +53,7 @@ export class NotionAPI {
5353 chunkNumber = 0 ,
5454 throwOnCollectionErrors = false ,
5555 collectionReducerLimit = 999 ,
56+ fetchRelationPages = false ,
5657 kyOptions
5758 } : {
5859 concurrency ?: number
@@ -63,6 +64,7 @@ export class NotionAPI {
6364 chunkNumber ?: number
6465 throwOnCollectionErrors ?: boolean
6566 collectionReducerLimit ?: number
67+ fetchRelationPages ?: boolean
6668 kyOptions ?: KyOptions
6769 } = { }
6870 ) : Promise < notion . ExtendedRecordMap > {
@@ -220,9 +222,97 @@ export class NotionAPI {
220222 await this . addSignedUrls ( { recordMap, contentBlockIds, kyOptions } )
221223 }
222224
225+ if ( fetchRelationPages ) {
226+ const newBlocks = await this . fetchRelationPages ( recordMap , kyOptions )
227+ recordMap . block = { ...recordMap . block , ...newBlocks }
228+ }
229+
223230 return recordMap
224231 }
225232
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+
226316 public async addSignedUrls ( {
227317 recordMap,
228318 contentBlockIds,
0 commit comments