npm package: @ravenflow/mongo
Description: MongoDB utilities, typed collections, and base repository for Ravenflow
Author: Jeremy LP
Latest version: 0.0.1

# @ravenflow/mongo

Typed MongoDB utilities with Zod schema validation and a ready-to-use base repository.

## Install

```bash
npm install @ravenflow/mongo mongodb zod
```

## Quick Start

### Define a collection

```ts
import { defineCollectionConfig } from "@ravenflow/mongo";
import { z } from "zod";

export const userCollection = defineCollectionConfig({
  name: "users",
  schema: z.object({
    id: z.string(),
    name: z.string(),
    email: z.string(),
    role: z.enum(["admin", "user"]).default("user"),
  }),
  indexes: [
    { keys: { email: 1 }, options: { unique: true } },
  ],
});
```

### Create a repository

```ts
import { BaseMongoRepository } from "@ravenflow/mongo";
import { userCollection } from "./collections";

class UserRepository extends BaseMongoRepository<typeof userCollection> {
  constructor() {
    super(userCollection);
  }

  async findByEmail(email: string) {
    return this.findOne({ email });
  }
}

export const userRepo = new UserRepository();
```

### Connect and use

```ts
import { initDatabase, disconnectDatabase } from "@ravenflow/mongo";
import { userCollection } from "./collections";
import { userRepo } from "./repositories";

await initDatabase("mongodb://localhost:27017/myapp", {
  users: userCollection,
});

// Create — input is typed (no id, no timestamps)
const user = await userRepo.create({ name: "Alice", email: "alice@test.com" });
// => { id: "6654...", name: "Alice", email: "alice@test.com", role: "user" }

// Read
const found = await userRepo.findByEmail("alice@test.com");
const all = await userRepo.find({}, { sort: { name: 1 }, limit: 10 });

// Update — typed partial
await userRepo.update(user.id, { set: { role: "admin" } });

// Delete
await userRepo.delete(user.id);

await disconnectDatabase();
```

## API

### Collection Setup

| Function | Description |
|----------|-------------|
| `defineCollectionConfig(config)` | Type helper for defining a collection config |
| `initDatabase(uri, configs)` | Connect to MongoDB and set up collections + indexes |
| `disconnectDatabase()` | Close the connection |
| `getDb()` | Access the underlying `Db` instance |
| `connectDatabase(uri)` | Low-level connect (returns `{ client, db }`) |
| `setupCollections(db, configs)` | Low-level index sync with stale index cleanup |

### BaseMongoRepository

Extend `BaseMongoRepository<TConfig>` to get typed CRUD out of the box.

**Read**
- `findById(id)` — find by `_id`
- `findOne(filter)` — find one document
- `find(filter?, options?)` — find many (supports `sort`, `skip`, `limit`)
- `count(filter?)` — count documents

**Create**
- `create(data)` — insert one, returns the entity
- `createMany(data[])` — bulk insert

**Update**
- `update(id, { set?, unset? })` — update one by id
- `updateMany(filter, { set?, unset? })` — update many

**Delete**
- `delete(id)` — delete one by id
- `deleteMany(filter)` — delete many

### Timestamps

By default, `createdAt` and `updatedAt` are automatically managed. Opt out per field:

```ts
class EventRepository extends BaseMongoRepository<typeof eventCollection> {
  constructor() {
    super(eventCollection, { updatedAt: false });
  }
}
```

### Types

| Type | Description |
|------|-------------|
| `CollectionConfig<TSchema>` | Collection definition (name, schema, indexes) |
| `EntityOf<TConfig>` | Inferred entity type from the Zod schema |
| `CreateInput<TConfig>` | Input type for `create()` (omits `id`, `createdAt`, `updatedAt`) |
| `DocOf<TConfig>` | MongoDB document type (`_id` instead of `id`) |
| `MongoDoc<TSchema>` | Low-level doc type from a Zod schema |

## License

MIT