@shadow-dev/orm 2.0.0 → 2.0.1

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 CHANGED
@@ -1,118 +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.
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.
@@ -66,12 +66,12 @@ export async function transaction(fn) {
66
66
  /* Migrations */
67
67
  /* ---------------------------------- */
68
68
  async function ensureMigrationTable() {
69
- await exec(`
70
- CREATE TABLE IF NOT EXISTS shadoworm_migrations (
71
- id VARCHAR(255) PRIMARY KEY,
72
- name VARCHAR(255) NOT NULL,
73
- executed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
74
- );
69
+ await exec(`
70
+ CREATE TABLE IF NOT EXISTS shadoworm_migrations (
71
+ id VARCHAR(255) PRIMARY KEY,
72
+ name VARCHAR(255) NOT NULL,
73
+ executed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
74
+ );
75
75
  `);
76
76
  }
77
77
  async function loadMigrations(dir) {
@@ -16,10 +16,10 @@ export class Repository {
16
16
  const keys = this.getInsertableKeys(data);
17
17
  if (keys.length === 0)
18
18
  throw new Error("create(): empty data");
19
- const sql = `
20
- INSERT INTO \`${this.model.name}\`
21
- (${keys.map(k => `\`${k}\``).join(",")})
22
- VALUES (${keys.map(() => "?").join(",")})
19
+ const sql = `
20
+ INSERT INTO \`${this.model.name}\`
21
+ (${keys.map(k => `\`${k}\``).join(",")})
22
+ VALUES (${keys.map(() => "?").join(",")})
23
23
  `;
24
24
  const values = keys.map(k => this.normalizeWriteValue(k, data[k]));
25
25
  const [res] = await getPool().execute(sql, values);
@@ -39,10 +39,10 @@ export class Repository {
39
39
  .map(() => `(${keys.map(() => "?").join(",")})`)
40
40
  .join(",");
41
41
  const values = rows.flatMap(row => keys.map(k => this.normalizeWriteValue(k, row[k])));
42
- const sql = `
43
- INSERT INTO \`${this.model.name}\`
44
- (${keys.map(k => `\`${k}\``).join(",")})
45
- VALUES ${placeholders}
42
+ const sql = `
43
+ INSERT INTO \`${this.model.name}\`
44
+ (${keys.map(k => `\`${k}\``).join(",")})
45
+ VALUES ${placeholders}
46
46
  `;
47
47
  const [res] = await getPool().execute(sql, values);
48
48
  const pk = this.getPrimaryKeyField();
@@ -59,10 +59,10 @@ export class Repository {
59
59
  .map(() => `(${keys.map(() => "?").join(",")})`)
60
60
  .join(",");
61
61
  const values = rows.flatMap(row => keys.map(k => this.normalizeWriteValue(k, row[k])));
62
- const sql = `
63
- INSERT INTO \`${this.model.name}\`
64
- (${keys.map(k => `\`${k}\``).join(",")})
65
- VALUES ${placeholders}
62
+ const sql = `
63
+ INSERT INTO \`${this.model.name}\`
64
+ (${keys.map(k => `\`${k}\``).join(",")})
65
+ VALUES ${placeholders}
66
66
  `;
67
67
  const [res] = await getPool().execute(sql, values);
68
68
  return res.affectedRows;
@@ -100,9 +100,9 @@ export class Repository {
100
100
  return [];
101
101
  const pk = this.getPrimaryKeyField();
102
102
  const placeholders = ids.map(() => "?").join(",");
103
- const query = `
104
- SELECT * FROM \`${this.model.name}\`
105
- WHERE \`${String(pk)}\` IN (${placeholders})
103
+ const query = `
104
+ SELECT * FROM \`${this.model.name}\`
105
+ WHERE \`${String(pk)}\` IN (${placeholders})
106
106
  `;
107
107
  const [rows] = await getPool().execute(query, ids);
108
108
  return rows;
@@ -120,10 +120,10 @@ export class Repository {
120
120
  const setClause = setKeys.map(k => `\`${k}\` = ?`).join(", ");
121
121
  const setValues = setKeys.map(k => this.normalizeWriteValue(k, data[k]));
122
122
  const { sql: whereClause, params: whereValues } = this.buildWhereClause(where);
123
- const query = `
124
- UPDATE \`${this.model.name}\`
125
- SET ${setClause}
126
- ${whereClause}
123
+ const query = `
124
+ UPDATE \`${this.model.name}\`
125
+ SET ${setClause}
126
+ ${whereClause}
127
127
  `;
128
128
  await getPool().execute(query, [...setValues, ...whereValues]);
129
129
  return this.findOne(where);
@@ -138,10 +138,10 @@ export class Repository {
138
138
  const setClause = setKeys.map(k => `\`${k}\` = ?`).join(", ");
139
139
  const setValues = setKeys.map(k => this.normalizeWriteValue(k, data[k]));
140
140
  const { sql, params } = this.buildWhereClause(where);
141
- const query = `
142
- UPDATE \`${this.model.name}\`
143
- SET ${setClause}
144
- ${sql}
141
+ const query = `
142
+ UPDATE \`${this.model.name}\`
143
+ SET ${setClause}
144
+ ${sql}
145
145
  `;
146
146
  const [res] = await getPool().execute(query, [
147
147
  ...setValues,
@@ -180,11 +180,11 @@ export class Repository {
180
180
  .map(k => `\`${k}\` = VALUES(\`${k}\`)`)
181
181
  .join(",");
182
182
  const values = keys.map(k => this.normalizeWriteValue(k, data[k]));
183
- const sql = `
184
- INSERT INTO \`${this.model.name}\`
185
- (${insertCols})
186
- VALUES (${insertVals})
187
- ON DUPLICATE KEY UPDATE ${updateCols}
183
+ const sql = `
184
+ INSERT INTO \`${this.model.name}\`
185
+ (${insertCols})
186
+ VALUES (${insertVals})
187
+ ON DUPLICATE KEY UPDATE ${updateCols}
188
188
  `;
189
189
  await getPool().execute(sql, values);
190
190
  return (await this.findOne({ [pk]: data[pk] }));
@@ -2,18 +2,18 @@ import { getPool } from "../core/Database.js";
2
2
  export async function getNextId(prefix) {
3
3
  const pool = getPool();
4
4
  // Ensure table exists (safe to call multiple times)
5
- await pool.execute(`
6
- CREATE TABLE IF NOT EXISTS _id_counters (
7
- prefix VARCHAR(255) PRIMARY KEY,
8
- count INT NOT NULL
9
- )
5
+ await pool.execute(`
6
+ CREATE TABLE IF NOT EXISTS _id_counters (
7
+ prefix VARCHAR(255) PRIMARY KEY,
8
+ count INT NOT NULL
9
+ )
10
10
  `);
11
11
  // Atomic upsert + increment
12
12
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
13
- const [result] = await pool.execute(`
14
- INSERT INTO _id_counters (prefix, count)
15
- VALUES (?, 1)
16
- ON DUPLICATE KEY UPDATE count = count + 1
13
+ const [result] = await pool.execute(`
14
+ INSERT INTO _id_counters (prefix, count)
15
+ VALUES (?, 1)
16
+ ON DUPLICATE KEY UPDATE count = count + 1
17
17
  `, [prefix]);
18
18
  // Fetch the new value
19
19
  const [rows] = await pool.query(`SELECT count FROM _id_counters WHERE prefix = ?`, [prefix]);
@@ -85,10 +85,20 @@ function generateCreateTableSQL(model) {
85
85
  col += ` DEFAULT ${formatDefault(field.default)}`;
86
86
  columns.push(col);
87
87
  }
88
- const fks = model.foreignKeys.map(fk => `FOREIGN KEY (\`${fk.column}\`) REFERENCES \`${fk.references.table}\`(\`${fk.references.column}\`)`);
89
- return `
90
- CREATE TABLE \`${model.name}\` (
91
- ${[...columns, ...fks].join(",\n ")}
88
+ const fks = model.foreignKeys.map(fk => {
89
+ let sql = `FOREIGN KEY (\`${fk.column}\`) ` +
90
+ `REFERENCES \`${fk.references.table}\`(\`${fk.references.column}\`)`;
91
+ if (fk.onDelete) {
92
+ sql += ` ON DELETE ${fk.onDelete}`;
93
+ }
94
+ if (fk.onUpdate) {
95
+ sql += ` ON UPDATE ${fk.onUpdate}`;
96
+ }
97
+ return sql;
98
+ });
99
+ return `
100
+ CREATE TABLE \`${model.name}\` (
101
+ ${[...columns, ...fks].join(",\n ")}
92
102
  );`.trim();
93
103
  }
94
104
  function generateIndexSQL(model) {
@@ -114,19 +124,19 @@ function emitMigration(sql, dir) {
114
124
  .slice(0, 14);
115
125
  const filename = `${id}_auto_sync.ts`;
116
126
  const filePath = path.join(dir, filename);
117
- const content = `
118
- import type { Migration } from "@shadow-dev/orm";
119
-
120
- export const migration: Migration = {
121
- id: "${id}",
122
- name: "auto_sync",
123
-
124
- async up(db) {
127
+ const content = `
128
+ import type { Migration } from "@shadow-dev/orm";
129
+
130
+ export const migration: Migration = {
131
+ id: "${id}",
132
+ name: "auto_sync",
133
+
134
+ async up(db) {
125
135
  ${sql
126
136
  .map(s => ` await db.exec(\`${s.replace(/`/g, "\\`")}\`);`)
127
- .join("\n")}
128
- }
129
- };
137
+ .join("\n")}
138
+ }
139
+ };
130
140
  `.trim();
131
141
  fs.writeFileSync(filePath, content, { encoding: "utf8" });
132
142
  console.log(`📝 Migration generated: ${filePath}`);
package/package.json CHANGED
@@ -1,54 +1,55 @@
1
- {
2
- "name": "@shadow-dev/orm",
3
- "version": "2.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
- "files": ["dist"],
8
- "type": "module",
9
- "exports": {
10
- ".": {
11
- "import": "./dist/index.js",
12
- "types": "./dist/index.d.ts"
13
- }
14
- },
15
- "scripts": {
16
- "prepublishOnly": "npm run build",
17
- "dev": "ts-node src/index.ts",
18
- "build": "tsc",
19
- "lint": "eslint . --ext .ts",
20
- "type-check": "tsc --noEmit",
21
- "setup": "node scripts/setup.js",
22
- "test": "jest"
23
- },
24
- "repository": {
25
- "type": "git",
26
- "url": "https://github.com/Shadows-Development/ShadowORM.git"
27
- },
28
- "author": "Shadow Development LLC",
29
- "license": "LGPL-3.0-or-later",
30
- "keywords": [
31
- "orm",
32
- "mysql",
33
- "typescript",
34
- "lightweight",
35
- "shadowcore",
36
- "shadow-dev"
37
- ],
38
- "devDependencies": {
39
- "@eslint/json": "^0.13.1",
40
- "@types/jest": "^30.0.0",
41
- "@types/node": "^24.1.0",
42
- "@typescript-eslint/eslint-plugin": "^8.38.0",
43
- "@typescript-eslint/parser": "^8.38.0",
44
- "eslint": "^9.32.0",
45
- "jest": "^30.0.5",
46
- "ts-jest": "^29.4.0",
47
- "ts-node": "^10.9.2",
48
- "typescript": "^5.8.3",
49
- "typescript-eslint": "^8.38.0"
50
- },
51
- "dependencies": {
52
- "mysql2": "^3.14.3"
53
- }
54
- }
1
+ {
2
+ "name": "@shadow-dev/orm",
3
+ "version": "2.0.1",
4
+ "description": "Lightweight dynamic MySQL ORM designed for ShadowCore and modular apps.",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "files": ["dist"],
8
+ "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "prepublishOnly": "npm run build",
17
+ "dev": "ts-node src/index.ts",
18
+ "build": "tsc",
19
+ "lint": "eslint . --ext .ts",
20
+ "type-check": "tsc --noEmit",
21
+ "setup": "node scripts/setup.js",
22
+ "test": "jest"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/Shadows-Development/ShadowORM.git"
27
+ },
28
+ "author": "Shadow Development LLC",
29
+ "license": "LGPL-3.0-or-later",
30
+ "keywords": [
31
+ "orm",
32
+ "mysql",
33
+ "typescript",
34
+ "lightweight",
35
+ "shadowcore",
36
+ "shadow-dev"
37
+ ],
38
+ "devDependencies": {
39
+ "@eslint/json": "^0.13.1",
40
+ "@types/jest": "^30.0.0",
41
+ "@types/node": "^24.1.0",
42
+ "@typescript-eslint/eslint-plugin": "^8.38.0",
43
+ "@typescript-eslint/parser": "^8.38.0",
44
+ "eslint": "^9.32.0",
45
+ "jest": "^30.0.5",
46
+ "ts-jest": "^29.4.0",
47
+ "ts-node": "^10.9.2",
48
+ "typescript": "^5.8.3",
49
+ "typescript-eslint": "^8.38.0"
50
+ },
51
+ "dependencies": {
52
+ "mysql2": "^3.14.3"
53
+ }
54
+ }
55
+