@3lineas/d1-orm 1.0.3
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 +207 -0
- package/dist/chunk-X6BYQHVC.mjs +12 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +250 -0
- package/dist/cli/index.mjs +260 -0
- package/dist/index.d.mts +155 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +474 -0
- package/dist/index.mjs +439 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# D1 ORM
|
|
2
|
+
|
|
3
|
+
Un ORM ligero y potente dise帽ado espec铆ficamente para Cloudflare D1, inspirado en Eloquent de Laravel.
|
|
4
|
+
|
|
5
|
+
## Caracter铆sticas
|
|
6
|
+
|
|
7
|
+
- 馃殌 **Ligero y R谩pido**: Optimizado para Cloudflare Workers.
|
|
8
|
+
- 馃洜 **Basado en Eloquent**: Sintaxis familiar para desarrolladores de Laravel.
|
|
9
|
+
- 馃摝 **TypeScript**: Tipado est谩tico completo para mayor seguridad.
|
|
10
|
+
- relations **Relaciones**: Soporte para `hasOne`, `hasMany` y `belongsTo`.
|
|
11
|
+
- 鈱笍 **CLI Integrado**: Herramientas para migraciones y generaci贸n de modelos.
|
|
12
|
+
|
|
13
|
+
## Instalaci贸n
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @3lineas/d1-orm
|
|
17
|
+
# O usando pnpm
|
|
18
|
+
pnpm add @3lineas/d1-orm
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Configuraci贸n Inicial
|
|
22
|
+
|
|
23
|
+
Para comenzar, debes configurar la conexi贸n a tu base de datos D1 en tu Worker. Normalmente esto se hace en el punto de entrada de tu aplicaci贸n (por ejemplo, `index.ts` o `server.ts`).
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { Database } from "@3lineas/d1-orm";
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
async fetch(request, env, ctx) {
|
|
30
|
+
// Inicializa la conexi贸n con tu base de datos D1 (asumiendo que se llama DB en wrangler.toml)
|
|
31
|
+
Database.setup(env.DB);
|
|
32
|
+
|
|
33
|
+
// ... tu c贸digo de manejo de rutas
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Definici贸n de Modelos
|
|
39
|
+
|
|
40
|
+
Los modelos se definen extendiendo la clase `Model`. Por defecto, el ORM asumir谩 que la tabla es el nombre de la clase en plural y min煤sculas (ej: `User` -> `users`).
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { Model } from "@3lineas/d1-orm";
|
|
44
|
+
|
|
45
|
+
export class User extends Model {
|
|
46
|
+
// Opcional: Definir tabla personalizada
|
|
47
|
+
// protected static table = 'my_users';
|
|
48
|
+
|
|
49
|
+
// Opcional: Definir atributos para tipado (recomendado)
|
|
50
|
+
declare id: number;
|
|
51
|
+
declare name: string;
|
|
52
|
+
declare email: string;
|
|
53
|
+
declare created_at: string;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Operaciones CRUD
|
|
58
|
+
|
|
59
|
+
### Crear
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
const user = await User.create({
|
|
63
|
+
name: "Juan Perez",
|
|
64
|
+
email: "juan@example.com",
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Leer
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// Obtener todos los registros
|
|
72
|
+
const users = await User.all();
|
|
73
|
+
|
|
74
|
+
// Encontrar por ID
|
|
75
|
+
const user = await User.find(1);
|
|
76
|
+
|
|
77
|
+
// Consultas personalizadas
|
|
78
|
+
const activeUsers = await User.where("status", "=", "active")
|
|
79
|
+
.orderBy("created_at", "desc")
|
|
80
|
+
.get();
|
|
81
|
+
|
|
82
|
+
// Obtener el primer resultado
|
|
83
|
+
const firstUser = await User.where("email", "juan@example.com").first();
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Actualizar
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const user = await User.find(1);
|
|
90
|
+
if (user) {
|
|
91
|
+
user.fill({ name: "Juan Actualizado" });
|
|
92
|
+
await user.save();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// O actualizar directamente desde una consulta
|
|
96
|
+
await User.where("status", "inactive").update({ status: "active" });
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Eliminar
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const user = await User.find(1);
|
|
103
|
+
if (user) {
|
|
104
|
+
await user.delete();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// O eliminar directamente desde una consulta
|
|
108
|
+
await User.where("status", "banned").delete();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Relaciones
|
|
112
|
+
|
|
113
|
+
El ORM soporta relaciones b谩sicas para estructurar tus datos.
|
|
114
|
+
|
|
115
|
+
### Uno a Uno (HasOne)
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// Modelo User
|
|
119
|
+
hasOne(Profile) {
|
|
120
|
+
return this.hasOne(Profile);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Uso
|
|
124
|
+
const profile = await user.hasOne(Profile).get();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Uno a Muchos (HasMany)
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Modelo User
|
|
131
|
+
posts() {
|
|
132
|
+
return this.hasMany(Post);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Uso
|
|
136
|
+
const posts = await user.posts().get();
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Pertenece A (BelongsTo)
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// Modelo Post
|
|
143
|
+
user() {
|
|
144
|
+
return this.belongsTo(User);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Uso
|
|
148
|
+
const author = await post.user().get();
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## CLI y Migraciones
|
|
152
|
+
|
|
153
|
+
El ORM incluye una CLI para facilitar la gesti贸n de la base de datos.
|
|
154
|
+
|
|
155
|
+
### Scripts Recomendados
|
|
156
|
+
|
|
157
|
+
Agrega este script a tu `package.json` para facilitar el uso:
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
"scripts": {
|
|
161
|
+
"orm": "npx tsx src/cli/index.ts"
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Comandos Disponibles
|
|
166
|
+
|
|
167
|
+
#### Inicializar Proyecto
|
|
168
|
+
|
|
169
|
+
Crea las carpetas necesarias (`models`, `database/migrations`).
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
pnpm orm init
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### Crear un Modelo
|
|
176
|
+
|
|
177
|
+
Genera un archivo de modelo en `src/models`.
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
pnpm orm make:model NombreModelo
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Crear una Migraci贸n
|
|
184
|
+
|
|
185
|
+
Genera un archivo de migraci贸n en `database/migrations`.
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pnpm orm make:migration create_users_table
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### Ejecutar Migraciones
|
|
192
|
+
|
|
193
|
+
Ejecuta las migraciones pendientes.
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
# Local (por defecto)
|
|
197
|
+
pnpm orm migrate
|
|
198
|
+
|
|
199
|
+
# Remoto (Producci贸n)
|
|
200
|
+
pnpm orm migrate -- --remote
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
> **Nota:** Las migraciones locales requieren que `wrangler` est茅 configurado y funcionando correctamente en tu entorno.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
Desarrollado con 鉂わ笍 por **3Lineas**.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __esm = (fn, res) => function __init() {
|
|
3
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
4
|
+
};
|
|
5
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
6
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
__esm,
|
|
11
|
+
__commonJS
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/cli/commands/init.ts
|
|
27
|
+
var fs = __toESM(require("fs"));
|
|
28
|
+
var path = __toESM(require("path"));
|
|
29
|
+
var readline = __toESM(require("readline"));
|
|
30
|
+
async function init() {
|
|
31
|
+
const rl = readline.createInterface({
|
|
32
|
+
input: process.stdin,
|
|
33
|
+
output: process.stdout
|
|
34
|
+
});
|
|
35
|
+
const question = (query) => {
|
|
36
|
+
return new Promise((resolve) => {
|
|
37
|
+
rl.question(query, resolve);
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
console.log("Initializing D1 ORM...");
|
|
41
|
+
const defaultModelsPath = "src/models";
|
|
42
|
+
const userModelsPath = await question(
|
|
43
|
+
`Where would you like to install your models? (default: ${defaultModelsPath}): `
|
|
44
|
+
) || defaultModelsPath;
|
|
45
|
+
rl.close();
|
|
46
|
+
const folders = [userModelsPath, "database/migrations", "database/seeders"];
|
|
47
|
+
folders.forEach((folder) => {
|
|
48
|
+
const fullPath = path.join(process.cwd(), folder);
|
|
49
|
+
if (!fs.existsSync(fullPath)) {
|
|
50
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
51
|
+
console.log(`Created directory: ${folder}`);
|
|
52
|
+
} else {
|
|
53
|
+
console.log(`Directory already exists: ${folder}`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
const userModelContent = `import { Model } from 'd1-orm';
|
|
57
|
+
|
|
58
|
+
export class User extends Model {
|
|
59
|
+
// protected static table = 'users';
|
|
60
|
+
|
|
61
|
+
declare id: number;
|
|
62
|
+
declare name: string;
|
|
63
|
+
declare email: string;
|
|
64
|
+
declare password?: string;
|
|
65
|
+
declare created_at: string;
|
|
66
|
+
declare updated_at: string;
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
const userModelPath = path.join(process.cwd(), userModelsPath, "User.ts");
|
|
70
|
+
if (!fs.existsSync(userModelPath)) {
|
|
71
|
+
fs.writeFileSync(userModelPath, userModelContent);
|
|
72
|
+
console.log(`Created model: ${userModelPath}`);
|
|
73
|
+
}
|
|
74
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").split(".")[0].replace("T", "_");
|
|
75
|
+
const migrationName = `${timestamp}_create_users_table.ts`;
|
|
76
|
+
const migrationContent = `import { Blueprint, Schema } from 'd1-orm';
|
|
77
|
+
|
|
78
|
+
export const up = async () => {
|
|
79
|
+
return Schema.create('users', (table: Blueprint) => {
|
|
80
|
+
table.id();
|
|
81
|
+
table.string('name');
|
|
82
|
+
table.string('email').unique();
|
|
83
|
+
table.string('password').nullable();
|
|
84
|
+
table.timestamps();
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const down = async () => {
|
|
89
|
+
return Schema.dropIfExists('users');
|
|
90
|
+
};
|
|
91
|
+
`;
|
|
92
|
+
const migrationPath = path.join(
|
|
93
|
+
process.cwd(),
|
|
94
|
+
"database/migrations",
|
|
95
|
+
migrationName
|
|
96
|
+
);
|
|
97
|
+
if (!fs.existsSync(migrationPath)) {
|
|
98
|
+
fs.writeFileSync(migrationPath, migrationContent);
|
|
99
|
+
console.log(`Created migration: ${migrationPath}`);
|
|
100
|
+
}
|
|
101
|
+
const seederContent = `import { User } from '${path.relative(
|
|
102
|
+
path.join(process.cwd(), "database/seeders"),
|
|
103
|
+
path.join(process.cwd(), userModelsPath, "User")
|
|
104
|
+
).replace(/\\/g, "/")}';
|
|
105
|
+
|
|
106
|
+
export const seed = async () => {
|
|
107
|
+
await User.create({
|
|
108
|
+
name: 'Juan Perez',
|
|
109
|
+
email: 'juan@perez.com',
|
|
110
|
+
password: 'password'
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
`;
|
|
114
|
+
const seederPath = path.join(
|
|
115
|
+
process.cwd(),
|
|
116
|
+
"database/seeders",
|
|
117
|
+
"UserSeeder.ts"
|
|
118
|
+
);
|
|
119
|
+
if (!fs.existsSync(seederPath)) {
|
|
120
|
+
fs.writeFileSync(seederPath, seederContent);
|
|
121
|
+
console.log(`Created seeder: ${seederPath}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/cli/commands/make-model.ts
|
|
126
|
+
var fs2 = __toESM(require("fs"));
|
|
127
|
+
var path2 = __toESM(require("path"));
|
|
128
|
+
function makeModel(name) {
|
|
129
|
+
const filename = `${name}.ts`;
|
|
130
|
+
const targetPath = path2.join(process.cwd(), "models", filename);
|
|
131
|
+
const template = `import { Model } from 'd1-orm';
|
|
132
|
+
|
|
133
|
+
export class ${name} extends Model {
|
|
134
|
+
// protected static table = '${name.toLowerCase()}s';
|
|
135
|
+
|
|
136
|
+
// Define attributes explicitly for type safety if desired
|
|
137
|
+
// declare id: number;
|
|
138
|
+
// declare created_at: string;
|
|
139
|
+
// declare updated_at: string;
|
|
140
|
+
}
|
|
141
|
+
`;
|
|
142
|
+
if (fs2.existsSync(targetPath)) {
|
|
143
|
+
console.error(`Model ${filename} already exists.`);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
fs2.writeFileSync(targetPath, template);
|
|
147
|
+
console.log(`Created model: models/${filename}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/cli/commands/make-migration.ts
|
|
151
|
+
var fs3 = __toESM(require("fs"));
|
|
152
|
+
var path3 = __toESM(require("path"));
|
|
153
|
+
function makeMigration(name) {
|
|
154
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").split(".")[0].replace("T", "_");
|
|
155
|
+
const filename = `${timestamp}_${name}.ts`;
|
|
156
|
+
const targetPath = path3.join(process.cwd(), "database/migrations", filename);
|
|
157
|
+
const template = `import { Blueprint, Schema } from 'd1-orm';
|
|
158
|
+
|
|
159
|
+
export const up = async () => {
|
|
160
|
+
return Schema.create('${name}', (table: Blueprint) => {
|
|
161
|
+
table.id();
|
|
162
|
+
table.timestamps();
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const down = async () => {
|
|
167
|
+
return Schema.dropIfExists('${name}');
|
|
168
|
+
};
|
|
169
|
+
`;
|
|
170
|
+
if (fs3.existsSync(targetPath)) {
|
|
171
|
+
console.error(`Migration ${filename} already exists.`);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
fs3.writeFileSync(targetPath, template);
|
|
175
|
+
console.log(`Created migration: database/migrations/${filename}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/cli/commands/migrate.ts
|
|
179
|
+
var fs4 = __toESM(require("fs"));
|
|
180
|
+
var path4 = __toESM(require("path"));
|
|
181
|
+
var import_child_process = require("child_process");
|
|
182
|
+
async function migrate(args2) {
|
|
183
|
+
console.log("Running migrations...");
|
|
184
|
+
const migrationsDir = path4.join(process.cwd(), "database/migrations");
|
|
185
|
+
if (!fs4.existsSync(migrationsDir)) {
|
|
186
|
+
console.log("No migrations directory found.");
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const files = fs4.readdirSync(migrationsDir).filter((f) => f.endsWith(".ts") || f.endsWith(".js")).sort();
|
|
190
|
+
for (const file of files) {
|
|
191
|
+
console.log(`Processing ${file}...`);
|
|
192
|
+
const filePath = path4.join(migrationsDir, file);
|
|
193
|
+
try {
|
|
194
|
+
const migration = await import(filePath);
|
|
195
|
+
if (migration.up) {
|
|
196
|
+
const sql = await migration.up();
|
|
197
|
+
if (sql) {
|
|
198
|
+
console.log(`Executing SQL: ${sql}`);
|
|
199
|
+
const isRemote = args2.includes("--remote");
|
|
200
|
+
const dbName = "DB";
|
|
201
|
+
const command2 = isRemote ? "--remote" : "--local";
|
|
202
|
+
try {
|
|
203
|
+
const execCmd = `npx wrangler d1 execute ${dbName} --command "${sql.replace(/"/g, '\\"')}" ${command2}`;
|
|
204
|
+
console.log(`Running: ${execCmd}`);
|
|
205
|
+
(0, import_child_process.execSync)(execCmd, { stdio: "inherit" });
|
|
206
|
+
} catch (e) {
|
|
207
|
+
console.error(`Failed to execute migration ${file}.`);
|
|
208
|
+
throw e;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error(`Error processing migration ${file}:`, error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/cli/index.ts
|
|
219
|
+
var args = process.argv.slice(2);
|
|
220
|
+
var command = args[0];
|
|
221
|
+
var param = args[1];
|
|
222
|
+
switch (command) {
|
|
223
|
+
case "init":
|
|
224
|
+
init();
|
|
225
|
+
break;
|
|
226
|
+
case "make:model":
|
|
227
|
+
if (!param) {
|
|
228
|
+
console.error("Usage: d1-orm make:model <Name>");
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
makeModel(param);
|
|
232
|
+
break;
|
|
233
|
+
case "make:migration":
|
|
234
|
+
if (!param) {
|
|
235
|
+
console.error("Usage: d1-orm make:migration <Name>");
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
makeMigration(param);
|
|
239
|
+
break;
|
|
240
|
+
case "migrate":
|
|
241
|
+
const isLocal = args.includes("--local");
|
|
242
|
+
const isRemote = args.includes("--remote");
|
|
243
|
+
migrate(args);
|
|
244
|
+
break;
|
|
245
|
+
default:
|
|
246
|
+
console.log(
|
|
247
|
+
"Available commands: init, make:model, make:migration, migrate"
|
|
248
|
+
);
|
|
249
|
+
break;
|
|
250
|
+
}
|