@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/dist/index.mjs
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import "./chunk-X6BYQHVC.mjs";
|
|
2
|
+
|
|
3
|
+
// src/core/connection.ts
|
|
4
|
+
var Connection = class {
|
|
5
|
+
db;
|
|
6
|
+
constructor(database) {
|
|
7
|
+
this.db = database;
|
|
8
|
+
}
|
|
9
|
+
async select(query, bindings = []) {
|
|
10
|
+
const stmt = this.db.prepare(query).bind(...bindings);
|
|
11
|
+
const result = await stmt.all();
|
|
12
|
+
return result.results || [];
|
|
13
|
+
}
|
|
14
|
+
async insert(query, bindings = []) {
|
|
15
|
+
const stmt = this.db.prepare(query).bind(...bindings);
|
|
16
|
+
const result = await stmt.run();
|
|
17
|
+
return result.success;
|
|
18
|
+
}
|
|
19
|
+
async update(query, bindings = []) {
|
|
20
|
+
const stmt = this.db.prepare(query).bind(...bindings);
|
|
21
|
+
const result = await stmt.run();
|
|
22
|
+
return result.success;
|
|
23
|
+
}
|
|
24
|
+
async delete(query, bindings = []) {
|
|
25
|
+
const stmt = this.db.prepare(query).bind(...bindings);
|
|
26
|
+
const result = await stmt.run();
|
|
27
|
+
return result.success;
|
|
28
|
+
}
|
|
29
|
+
async statement(query, bindings = []) {
|
|
30
|
+
const stmt = this.db.prepare(query).bind(...bindings);
|
|
31
|
+
const result = await stmt.run();
|
|
32
|
+
return result.success;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// src/core/query-builder.ts
|
|
37
|
+
var QueryBuilder = class {
|
|
38
|
+
connection;
|
|
39
|
+
table;
|
|
40
|
+
columns = ["*"];
|
|
41
|
+
wheres = [];
|
|
42
|
+
bindings = [];
|
|
43
|
+
orders = [];
|
|
44
|
+
limitValue = null;
|
|
45
|
+
offsetValue = null;
|
|
46
|
+
constructor(connection, table) {
|
|
47
|
+
this.connection = connection;
|
|
48
|
+
this.table = table;
|
|
49
|
+
}
|
|
50
|
+
select(...columns) {
|
|
51
|
+
this.columns = columns.length > 0 ? columns : ["*"];
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
where(column, operator, value) {
|
|
55
|
+
if (value === void 0) {
|
|
56
|
+
value = operator;
|
|
57
|
+
operator = "=";
|
|
58
|
+
}
|
|
59
|
+
this.wheres.push(`${column} ${operator} ?`);
|
|
60
|
+
this.bindings.push(value);
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
orWhere(column, operator, value) {
|
|
64
|
+
if (value === void 0) {
|
|
65
|
+
value = operator;
|
|
66
|
+
operator = "=";
|
|
67
|
+
}
|
|
68
|
+
this.wheres.push(`OR ${column} ${operator} ?`);
|
|
69
|
+
this.bindings.push(value);
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
orderBy(column, direction = "asc") {
|
|
73
|
+
this.orders.push(`${column} ${direction.toUpperCase()}`);
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
limit(limit) {
|
|
77
|
+
this.limitValue = limit;
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
offset(offset) {
|
|
81
|
+
this.offsetValue = offset;
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
toSql() {
|
|
85
|
+
let sql = `SELECT ${this.columns.join(", ")} FROM ${this.table}`;
|
|
86
|
+
if (this.wheres.length > 0) {
|
|
87
|
+
const constraints = this.wheres.map((w, i) => {
|
|
88
|
+
if (i === 0) return w.replace(/^OR\s+/, "");
|
|
89
|
+
return w.startsWith("OR") ? w : `AND ${w}`;
|
|
90
|
+
});
|
|
91
|
+
sql += ` WHERE ${constraints.join(" ")}`;
|
|
92
|
+
}
|
|
93
|
+
if (this.orders.length > 0) {
|
|
94
|
+
sql += ` ORDER BY ${this.orders.join(", ")}`;
|
|
95
|
+
}
|
|
96
|
+
if (this.limitValue !== null) {
|
|
97
|
+
sql += ` LIMIT ${this.limitValue}`;
|
|
98
|
+
}
|
|
99
|
+
if (this.offsetValue !== null) {
|
|
100
|
+
sql += ` OFFSET ${this.offsetValue}`;
|
|
101
|
+
}
|
|
102
|
+
return { sql, bindings: this.bindings };
|
|
103
|
+
}
|
|
104
|
+
async get() {
|
|
105
|
+
const { sql, bindings } = this.toSql();
|
|
106
|
+
return this.connection.select(sql, bindings);
|
|
107
|
+
}
|
|
108
|
+
async first() {
|
|
109
|
+
this.limit(1);
|
|
110
|
+
const results = await this.get();
|
|
111
|
+
return results.length > 0 ? results[0] : null;
|
|
112
|
+
}
|
|
113
|
+
async insert(data) {
|
|
114
|
+
const columns = Object.keys(data);
|
|
115
|
+
const values = Object.values(data);
|
|
116
|
+
const placeholders = values.map(() => "?").join(", ");
|
|
117
|
+
const sql = `INSERT INTO ${this.table} (${columns.join(", ")}) VALUES (${placeholders})`;
|
|
118
|
+
return this.connection.insert(sql, values);
|
|
119
|
+
}
|
|
120
|
+
async update(data) {
|
|
121
|
+
const columns = Object.keys(data);
|
|
122
|
+
const values = Object.values(data);
|
|
123
|
+
const sets = columns.map((col) => `${col} = ?`).join(", ");
|
|
124
|
+
let sql = `UPDATE ${this.table} SET ${sets}`;
|
|
125
|
+
if (this.wheres.length > 0) {
|
|
126
|
+
const constraints = this.wheres.map((w, i) => {
|
|
127
|
+
if (i === 0) return w.replace(/^OR\s+/, "");
|
|
128
|
+
return w.startsWith("OR") ? w : `AND ${w}`;
|
|
129
|
+
});
|
|
130
|
+
sql += ` WHERE ${constraints.join(" ")}`;
|
|
131
|
+
} else {
|
|
132
|
+
}
|
|
133
|
+
return this.connection.update(sql, [...values, ...this.bindings]);
|
|
134
|
+
}
|
|
135
|
+
async delete() {
|
|
136
|
+
let sql = `DELETE FROM ${this.table}`;
|
|
137
|
+
if (this.wheres.length > 0) {
|
|
138
|
+
const constraints = this.wheres.map((w, i) => {
|
|
139
|
+
if (i === 0) return w.replace(/^OR\s+/, "");
|
|
140
|
+
return w.startsWith("OR") ? w : `AND ${w}`;
|
|
141
|
+
});
|
|
142
|
+
sql += ` WHERE ${constraints.join(" ")}`;
|
|
143
|
+
}
|
|
144
|
+
return this.connection.delete(sql, this.bindings);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// src/core/model-query-builder.ts
|
|
149
|
+
var ModelQueryBuilder = class extends QueryBuilder {
|
|
150
|
+
modelClass;
|
|
151
|
+
constructor(connection, table, modelClass) {
|
|
152
|
+
super(connection, table);
|
|
153
|
+
this.modelClass = modelClass;
|
|
154
|
+
}
|
|
155
|
+
async get() {
|
|
156
|
+
const { sql, bindings } = this.toSql();
|
|
157
|
+
const results = await this.connection.select(sql, bindings);
|
|
158
|
+
return results.map((result) => {
|
|
159
|
+
const instance = new this.modelClass(result);
|
|
160
|
+
instance.exists = true;
|
|
161
|
+
instance.syncOriginal();
|
|
162
|
+
return instance;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
async first() {
|
|
166
|
+
this.limit(1);
|
|
167
|
+
const results = await this.get();
|
|
168
|
+
return results.length > 0 ? results[0] : null;
|
|
169
|
+
}
|
|
170
|
+
getModel() {
|
|
171
|
+
return this.modelClass;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// src/core/database.ts
|
|
176
|
+
var Database = class _Database {
|
|
177
|
+
static instance;
|
|
178
|
+
connection;
|
|
179
|
+
constructor(d1) {
|
|
180
|
+
this.connection = new Connection(d1);
|
|
181
|
+
}
|
|
182
|
+
static setup(d1) {
|
|
183
|
+
_Database.instance = new _Database(d1);
|
|
184
|
+
}
|
|
185
|
+
static getInstance() {
|
|
186
|
+
if (!_Database.instance) {
|
|
187
|
+
throw new Error(
|
|
188
|
+
"Database not initialized. Call Database.setup(env.DB) first."
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
return _Database.instance;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// src/core/relationships/relationship.ts
|
|
196
|
+
var Relationship = class {
|
|
197
|
+
query;
|
|
198
|
+
parent;
|
|
199
|
+
constructor(query, parent) {
|
|
200
|
+
this.query = query;
|
|
201
|
+
this.parent = parent;
|
|
202
|
+
}
|
|
203
|
+
getQuery() {
|
|
204
|
+
return this.query;
|
|
205
|
+
}
|
|
206
|
+
// Common methods proxying to query helper
|
|
207
|
+
async get() {
|
|
208
|
+
return this.query.get();
|
|
209
|
+
}
|
|
210
|
+
async first() {
|
|
211
|
+
return this.query.first();
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// src/core/relationships/has-one.ts
|
|
216
|
+
var HasOne = class extends Relationship {
|
|
217
|
+
foreignKey;
|
|
218
|
+
localKey;
|
|
219
|
+
constructor(query, parent, foreignKey, localKey) {
|
|
220
|
+
super(query, parent);
|
|
221
|
+
this.foreignKey = foreignKey;
|
|
222
|
+
this.localKey = localKey;
|
|
223
|
+
this.addConstraints();
|
|
224
|
+
}
|
|
225
|
+
addConstraints() {
|
|
226
|
+
const value = this.parent.attributes[this.localKey];
|
|
227
|
+
this.query.where(this.foreignKey, "=", value);
|
|
228
|
+
this.query.limit(1);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// src/core/relationships/has-many.ts
|
|
233
|
+
var HasMany = class extends Relationship {
|
|
234
|
+
foreignKey;
|
|
235
|
+
localKey;
|
|
236
|
+
constructor(query, parent, foreignKey, localKey) {
|
|
237
|
+
super(query, parent);
|
|
238
|
+
this.foreignKey = foreignKey;
|
|
239
|
+
this.localKey = localKey;
|
|
240
|
+
this.addConstraints();
|
|
241
|
+
}
|
|
242
|
+
addConstraints() {
|
|
243
|
+
const value = this.parent.attributes[this.localKey];
|
|
244
|
+
this.query.where(this.foreignKey, "=", value);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// src/core/relationships/belongs-to.ts
|
|
249
|
+
var BelongsTo = class extends Relationship {
|
|
250
|
+
foreignKey;
|
|
251
|
+
// on parent
|
|
252
|
+
ownerKey;
|
|
253
|
+
// on related
|
|
254
|
+
constructor(query, parent, foreignKey, ownerKey) {
|
|
255
|
+
super(query, parent);
|
|
256
|
+
this.foreignKey = foreignKey;
|
|
257
|
+
this.ownerKey = ownerKey;
|
|
258
|
+
this.addConstraints();
|
|
259
|
+
}
|
|
260
|
+
addConstraints() {
|
|
261
|
+
const value = this.parent.attributes[this.foreignKey];
|
|
262
|
+
this.query.where(this.ownerKey, "=", value);
|
|
263
|
+
this.query.limit(1);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// src/models/model.ts
|
|
268
|
+
var Model = class {
|
|
269
|
+
static table;
|
|
270
|
+
static connectionResolver;
|
|
271
|
+
attributes = {};
|
|
272
|
+
fillable = [];
|
|
273
|
+
hidden = [];
|
|
274
|
+
exists = false;
|
|
275
|
+
original = {};
|
|
276
|
+
constructor(attributes = {}) {
|
|
277
|
+
this.fill(attributes);
|
|
278
|
+
}
|
|
279
|
+
// Static Query Builder
|
|
280
|
+
static query() {
|
|
281
|
+
const instance = new this();
|
|
282
|
+
const table = instance.getTable();
|
|
283
|
+
const connection = Database.getInstance().connection;
|
|
284
|
+
return new ModelQueryBuilder(connection, table, this);
|
|
285
|
+
}
|
|
286
|
+
static on(connectionName) {
|
|
287
|
+
return this.query();
|
|
288
|
+
}
|
|
289
|
+
static async all() {
|
|
290
|
+
return this.query().get();
|
|
291
|
+
}
|
|
292
|
+
static async find(id) {
|
|
293
|
+
return this.query().where("id", "=", id).first();
|
|
294
|
+
}
|
|
295
|
+
static async create(attributes) {
|
|
296
|
+
const instance = new this();
|
|
297
|
+
instance.fill(attributes);
|
|
298
|
+
await instance.save();
|
|
299
|
+
return instance;
|
|
300
|
+
}
|
|
301
|
+
// Instance Methods
|
|
302
|
+
fill(attributes) {
|
|
303
|
+
Object.assign(this.attributes, attributes);
|
|
304
|
+
return this;
|
|
305
|
+
}
|
|
306
|
+
async save() {
|
|
307
|
+
const query = this.constructor.query();
|
|
308
|
+
if (this.exists) {
|
|
309
|
+
const dirty = this.getDirty();
|
|
310
|
+
if (Object.keys(dirty).length === 0) return true;
|
|
311
|
+
const id = this.attributes["id"];
|
|
312
|
+
await query.where("id", "=", id).update(dirty);
|
|
313
|
+
this.syncOriginal();
|
|
314
|
+
return true;
|
|
315
|
+
} else {
|
|
316
|
+
const result = await query.insert(this.attributes);
|
|
317
|
+
if (result) {
|
|
318
|
+
this.exists = true;
|
|
319
|
+
this.syncOriginal();
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async delete() {
|
|
326
|
+
if (!this.exists) return false;
|
|
327
|
+
const query = this.constructor.query();
|
|
328
|
+
const id = this.attributes["id"];
|
|
329
|
+
await query.where("id", "=", id).delete();
|
|
330
|
+
this.exists = false;
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
getTable() {
|
|
334
|
+
if (this.constructor.table) {
|
|
335
|
+
return this.constructor.table;
|
|
336
|
+
}
|
|
337
|
+
return this.constructor.name.toLowerCase() + "s";
|
|
338
|
+
}
|
|
339
|
+
getDirty() {
|
|
340
|
+
const dirty = {};
|
|
341
|
+
for (const key in this.attributes) {
|
|
342
|
+
if (this.attributes[key] !== this.original[key]) {
|
|
343
|
+
dirty[key] = this.attributes[key];
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return dirty;
|
|
347
|
+
}
|
|
348
|
+
syncOriginal() {
|
|
349
|
+
this.original = { ...this.attributes };
|
|
350
|
+
}
|
|
351
|
+
// Relationships
|
|
352
|
+
hasOne(related, foreignKey, localKey) {
|
|
353
|
+
const instance = new related();
|
|
354
|
+
const fk = foreignKey || this.getForeignKey();
|
|
355
|
+
const lk = localKey || "id";
|
|
356
|
+
return new HasOne(instance.newQuery(), this, fk, lk);
|
|
357
|
+
}
|
|
358
|
+
hasMany(related, foreignKey, localKey) {
|
|
359
|
+
const instance = new related();
|
|
360
|
+
const fk = foreignKey || this.getForeignKey();
|
|
361
|
+
const lk = localKey || "id";
|
|
362
|
+
return new HasMany(instance.newQuery(), this, fk, lk);
|
|
363
|
+
}
|
|
364
|
+
belongsTo(related, foreignKey, ownerKey) {
|
|
365
|
+
const instance = new related();
|
|
366
|
+
const fk = foreignKey || instance.getForeignKey();
|
|
367
|
+
const ok = ownerKey || "id";
|
|
368
|
+
return new BelongsTo(instance.newQuery(), this, fk, ok);
|
|
369
|
+
}
|
|
370
|
+
getForeignKey() {
|
|
371
|
+
return this.getTable().slice(0, -1) + "_id";
|
|
372
|
+
}
|
|
373
|
+
newQuery() {
|
|
374
|
+
return this.constructor.query();
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// src/database/blueprint.ts
|
|
379
|
+
var Blueprint = class {
|
|
380
|
+
table;
|
|
381
|
+
columns = [];
|
|
382
|
+
commands = [];
|
|
383
|
+
constructor(table) {
|
|
384
|
+
this.table = table;
|
|
385
|
+
}
|
|
386
|
+
id() {
|
|
387
|
+
this.columns.push("id INTEGER PRIMARY KEY AUTOINCREMENT");
|
|
388
|
+
return this;
|
|
389
|
+
}
|
|
390
|
+
string(column, length) {
|
|
391
|
+
this.columns.push(`${column} TEXT`);
|
|
392
|
+
return this;
|
|
393
|
+
}
|
|
394
|
+
integer(column) {
|
|
395
|
+
this.columns.push(`${column} INTEGER`);
|
|
396
|
+
return this;
|
|
397
|
+
}
|
|
398
|
+
boolean(column) {
|
|
399
|
+
this.columns.push(`${column} BOOLEAN`);
|
|
400
|
+
return this;
|
|
401
|
+
}
|
|
402
|
+
timestamps() {
|
|
403
|
+
this.columns.push("created_at DATETIME DEFAULT CURRENT_TIMESTAMP");
|
|
404
|
+
this.columns.push("updated_at DATETIME DEFAULT CURRENT_TIMESTAMP");
|
|
405
|
+
return this;
|
|
406
|
+
}
|
|
407
|
+
toSql() {
|
|
408
|
+
if (this.columns.length > 0) {
|
|
409
|
+
const cols = this.columns.join(", ");
|
|
410
|
+
return [`CREATE TABLE IF NOT EXISTS ${this.table} (${cols})`];
|
|
411
|
+
}
|
|
412
|
+
return this.commands;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
// src/database/schema.ts
|
|
417
|
+
var Schema = class {
|
|
418
|
+
static create(table, callback) {
|
|
419
|
+
const blueprint = new Blueprint(table);
|
|
420
|
+
callback(blueprint);
|
|
421
|
+
return blueprint.toSql()[0];
|
|
422
|
+
}
|
|
423
|
+
static dropIfExists(table) {
|
|
424
|
+
return `DROP TABLE IF EXISTS ${table}`;
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
export {
|
|
428
|
+
BelongsTo,
|
|
429
|
+
Blueprint,
|
|
430
|
+
Connection,
|
|
431
|
+
Database,
|
|
432
|
+
HasMany,
|
|
433
|
+
HasOne,
|
|
434
|
+
Model,
|
|
435
|
+
ModelQueryBuilder,
|
|
436
|
+
QueryBuilder,
|
|
437
|
+
Relationship,
|
|
438
|
+
Schema
|
|
439
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@3lineas/d1-orm",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"d1-orm": "./dist/cli/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsup src/index.ts src/cli/index.ts --format cjs,esm --dts --clean",
|
|
15
|
+
"orm": "npx tsx src/cli/index.ts",
|
|
16
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
17
|
+
"postinstall": "node scripts/postinstall.js"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [],
|
|
20
|
+
"author": "",
|
|
21
|
+
"license": "ISC",
|
|
22
|
+
"type": "commonjs",
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@cloudflare/workers-types": "^4.20260124.0",
|
|
25
|
+
"@types/node": "^25.0.10",
|
|
26
|
+
"tsup": "^8.5.1",
|
|
27
|
+
"typescript": "^5.9.3",
|
|
28
|
+
"vitest": "^4.0.18",
|
|
29
|
+
"wrangler": "^4.60.0"
|
|
30
|
+
},
|
|
31
|
+
"pnpm": {
|
|
32
|
+
"onlyBuiltDependencies": [
|
|
33
|
+
"esbuild",
|
|
34
|
+
"sharp",
|
|
35
|
+
"workerd"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
}
|