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

data-operations.mddocs/

Data Operations

Utilities for working with Notion's record maps, data structures, URL operations, and performing transformations on Notion's internal data formats.

Capabilities

Record Map Operations

Functions for manipulating and combining Notion's core data structures.

/**
 * Merges two Notion record maps into a single record map
 * @param recordMapA - First record map
 * @param recordMapB - Second record map (takes precedence on conflicts)
 * @returns Combined record map with all data from both inputs
 */
function mergeRecordMaps(recordMapA: ExtendedRecordMap, recordMapB: ExtendedRecordMap): ExtendedRecordMap;

Usage Example:

import { mergeRecordMaps } from "notion-utils";

// Merge data from multiple page fetches
const pageData = await getPage("page-id");
const additionalData = await getPage("related-page-id"); 

const combinedData = mergeRecordMaps(pageData, additionalData);

// Now you can access blocks from both pages
const allBlocks = Object.keys(combinedData.block).length;
console.log(`Combined data contains ${allBlocks} blocks`);

// recordMapB takes precedence for duplicate keys
const merged = mergeRecordMaps(
  { block: { "id1": { value: "old" } } },
  { block: { "id1": { value: "new" } } }
);
// Result: merged.block["id1"].value === "new"

URL Operations

Advanced URL validation, normalization, and processing functions.

/**
 * Validates if a string is a valid URL
 * @param input - String to validate
 * @returns True if valid URL, false otherwise
 */
function isUrl(input: string): boolean;

/**
 * Normalizes URLs for consistency (memoized for performance)
 * @param url - URL to normalize
 * @returns Normalized URL string or empty string if invalid
 */
function normalizeUrl(url?: string): string;

Usage Examples:

import { isUrl, normalizeUrl } from "notion-utils";

// URL validation
const validUrls = [
  "https://example.com",
  "http://localhost:3000",
  "ftp://files.example.com"
].filter(isUrl);

console.log(`Found ${validUrls.length} valid URLs`);

// URL normalization (strips protocol, www, query params, fragments)
const urls = [
  "https://www.example.com/path?param=value#section",
  "http://example.com/path/",
  "https://example.com/path"
];

const normalized = urls.map(normalizeUrl);
// All three normalize to: "example.com/path"

// Useful for deduplication
const uniqueUrls = [...new Set(urls.map(normalizeUrl))];
console.log(`${urls.length} URLs normalized to ${uniqueUrls.length} unique URLs`);

Image URL Processing

Functions for handling and transforming image URLs in Notion's system.

/**
 * Default function for mapping/transforming image URLs for Notion
 * @param url - Image URL to transform
 * @param block - Block containing the image (for context)
 * @returns Transformed image URL or undefined to skip
 */
function defaultMapImageUrl(url: string | undefined, block: Block): string | undefined;

Usage Examples:

import { defaultMapImageUrl } from "notion-utils";

// Transform various image URL types
const dataUrl = "data:image/png;base64,iVBOR...";
const result1 = defaultMapImageUrl(dataUrl, imageBlock);
// Returns: original data URL (passed through)

const unsplashUrl = "https://images.unsplash.com/photo-abc123";
const result2 = defaultMapImageUrl(unsplashUrl, imageBlock);
// Returns: original Unsplash URL (passed through)

const externalUrl = "https://example.com/image.jpg";
const result3 = defaultMapImageUrl(externalUrl, imageBlock);
// Returns: Notion-proxied URL for optimization and security

// Use with image extraction
import { getPageImageUrls } from "notion-utils";

const optimizedImages = getPageImageUrls(recordMap, {
  mapImageUrl: defaultMapImageUrl
});

// Custom image processing
const customImages = getPageImageUrls(recordMap, {
  mapImageUrl: (url, block) => {
    const processed = defaultMapImageUrl(url, block);
    if (processed && !processed.startsWith('data:')) {
      // Add resize parameters
      return `${processed}&w=800&h=600&fit=crop`;
    }
    return processed;
  }
});

Page URL Mapping

Functions for generating URL paths from Notion page IDs.

/**
 * Returns a function that maps page IDs to URL paths
 * @param rootPageId - Optional root page ID that maps to '/'
 * @returns Function that converts page IDs to URL paths
 */
function defaultMapPageUrl(rootPageId?: string): (pageId: string) => string;

Usage Examples:

import { defaultMapPageUrl } from "notion-utils";

// Create URL mapping function
const mapPageUrl = defaultMapPageUrl("root-page-id-123");

// Map various page IDs to URLs
const homeUrl = mapPageUrl("root-page-id-123");
// Returns: "/"

const pageUrl = mapPageUrl("abc-123-def-456");
// Returns: "/abc123def456" (dashes removed)

const anotherUrl = mapPageUrl("my-blog-post-789");
// Returns: "/myblogpost789"

// Use in navigation generation
const pages = ["root-page-id-123", "about-page-456", "contact-page-789"];
const navigation = pages.map(pageId => ({
  pageId,
  url: mapPageUrl(pageId),
  title: getPageTitle(recordMap) // Get title from your data
}));

console.log("Navigation:", navigation);
// [
//   { pageId: "root-page-id-123", url: "/", title: "Home" },
//   { pageId: "about-page-456", url: "/aboutpage456", title: "About" },
//   { pageId: "contact-page-789", url: "/contactpage789", title: "Contact" }
// ]

// No root page specified
const simpleMapper = defaultMapPageUrl();
const url = simpleMapper("any-page-id");
// Returns: "/anypageid"

Advanced Data Operations

Record Map Structure

Understanding Notion's record map structure for advanced operations:

interface ExtendedRecordMap {
  block: Record<string, { value: Block; role: Role }>;
  collection?: Record<string, { value: Collection; role: Role }>;
  collection_view?: Record<string, { value: CollectionView; role: Role }>;
  notion_user?: Record<string, { value: NotionUser; role: Role }>;
  collection_query?: Record<string, { value: any; role: Role }>;
  signed_urls?: Record<string, string>;
  preview_images?: Record<string, string>;
}

// Each record has a value and role structure
interface RecordWithRole<T> {
  value: T;
  role: Role;
}

type Role = 'editor' | 'reader' | 'comment_only' | 'read_and_comment';

Working with Record Maps:

import { mergeRecordMaps } from "notion-utils";

// Access blocks with proper typing
function getBlockValue(recordMap: ExtendedRecordMap, blockId: string): Block | undefined {
  return recordMap.block[blockId]?.value;
}

// Merge multiple data sources
function combinePageData(pageMaps: ExtendedRecordMap[]): ExtendedRecordMap {
  return pageMaps.reduce((combined, current) => {
    return mergeRecordMaps(combined, current);
  }, { block: {} } as ExtendedRecordMap);
}

// Extract all user IDs from a record map
function getUserIds(recordMap: ExtendedRecordMap): string[] {
  return Object.keys(recordMap.notion_user || {});
}

// Get all collection IDs
function getCollectionIds(recordMap: ExtendedRecordMap): string[] {
  return Object.keys(recordMap.collection || {});
}

Types

// Core data structure
interface ExtendedRecordMap {
  block: Record<string, { value: Block; role: Role }>;
  collection?: Record<string, { value: Collection; role: Role }>;
  collection_view?: Record<string, { value: CollectionView; role: Role }>;
  notion_user?: Record<string, { value: NotionUser; role: Role }>;
  collection_query?: Record<string, { value: any; role: Role }>;
  signed_urls?: Record<string, string>;
  preview_images?: Record<string, string>;
}

interface Block {
  id: string;
  type: BlockType;
  properties?: Record<string, any>;
  format?: Record<string, any>;
  content?: string[];
  parent_id: string;
  parent_table: string;
  alive: boolean;
  created_time: number;
  last_edited_time: number;
}

type Role = 'editor' | 'reader' | 'comment_only' | 'read_and_comment';

// URL mapping function type
type PageUrlMapper = (pageId: string) => string;

// Image URL mapping function type
type ImageUrlMapper = (url: string, block: Block) => string | undefined;

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