notion-md-crawler
Version:
A library to recursively retrieve and serialize Notion pages with customization for machine learning applications.
1 lines • 54 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/crawler.ts","../src/clients.ts","../src/libs.ts","../src/serializer/block/defaults.ts","../src/serializer/utils.ts","../src/serializer/block/strategy.ts","../src/serializer/block/index.ts","../src/serializer/property/defaults.ts","../src/serializer/property/strategy.ts","../src/serializer/property/index.ts","../src/serializer/index.ts","../src/utils.ts"],"sourcesContent":["export * from \"./crawler.js\";\nexport * from \"./serializer/index.js\";\nexport * from \"./types.js\";\nexport * from \"./utils.js\";\n","import { indent as _indent } from \"md-utils-ts\";\nimport {\n fetchNotionBlocks,\n fetchNotionDatabase,\n fetchNotionPage,\n} from \"./clients.js\";\nimport { has } from \"./libs.js\";\nimport { serializer } from \"./serializer/index.js\";\nimport { propertiesSerializer } from \"./serializer/property/index.js\";\nimport {\n CrawlerOptions,\n CrawlingResult,\n Dictionary,\n Metadata,\n MetadataBuilder,\n NotionBlock,\n NotionBlockObjectResponse,\n NotionChildPage,\n NotionPage,\n NotionProperties,\n Page,\n} from \"./types.js\";\n\nconst blockIs = <T extends NotionBlock[\"type\"][]>(\n block: NotionBlock,\n types: T,\n): block is Extract<NotionBlock, { type: T[number] }> =>\n types.includes(block.type);\n\nconst shouldSkipPage = (currentPageId: string, skipPageIds?: string[]) =>\n skipPageIds && skipPageIds.includes(currentPageId);\n\nconst pageInit =\n <T extends Dictionary>(metadataBuilder?: MetadataBuilder<T>) =>\n async (\n page: NotionPage | NotionBlock,\n title: string,\n parent?: Page<T>,\n properties?: string[],\n ): Promise<Page<T>> => {\n const metadata: Metadata = {\n id: page.id,\n title,\n createdTime: page.created_time,\n lastEditedTime: page.last_edited_time,\n parentId: parent?.metadata.id,\n };\n\n const userMetadata = metadataBuilder\n ? await metadataBuilder({ page, title, properties, parent })\n : ({} as T);\n\n return {\n metadata: { ...metadata, ...userMetadata },\n properties: properties || [],\n lines: [],\n };\n };\n\n/**\n * List of block types that do not need to be nested.\n * Avoid nesting when serializing due to the Notion Block structure.\n */\nconst IGNORE_NEST_LIST = [\"table\", \"table_row\", \"column_list\", \"column\"];\n\nconst indent = _indent();\n\nconst getBlockSerializer = <T extends Dictionary>(\n type: NotionBlock[\"type\"],\n { urlMask = false, serializers }: CrawlerOptions<T>,\n) =>\n ({\n ...serializer.block.strategy({ urlMask }),\n ...serializers?.block,\n })[type];\n\nconst isPage = (block: NotionBlock): block is NotionChildPage =>\n blockIs(block, [\"child_page\", \"child_database\"]);\n\nconst getSuccessResult = <T extends Dictionary>(\n page: Page<T>,\n): CrawlingResult<T> => ({\n id: page.metadata.id,\n success: true,\n page,\n});\n\nconst getFailedResult = <T extends Dictionary>(\n page: Page<T>,\n err: unknown,\n): CrawlingResult<T> => ({\n id: page.metadata.id,\n success: false,\n failure: {\n parentId: page.metadata.parentId,\n reason:\n err instanceof Error\n ? `${err.name}: ${err.message}\\n${err.stack}`\n : `${err}`,\n },\n});\n\nconst readLines =\n <T extends Dictionary>(options: CrawlerOptions<T>) =>\n async (blocks: NotionBlockObjectResponse[], depth = 0) => {\n let lines: string[] = [];\n let pages: NotionChildPage[] = [];\n\n for (const block of blocks) {\n if (!has(block, \"type\")) continue;\n const { type } = block;\n const serialize = getBlockSerializer(type, options);\n const text = await serialize(block as any);\n\n if (text !== false) {\n const line = indent(text, depth);\n lines.push(line);\n }\n\n if (isPage(block)) {\n pages.push(block);\n\n continue;\n }\n\n if (blockIs(block, [\"synced_block\"])) {\n // Specify the sync destination block id\n const blockId = block.synced_block.synced_from?.block_id || block.id;\n const _blocks = await fetchNotionBlocks(options.client)(blockId);\n const result = await readLines(options)(_blocks, depth);\n\n lines = [...lines, ...result.lines];\n pages = [...pages, ...result.pages];\n\n continue;\n }\n\n if (block.has_children) {\n const _blocks = await fetchNotionBlocks(options.client)(block.id);\n const _depth = IGNORE_NEST_LIST.includes(type) ? depth : depth + 1;\n const result = await readLines(options)(_blocks, _depth);\n\n lines = [...lines, ...result.lines];\n pages = [...pages, ...result.pages];\n }\n }\n\n return { lines, pages };\n };\n\nconst walking = <T extends Dictionary>(options: CrawlerOptions<T>) =>\n async function* (\n parent: Page<T>,\n blocks: NotionBlockObjectResponse[],\n depth = 0,\n ): AsyncGenerator<CrawlingResult<T>> {\n try {\n const { client, metadataBuilder } = options;\n const initPage = pageInit(metadataBuilder);\n\n const { lines, pages } = await readLines(options)(blocks, depth);\n yield getSuccessResult({ ...parent, lines });\n\n for (const page of pages) {\n if (shouldSkipPage(page.id, options.skipPageIds)) continue;\n\n if (blockIs(page, [\"child_page\"])) {\n const { title } = page.child_page;\n const _parent = await initPage(page, title, parent);\n const _blocks = await fetchNotionBlocks(client)(page.id);\n\n yield* walking(options)(_parent, _blocks, 0);\n\n continue;\n }\n\n if (blockIs(page, [\"child_database\"])) {\n const { title } = page.child_database;\n const _parent = await initPage(page, title, parent);\n const _options = { ...options, parent: _parent };\n\n yield* dbCrawler(_options)(page.id);\n }\n }\n } catch (err) {\n yield getFailedResult(parent, err);\n }\n };\n\nconst serializeProperties = <T extends Dictionary>(\n properties: NotionProperties,\n options: CrawlerOptions<T>,\n) => {\n const { urlMask = false, serializers } = options;\n const _serializers = {\n ...serializer.property.strategy({ urlMask }),\n ...serializers?.property,\n };\n\n return propertiesSerializer(_serializers)(properties);\n};\n\nconst extractPageTitle = (page: NotionPage) => {\n if (!has(page, \"properties\")) return \"\";\n\n let title = \"\";\n\n for (const prop of Object.values(page.properties)) {\n if (prop.type !== \"title\") continue;\n\n const text = serializer.property.defaults.title(\"\", prop) as string;\n title = text.replace(\"[] \", \"\");\n }\n\n return title;\n};\n\n/**\n * `crawler` is a higher-order function that returns a function designed to crawl through Notion pages.\n * It utilizes given client, optional serializers, and an optional parentId to customize its operation.\n *\n * @param {CrawlerOptions} options - The crawler options which contains:\n * - client: An instance of the Notion client.\n * - serializers?: An optional object that can be used to define custom serializers for blocks and properties.\n * - urlMask?: If specified, the url is masked with the string.\n *\n * @returns {Function} A generator function that takes a `rootPageId` (the ID of the starting Notion page) and yields a Promise that resolves to the crawled pages or an error object.\n *\n * @example\n * // Initialize the crawler with options.\n * const crawl = crawler({ client: myClient });\n *\n * // Use the initialized crawler\n * for await (const result of crawl(\"someRootPageId\")) {\n * if (result.success) {\n * console.log(\"Crawled page:\", result.page);\n * } else {\n * console.error(\"Crawling failed:\", result.failure);\n * }\n * }\n */\nexport const crawler = <T extends Dictionary>(options: CrawlerOptions<T>) =>\n async function* (rootPageId: string): AsyncGenerator<CrawlingResult<T>> {\n const { client, parent, metadataBuilder, skipPageIds } = options;\n if (shouldSkipPage(rootPageId, skipPageIds)) return;\n\n try {\n const notionPage = await fetchNotionPage(client)(rootPageId);\n\n if (!has(notionPage, \"parent\")) {\n const reason = \"Unintended Notion Page object.\";\n\n return yield {\n id: rootPageId,\n success: false,\n failure: { parentId: parent?.metadata.id, reason },\n };\n }\n\n // Preparation Before Exploring\n const props = await serializeProperties(notionPage.properties, options);\n const blocks = await fetchNotionBlocks(client)(notionPage.id);\n const title = extractPageTitle(notionPage);\n const initPage = pageInit(metadataBuilder);\n const rootPage = await initPage(notionPage, title, parent, props);\n\n yield* walking(options)(rootPage, blocks);\n } catch {\n // Try as DB Page may have been passed.\n yield* dbCrawler(options)(rootPageId);\n }\n };\n\n/**\n * `dbCrawler` is specifically designed to crawl Notion databases. This function retrieves all records in a database and then\n * utilizes the `crawler` function for each individual record.\n *\n * Note: When working with a root page that is a database, use `dbCrawler` instead of the regular `crawler`.\n *\n * @param {CrawlerOptions} options - The options necessary for the crawl operation, which includes:\n * - client: The Notion client used for making requests.\n * - serializers: Optional serializers for block and property.\n * - urlMask?: If specified, the url is masked with the string.\n *\n * @returns {Function} A function that takes a `databaseId` and returns a promise that resolves to a `Pages` object, which is a collection of\n * all the pages found within the specified Notion database.\n */\nexport const dbCrawler = <T extends Dictionary>(options: CrawlerOptions<T>) =>\n async function* (rootDatabaseId: string): AsyncGenerator<CrawlingResult<T>> {\n const { skipPageIds } = options;\n if (shouldSkipPage(rootDatabaseId, skipPageIds)) return;\n\n const crawl = crawler(options);\n const records = await fetchNotionDatabase(options.client)(rootDatabaseId);\n\n const { parent } = options;\n if (parent) {\n yield getSuccessResult<T>(parent);\n }\n\n for (const record of records) {\n yield* crawl(record.id);\n }\n };\n","import {\n APIErrorCode,\n Client,\n collectPaginatedAPI,\n isNotionClientError,\n} from \"@notionhq/client\";\nimport { wait } from \"./libs.js\";\n\nconst isRateLimitError = (error: unknown) =>\n isNotionClientError(error) && error.code === APIErrorCode.RateLimited;\n\n/**\n * Executes a function with exponential backoff on rate limit error.\n * @param fn - The function to execute.\n * @param retries - The number of retries before giving up. Default is 5.\n * @param delay - The delay in milliseconds before each retry. Default is 1000ms.\n * @returns A promise that resolves to the result of the function.\n * @throws If the function throws an error other than rate limit error.\n */\nconst backoffOnRateLimit = async <T>(\n fn: () => Promise<T>,\n retries: number = 5,\n delay: number = 1000,\n): Promise<T> => {\n try {\n return await fn();\n } catch (error) {\n if (isRateLimitError(error)) {\n if (retries === 0) throw error;\n console.log(\n `Rate limited. Retries left: ${retries}. Waiting ${delay}ms before retrying...`,\n );\n await wait(delay);\n return backoffOnRateLimit(fn, retries - 1, delay * 2);\n } else {\n throw error;\n }\n }\n};\n\n/**\n * Fetches Notion blocks for a given block ID.\n *\n * @param client - The Notion client.\n * @returns A function that takes a block ID and returns a promise that resolves to an array of Notion blocks.\n */\nexport const fetchNotionBlocks = (client: Client) => async (blockId: string) =>\n backoffOnRateLimit(() =>\n collectPaginatedAPI(client.blocks.children.list, {\n block_id: blockId,\n }),\n ).catch(() => []);\n\n/**\n * Fetches a Notion page using the provided client and page ID.\n * @param client The Notion client.\n * @returns A function that takes a page ID and returns a Promise that resolves to the retrieved page.\n */\nexport const fetchNotionPage = (client: Client) => (pageId: string) =>\n backoffOnRateLimit(() => client.pages.retrieve({ page_id: pageId }));\n\n/**\n * Fetches the Notion database with the specified database ID.\n *\n * @param client - The Notion client.\n * @returns A function that takes a database ID and returns a promise that resolves to an array of database results.\n */\nexport const fetchNotionDatabase = (client: Client) => (databaseId: string) =>\n backoffOnRateLimit(() =>\n client.databases\n .query({ database_id: databaseId })\n .then(({ results }) => results),\n ).catch(() => []);\n","export const has = <T extends Object, K extends string>(\n obj: T,\n key: K,\n): obj is Extract<T, { [k in K]: any }> => key in obj;\n\nexport const wait = (ms: number) =>\n new Promise((resolve) => setTimeout(resolve, ms));\n","import * as md from \"md-utils-ts\";\nimport { fromLink, fromRichText } from \"../utils.js\";\nimport { FactoryOptions, SerializerFactory } from \"./types.js\";\n\ntype Audio = SerializerFactory<\"audio\">;\nexport const audio: Audio =\n ({ urlMask }) =>\n (block) => {\n const { title, href } = fromLink(block.audio);\n return md.anchor(title, urlMask || href);\n };\n\ntype Bookmark = SerializerFactory<\"bookmark\">;\nexport const bookmark: Bookmark =\n ({ urlMask }) =>\n (block) =>\n md.anchor(\n fromRichText(block.bookmark.caption),\n urlMask || block.bookmark.url,\n );\n\ntype Breadcrumb = SerializerFactory<\"breadcrumb\">;\nexport const breadcrumb: Breadcrumb = () => () => false;\n\ntype BulletedListItem = SerializerFactory<\"bulleted_list_item\">;\nexport const bulletedListItem: BulletedListItem =\n ({ urlMask }) =>\n (block) =>\n md.bullet(fromRichText(block.bulleted_list_item.rich_text, urlMask));\n\ntype Callout = SerializerFactory<\"callout\">;\nexport const callout: Callout =\n ({ urlMask }) =>\n (block) =>\n md.quote(fromRichText(block.callout.rich_text, urlMask));\n\ntype ChildPage = SerializerFactory<\"child_page\">;\nexport const childPage: ChildPage = () => (block) =>\n `[${block.child_page.title}]`;\n\ntype ChildDatabase = SerializerFactory<\"child_database\">;\nexport const childDatabase: ChildDatabase = () => (block) =>\n `[${block.child_database.title}]`;\n\ntype Code = SerializerFactory<\"code\">;\nexport const code: Code =\n ({ urlMask }) =>\n (block) =>\n md.codeBlock(block.code.language)(\n fromRichText(block.code.rich_text, urlMask),\n );\n\ntype Column = SerializerFactory<\"column\">;\nexport const column: Column = () => () => false;\n\ntype ColumnList = SerializerFactory<\"column_list\">;\nexport const columnList: ColumnList = () => () => false;\n\ntype Divider = SerializerFactory<\"divider\">;\nexport const divider: Divider = () => () => md.hr();\n\ntype Embed = SerializerFactory<\"embed\">;\nexport const embed: Embed =\n ({ urlMask }) =>\n (block) => {\n const caption = fromRichText(block.embed.caption, urlMask);\n return md.anchor(caption, urlMask || block.embed.url);\n };\n\ntype Equation = SerializerFactory<\"equation\">;\nexport const equation: Equation = () => (block) =>\n md.equationBlock(block.equation.expression);\n\ntype File = SerializerFactory<\"file\">;\nexport const file: File =\n ({ urlMask }) =>\n (block) => {\n const { title, href } = fromLink(block.file);\n return md.anchor(title, urlMask || href);\n };\n\ntype Heading1 = SerializerFactory<\"heading_1\">;\nexport const heading1: Heading1 =\n ({ urlMask }) =>\n (block) =>\n md.h1(fromRichText(block.heading_1.rich_text, urlMask));\n\ntype Heading2 = SerializerFactory<\"heading_2\">;\nexport const heading2: Heading2 =\n ({ urlMask }) =>\n (block) =>\n md.h2(fromRichText(block.heading_2.rich_text, urlMask));\n\ntype Heading3 = SerializerFactory<\"heading_3\">;\nexport const heading3: Heading3 =\n ({ urlMask }) =>\n (block) =>\n md.h3(fromRichText(block.heading_3.rich_text, urlMask));\n\ntype Image = SerializerFactory<\"image\">;\nexport const image: Image =\n ({ urlMask }) =>\n (block) => {\n const { title, href } = fromLink(block.image);\n return md.image(title, urlMask || href);\n };\n\ntype LinkPreview = SerializerFactory<\"link_preview\">;\nexport const linkPreview: LinkPreview =\n ({ urlMask }) =>\n (block) =>\n md.anchor(block.type, urlMask || block.link_preview.url);\n\ntype LinkToPage = SerializerFactory<\"link_to_page\">;\nexport const linkToPage: LinkToPage =\n ({ urlMask }) =>\n (block) => {\n const href =\n block.link_to_page.type === \"page_id\" ? block.link_to_page.page_id : \"\";\n return md.anchor(block.type, urlMask || href);\n };\n\ntype NumberedListItem = SerializerFactory<\"numbered_list_item\">;\nexport const numberedListItem: NumberedListItem =\n ({ urlMask }) =>\n (block) =>\n md.bullet(fromRichText(block.numbered_list_item.rich_text, urlMask), 1);\n\ntype Paragraph = SerializerFactory<\"paragraph\">;\nexport const paragraph: Paragraph =\n ({ urlMask }) =>\n (block) =>\n fromRichText(block.paragraph.rich_text, urlMask);\n\ntype PDF = SerializerFactory<\"pdf\">;\nexport const pdf: PDF =\n ({ urlMask }) =>\n (block) => {\n const { title, href } = fromLink(block.pdf);\n return md.anchor(title, urlMask || href);\n };\n\ntype Quote = SerializerFactory<\"quote\">;\nexport const quote: Quote =\n ({ urlMask }) =>\n (block) =>\n md.quote(fromRichText(block.quote.rich_text, urlMask));\n\ntype SyncedBlock = SerializerFactory<\"synced_block\">;\nexport const syncedBlock: SyncedBlock = () => () => false;\n\ntype Table = SerializerFactory<\"table\">;\nexport const table: Table = () => () => false;\n\ntype TableOfContents = SerializerFactory<\"table_of_contents\">;\nexport const tableOfContents: TableOfContents = () => () => false;\n\ntype TableRow = SerializerFactory<\"table_row\">;\nexport const tableRow: TableRow =\n ({ urlMask }) =>\n (block) =>\n `| ${block.table_row.cells\n .flatMap((row) => row.map((column) => fromRichText([column], urlMask)))\n .join(\" | \")} |`;\n\ntype Template = SerializerFactory<\"template\">;\nexport const template: Template =\n ({ urlMask }) =>\n (block) =>\n fromRichText(block.template.rich_text, urlMask);\n\ntype ToDo = SerializerFactory<\"to_do\">;\nexport const toDo: ToDo =\n ({ urlMask }) =>\n (block) =>\n md.todo(fromRichText(block.to_do.rich_text, urlMask), block.to_do.checked);\n\ntype Toggle = SerializerFactory<\"toggle\">;\nexport const toggle: Toggle =\n ({ urlMask }) =>\n (block) =>\n fromRichText(block.toggle.rich_text, urlMask);\n\ntype Unsupported = SerializerFactory<\"unsupported\">;\nexport const unsupported: Unsupported = () => () => false;\n\ntype Video = SerializerFactory<\"video\">;\nexport const video: Video =\n ({ urlMask }) =>\n (block) => {\n const { title, href } = fromLink(block.video);\n return md.anchor(title, urlMask || href);\n };\n\nexport const factory = (options: FactoryOptions) => ({\n audio: audio(options),\n bookmark: bookmark(options),\n breadcrumb: breadcrumb(options),\n bulleted_list_item: bulletedListItem(options),\n callout: callout(options),\n child_database: childDatabase(options),\n child_page: childPage(options),\n code: code(options),\n column: column(options),\n column_list: columnList(options),\n divider: divider(options),\n embed: embed(options),\n equation: equation(options),\n file: file(options),\n heading_1: heading1(options),\n heading_2: heading2(options),\n heading_3: heading3(options),\n image: image(options),\n link_preview: linkPreview(options),\n link_to_page: linkToPage(options),\n numbered_list_item: numberedListItem(options),\n paragraph: paragraph(options),\n pdf: pdf(options),\n quote: quote(options),\n synced_block: syncedBlock(options),\n table: table(options),\n table_of_contents: tableOfContents(options),\n table_row: tableRow(options),\n template: template(options),\n to_do: toDo(options),\n toggle: toggle(options),\n unsupported: unsupported(options),\n video: video(options),\n});\n\nexport const defaults = factory({ urlMask: false });\n","import * as md from \"md-utils-ts\";\nimport { has } from \"../libs.js\";\nimport { ExtractBlock, ExtractProperty } from \"../types.js\";\n\ntype NotionParagraphBlock = ExtractBlock<\"paragraph\">;\ntype NotionRichText = NotionParagraphBlock[\"paragraph\"][\"rich_text\"];\ntype NotionAnnotations = NotionRichText[number][\"annotations\"];\ntype NotionImageBlock = ExtractBlock<\"image\">;\ntype NotionLinkObject = NotionImageBlock[\"image\"];\n\nexport type Annotate = (text: string, annotations: NotionAnnotations) => string;\n\n/**\n * `annotate` is a function designed to apply various annotations to a given text. It transforms the text based on the `NotionAnnotations` provided.\n *\n * Annotations include: code, bold, italic, strikethrough, and underline.\n * Multiple annotations can be applied to the text at once.\n *\n * @param {string} text - The original text to which annotations should be applied.\n * @param {NotionAnnotations} annotations - An object that specifies which annotations to apply to the text.\n * The object can have properties such as `code`, `bold`, `italic`, `strikethrough`, and `underline` set to `true` to apply the corresponding annotation.\n *\n * @returns {string} The annotated text.\n */\nexport const annotate: Annotate = (text, annotations) => {\n if (annotations.code) text = md.inlineCode(text);\n if (annotations.bold) text = md.bold(text);\n if (annotations.italic) text = md.italic(text);\n if (annotations.strikethrough) text = md.del(text);\n if (annotations.underline) text = md.underline(text);\n\n return text;\n};\n\nexport type FromRichText = (\n richText: NotionRichText,\n urlMask?: string | false,\n) => string;\n\n/**\n * `fromRichText` transforms a Notion-rich text object into a plain string representation, preserving annotations such as bold, italic, etc., and links (hrefs).\n *\n * The function first determines if the provided text is whitespace only. If true, it just returns the whitespace.\n * Otherwise, it preserves the leading and trailing spaces, trims the main content, applies annotations, and embeds links if present.\n *\n * @param {NotionRichText} richTextObject - An array of Notion rich text objects. Each object has a `plain_text` field with the raw text,\n * `annotations` detailing style attributes, and an optional `href` for links.\n *\n * @returns {string} A transformed string representation of the provided Notion-rich text object.\n */\nexport const fromRichText: FromRichText = (richTextObject, urlMask = false) =>\n richTextObject\n .map(({ plain_text, annotations, href }) => {\n if (plain_text.match(/^\\s*$/)) return plain_text;\n\n const leadingSpaceMatch = plain_text.match(/^(\\s*)/);\n const trailingSpaceMatch = plain_text.match(/(\\s*)$/);\n\n const leading_space = leadingSpaceMatch ? leadingSpaceMatch[0] : \"\";\n const trailing_space = trailingSpaceMatch ? trailingSpaceMatch[0] : \"\";\n\n const text = plain_text.trim();\n\n if (text === \"\") return leading_space + trailing_space;\n\n const annotatedText = annotate(text, annotations);\n const linkedText = href\n ? md.anchor(annotatedText, urlMask || href)\n : annotatedText;\n\n return leading_space + linkedText + trailing_space;\n })\n .join(\"\");\n\nexport type fromLink = (linkObject: NotionLinkObject) => {\n title: string;\n href: string;\n};\n\n/**\n * `fromLink` transforms a Notion link object into a simpler representation with a title and href.\n *\n * @param {NotionLinkObject} linkObject - The Notion link object to be transformed.\n *\n * @returns {Object} An object with a `title` which is either the caption of the link, the file name, or a default \"link\" string,\n * and `href` which is the URL of the link.\n */\nexport const fromLink: fromLink = (linkObject) => {\n const caption = fromRichText(linkObject.caption);\n const href =\n linkObject.type === \"external\"\n ? linkObject.external.url\n : linkObject.file.url;\n const fileName = href.match(/[^\\/\\\\&\\?]+\\.\\w{3,4}(?=([\\?&].*$|$))/);\n const title = caption.trim() ? caption : fileName ? fileName[0] : \"link\";\n return { title, href };\n};\n\ntype NotionUserObject = ExtractProperty<\"created_by\">[\"created_by\"];\ntype FromUser = (_user: NotionUserObject) => string;\n\n/**\n * `fromUser` transforms a Notion user object into a string representation of the user's name.\n * If the user is a bot, \"[bot]\" is appended to the name.\n *\n * @param {NotionUserObject} _user - The Notion user object to be transformed.\n *\n * @returns {string} A string representation of the user's name.\n */\nexport const fromUser: FromUser = (_user) => {\n if (!has(_user, \"type\")) return \"<empty>\";\n\n const name = _user.name ?? \"<empty>\";\n return _user.type === \"person\" ? `${name}` : `${name}[bot]`;\n};\n\ntype NotionDateObject = ExtractProperty<\"date\">[\"date\"];\ntype FromDate = (date: NotionDateObject) => string;\n\n/**\n * `fromDate` transforms a Notion date object into a string representation.\n * If the date object contains both a start and end date, both dates are returned. Otherwise, only the start date is returned.\n *\n * @param {NotionDateObject} date - The Notion date object to be transformed.\n *\n * @returns {string} A string representation of the date or dates.\n */\nexport const fromDate: FromDate = (date) => {\n if (!date) return \"<empty>\";\n\n return date.end ? `(start)${date.start}, (end): ${date.end}` : date.start;\n};\n","import { factory } from \"./defaults.js\";\n\nexport const strategy = factory;\n","import { defaults } from \"./defaults.js\";\nimport { strategy } from \"./strategy.js\";\n\nexport default { defaults, strategy };\n","import { anchor } from \"md-utils-ts\";\nimport { has } from \"../../libs.js\";\nimport { NotionProperty } from \"../../types.js\";\nimport { fromDate, fromRichText, fromUser } from \"../utils.js\";\nimport { FactoryOptions, Serializer, SerializerFactory } from \"./types.js\";\n\nconst DELIMITER = \", \";\nconst EMPTY_STR = \"<empty>\";\n\ntype Checkbox = SerializerFactory<\"checkbox\">;\nexport const checkbox: Checkbox =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${prop.checkbox}`;\n\ntype CreatedBy = SerializerFactory<\"created_by\">;\nexport const createdBy: CreatedBy =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${fromUser(prop.created_by)}`;\n\ntype CreatedTime = SerializerFactory<\"created_time\">;\nexport const createdTime: CreatedTime =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${prop.created_time}`;\n\ntype _Date = SerializerFactory<\"date\">;\nexport const date: _Date =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${fromDate(prop.date)}`;\n\ntype Email = SerializerFactory<\"email\">;\nexport const email: Email =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${prop.email ?? EMPTY_STR}`;\n\ntype Files = SerializerFactory<\"files\">;\nexport const files: Files =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ` +\n prop.files\n .map((file) => {\n const href = has(file, \"external\") ? file.external.url : file.file.url;\n return anchor(file.name, href);\n })\n .join(DELIMITER);\n\ntype Formula = SerializerFactory<\"formula\">;\nexport const formula: Formula =\n ({ urlMask }) =>\n (name, prop) => {\n switch (prop.formula.type) {\n case \"string\":\n return `[${name}] ${prop.formula.string ?? EMPTY_STR}`;\n case \"boolean\":\n return `[${name}] ${prop.formula.boolean ?? EMPTY_STR}`;\n case \"date\":\n return `[${name}] ${fromDate(prop.formula.date)}`;\n case \"number\":\n return `[${name}] ${prop.formula.number}`;\n }\n };\n\ntype LastEditedBy = SerializerFactory<\"last_edited_by\">;\nexport const lastEditedBy: LastEditedBy =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${fromUser(prop.last_edited_by)}`;\n\ntype LastEditedTime = SerializerFactory<\"last_edited_time\">;\nexport const lastEditedTime: LastEditedTime =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${prop.last_edited_time}`;\n\ntype MultiSelect = SerializerFactory<\"multi_select\">;\nexport const multiSelect: MultiSelect =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ` +\n prop.multi_select.map((select) => select.name).join(DELIMITER);\n\ntype _Number = SerializerFactory<\"number\">;\nexport const number: _Number =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${prop.number ?? EMPTY_STR}`;\n\ntype People = SerializerFactory<\"people\">;\nexport const people: People =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ` +\n prop.people.map((person) => fromUser(person)).join(DELIMITER);\n\ntype PhoneNumber = SerializerFactory<\"phone_number\">;\nexport const phoneNumber: PhoneNumber =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${prop.phone_number ?? EMPTY_STR}`;\n\ntype Relation = SerializerFactory<\"relation\">;\nexport const relation: Relation =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ` + prop.relation.map((item) => `${item.id}`).join(DELIMITER);\n\ntype RichText = SerializerFactory<\"rich_text\">;\nexport const richText: RichText =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${fromRichText(prop.rich_text)}`;\n\ntype Select = SerializerFactory<\"select\">;\nexport const select: Select =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${prop.select?.name ?? EMPTY_STR}`;\n\ntype Status = SerializerFactory<\"status\">;\nexport const status: Status =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${prop.status?.name ?? EMPTY_STR}`;\n\ntype Title = SerializerFactory<\"title\">;\nexport const title: Title =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${fromRichText(prop.title)}`;\n\ntype UniqueId = SerializerFactory<\"unique_id\">;\nexport const uniqueId: UniqueId =\n ({ urlMask }) =>\n (name, prop) => {\n const prefix = prop.unique_id.prefix ?? \"\";\n const _number = prop.unique_id.number ?? \"\";\n const id = prefix + _number;\n return `[${name}] ${id || EMPTY_STR}`;\n };\n\ntype Url = SerializerFactory<\"url\">;\nexport const url: Url =\n ({ urlMask }) =>\n (name, prop) =>\n `[${name}] ${prop.url ?? EMPTY_STR}`;\n\ntype Verification = SerializerFactory<\"verification\">;\nexport const verification: Verification =\n ({ urlMask }) =>\n () =>\n false;\n\ntype OmitFromUnion<T, U extends T> = T extends U ? never : T;\ntype RollupFactory = (options: FactoryOptions) => {\n [K in OmitFromUnion<NotionProperty[\"type\"], \"rollup\">]: Serializer<K>;\n};\nconst rollupFactory: RollupFactory = (options) => ({\n checkbox: checkbox(options),\n created_by: createdBy(options),\n created_time: createdTime(options),\n date: date(options),\n email: email(options),\n files: files(options),\n formula: formula(options),\n last_edited_by: lastEditedBy(options),\n last_edited_time: lastEditedTime(options),\n multi_select: multiSelect(options),\n number: number(options),\n people: people(options),\n phone_number: phoneNumber(options),\n relation: relation(options),\n rich_text: richText(options),\n select: select(options),\n status: status(options),\n title: title(options),\n unique_id: uniqueId(options),\n url: url(options),\n verification: verification(options),\n});\n\ntype Rollup = SerializerFactory<\"rollup\">;\nexport const rollup: Rollup = (options) => (name, prop) => {\n switch (prop.rollup.type) {\n case \"number\":\n return number(options)(name, prop.rollup);\n case \"date\":\n return date(options)(name, prop.rollup);\n case \"array\":\n const strategy = rollupFactory(options);\n return Promise.all(\n prop.rollup.array.map((item) => strategy[item.type](name, item as any)),\n ).then(\n (items) =>\n `[${name}] ` +\n items\n .map((item) => item as string)\n .map((text) => text.replace(`[${name}] `, \"\"))\n .join(DELIMITER),\n );\n }\n};\n\nexport const factory = (options: FactoryOptions) => ({\n ...rollupFactory(options),\n rollup: rollup(options),\n});\n\nexport const defaults = factory({ urlMask: false });\n","import { factory } from \"./defaults.js\";\n\nexport const strategy = factory;\n","import { NotionProperties } from \"../../notion.types.js\";\nimport { defaults } from \"./defaults.js\";\nimport { strategy } from \"./strategy.js\";\nimport { Serializers } from \"./types.js\";\n\nexport default { defaults, strategy };\n\nexport const propertiesSerializer =\n (serializers: Serializers) => (props: NotionProperties) =>\n Promise.all(\n Object.entries(props).map(([key, prop]) =>\n serializers[prop.type](key, prop as any),\n ),\n ).then((texts) => texts.filter((text): text is string => text !== false));\n","import block from \"./block/index.js\";\nimport { Serializers as BlockSerializers } from \"./block/types.js\";\nimport property from \"./property/index.js\";\nimport { Serializers as PropertySerializers } from \"./property/types.js\";\nimport * as utils from \"./utils.js\";\n\nexport type {\n Serializer as BlockSerializer,\n Serializers as BlockSerializers,\n} from \"./block/types.js\";\n\nexport type {\n Serializer as PropertySerializer,\n Serializers as PropertySerializers,\n} from \"./property/types.js\";\n\nexport type Serializers = {\n block: BlockSerializers;\n property: PropertySerializers;\n};\n\nexport const serializer = { block, property, utils };\n","import { h1 } from \"md-utils-ts\";\nimport {\n Crawler,\n CrawlingResult,\n DBCrawler,\n Dictionary,\n Page,\n} from \"./types.js\";\n\nconst nestHeading = (text: string) => (text.match(/^#+\\s/) ? \"#\" + text : text);\n\n/**\n * `pageToString` transforms a `Page` object into a string representation. It formats the metadata, properties, and lines\n * into a unified string, with the metadata as an H1 heading and the properties nested between triple-dashes.\n *\n * @param {Page} params - An object containing:\n * - metadata: The metadata of the page which includes the title.\n * - properties: An array of property strings.\n * - lines: An array of line strings.\n *\n * @returns {string} A string representation of the provided page.\n */\nexport const pageToString = <T extends Dictionary>({\n metadata,\n properties,\n lines,\n}: Page<T>): string => {\n const title = h1(metadata.title);\n const data = [\"---\", properties.join(\"\\n\"), \"---\"].join(\"\\n\");\n const body = lines.map(nestHeading);\n return [title, data, ...body].join(\"\\n\");\n};\n\ntype Crawling<T extends Dictionary = {}> =\n | ReturnType<ReturnType<Crawler<T>>>\n | ReturnType<ReturnType<DBCrawler<T>>>;\n\n/**\n * Asynchronously waits for all results from a given crawling operation and collects them into an array.\n * This function is compatible with both `Crawler` and `DBCrawler` types.\n *\n * @param {Crawling} crawling - A generator function that yields crawling results. It can be an instance of `Crawler` or `DBCrawler`.\n *\n * @returns {Promise<CrawlingResult[]>} A Promise that resolves to an array of `CrawlingResult` objects, which contain the results of the crawling operation.\n *\n * @example\n * // Initialize a Crawler or DBCrawler instance\n * const crawl = crawler({ client: myClient });\n * // OR\n * const dbCrawl = dbCrawler({ client: myDbClient });\n *\n * // Wait for all results and collect them\n * waitAllResults(crawl(\"someRootPageId\"))\n * .then((allResults) => {\n * console.log(\"All crawled results:\", allResults);\n * })\n * .catch((error) => {\n * console.error(\"Error during crawling:\", error);\n * });\n */\nexport const waitAllResults = async <T extends Dictionary>(\n crawling: Crawling<T>,\n) => {\n const results: CrawlingResult<T>[] = [];\n\n for await (const result of crawling) {\n results.push(result);\n }\n\n return results;\n};\n"],"mappings":"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