CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-notion-utils

Useful utilities for working with Notion data structures and operations in both Node.js and browser environments.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

navigation-structure.mddocs/

Navigation & Structure

Tools for building navigation elements and extracting structural information from Notion pages including table of contents generation and workspace traversal.

Capabilities

Get Page Table of Contents

Generates a table of contents by parsing H1, H2, and H3 header elements from a page.

/**
 * Gets the metadata for a table of contents block by parsing the page's H1, H2, and H3 elements
 * @param page - The page block to analyze
 * @param recordMap - Extended record map containing all blocks
 * @returns Array of TOC entries with hierarchy information
 */
function getPageTableOfContents(
  page: PageBlock,
  recordMap: ExtendedRecordMap
): Array<TableOfContentsEntry>;

interface TableOfContentsEntry {
  /** Block ID of the header */
  id: string;
  /** Block type (header_1, header_2, header_3) */
  type: BlockType;
  /** Header text content */
  text: string;
  /** Indentation level (0, 1, 2) */
  indentLevel: number;
}

Usage Example:

import { getPageTableOfContents } from "notion-utils";

const toc = getPageTableOfContents(pageBlock, recordMap);
toc.forEach(entry => {
  const indent = "  ".repeat(entry.indentLevel);
  console.log(`${indent}- ${entry.text} (${entry.type})`);
});

// Example output:
// - Introduction (header_1)
//   - Overview (header_2)
//   - Getting Started (header_2)
//     - Installation (header_3)
// - Advanced Topics (header_1)

Get All Pages in Space

Performs a traversal over a Notion workspace to collect all reachable pages.

/**
 * Performs a traversal over a Notion workspace starting from a seed page
 * @param rootPageId - Starting page ID for traversal
 * @param rootSpaceId - Space ID containing the pages (can be undefined)
 * @param getPage - Function to fetch page data by ID
 * @param options - Traversal configuration options
 * @returns Promise resolving to a map of all discovered pages
 */
function getAllPagesInSpace(
  rootPageId: string,
  rootSpaceId: string | undefined,
  getPage: (pageId: string) => Promise<ExtendedRecordMap>,
  options?: TraversalOptions
): Promise<PageMap>;

interface TraversalOptions {
  /** Number of concurrent page requests (default: 4) */
  concurrency?: number;
  /** Whether to traverse collection database items (default: true) */
  traverseCollections?: boolean;
  /** Optional target page ID to stop at when found */
  targetPageId?: string;
  /** Maximum traversal depth (default: Infinity) */
  maxDepth?: number;
}

type PageMap = Record<string, ExtendedRecordMap>;

Usage Example:

import { getAllPagesInSpace } from "notion-utils";

// Function to fetch a page (you provide this)
async function fetchPage(pageId: string): Promise<ExtendedRecordMap> {
  // Your implementation to fetch page data
  return await notion.getPage(pageId);
}

// Traverse workspace starting from a root page
const allPages = await getAllPagesInSpace(
  "root-page-id",
  "space-id",
  fetchPage,
  {
    concurrency: 6,
    traverseCollections: true,
    maxDepth: 10
  }
);

console.log(`Found ${Object.keys(allPages).length} pages`);

// Access individual pages
Object.entries(allPages).forEach(([pageId, recordMap]) => {
  const title = getPageTitle(recordMap);
  console.log(`Page ${pageId}: ${title}`);
});

Get Page Content Block IDs

Gets all block IDs contained within a page, including nested content.

/**
 * Gets the IDs of all blocks contained on a page starting from a root block ID
 * @param recordMap - Extended record map containing all blocks
 * @param blockId - Starting block ID (uses first block if not provided)
 * @returns Array of all block IDs found on the page
 */
function getPageContentBlockIds(recordMap: ExtendedRecordMap, blockId?: string): string[];

Usage Example:

import { getPageContentBlockIds } from "notion-utils";

// Get all block IDs for a page
const blockIds = getPageContentBlockIds(recordMap);
console.log(`Page contains ${blockIds.length} blocks`);

// Get blocks starting from specific block
const childBlockIds = getPageContentBlockIds(recordMap, "specific-block-id");
console.log(`Section contains ${childBlockIds.length} blocks`);

// Use block IDs to access individual blocks
blockIds.forEach(blockId => {
  const block = recordMap.block[blockId]?.value;
  if (block) {
    console.log(`Block ${blockId}: ${block.type}`);
  }
});

Types

// Re-exported from notion-types
interface ExtendedRecordMap {
  block: Record<string, Block>;
  collection?: Record<string, Collection>;
  collection_view?: Record<string, CollectionView>;
  notion_user?: Record<string, NotionUser>;
  collection_query?: Record<string, any>;
  signed_urls?: Record<string, string>;
  preview_images?: Record<string, string>;
}

interface PageBlock extends Block {
  type: "page";
  properties?: {
    title?: Decoration[];
  };
  format?: {
    page_icon?: string;
    page_cover?: string;
    page_cover_position?: number;
    page_full_width?: boolean;
    page_small_text?: boolean;
  };
}

// Navigation-specific types
interface TableOfContentsEntry {
  id: string;
  type: BlockType;
  text: string;
  indentLevel: number;
}

interface TraversalOptions {
  concurrency?: number;
  traverseCollections?: boolean;
  targetPageId?: string;
  maxDepth?: number;
}

type PageMap = Record<string, ExtendedRecordMap>;
type BlockType = 'header_1' | 'header_2' | 'header_3' | 'text' | 'bulleted_list' | 'numbered_list' | 'page' | /* ... many other block types */;

Install with Tessl CLI

npx tessl i tessl/npm-notion-utils

docs

block-property-utilities.md

content-extraction.md

data-operations.md

id-url-management.md

index.md

navigation-structure.md

page-analysis.md

text-processing.md

tile.json