Skip to main content

Overview

The @brainbox/core package provides shared types, utilities, node models, and business logic used across all Brainbox packages. It defines the core data structures for workspaces, nodes, documents, and synchronization.

Installation

npm install @brainbox/core

Exports

import * as Core from '@brainbox/core';

// Or import specific modules
import { generateId, IdType, NodeModel } from '@brainbox/core';

ID Generation

Utilities for generating type-safe unique identifiers using ULID.

generateId

Generates a new unique identifier for a specific type.
type
IdType
required
The type of ID to generate (Account, Workspace, User, Node, etc.)
returns
string
A unique ID string with the type suffix
import { generateId, IdType } from '@brainbox/core';

const workspaceId = generateId(IdType.Workspace); // '01j1234567890abcdefghwc'
const nodeId = generateId(IdType.Node); // '01j1234567890abcdefghnd'

isIdOfType

Checks if an ID belongs to a specific type.
id
string
required
The ID to check
type
IdType
required
The expected type
returns
boolean
True if the ID matches the type
import { isIdOfType, IdType } from '@brainbox/core';

const isWorkspace = isIdOfType('01j1234567890abcdefghwc', IdType.Workspace); // true

getIdType

Extracts the type from an ID.
id
string
required
The ID to extract type from
returns
IdType
The ID type
import { getIdType, IdType } from '@brainbox/core';

const type = getIdType('01j1234567890abcdefghwc'); // IdType.Workspace

IdType Enum

enum IdType {
  Account = 'ac',
  Workspace = 'wc',
  User = 'us',
  Space = 'sp',
  Page = 'pg',
  Channel = 'ch',
  Chat = 'ct',
  Node = 'nd',
  Message = 'ms',
  Database = 'db',
  Record = 'rc',
  Folder = 'fl',
  DatabaseView = 'dv',
  Field = 'fd',
  File = 'fi',
  // ... and more
}

Node Models

Node models define the schema, permissions, and behavior for each node type.

Node Types

Space
NodeModel
Top-level workspace container
Folder
NodeModel
Organizes pages and other nodes
Page
NodeModel
Rich text document with CRDT support
Database
NodeModel
Structured data collection with fields and records
DatabaseView
NodeModel
Filtered/sorted view of a database
Record
NodeModel
Row in a database
Field
NodeModel
Column definition in a database
Channel
NodeModel
Public discussion channel
Chat
NodeModel
Private chat conversation
Message
NodeModel
Message in a channel or chat
File
NodeModel
Uploaded file attachment

NodeModel Interface

interface NodeModel {
  type: string;
  attributesSchema: z.ZodType;
  documentSchema?: z.ZodType;
  canCreate: (context: CanCreateNodeContext) => boolean;
  canUpdateAttributes: (context: CanUpdateAttributesContext) => boolean;
  canUpdateDocument: (context: CanUpdateDocumentContext) => boolean;
  canDelete: (context: CanDeleteNodeContext) => boolean;
  canReact: (context: CanReactNodeContext) => boolean;
  extractText: (id: string, attributes: NodeAttributes) => NodeText | null;
  extractMentions: (id: string, attributes: NodeAttributes) => Mention[];
}

getNodeModel

Retrieve the model for a specific node type.
type
NodeType
required
Node type (space, page, database, etc.)
returns
NodeModel
The node model with schema and permissions
import { getNodeModel } from '@brainbox/core';

const pageModel = getNodeModel('page');
const canCreate = pageModel.canCreate(context);

Node Structure

type Node = {
  id: string;
  type: NodeType;
  parentId: string;
  rootId: string;
  attributes: NodeAttributes;
  createdAt: string;
  createdBy: string;
  updatedAt: string | null;
  updatedBy: string | null;
};

Node Utilities

extractNodeCollaborators

Extracts collaborators and their roles from node attributes.
attributes
NodeAttributes | NodeAttributes[]
required
Node attributes
returns
Record<string, NodeRole>
Map of user IDs to roles
import { extractNodeCollaborators } from '@brainbox/core';

const collaborators = extractNodeCollaborators(node.attributes);
// { 'user123': 'admin', 'user456': 'member' }

extractNodeName

Extracts the display name from node attributes.
attributes
NodeAttributes
required
Node attributes
returns
string | null
The node name or null
import { extractNodeName } from '@brainbox/core';

const name = extractNodeName(node.attributes); // 'My Page'

extractNodeRole

Determines the effective role for a collaborator on a node.
tree
Node | Node[]
required
Node ancestry tree
collaboratorId
string
required
User ID to check
workspaceRole
WorkspaceRole
User’s workspace-level role
returns
NodeRole | null
The effective role (admin, member, viewer)
import { extractNodeRole } from '@brainbox/core';

const role = extractNodeRole(tree, userId, workspaceRole);
if (role === 'admin') {
  // User can edit
}

generateFractionalIndex

Generates a fractional index for positioning between two items.
previous
string | null
Previous item’s index
next
string | null
Next item’s index
returns
string
Fractional index string
import { generateFractionalIndex } from '@brainbox/core';

const newIndex = generateFractionalIndex(prevIndex, nextIndex);

Mutations

Mutation types for sync operations.

Mutation Types

type Mutation =
  | CreateNodeMutation
  | UpdateNodeMutation
  | DeleteNodeMutation
  | CreateNodeReactionMutation
  | DeleteNodeReactionMutation
  | NodeInteractionSeenMutation
  | NodeInteractionOpenedMutation
  | UpdateDocumentMutation;

CreateNodeMutation

type CreateNodeMutation = {
  id: string;
  type: 'node.create';
  createdAt: string;
  data: {
    nodeId: string;
    updateId: string;
    createdAt: string;
    data: string; // Encoded CRDT update
  };
};

UpdateNodeMutation

type UpdateNodeMutation = {
  id: string;
  type: 'node.update';
  createdAt: string;
  data: {
    nodeId: string;
    updateId: string;
    createdAt: string;
    data: string; // Encoded CRDT update
  };
};

UpdateDocumentMutation

type UpdateDocumentMutation = {
  id: string;
  type: 'document.update';
  createdAt: string;
  data: {
    documentId: string;
    updateId: string;
    createdAt: string;
    data: string; // Encoded CRDT update
  };
};

Synchronizers

Synchronizer definitions for real-time sync.

SynchronizerMap Interface

interface SynchronizerMap {
  'nodes.updates': {
    input: NodesUpdatesSyncInput;
    data: NodesUpdatesData;
  };
  'document.updates': {
    input: DocumentUpdatesSyncInput;
    data: DocumentUpdatesData;
  };
  'users': {
    input: UsersSyncInput;
    data: UsersData;
  };
  'node.reactions': {
    input: NodeReactionsSyncInput;
    data: NodeReactionsData;
  };
  'node.interactions': {
    input: NodeInteractionsSyncInput;
    data: NodeInteractionsData;
  };
  'node.tombstones': {
    input: NodeTombstonesSyncInput;
    data: NodeTombstonesData;
  };
  'collaborations': {
    input: CollaborationsSyncInput;
    data: CollaborationsData;
  };
}

type SynchronizerInput = SynchronizerMap[keyof SynchronizerMap]['input'];

Type Definitions

WorkspaceRole

type WorkspaceRole = 'owner' | 'admin' | 'member' | 'viewer';

NodeRole

type NodeRole = 'admin' | 'member' | 'viewer';

MutationStatus

enum MutationStatus {
  OK = 200,
  CREATED = 201,
  BAD_REQUEST = 400,
  UNAUTHORIZED = 401,
  FORBIDDEN = 403,
  NOT_FOUND = 404,
  CONFLICT = 409,
  METHOD_NOT_ALLOWED = 405,
  INTERNAL_SERVER_ERROR = 500,
}

Constants

// Export from lib/constants
export const MAX_FILE_SIZE: number;
export const SUPPORTED_FILE_TYPES: string[];
export const DEFAULT_PAGE_SIZE: number;

Utilities

// Export from lib/utils
export function debounce<T>(fn: T, delay: number): T;
export function throttle<T>(fn: T, delay: number): T;
export function clamp(value: number, min: number, max: number): number;

Build docs developers (and LLMs) love