@tylix/orm 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @tylix/orm
2
+
3
+ A lightweight ORM for Tylix applications with adapters for SQLite,
4
+ PostgreSQL, MySQL, and MongoDB.
5
+
6
+ ## What's included
7
+
8
+ - **Adapters** — `DatabaseAdapter` (base interface), `SqliteAdapter`,
9
+ `PostgresAdapter`, `MysqlAdapter`, `MongoAdapter`, selected at
10
+ runtime via `AdapterFactory`
11
+ - **Model** — `Model` base class, backed by `ConnectionManager`
12
+ - **Query** — `QueryBuilder` and `buildSelectSql` for building queries
13
+ independent of the underlying adapter
14
+
15
+ ## Usage
16
+
17
+ ```js
18
+ import { Model, QueryBuilder } from '@tylix/orm'
19
+
20
+ class Invoice extends Model {
21
+ static table = 'invoices'
22
+ }
23
+ ```
24
+
25
+ Ships with a `d.ts` declaration file. Pairs with the model/migration
26
+ templates in `@tylix/generator`.
27
+
28
+ ## License
29
+
30
+ MIT
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@tylix/orm",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
+ "types": "./src/index.d.ts",
6
7
  "scripts": {
7
8
  "test": "node --test src/**/*.test.js"
8
9
  },
9
10
  "dependencies": {
10
- "@tylix/shared": "*",
11
+ "@tylix/shared": "^0.1.0",
11
12
  "mongodb": "^6.5.0",
12
13
  "mysql2": "^3.9.0",
13
14
  "pg": "^8.11.0"
@@ -0,0 +1,8 @@
1
+ import type { DatabaseAdapter } from './DatabaseAdapter.js';
2
+
3
+ export interface DatabaseConfig {
4
+ driver: 'sqlite' | 'postgres' | 'mysql' | 'mongodb';
5
+ [key: string]: unknown;
6
+ }
7
+
8
+ export declare function createAdapter(databaseConfig: DatabaseConfig): DatabaseAdapter;
@@ -1,24 +1,24 @@
1
- import { SqliteAdapter } from "./SqliteAdapter.js";
2
- import { PostgresAdapter } from "./PostgresAdapter.js";
3
- import { MysqlAdapter } from "./MysqlAdapter.js";
4
- import { MongoAdapter } from "./MongoAdapter.js";
1
+ import { SqliteAdapter } from './SqliteAdapter.js'
2
+ import { PostgresAdapter } from './PostgresAdapter.js'
3
+ import { MysqlAdapter } from './MysqlAdapter.js'
4
+ import { MongoAdapter } from './MongoAdapter.js'
5
5
 
6
6
  const ADAPTERS = {
7
7
  sqlite: SqliteAdapter,
8
8
  postgres: PostgresAdapter,
9
9
  mysql: MysqlAdapter,
10
10
  mongodb: MongoAdapter,
11
- };
11
+ }
12
12
 
13
13
  export function createAdapter(databaseConfig) {
14
- const { driver, ...options } = databaseConfig;
14
+ const { driver, ...options } = databaseConfig
15
15
 
16
- const AdapterClass = ADAPTERS[driver];
16
+ const AdapterClass = ADAPTERS[driver]
17
17
  if (!AdapterClass) {
18
18
  throw new Error(
19
- `Unknown database driver "${driver}". Available drivers: ${Object.keys(ADAPTERS).join(", ")}`
20
- );
19
+ `Unknown database driver "${driver}". Available drivers: ${Object.keys(ADAPTERS).join(', ')}`,
20
+ )
21
21
  }
22
22
 
23
- return new AdapterClass(options);
23
+ return new AdapterClass(options)
24
24
  }
@@ -0,0 +1,18 @@
1
+ import type { QueryDescriptor } from '../query/types.js';
2
+
3
+ export interface RunResult {
4
+ lastInsertRowid: number;
5
+ }
6
+
7
+ export declare class DatabaseAdapter {
8
+ connect(): Promise<this>;
9
+ close(): Promise<void>;
10
+ run(sql: string, params?: unknown[]): Promise<RunResult>;
11
+ get(sql: string, params?: unknown[]): Promise<Record<string, unknown> | null>;
12
+ all(sql: string, params?: unknown[]): Promise<Record<string, unknown>[]>;
13
+ columnType(logicalType: string): string | null;
14
+ count(table: string): Promise<number>;
15
+ paginate(table: string, limit: number, offset: number): Promise<Record<string, unknown>[]>;
16
+ listTables(): Promise<string[]>;
17
+ query(table: string, descriptor?: QueryDescriptor): Promise<Record<string, unknown>[]>;
18
+ }
@@ -0,0 +1,23 @@
1
+ import { DatabaseAdapter } from './DatabaseAdapter.js';
2
+
3
+ export interface MongoAdapterOptions {
4
+ url?: string;
5
+ database?: string;
6
+ }
7
+
8
+ export type ParsedMongoQuery =
9
+ | { type: 'createTable'; table: string }
10
+ | { type: 'dropTable'; table: string }
11
+ | { type: 'alterAddColumn'; table: string; column: string }
12
+ | { type: 'alterDropColumn'; table: string; column: string }
13
+ | { type: 'findOne'; table: string; field: string }
14
+ | { type: 'findAll'; table: string }
15
+ | { type: 'insert'; table: string; columns: string[] }
16
+ | { type: 'update'; table: string; columns: string[]; whereField: string }
17
+ | { type: 'delete'; table: string; field: string };
18
+
19
+ export declare function parseQuery(sql: string): ParsedMongoQuery;
20
+
21
+ export declare class MongoAdapter extends DatabaseAdapter {
22
+ constructor(options?: MongoAdapterOptions);
23
+ }
@@ -0,0 +1,15 @@
1
+ import { DatabaseAdapter } from './DatabaseAdapter.js';
2
+
3
+ export interface MysqlAdapterOptions {
4
+ host?: string;
5
+ port?: number;
6
+ user?: string;
7
+ password?: string;
8
+ database?: string;
9
+ }
10
+
11
+ export declare class MysqlAdapter extends DatabaseAdapter {
12
+ constructor(options?: MysqlAdapterOptions);
13
+ }
14
+
15
+ export declare function toMysqlDateTime(value: unknown): unknown;
@@ -0,0 +1,17 @@
1
+ import { DatabaseAdapter } from './DatabaseAdapter.js';
2
+
3
+ export interface PostgresAdapterOptions {
4
+ connectionString?: string;
5
+ host?: string;
6
+ port?: number;
7
+ user?: string;
8
+ password?: string;
9
+ database?: string;
10
+ }
11
+
12
+ export declare class PostgresAdapter extends DatabaseAdapter {
13
+ constructor(options?: PostgresAdapterOptions);
14
+ }
15
+
16
+ export declare function toPositionalPlaceholders(sql: string): string;
17
+ export declare function ensureReturningId(sql: string): string;
@@ -0,0 +1,9 @@
1
+ import { DatabaseAdapter } from './DatabaseAdapter.js';
2
+
3
+ export interface SqliteAdapterOptions {
4
+ filename?: string;
5
+ }
6
+
7
+ export declare class SqliteAdapter extends DatabaseAdapter {
8
+ constructor(options?: SqliteAdapterOptions);
9
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export * from "./adapters/DatabaseAdapter.js";
2
+ export * from "./adapters/SqliteAdapter.js";
3
+ export * from "./adapters/PostgresAdapter.js";
4
+ export * from "./model/ConnectionManager.js";
5
+ export * from "./model/Model.js";
6
+ export * from "./adapters/AdapterFactory.js";
7
+ export * from "./adapters/MysqlAdapter.js";
8
+ export * from "./adapters/MongoAdapter.js";
9
+ export * from "./query/QueryBuilder.js";
10
+ export * from "./query/buildSelectSql.js";
11
+ export * from "./query/types.js";
package/src/index.js CHANGED
@@ -1,9 +1,10 @@
1
- export * from "./adapters/DatabaseAdapter.js";
2
- export * from "./adapters/SqliteAdapter.js";
3
- export * from "./model/ConnectionManager.js";
4
- export * from "./model/Model.js";
5
- export * from "./adapters/AdapterFactory.js";
6
- export * from "./adapters/MysqlAdapter.js";
7
- export * from "./adapters/MongoAdapter.js";
8
- export * from "./query/QueryBuilder.js";
9
- export * from "./query/buildSelectSql.js";
1
+ export * from './adapters/DatabaseAdapter.js'
2
+ export * from './adapters/SqliteAdapter.js'
3
+ export * from './adapters/PostgresAdapter.js'
4
+ export * from './model/ConnectionManager.js'
5
+ export * from './model/Model.js'
6
+ export * from './adapters/AdapterFactory.js'
7
+ export * from './adapters/MysqlAdapter.js'
8
+ export * from './adapters/MongoAdapter.js'
9
+ export * from './query/QueryBuilder.js'
10
+ export * from './query/buildSelectSql.js'
@@ -0,0 +1,7 @@
1
+ import type { DatabaseAdapter } from '../adapters/DatabaseAdapter.js';
2
+
3
+ export declare const ConnectionManager: {
4
+ setAdapter(adapter: DatabaseAdapter): void;
5
+ getAdapter(): DatabaseAdapter;
6
+ reset(): void;
7
+ };
@@ -0,0 +1,49 @@
1
+ // orm/src/model/Model.d.ts
2
+ import { QueryBuilder } from '../query/QueryBuilder.js';
3
+
4
+ interface PaginateOptions {
5
+ page?: number;
6
+ limit?: number;
7
+ where?: Record<string, unknown>;
8
+ }
9
+
10
+ interface PaginateResult<T> {
11
+ data: T[];
12
+ meta: {
13
+ page: number;
14
+ limit: number;
15
+ total: number;
16
+ totalPages: number;
17
+ };
18
+ }
19
+
20
+ export declare class Model {
21
+ static table: string | null;
22
+ static fillable: string[];
23
+ static timestamps: boolean;
24
+
25
+ static getTable(): string;
26
+ static getAdapter(): unknown;
27
+
28
+ static all(): Promise<Record<string, unknown>[]>;
29
+ static query(): QueryBuilder<Record<string, unknown>>;
30
+ static paginate(options?: PaginateOptions): Promise<PaginateResult<Record<string, unknown>>>;
31
+ static find(id: number | string): Promise<Record<string, unknown> | null>;
32
+ static create(data: Record<string, unknown>): Promise<Record<string, unknown> | null>;
33
+ static update(id: number | string, data: Record<string, unknown>): Promise<Record<string, unknown> | null>;
34
+ static delete(id: number | string): Promise<boolean>;
35
+
36
+ static belongsTo(
37
+ row: Record<string, unknown> | null,
38
+ foreignKey: string,
39
+ RelatedModel: typeof Model,
40
+ ): Promise<Record<string, unknown> | null>;
41
+ static hasMany(
42
+ row: { id: number },
43
+ RelatedModel: typeof Model,
44
+ foreignKey: string,
45
+ ): Promise<Record<string, unknown>[]>;
46
+
47
+ static first(): Promise<Record<string, unknown> | null>;
48
+ static count(): Promise<number>;
49
+ }
@@ -30,14 +30,18 @@ export class Model {
30
30
  return new QueryBuilder(this)
31
31
  }
32
32
 
33
- static async paginate({ page = 1, limit = 20 } = {}) {
34
- const adapter = ConnectionManager.getAdapter()
33
+ static async paginate({ page = 1, limit = 20, where = {} } = {}) {
35
34
  const safePage = Math.max(1, Math.floor(page))
36
35
  const safeLimit = Math.max(1, Math.min(100, Math.floor(limit)))
37
36
  const offset = (safePage - 1) * safeLimit
38
37
 
39
- const total = await adapter.count(this.getTable())
40
- const data = await adapter.paginate(this.getTable(), safeLimit, offset)
38
+ let query = this.query()
39
+ for (const [field, value] of Object.entries(where)) {
40
+ query = query.where(field, value)
41
+ }
42
+
43
+ const total = await query.count()
44
+ const data = await query.limit(safeLimit).offset(offset).get()
41
45
 
42
46
  return {
43
47
  data,
@@ -49,7 +53,6 @@ export class Model {
49
53
  },
50
54
  }
51
55
  }
52
-
53
56
  static async find(id) {
54
57
  const adapter = ConnectionManager.getAdapter()
55
58
  return adapter.get(`SELECT * FROM ${this.getTable()} WHERE id = ?`, [id])
@@ -0,0 +1,16 @@
1
+ import type { WhereClause, QueryDescriptor } from './types.js';
2
+
3
+ export declare class QueryBuilder<TRow> {
4
+ constructor(ModelClass: { getAdapter(): unknown; getTable(): string });
5
+
6
+ where(field: string, value: unknown): this;
7
+ where(field: string, operator: WhereClause['operator'], value: unknown): this;
8
+ orderBy(field: string, direction?: 'ASC' | 'DESC'): this;
9
+ limit(value: number): this;
10
+ offset(value: number): this;
11
+ toDescriptor(): QueryDescriptor;
12
+
13
+ get(): Promise<TRow[]>;
14
+ first(): Promise<TRow | null>;
15
+ count(): Promise<number>;
16
+ }
@@ -0,0 +1,11 @@
1
+ import type { QueryDescriptor } from './types.js';
2
+
3
+ export interface BuiltSelect {
4
+ sql: string;
5
+ params: unknown[];
6
+ }
7
+
8
+ export declare function buildSelectSql(
9
+ table: string,
10
+ descriptor?: QueryDescriptor
11
+ ): BuiltSelect;
@@ -0,0 +1,18 @@
1
+ // src/query/types.d.ts
2
+ export interface WhereClause {
3
+ field: string
4
+ operator: '=' | '!=' | '>' | '<' | '>=' | '<='
5
+ value: unknown
6
+ }
7
+
8
+ export interface OrderClause {
9
+ field: string
10
+ direction: 'ASC' | 'DESC'
11
+ }
12
+
13
+ export interface QueryDescriptor {
14
+ wheres: WhereClause[]
15
+ orders: OrderClause[]
16
+ limitValue?: number
17
+ offsetValue?: number
18
+ }
@@ -0,0 +1,4 @@
1
+ // No runtime values — this module exists purely so its .d.ts sibling
2
+ // can export shared query-shape types (WhereClause, QueryDescriptor)
3
+ // referenced by QueryBuilder and DatabaseAdapter.
4
+ export {}
package/src/types.ts ADDED
@@ -0,0 +1,25 @@
1
+
2
+
3
+ export interface ColumnBuilder {
4
+ unique(): void;
5
+ }
6
+
7
+ export interface TableBuilder {
8
+ increments(name: string): ColumnBuilder;
9
+ string(name: string): ColumnBuilder;
10
+ text(name: string): ColumnBuilder;
11
+ boolean(name: string): ColumnBuilder;
12
+ integer(name: string): ColumnBuilder;
13
+ date(name: string): ColumnBuilder;
14
+ datetime(name: string): ColumnBuilder;
15
+ json(name: string): ColumnBuilder;
16
+ timestamps(): void;
17
+ // present only in alterTable's callback, absent from createTable's
18
+ dropColumn?(name: string): void;
19
+ }
20
+
21
+ export interface Schema {
22
+ createTable(tableName: string, callback: (table: TableBuilder) => void | Promise<void>): Promise<void>;
23
+ dropTable(tableName: string): Promise<void>;
24
+ alterTable(tableName: string, callback: (table: TableBuilder) => void | Promise<void>): Promise<void>;
25
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "allowJs": true,
7
+ "checkJs": false,
8
+ "declaration": false,
9
+ "noEmit": true,
10
+ "strict": true,
11
+ "types": ["node"]
12
+ },
13
+ "include": ["src/**/*"]
14
+ }