Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added data.db
Binary file not shown.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"plugins/session",
"plugins/rate-limit",
"plugins/edge",
"plugins/jwt"
"plugins/jwt",
"plugins/orm"
],
"author": "angga7togk",
"license": "MIT",
Expand Down
19 changes: 19 additions & 0 deletions plugins/orm/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Changelog

## 1.1.0

### Changed
- Improved relation method implementation in BaseModel for better handling of asynchronous operations and parameter passing
- Refactored type definitions and generated declaration files for enhanced TypeScript support
- Updated tests to reflect changes in relation methods and overall functionality

## 1.0.0

### Added
- Initial release of the Gaman ORM plugin
- Core ORM class (`GamanORM`) for database operations via providers
- Base model class (`BaseModel`) with support for CRUD operations
- Automatic data type casting (int, float, string, boolean, json, datetime)
- Model relations: hasMany, belongsTo, hasOne
- SQLite provider implementation for database connectivity
- Sample models demonstrating usage and relations
203 changes: 203 additions & 0 deletions plugins/orm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
# Gaman ORM Plugin

The Gaman ORM Plugin provides a lightweight Object-Relational Mapping (ORM) system for Gaman applications. It supports basic CRUD operations, automatic data type casting, and model relations through a provider-based architecture.

## Features

- Database-agnostic via providers (e.g., SQLite)
- Automatic data type casting (int, float, string, boolean, json, datetime)
- Model-based relations (hasMany, belongsTo, hasOne)
- Simple query interface
- Lightweight and easy to integrate

## Installation

The ORM plugin is part of the Gaman ecosystem. Ensure you have Gaman installed and include the ORM plugin in your project dependencies.

```bash
npm install @gaman/orm
```

## Setup

1. Import the necessary classes in your application:

```typescript
import { GamanORM, BaseModel, SQLiteProvider } from '@gaman/orm';
```

2. Initialize the ORM with a provider:

```typescript
const orm = new GamanORM(new SQLiteProvider());
await orm.connect();
```

3. Define your models by extending `BaseModel`:

```typescript
interface User {
id: number;
name: string;
email: string;
created_at: Date;
settings: Record<string, any>;
}

class UserModel extends BaseModel<User> {
static options = {
table: 'users',
casts: {
id: 'int',
created_at: 'datetime',
settings: 'json',
},
};

constructor(orm: GamanORM) {
super(orm, UserModel.options);
}
}
```

## Usage

### Basic CRUD Operations

#### Create
```typescript
const userModel = new UserModel(orm);
await userModel.create({
name: 'John Doe',
email: 'john@example.com',
settings: { theme: 'dark' }
});
```

#### Read
```typescript
// Find all users
const users = await userModel.find();

// Find users with specific criteria
const activeUsers = await userModel.find({ active: true });

// Find one user
const user = await userModel.findOne({ id: 1 });
```

#### Update
```typescript
await userModel.update({ id: 1 }, { name: 'Jane Doe' });
```

#### Delete
```typescript
await userModel.delete({ id: 1 });
```

### Data Casting

The ORM automatically casts data types based on the `casts` option in your model:

- `int` or `integer`: Converts to number
- `float` or `double`: Converts to number
- `string`: Converts to string
- `bool` or `boolean`: Converts to boolean
- `json`: Parses JSON string or keeps as object
- `datetime`: Converts to Date object

### Relations

#### hasMany
```typescript
// Assuming a Post model related to User
class PostModel extends BaseModel<Post> {
// ... options
}

const posts = await userModel.hasManyPosts(1); // Get posts for user ID 1
```

#### belongsTo
```typescript
const user = await postModel.belongsToUser(1); // Get user for post ID 1
```

#### hasOne
```typescript
const profile = await userModel.hasOneProfile(1); // Get profile for user ID 1
```

## Providers

### SQLite Provider

The SQLite provider uses `sqlite3` and `sqlite` packages. It connects to a `data.db` file in the current directory.

```typescript
import { SQLiteProvider } from '@gaman/orm';

const provider = new SQLiteProvider();
await provider.connect();
// Perform operations
await provider.disconnect();
```

## Example Application

Here's a complete example:

```typescript
import { GamanORM, BaseModel, SQLiteProvider } from '@gaman/orm';

interface User {
id: number;
name: string;
email: string;
created_at: Date;
}

class UserModel extends BaseModel<User> {
static options = {
table: 'users',
casts: {
id: 'int',
created_at: 'datetime',
},
};

constructor(orm: GamanORM) {
super(orm, UserModel.options);
}
}

async function main() {
const orm = new GamanORM(new SQLiteProvider());
await orm.connect();

const userModel = new UserModel(orm);

// Create a user
await userModel.create({
name: 'Alice',
email: 'alice@example.com',
});

// Find users
const users = await userModel.find();
console.log(users);

await orm.disconnect();
}

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

## Notes

- Ensure your database tables exist before performing operations.
- The ORM does not handle migrations; use your database tools for schema management.
- For production, consider using more robust providers or databases.

For more advanced usage, refer to the source code in `index.ts`, `orm.ts`, and `model/base.ts`.
Binary file added plugins/orm/data.db
Binary file not shown.
55 changes: 32 additions & 23 deletions plugins/orm/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
/**
* @module
* CORS Middleware for Gaman.
* Implements Cross-Origin Resource Sharing (CORS) with customizable options.
* @fileoverview Gaman ORM Plugin
*
* This module provides a lightweight Object-Relational Mapping (ORM) system for Gaman applications.
* It supports basic CRUD operations, data casting, and model relations through a provider-based architecture.
*
* Key features:
* - Database-agnostic via providers (e.g., SQLite)
* - Automatic data type casting
* - Model-based relations (hasMany, belongsTo, hasOne)
* - Simple query interface
*
* @example
* ```typescript
* import { GamanORM, BaseModel, SQLiteProvider } from '@gaman/orm';
*
* const orm = new GamanORM(new SQLiteProvider());
* class User extends BaseModel<User> {
* // Define model options
* }
* ```
*/
import { AppConfig, Handler } from "@gaman/core/types";
/**
* CORS middleware options.
* The main ORM class that handles database connections and operations.
*/
export type CorsOptions = {
/** Allowed origin(s) for the request. */
origin?: string | string[] | null;
/** HTTP methods allowed for the request. Default: `["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"]` */
allowMethods?: string[];
/** Headers allowed in the request. Default: `["Content-Type", "Authorization"]` */
allowHeaders?: string[];
/** Maximum cache age for preflight requests (in seconds). */
maxAge?: number;
/** Whether to include credentials (cookies, HTTP auth, etc.) in the request. */
credentials?: boolean;
/** Headers exposed to the client in the response. */
exposeHeaders?: string[];
};
export { GamanORM } from './orm.js';
/**
* Middleware for handling Cross-Origin Resource Sharing (CORS).
* @param options - The options for configuring CORS behavior.
* @returns Middleware function for handling CORS.
* Base model class for defining database models with casting and relations.
*/
export declare const cors: (options: CorsOptions) => Handler<AppConfig>;
export { BaseModel, BaseModelOptions } from './model/base.js';
/**
* Interface for database providers that implement the actual database interactions.
*/
export { GamanProvider } from './provider/base.js';
/**
* SQLite implementation of the GamanProvider interface.
*/
export { SQLiteProvider } from './provider/sqlite.js';
95 changes: 30 additions & 65 deletions plugins/orm/index.js
Original file line number Diff line number Diff line change
@@ -1,69 +1,34 @@
/**
* @module
* CORS Middleware for Gaman.
* Implements Cross-Origin Resource Sharing (CORS) with customizable options.
* @fileoverview Gaman ORM Plugin
*
* This module provides a lightweight Object-Relational Mapping (ORM) system for Gaman applications.
* It supports basic CRUD operations, data casting, and model relations through a provider-based architecture.
*
* Key features:
* - Database-agnostic via providers (e.g., SQLite)
* - Automatic data type casting
* - Model-based relations (hasMany, belongsTo, hasOne)
* - Simple query interface
*
* @example
* ```typescript
* import { GamanORM, BaseModel, SQLiteProvider } from '@gaman/orm';
*
* const orm = new GamanORM(new SQLiteProvider());
* class User extends BaseModel<User> {
* // Define model options
* }
* ```
*/
import { next } from "@gaman/core/next";
/**
* Middleware for handling Cross-Origin Resource Sharing (CORS).
* @param options - The options for configuring CORS behavior.
* @returns Middleware function for handling CORS.
* The main ORM class that handles database connections and operations.
*/
export const cors = (options) => {
const { origin = "*", allowMethods = ["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"], allowHeaders = [], maxAge, credentials, exposeHeaders, } = options;
return async (ctx) => {
const requestOrigin = ctx.header("Origin");
// Determine allowed origin
let allowedOrigin = "*";
if (typeof origin === "string") {
allowedOrigin = origin;
}
else if (Array.isArray(origin) && origin.includes(requestOrigin || '')) {
allowedOrigin = requestOrigin;
}
else {
allowedOrigin = undefined;
}
// Set CORS headers
const headers = {};
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
if (allowedOrigin !== "*") {
const existingVary = ctx.header("Vary");
if (existingVary) {
ctx.headers.set("Vary", existingVary);
}
else {
ctx.headers.set("Vary", "Origin");
}
}
if (allowedOrigin) {
headers["Access-Control-Allow-Origin"] = allowedOrigin;
}
if (allowMethods.length) {
headers["Access-Control-Allow-Methods"] = allowMethods.join(", ");
}
if (allowHeaders.length) {
headers["Access-Control-Allow-Headers"] = allowHeaders.join(", ");
}
if (maxAge) {
headers["Access-Control-Max-Age"] = maxAge.toString();
}
if (credentials) {
headers["Access-Control-Allow-Credentials"] = "true";
}
if (exposeHeaders?.length) {
headers["Access-Control-Expose-Headers"] = exposeHeaders.join(", ");
}
// Handle preflight request
if (ctx.request.method === "OPTIONS" && requestOrigin) {
return new Response(null, { status: 204, headers });
}
// Add headers to the response and proceed to the next middleware
Object.entries(headers).forEach(([key, value]) => {
if (!ctx.headers.has(key)) {
ctx.headers.set(key, value);
}
});
return await next();
};
};
export { GamanORM } from './orm.js';
/**
* Base model class for defining database models with casting and relations.
*/
export { BaseModel } from './model/base.js';
/**
* SQLite implementation of the GamanProvider interface.
*/
export { SQLiteProvider } from './provider/sqlite.js';
Loading