npm package: @protoxyz/orm-core
Description: Core utilities and helpers for Protocol projects, including enhanced table operations, pagination, filtering, and sorting for Drizzle ORM
Author: Protoxyz, Inc.
Latest version: 1.0.6

# @protoxyz/orm-core

[![npm version](https://img.shields.io/npm/v/@protoxyz/orm-core.svg)](https://www.npmjs.com/package/@protoxyz/orm-core)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Core utilities and helpers for Protocol projects.

## Installation

Install the package using your preferred package manager:

```bash
bun add @protoxyz/orm-core
# or
pnpm add @protoxyz/orm-core
# or
npm install @protoxyz/orm-core
```

## Usage

This package provides various utility functions and types for use in Protocol projects. Here are some examples:

### Minimal Table Setup

```typescript
import { createEnhancedTable } from '@protoxyz/orm-core';
import { pgTable, text, timestamp, varchar } from 'drizzle-orm/pg-core';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';

// Define your table
const posts = pgTable('posts', {
  id: varchar('id').primaryKey(),
  title: text('title').notNull(),
  content: text('content').notNull(),
  createdAt: timestamp('created_at').notNull().defaultNow(),
  updatedAt: timestamp('updated_at').notNull().defaultNow(),
});

// Create a database connection
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
});
const db = drizzle(pool);

// Create an enhanced table
const enhancedPosts = createEnhancedTable({
  table: posts,
  createdAtColumn: posts.createdAt,
  updatedAtColumn: posts.updatedAt,
});

// Usage example
async function getPosts() {
  const result = await enhancedPosts.paginateWithCursor(db, {
    limit: 10,
    sort: { column: 'createdAt', order: 'desc' },
  });

  console.log(result.items);
  console.log(result.meta);
}

getPosts();
```

### Kitchen Sink Example

```typescript
import { createEnhancedTable, createPaginateParamsCache, parseAsDateRange, parseAsClampedInteger } from '@protoxyz/orm-core';
import { pgTable, text, timestamp, varchar, pgEnum } from 'drizzle-orm/pg-core';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import { eq } from 'drizzle-orm';

// Define an enum for post status
const postStatusEnum = pgEnum('post_status', ['draft', 'published', 'archived']);

// Define a more complex table
const posts = pgTable('posts', {
  id: varchar('id').primaryKey(),
  title: text('title').notNull(),
  content: text('content').notNull(),
  slug: text('slug').notNull().unique(),
  status: postStatusEnum('status').notNull().default('draft'),
  createdAt: timestamp('created_at').notNull().defaultNow(),
  updatedAt: timestamp('updated_at').notNull().defaultNow(),
  deletedAt: timestamp('deleted_at'),
});

// Create a database connection
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
});
const db = drizzle(pool);

// Create an enhanced table with more options
const enhancedPosts = createEnhancedTable({
  table: posts,
  slugColumn: posts.slug,
  createdAtColumn: posts.createdAt,
  updatedAtColumn: posts.updatedAt,
  deletedAtColumn: posts.deletedAt,
  searchColumns: [posts.title, posts.content],
  defaultSort: { column: 'createdAt', order: 'desc' },
  logger: console.log,
});

// Create a pagination params cache
const paginationCache = createPaginateParamsCache({
  status: postStatusEnum,
  createdAt: parseAsDateRange,
  limit: parseAsClampedInteger(1, 100),
});

// Usage examples
async function createPost(title: string, content: string, status: 'draft' | 'published' | 'archived' = 'draft') {
  const result = await enhancedPosts.create(db, { title, content, status });
  console.log('Created post:', result);
}

async function getPosts(searchParams: URLSearchParams) {
  const { cursor, limit, search, status, createdAt, sort } = paginationCache.parse(searchParams);

  const result = await enhancedPosts.paginateWithCursor(db, {
    cursor,
    limit,
    search,
    filters: {
      status: status || undefined,
      createdAt: createdAt || undefined,
    },
    sort,
  });

  console.log('Posts:', result.items);
  console.log('Pagination meta:', result.meta);
}

async function updatePost(id: string, data: Partial<typeof posts.$inferInsert>) {
  const result = await enhancedPosts.update(db, id, data);
  console.log('Updated post:', result);
}

async function deletePost(id: string) {
  const result = await enhancedPosts.delete(db, id);
  console.log('Deleted post:', result);
}

// Example usage
async function main() {
  await createPost('Hello, World!', 'This is my first post.', 'published');

  const searchParams = new URLSearchParams({
    limit: '20',
    status: 'published',
    createdAt: JSON.stringify({ from: '2023-01-01', to: '2023-12-31' }),
    search: 'Hello',
    sort: 'createdAt:desc',
  });

  await getPosts(searchParams);

  await updatePost('some-id', { title: 'Updated Title' });

  await deletePost('some-id');
}

main().catch(console.error);
```

### Working with Cursors

```typescript
import { encodeCursor, decodeCursor } from '@protoxyz/orm-core';

const cursor = 'some-data';
const encoded = encodeCursor(cursor);
console.log(encoded); // Outputs base64url encoded string

const decoded = decodeCursor(encoded);
console.log(decoded); // Outputs 'some-data'
```

## API Documentation

### Enhanced Table

- `createEnhancedTable<T extends PgTableWithColumns<any>>(options: EnhancedTableOptions<T>)`: Creates an enhanced table with additional functionality for pagination, filtering, and sorting.

### Helpers

- `generateUniqueString(length: number): string`: Generates a random string of specified length.
- `encodeCursor(cursor: string): string`: Encodes a cursor string to base64url format.
- `decodeCursor(encodedCursor: string): string`: Decodes a base64url encoded cursor.
- `findColumn(table: Table, dbColumnName: string): keyof typeof table`: Finds a column in a Drizzle ORM table by its database column name.
- `slugify(nameOrSlug: string): string`: Converts a string into a URL-friendly slug.

### Params

- `parseAsSortAndOrder`: Parser for sorting and ordering parameters.
- `parseAsDateRange`: Parser for date range parameters.
- `parseAsClampedInteger(min: number, max: number): ParserBuilder<number>`: Creates a parser for integer values within a specified range.
- `createPaginateParamsCache<Parsers extends PaginateParamParsers>(parsers: Omit<Parsers, "page" | "limit" | "sort" | "search">)`: Creates a cache for pagination parameters.

### Types

- `DB`: Represents a generic PostgreSQL database instance.
- `EnhancedMetricsOptions<T extends Table>`: Options for enhancing metrics functionality for a database table.
- `EnhancedTableOptions<T extends Table>`: Extended options for enhancing table operations and queries.
- `SortOrder`: Defines possible sort orders for query results.
- `SortOption<T extends Table>`: Represents a sorting option for a table.
- `EnhancedTableMeta<T extends Table>`: Metadata for paginated and sorted table queries.
- `DateRange`: Represents a date range for filtering queries.

## Configuration

This package doesn't require any specific configuration. It's designed to work out of the box with Protocol projects.

## Contributing

Contributions are welcome! Please follow these steps to contribute:

1. Fork the repository
2. Create a new branch: `git checkout -b feature/your-feature-name`
3. Make your changes and commit them: `git commit -m 'Add some feature'`
4. Push to the branch: `git push origin feature/your-feature-name`
5. Submit a pull request

Please ensure your code adheres to the existing style and passes all tests.

## Testing

To run the tests, use the following command:

```
bun test
```

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.