@shadow-dev/orm 1.0.0

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/.eslintrc.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "root": true,
3
+ "parser": "@typescript-eslint/parser",
4
+ "plugins": ["@typescript-eslint"],
5
+ "extends": [
6
+ "eslint:recommended",
7
+ "plugin:@typescript-eslint/recommended"
8
+ ],
9
+ "env": {
10
+ "node": true,
11
+ "es2021": true
12
+ },
13
+ "parserOptions": {
14
+ "ecmaVersion": 2021,
15
+ "sourceType": "module"
16
+ },
17
+ "rules": {
18
+ "@typescript-eslint/no-unused-vars": ["warn"],
19
+ "@typescript-eslint/no-explicit-any": "off"
20
+ }
21
+ }
@@ -0,0 +1,37 @@
1
+ ## 📝 Description
2
+ <!-- Describe your changes in detail. Explain the problem and how your PR fixes it. -->
3
+
4
+ ## 🔍 Related Issues
5
+ <!-- Link any relevant issues (e.g., "Closes #123") -->
6
+
7
+ ## 🚀 Changes Made
8
+ - [ ] Bug fix 🐛
9
+ - [ ] New feature ✨
10
+ - [ ] Code refactor 🔧
11
+ - [ ] Documentation update 📖
12
+ - [ ] Security improvement 🔒
13
+ - [ ] Other (please specify): ____________
14
+
15
+ ## ✅ Testing Steps
16
+ <!-- Provide instructions for testing your changes. Include commands, expected behavior, and test cases if applicable. -->
17
+ 1.
18
+ 2.
19
+ 3.
20
+
21
+ ## 🔒 Security Considerations
22
+ <!-- Any security implications? Dependencies updated? New permissions required? -->
23
+
24
+ ## 📸 Screenshots / Logs (if applicable)
25
+ <!-- Add screenshots or logs if they help understand the change. -->
26
+
27
+ ## ⏳ Checklist
28
+ - [ ] My code follows the project coding style.
29
+ - [ ] I have run tests to verify my changes.
30
+ - [ ] I have updated the documentation if needed.
31
+ - [ ] My changes do not introduce new vulnerabilities.
32
+ - [ ] This PR is ready for review.
33
+
34
+ ---
35
+
36
+ ### 🚀 Additional Notes
37
+ <!-- Anything else reviewers should know? -->
@@ -0,0 +1,34 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
+
6
+ version: 2
7
+ updates:
8
+ # 📦 Keep npm dependencies up to date
9
+ - package-ecosystem: "npm"
10
+ directory: "/" # Root directory (where package.json is)
11
+ schedule:
12
+ interval: "daily"
13
+ open-pull-requests-limit: 5
14
+ versioning-strategy: increase
15
+ labels:
16
+ - "dependencies"
17
+ - "npm"
18
+ reviewers:
19
+ - "Shadows-Development" # Add yourself or contributors as reviewers
20
+ commit-message:
21
+ prefix: "⬆️ Bump"
22
+ include: "scope" # Includes dependency name in commit message
23
+
24
+ # 🔒 Check for security updates in GitHub Actions (workflow dependencies)
25
+ - package-ecosystem: "github-actions"
26
+ directory: "/" # Root directory (where workflows are)
27
+ schedule:
28
+ interval: "daily"
29
+ labels:
30
+ - "security"
31
+ - "github-actions"
32
+ commit-message:
33
+ prefix: "🔄 Update"
34
+ include: "scope"
@@ -0,0 +1,139 @@
1
+ name: PR Checks
2
+
3
+ on:
4
+ pull_request: # Runs on every commit in an open PR
5
+
6
+ jobs:
7
+ build:
8
+ name: 🏗 Build & Type Check
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Checkout repository
12
+ uses: actions/checkout@v4
13
+
14
+ - name: Setup Node.js
15
+ uses: actions/setup-node@v4
16
+ with:
17
+ node-version: 20
18
+ cache: 'npm' # Speeds up installs
19
+
20
+ - name: Restore dependency cache
21
+ uses: actions/cache@v4
22
+ with:
23
+ path: ~/.npm
24
+ key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
25
+ restore-keys: |
26
+ npm-${{ runner.os }}-
27
+
28
+ - name: Install dependencies
29
+ run: |
30
+ if [ -f package-lock.json ]; then
31
+ npm ci
32
+ else
33
+ npm install
34
+ fi
35
+
36
+ - name: TypeScript type checking
37
+ run: npm run type-check
38
+
39
+ - name: Build project
40
+ run: npm run build
41
+
42
+ lint:
43
+ name: 🎨 Lint Code
44
+ runs-on: ubuntu-latest
45
+ needs: build # Runs only if "build" passes
46
+ steps:
47
+ - name: Checkout repository
48
+ uses: actions/checkout@v4
49
+
50
+ - name: Setup Node.js
51
+ uses: actions/setup-node@v4
52
+ with:
53
+ node-version: 20
54
+ cache: 'npm'
55
+
56
+ - name: Restore dependency cache
57
+ uses: actions/cache@v4
58
+ with:
59
+ path: ~/.npm
60
+ key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
61
+ restore-keys: |
62
+ npm-${{ runner.os }}-
63
+
64
+ - name: Install dependencies
65
+ run: |
66
+ if [ -f package-lock.json ]; then
67
+ npm ci
68
+ else
69
+ npm install
70
+ fi
71
+
72
+ - name: Run ESLint
73
+ run: npm run lint
74
+
75
+ security:
76
+ name: 🔒 Security Audit
77
+ runs-on: ubuntu-latest
78
+ needs: lint # Runs only if "lint" passes
79
+ steps:
80
+ - name: Checkout repository
81
+ uses: actions/checkout@v4
82
+
83
+ - name: Setup Node.js
84
+ uses: actions/setup-node@v4
85
+ with:
86
+ node-version: 20
87
+ cache: 'npm'
88
+
89
+ - name: Restore dependency cache
90
+ uses: actions/cache@v4
91
+ with:
92
+ path: ~/.npm
93
+ key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
94
+ restore-keys: |
95
+ npm-${{ runner.os }}-
96
+
97
+ - name: Install dependencies
98
+ run: |
99
+ if [ -f package-lock.json ]; then
100
+ npm ci
101
+ else
102
+ npm install
103
+ fi
104
+
105
+ - name: Run npm audit
106
+ run: npm audit --audit-level=high || exit 1
107
+
108
+ tests:
109
+ name: 🧪 Run Tests
110
+ runs-on: ubuntu-latest
111
+ needs: security # Runs only if "security" passes
112
+ steps:
113
+ - name: Checkout repository
114
+ uses: actions/checkout@v4
115
+
116
+ - name: Setup Node.js
117
+ uses: actions/setup-node@v4
118
+ with:
119
+ node-version: 20
120
+ cache: 'npm'
121
+
122
+ - name: Restore dependency cache
123
+ uses: actions/cache@v4
124
+ with:
125
+ path: ~/.npm
126
+ key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
127
+ restore-keys: |
128
+ npm-${{ runner.os }}-
129
+
130
+ - name: Install dependencies
131
+ run: |
132
+ if [ -f package-lock.json ]; then
133
+ npm ci
134
+ else
135
+ npm install
136
+ fi
137
+
138
+ - name: Run tests
139
+ run: npm test
@@ -0,0 +1,31 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*" # Triggers when you push a tag like "v1.0.0"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: Checkout Repository
14
+ uses: actions/checkout@v4
15
+
16
+ - name: Setup Node.js
17
+ uses: actions/setup-node@v4
18
+ with:
19
+ node-version: 18
20
+ registry-url: "https://registry.npmjs.org/"
21
+
22
+ - name: Install Dependencies
23
+ run: npm install
24
+
25
+ - name: Build Package
26
+ run: npm run build # Skip if no build step
27
+
28
+ - name: Publish to npm
29
+ run: npm publish --access public
30
+ env:
31
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,23 @@
1
+ name: Release Please
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ permissions:
9
+ contents: write
10
+ pull-requests: write
11
+
12
+
13
+ jobs:
14
+ release:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - name: Checkout Repo
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Run Release Please
21
+ uses: googleapis/release-please-action@v4
22
+ with:
23
+ release-type: node
@@ -0,0 +1,31 @@
1
+ name: Setup Project
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ permissions:
7
+ contents: write
8
+
9
+ jobs:
10
+ setup:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout code
14
+ uses: actions/checkout@v4
15
+
16
+ - name: Setup Node.js
17
+ uses: actions/setup-node@v4
18
+ with:
19
+ node-version: 18
20
+
21
+ - name: Run setup script
22
+ run: node scripts/setup.js
23
+
24
+ - name: Push updated files
25
+ run: |
26
+ git config user.name "ShadowBot"
27
+ git config user.email "bot@shadowdev.local"
28
+ git remote set-url origin https://x-access-token:${{ secrets.PAT_TOKEN }}@github.com/${{ github.repository }}
29
+ git add .
30
+ git commit -m "chore: initialize project metadata from setup script" || echo "No changes"
31
+ git push
@@ -0,0 +1,26 @@
1
+ name: SonarCloud Analysis
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ pull_request:
7
+ branches:
8
+ - main
9
+
10
+ jobs:
11
+ sonarcloud:
12
+ name: SonarCloud Scan
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - name: Checkout code
17
+ uses: actions/checkout@v2
18
+
19
+ - name: Set up SonarCloud
20
+ uses: SonarSource/sonarcloud-github-action@v1.1
21
+
22
+ - name: Run SonarCloud analysis
23
+ env:
24
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
25
+ run: |
26
+ sonar-scanner
package/LICENSE ADDED
@@ -0,0 +1 @@
1
+ MIT License © Shadows Development
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # ShadowORM
2
+
3
+ > 🧩 Lightweight, type-safe MySQL ORM for ShadowCore projects.
4
+ > ShadowORM is built for **modularity**, **security**, and **runtime schema sync** ΓÇö perfect for bots, services, or web apps using ShadowCore.
5
+
6
+ ![npm version](https://img.shields.io/npm/v/@shadow-dev/orm?style=flat-square)
7
+ ![license](https://img.shields.io/github/license/Shadows-Development/ShadowORM?style=flat-square)
8
+
9
+ ---
10
+
11
+ ## 🔍 Overview
12
+
13
+ ShadowORM is a minimalist ORM that offers:
14
+
15
+ - ✅ **Type-safe models** using generics
16
+ - ✅ **Automatic schema synchronization** (no migration needed)
17
+ - ✅ **JSON + Date normalization**
18
+ - ✅ **Relational support** with foreign keys
19
+ - ✅ **No decorators, no reflection, no magic**
20
+
21
+ ItΓÇÖs designed to work cleanly alongside ShadowCore but can also be used standalone in any Node.js TypeScript project.
22
+
23
+ ---
24
+
25
+ ## 📦 Installation
26
+
27
+ ```bash
28
+ npm install @shadow-dev/orm mysql2
29
+ ```
30
+
31
+ ---
32
+
33
+ ## 🛠 Usage Example
34
+
35
+ ```ts
36
+ import { Model, Repository, initDatabase, registerModel } from "@shadow-dev/orm";
37
+
38
+ const Ticket = new Model<{
39
+ id: string;
40
+ type: "support" | "report";
41
+ data: { message: string };
42
+ createdAt: Date;
43
+ }>("tickets", {
44
+ id: "string",
45
+ type: "string",
46
+ data: "json",
47
+ createdAt: "datetime"
48
+ });
49
+
50
+ initDatabase({
51
+ host: "localhost",
52
+ user: "root",
53
+ password: "password",
54
+ database: "mydb"
55
+ });
56
+
57
+ registerModel(Ticket);
58
+
59
+ // Auto-create table on startup
60
+ await syncSchema();
61
+
62
+ const tickets = new Repository(Ticket);
63
+
64
+
65
+ // Use it
66
+ await tickets.create({
67
+ id: "ticket-001",
68
+ type: "support",
69
+ data: { message: "Help me!" },
70
+ createdAt: new Date()
71
+ });
72
+ ```
73
+
74
+ ---
75
+
76
+ ## 🧠 Schema Types
77
+
78
+ ShadowORM supports:
79
+
80
+ | Type | SQL Equivalent |
81
+ |---------------------------|-------------------|
82
+ | `string` | `VARCHAR(255)` |
83
+ | `number` | `INT` |
84
+ | `boolean` | `BOOLEAN` |
85
+ | `json` | `LONGTEXT` |
86
+ | `datetime` | `DATETIME` |
87
+ | `FOREIGN_KEY:<tbl.col>` | Foreign key ref |
88
+
89
+ ---
90
+
91
+ ## 🧱 Roadmap
92
+
93
+ - [x] CRUD repository
94
+ - [x] Relational schema support
95
+ - [x] Automatic schema sync
96
+ - [ ] CLI (optional)
97
+ - [ ] Migrations (optional)
98
+ - [ ] Postgres support (maybe)
99
+
100
+ ---
101
+
102
+ ## 📖 Documentation
103
+
104
+ 📚 Docs are coming soon and will be available on the ShadowCore documentation site:
105
+ ➡️ [docs.shadowdevelopment.net](https://docs.shadowdevelopment.net)
106
+
107
+ ---
108
+
109
+ ## 🏢 Project Ownership
110
+
111
+ ShadowORM is officially developed and maintained under [Shadow Development LLC](https://shadowdevelopment.net).
112
+
113
+ ---
114
+
115
+ ## 📜 License
116
+
117
+ Licensed under the **GNU General Public License v3.0**
118
+ See the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,39 @@
1
+ import js from "@eslint/js";
2
+ import globals from "globals";
3
+ import tseslint from "typescript-eslint";
4
+ import json from "@eslint/json";
5
+ import { defineConfig, globalIgnores } from "eslint/config";
6
+
7
+
8
+ // export default defineConfig([
9
+ // { files: ["**/*.{js,mjs,cjs,ts}"], plugins: { js }, extends: ["js/recommended"] },
10
+ // { files: ["**/*.js"], languageOptions: { sourceType: "script" } },
11
+ // { files: ["**/*.{js,mjs,cjs,ts}"], languageOptions: { globals: {...globals.browser, ...globals.node} } },
12
+ // tseslint.configs.recommended,
13
+ // ]);
14
+ export default defineConfig([
15
+ { files: ["**/*.{js,mjs,cjs,ts}"] },
16
+ {
17
+ files: ["**/*.{js,mjs,cjs,ts}"],
18
+ languageOptions: { globals: { ...globals.browser, ...globals.node } },
19
+ },
20
+ {
21
+ files: ["**/*.{js,mjs,cjs,ts}"],
22
+ plugins: { js },
23
+ extends: ["js/recommended"],
24
+ },
25
+ { files: ["**/*.json"], plugins: { json }, language: "json/json", extends: ["json/recommended"] },
26
+ tseslint.configs.recommended,
27
+ {
28
+ rules: {
29
+ "@typescript-eslint/explicit-module-boundary-types": "off",
30
+ "@typescript-eslint/no-explicit-any": "off",
31
+ "@typescript-eslint/no-require-imports": "off",
32
+ },
33
+ },
34
+ globalIgnores([
35
+ "node_modules/*", // ignore its content
36
+ "dist/*",
37
+ "package-lock.json",
38
+ ]),
39
+ ]);
package/jest.config.js ADDED
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ testMatch: ['**/tests/**/*.test.ts']
5
+ };
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@shadow-dev/orm",
3
+ "version": "1.0.0",
4
+ "description": "Lightweight dynamic MySQL ORM designed for ShadowCore and modular apps.",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "require": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "dev": "ts-node src/index.ts",
16
+ "build": "tsc",
17
+ "lint": "eslint . --ext .ts",
18
+ "type-check": "tsc --noEmit",
19
+ "setup": "node scripts/setup.js",
20
+ "test": "jest"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/Shadows-Development/ShadowORM.git"
25
+ },
26
+ "author": "Shadow Development LLC",
27
+ "license": "LGPL-3.0-or-later",
28
+ "keywords": [
29
+ "orm",
30
+ "mysql",
31
+ "typescript",
32
+ "lightweight",
33
+ "shadowcore",
34
+ "shadow-dev"
35
+ ],
36
+ "devDependencies": {
37
+ "@eslint/json": "^0.13.1",
38
+ "@types/jest": "^30.0.0",
39
+ "@types/node": "^24.1.0",
40
+ "@typescript-eslint/eslint-plugin": "^8.38.0",
41
+ "@typescript-eslint/parser": "^8.38.0",
42
+ "eslint": "^9.32.0",
43
+ "jest": "^30.0.5",
44
+ "ts-jest": "^29.4.0",
45
+ "ts-node": "^10.9.2",
46
+ "typescript": "^5.8.3",
47
+ "typescript-eslint": "^8.38.0"
48
+ },
49
+ "dependencies": {
50
+ "mysql2": "^3.14.3"
51
+ }
52
+ }
@@ -0,0 +1,24 @@
1
+ // Database.ts
2
+ import mysql from "mysql2/promise";
3
+ import { Model } from "./Model";
4
+
5
+ let pool: mysql.Pool;
6
+ const modelRegistry = new Map<string, Model<any>>();
7
+
8
+ export function initDatabase(config: mysql.PoolOptions) {
9
+ pool = mysql.createPool(config);
10
+ }
11
+
12
+ export function getPool() {
13
+ if (!pool) throw new Error("Database not initialized");
14
+ return pool;
15
+ }
16
+
17
+ // @ts-expect-error wierd generic errors
18
+ export function registerModel<T>(model: Model<T>) {
19
+ modelRegistry.set(model.name, model);
20
+ }
21
+
22
+ export function getAllModels() {
23
+ return modelRegistry;
24
+ }
@@ -0,0 +1,18 @@
1
+ export interface BaseSchema {
2
+ id: string;
3
+ data?: any;
4
+ createdAt: Date;
5
+ }
6
+
7
+ export interface ForeignKeyDefinition {
8
+ column: string;
9
+ reference: string;
10
+ }
11
+
12
+ export class Model<T extends Partial<BaseSchema> = BaseSchema> {
13
+ constructor(
14
+ public readonly name: string,
15
+ public readonly schema: Record<keyof T, string>,
16
+ public readonly foreignKeys: ForeignKeyDefinition[] = []
17
+ ) {}
18
+ }
@@ -0,0 +1,81 @@
1
+ // Repository.ts
2
+ import { getPool } from "./Database";
3
+ import { Model } from "./Model";
4
+
5
+ export class Repository<T extends object> {
6
+ constructor(public readonly model: Model<T>) {}
7
+
8
+ async create(data: T): Promise<void> {
9
+ const keys = Object.keys(data);
10
+ const sql = `INSERT INTO \`${this.model.name}\` (${keys.join(",")}) VALUES (${keys.map(() => "?").join(",")})`;
11
+ const values = keys.map((key) => this.normalizeValue((data as any)[key]));
12
+
13
+ await getPool().execute(sql, values);
14
+ }
15
+
16
+
17
+ async find(where: Partial<T> = {}): Promise<T[]> {
18
+ const { sql, params } = this.buildWhereClause(where);
19
+ const query = `SELECT * FROM \`${this.model.name}\` ${sql}`;
20
+ const [rows] = await getPool().execute(query, params.map(this.normalizeValue));
21
+ return rows as T[];
22
+ }
23
+
24
+ async findOne(where: Partial<T>): Promise<T | null> {
25
+ const { sql, params } = this.buildWhereClause(where);
26
+ const query = `SELECT * FROM \`${this.model.name}\` ${sql} LIMIT 1`;
27
+ const [rows] = await getPool().execute(query, params.map(this.normalizeValue));
28
+ const results = rows as T[];
29
+ return results.length > 0 ? results[0] : null;
30
+ }
31
+
32
+ async update(where: Partial<T>, data: Partial<T>): Promise<void> {
33
+ const setKeys = Object.keys(data);
34
+ const setClause = setKeys.map(k => `${k} = ?`).join(", ");
35
+ const setValues = setKeys.map(k => this.normalizeValue((data as any)[k]));
36
+
37
+ const { sql: whereClause, params: whereValues } = this.buildWhereClause(where);
38
+ const query = `UPDATE \`${this.model.name}\` SET ${setClause} ${whereClause}`;
39
+ await getPool().execute(query, [...setValues, ...whereValues.map(this.normalizeValue)]);
40
+ }
41
+
42
+ async delete(where: Partial<T>): Promise<void> {
43
+ const { sql, params } = this.buildWhereClause(where);
44
+ const query = `DELETE FROM \`${this.model.name}\` ${sql}`;
45
+ await getPool().execute(query, params.map(this.normalizeValue));
46
+ }
47
+
48
+ async count(where: Partial<T> = {}): Promise<number> {
49
+ const { sql, params } = this.buildWhereClause(where);
50
+ const query = `SELECT COUNT(*) as count FROM \`${this.model.name}\` ${sql}`;
51
+ const [rows] = await getPool().execute(query, params.map(this.normalizeValue));
52
+ return (rows as any[])[0].count || 0;
53
+ }
54
+
55
+ async exists(where: Partial<T>): Promise<boolean> {
56
+ const count = await this.count(where);
57
+ return count > 0;
58
+ }
59
+
60
+ private normalizeValue(value: any): any {
61
+ if (value instanceof Date) {
62
+ return value.toISOString().slice(0, 19).replace("T", " ");
63
+ }
64
+ if (typeof value === "object" && value !== null) {
65
+ return JSON.stringify(value);
66
+ }
67
+ return value ?? null;
68
+ }
69
+
70
+ private buildWhereClause(where: Partial<T>): { sql: string; params: any[] } {
71
+ const keys = Object.keys(where);
72
+ if (keys.length === 0) return { sql: "", params: [] };
73
+
74
+ const conditions = keys.map(k => `${k} = ?`).join(" AND ");
75
+ const values = keys.map(k => (where as any)[k]);
76
+ return {
77
+ sql: `WHERE ${conditions}`,
78
+ params: values,
79
+ };
80
+ }
81
+ }
@@ -0,0 +1,3 @@
1
+ export * from './Model';
2
+ export * from './Repository';
3
+ export * from './Database';
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './core';
2
+ export * from './utils'
@@ -0,0 +1,24 @@
1
+ import { getPool } from "../core";
2
+
3
+ export async function getNextId(prefix: string): Promise<string> {
4
+ const pool = getPool();
5
+
6
+ await pool.execute(`
7
+ CREATE TABLE IF NOT EXISTS _id_counters (
8
+ prefix VARCHAR(255) PRIMARY KEY,
9
+ count INT NOT NULL
10
+ )
11
+ `);
12
+
13
+ const [rows] = await pool.query(`SELECT count FROM _id_counters WHERE prefix = ?`, [prefix]);
14
+ let count = 1;
15
+
16
+ if ((rows as any[]).length > 0) {
17
+ count = (rows as any)[0].count + 1;
18
+ await pool.execute(`UPDATE _id_counters SET count = ? WHERE prefix = ?`, [count, prefix]);
19
+ } else {
20
+ await pool.execute(`INSERT INTO _id_counters (prefix, count) VALUES (?, ?)`, [prefix, count]);
21
+ }
22
+
23
+ return `${prefix}-${String(count).padStart(3, "0")}`;
24
+ }
@@ -0,0 +1,2 @@
1
+ export * from './getNextId';
2
+ export * from './syncSchema'
@@ -0,0 +1,37 @@
1
+ import {getAllModels, getPool} from '../core'
2
+
3
+ export async function syncSchema() {
4
+ const models = getAllModels();
5
+ const pool = getPool();
6
+
7
+ for (const [name, model] of models.entries()) {
8
+ const columns: string[] = [];
9
+ const foreignKeys: string[] = [];
10
+
11
+ for (const [key, type] of Object.entries(model.schema)) {
12
+ columns.push(`\`${key}\` ${mapType(type)}`);
13
+ }
14
+
15
+ for (const fk of model.foreignKeys) {
16
+ foreignKeys.push(`FOREIGN KEY (\`${fk.column}\`) REFERENCES ${fk.reference}`);
17
+ }
18
+
19
+ const columnDefs = [...columns, ...foreignKeys].join(",\n ");
20
+ const sql = `CREATE TABLE IF NOT EXISTS \`${name}\` (\n ${columnDefs}\n);`;
21
+
22
+ await pool.execute(sql);
23
+ }
24
+
25
+ console.log("✅ Schema synchronized.");
26
+ }
27
+
28
+ function mapType(type: string): string {
29
+ switch (type.toLowerCase()) {
30
+ case "string": return "VARCHAR(255)";
31
+ case "json": return "JSON";
32
+ case "datetime": return "DATETIME";
33
+ case "number": return "INT";
34
+ case "boolean": return "BOOLEAN";
35
+ default: return type; // fallback for raw SQL types if provided
36
+ }
37
+ }
@@ -0,0 +1,5 @@
1
+ describe("Example test", () => {
2
+ it("should return true", () => {
3
+ expect(true).toBe(true);
4
+ });
5
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "CommonJS",
5
+ "moduleResolution": "node",
6
+ "esModuleInterop": true,
7
+ "declaration": true,
8
+ "outDir": "dist",
9
+ "strict": true,
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["src"]
13
+ }