@3lineas/d1-orm 1.0.3 → 1.0.5

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/dist/index.js CHANGED
@@ -22,6 +22,7 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  BelongsTo: () => BelongsTo,
24
24
  Blueprint: () => Blueprint,
25
+ Column: () => Column,
25
26
  Connection: () => Connection,
26
27
  Database: () => Database,
27
28
  HasMany: () => HasMany,
@@ -36,30 +37,73 @@ module.exports = __toCommonJS(index_exports);
36
37
 
37
38
  // src/core/connection.ts
38
39
  var Connection = class {
40
+ /**
41
+ * The underlying D1Database instance.
42
+ */
39
43
  db;
44
+ /**
45
+ * Create a new Connection instance.
46
+ *
47
+ * @param database - The D1Database instance.
48
+ */
40
49
  constructor(database) {
41
50
  this.db = database;
42
51
  }
52
+ /**
53
+ * Execute a SELECT statement.
54
+ *
55
+ * @param query - The SQL query string.
56
+ * @param bindings - The parameter bindings.
57
+ * @returns A promise that resolves to an array of results.
58
+ */
43
59
  async select(query, bindings = []) {
44
60
  const stmt = this.db.prepare(query).bind(...bindings);
45
61
  const result = await stmt.all();
46
62
  return result.results || [];
47
63
  }
64
+ /**
65
+ * Execute an INSERT statement.
66
+ *
67
+ * @param query - The SQL query string.
68
+ * @param bindings - The parameter bindings.
69
+ * @returns A promise that resolves to true on success.
70
+ */
48
71
  async insert(query, bindings = []) {
49
72
  const stmt = this.db.prepare(query).bind(...bindings);
50
73
  const result = await stmt.run();
51
74
  return result.success;
52
75
  }
76
+ /**
77
+ * Execute an UPDATE statement.
78
+ *
79
+ * @param query - The SQL query string.
80
+ * @param bindings - The parameter bindings.
81
+ * @returns A promise that resolves to true on success.
82
+ */
53
83
  async update(query, bindings = []) {
54
84
  const stmt = this.db.prepare(query).bind(...bindings);
55
85
  const result = await stmt.run();
56
86
  return result.success;
57
87
  }
88
+ /**
89
+ * Execute a DELETE statement.
90
+ *
91
+ * @param query - The SQL query string.
92
+ * @param bindings - The parameter bindings.
93
+ * @returns A promise that resolves to true on success.
94
+ */
58
95
  async delete(query, bindings = []) {
59
96
  const stmt = this.db.prepare(query).bind(...bindings);
60
97
  const result = await stmt.run();
61
98
  return result.success;
62
99
  }
100
+ /**
101
+ * Execute an arbitrary SQL statement.
102
+ *
103
+ * @param query - The SQL query string.
104
+ * @param bindings - The parameter bindings.
105
+ * @returns A promise that resolves to true on success.
106
+ */
63
107
  async statement(query, bindings = []) {
64
108
  const stmt = this.db.prepare(query).bind(...bindings);
65
109
  const result = await stmt.run();
@@ -69,22 +113,66 @@ var Connection = class {
69
113
 
70
114
  // src/core/query-builder.ts
71
115
  var QueryBuilder = class {
116
+ /**
117
+ * The database connection.
118
+ */
72
119
  connection;
120
+ /**
121
+ * The table to query.
122
+ */
73
123
  table;
124
+ /**
125
+ * The columns to select.
126
+ */
74
127
  columns = ["*"];
128
+ /**
129
+ * The where constraints for the query.
130
+ */
75
131
  wheres = [];
132
+ /**
133
+ * The bindings for the query.
134
+ */
76
135
  bindings = [];
136
+ /**
137
+ * The orderings for the query.
138
+ */
77
139
  orders = [];
140
+ /**
141
+ * The maximum number of records to return.
142
+ */
78
143
  limitValue = null;
144
+ /**
145
+ * The number of records to skip.
146
+ */
79
147
  offsetValue = null;
148
+ /**
149
+ * Create a new QueryBuilder instance.
150
+ *
151
+ * @param connection - The connection instance.
152
+ * @param table - The table name.
153
+ */
80
154
  constructor(connection, table) {
81
155
  this.connection = connection;
82
156
  this.table = table;
83
157
  }
158
+ /**
159
+ * Add a SELECT clause to the query.
160
+ *
161
+ * @param columns - The columns to select.
162
+ * @returns The QueryBuilder instance.
163
+ */
84
164
  select(...columns) {
85
165
  this.columns = columns.length > 0 ? columns : ["*"];
86
166
  return this;
87
167
  }
168
+ /**
169
+ * Add a basic WHERE clause to the query.
170
+ *
171
+ * @param column - The column name.
172
+ * @param operator - The operator or value.
173
+ * @param value - The value if operator is provided.
174
+ * @returns The QueryBuilder instance.
175
+ */
88
176
  where(column, operator, value) {
89
177
  if (value === void 0) {
90
178
  value = operator;
@@ -94,6 +182,14 @@ var QueryBuilder = class {
94
182
  this.bindings.push(value);
95
183
  return this;
96
184
  }
185
+ /**
186
+ * Add an OR WHERE clause to the query.
187
+ *
188
+ * @param column - The column name.
189
+ * @param operator - The operator or value.
190
+ * @param value - The value if operator is provided.
191
+ * @returns The QueryBuilder instance.
192
+ */
97
193
  orWhere(column, operator, value) {
98
194
  if (value === void 0) {
99
195
  value = operator;
@@ -103,18 +199,42 @@ var QueryBuilder = class {
103
199
  this.bindings.push(value);
104
200
  return this;
105
201
  }
202
+ /**
203
+ * Add an ORDER BY clause to the query.
204
+ *
205
+ * @param column - The column name.
206
+ * @param direction - The direction of the order.
207
+ * @returns The QueryBuilder instance.
208
+ */
106
209
  orderBy(column, direction = "asc") {
107
210
  this.orders.push(`${column} ${direction.toUpperCase()}`);
108
211
  return this;
109
212
  }
213
+ /**
214
+ * Add a LIMIT clause to the query.
215
+ *
216
+ * @param limit - The limit value.
217
+ * @returns The QueryBuilder instance.
218
+ */
110
219
  limit(limit) {
111
220
  this.limitValue = limit;
112
221
  return this;
113
222
  }
223
+ /**
224
+ * Add an OFFSET clause to the query.
225
+ *
226
+ * @param offset - The offset value.
227
+ * @returns The QueryBuilder instance.
228
+ */
114
229
  offset(offset) {
115
230
  this.offsetValue = offset;
116
231
  return this;
117
232
  }
233
+ /**
234
+ * Get the SQL representation of the query.
235
+ *
236
+ * @returns An object containing the SQL string and the bindings.
237
+ */
118
238
  toSql() {
119
239
  let sql = `SELECT ${this.columns.join(", ")} FROM ${this.table}`;
120
240
  if (this.wheres.length > 0) {
@@ -135,15 +255,31 @@ var QueryBuilder = class {
135
255
  }
136
256
  return { sql, bindings: this.bindings };
137
257
  }
258
+ /**
259
+ * Execute the query and get the results.
260
+ *
261
+ * @returns A promise that resolves to an array of records.
262
+ */
138
263
  async get() {
139
264
  const { sql, bindings } = this.toSql();
140
265
  return this.connection.select(sql, bindings);
141
266
  }
267
+ /**
268
+ * Execute the query and get the first result.
269
+ *
270
+ * @returns A promise that resolves to the first record or null.
271
+ */
142
272
  async first() {
143
273
  this.limit(1);
144
274
  const results = await this.get();
145
275
  return results.length > 0 ? results[0] : null;
146
276
  }
277
+ /**
278
+ * Insert a new record into the database.
279
+ *
280
+ * @param data - The data to insert.
281
+ * @returns A promise that resolves to true on success.
282
+ */
147
283
  async insert(data) {
148
284
  const columns = Object.keys(data);
149
285
  const values = Object.values(data);
@@ -151,6 +287,12 @@ var QueryBuilder = class {
151
287
  const sql = `INSERT INTO ${this.table} (${columns.join(", ")}) VALUES (${placeholders})`;
152
288
  return this.connection.insert(sql, values);
153
289
  }
290
+ /**
291
+ * Update records in the database.
292
+ *
293
+ * @param data - The data to update.
294
+ * @returns A promise that resolves to true on success.
295
+ */
154
296
  async update(data) {
155
297
  const columns = Object.keys(data);
156
298
  const values = Object.values(data);
@@ -162,10 +304,14 @@ var QueryBuilder = class {
162
304
  return w.startsWith("OR") ? w : `AND ${w}`;
163
305
  });
164
306
  sql += ` WHERE ${constraints.join(" ")}`;
165
- } else {
166
307
  }
167
308
  return this.connection.update(sql, [...values, ...this.bindings]);
168
309
  }
310
+ /**
311
+ * Delete records from the database.
312
+ *
313
+ * @returns A promise that resolves to true on success.
314
+ */
169
315
  async delete() {
170
316
  let sql = `DELETE FROM ${this.table}`;
171
317
  if (this.wheres.length > 0) {
@@ -181,11 +327,26 @@ var QueryBuilder = class {
181
327
 
182
328
  // src/core/model-query-builder.ts
183
329
  var ModelQueryBuilder = class extends QueryBuilder {
330
+ /**
331
+ * The class of the model being queried.
332
+ */
184
333
  modelClass;
334
+ /**
335
+ * Create a new ModelQueryBuilder instance.
336
+ *
337
+ * @param connection - The database connection.
338
+ * @param table - The table name.
339
+ * @param modelClass - The class constructor for the model.
340
+ */
185
341
  constructor(connection, table, modelClass) {
186
342
  super(connection, table);
187
343
  this.modelClass = modelClass;
188
344
  }
345
+ /**
346
+ * Execute the query and return model instances.
347
+ *
348
+ * @returns A promise that resolves to an array of model instances.
349
+ */
189
350
  async get() {
190
351
  const { sql, bindings } = this.toSql();
191
352
  const results = await this.connection.select(sql, bindings);
@@ -196,11 +357,21 @@ var ModelQueryBuilder = class extends QueryBuilder {
196
357
  return instance;
197
358
  });
198
359
  }
360
+ /**
361
+ * Execute the query and return the first matching model instance.
362
+ *
363
+ * @returns A promise that resolves to the model instance or null.
364
+ */
199
365
  async first() {
200
366
  this.limit(1);
201
367
  const results = await this.get();
202
368
  return results.length > 0 ? results[0] : null;
203
369
  }
370
+ /**
371
+ * Get the underlying model class.
372
+ *
373
+ * @returns The model class constructor.
374
+ */
204
375
  getModel() {
205
376
  return this.modelClass;
206
377
  }
@@ -208,14 +379,36 @@ var ModelQueryBuilder = class extends QueryBuilder {
208
379
 
209
380
  // src/core/database.ts
210
381
  var Database = class _Database {
382
+ /**
383
+ * The singleton instance of the Database.
384
+ */
211
385
  static instance;
386
+ /**
387
+ * The active database connection.
388
+ */
212
389
  connection;
390
+ /**
391
+ * Private constructor to enforce singleton pattern.
392
+ *
393
+ * @param d1 - The D1Database instance from Cloudflare.
394
+ */
213
395
  constructor(d1) {
214
396
  this.connection = new Connection(d1);
215
397
  }
398
+ /**
399
+ * Setup the database connection.
400
+ *
401
+ * @param d1 - The D1Database instance from Cloudflare environment (env.DB).
402
+ */
216
403
  static setup(d1) {
217
404
  _Database.instance = new _Database(d1);
218
405
  }
406
+ /**
407
+ * Get the singleton Database instance.
408
+ *
409
+ * @throws Error if setup() has not been called.
410
+ * @returns The Database instance.
411
+ */
219
412
  static getInstance() {
220
413
  if (!_Database.instance) {
221
414
  throw new Error(
@@ -300,43 +493,106 @@ var BelongsTo = class extends Relationship {
300
493
 
301
494
  // src/models/model.ts
302
495
  var Model = class {
496
+ /**
497
+ * The table associated with the model.
498
+ */
303
499
  static table;
500
+ /**
501
+ * Resolver function for the database connection.
502
+ */
304
503
  static connectionResolver;
504
+ /**
505
+ * The model's attributes.
506
+ */
305
507
  attributes = {};
508
+ /**
509
+ * The attributes that are mass assignable.
510
+ */
306
511
  fillable = [];
512
+ /**
513
+ * The attributes that should be hidden for serialization.
514
+ */
307
515
  hidden = [];
516
+ /**
517
+ * Indicates if the model exists in the database.
518
+ */
308
519
  exists = false;
520
+ /**
521
+ * The model's original attribute values.
522
+ */
309
523
  original = {};
524
+ /**
525
+ * Create a new Model instance.
526
+ *
527
+ * @param attributes - Initial attributes for the model.
528
+ */
310
529
  constructor(attributes = {}) {
311
530
  this.fill(attributes);
312
531
  }
313
- // Static Query Builder
532
+ /**
533
+ * Begin querying the model.
534
+ *
535
+ * @returns A new ModelQueryBuilder instance.
536
+ */
314
537
  static query() {
315
538
  const instance = new this();
316
539
  const table = instance.getTable();
317
540
  const connection = Database.getInstance().connection;
318
541
  return new ModelQueryBuilder(connection, table, this);
319
542
  }
543
+ /**
544
+ * Start a query on a specific connection (currently uses default).
545
+ *
546
+ * @param connectionName - The name of the connection.
547
+ * @returns A new ModelQueryBuilder instance.
548
+ */
320
549
  static on(connectionName) {
321
550
  return this.query();
322
551
  }
552
+ /**
553
+ * Retrieve all records for the model.
554
+ *
555
+ * @returns A promise that resolves to an array of model instances.
556
+ */
323
557
  static async all() {
324
558
  return this.query().get();
325
559
  }
560
+ /**
561
+ * Find a model by its primary key.
562
+ *
563
+ * @param id - The ID of the record.
564
+ * @returns A promise that resolves to the model instance or null if not found.
565
+ */
326
566
  static async find(id) {
327
567
  return this.query().where("id", "=", id).first();
328
568
  }
569
+ /**
570
+ * Save a new model and return the instance.
571
+ *
572
+ * @param attributes - Attributes for the new record.
573
+ * @returns A promise that resolves to the created model instance.
574
+ */
329
575
  static async create(attributes) {
330
576
  const instance = new this();
331
577
  instance.fill(attributes);
332
578
  await instance.save();
333
579
  return instance;
334
580
  }
335
- // Instance Methods
581
+ /**
582
+ * Fill the model with an array of attributes.
583
+ *
584
+ * @param attributes - Attributes to fill.
585
+ * @returns The model instance.
586
+ */
336
587
  fill(attributes) {
337
588
  Object.assign(this.attributes, attributes);
338
589
  return this;
339
590
  }
591
+ /**
592
+ * Save the model to the database.
593
+ *
594
+ * @returns A promise that resolves to true on success, false otherwise.
595
+ */
340
596
  async save() {
341
597
  const query = this.constructor.query();
342
598
  if (this.exists) {
@@ -356,6 +612,11 @@ var Model = class {
356
612
  return false;
357
613
  }
358
614
  }
615
+ /**
616
+ * Delete the model from the database.
617
+ *
618
+ * @returns A promise that resolves to true on success, false otherwise.
619
+ */
359
620
  async delete() {
360
621
  if (!this.exists) return false;
361
622
  const query = this.constructor.query();
@@ -364,12 +625,22 @@ var Model = class {
364
625
  this.exists = false;
365
626
  return true;
366
627
  }
628
+ /**
629
+ * Get the table associated with the model.
630
+ *
631
+ * @returns The table name.
632
+ */
367
633
  getTable() {
368
634
  if (this.constructor.table) {
369
635
  return this.constructor.table;
370
636
  }
371
637
  return this.constructor.name.toLowerCase() + "s";
372
638
  }
639
+ /**
640
+ * Get the attributes that have been changed since the last sync.
641
+ *
642
+ * @returns A record of dirty attributes.
643
+ */
373
644
  getDirty() {
374
645
  const dirty = {};
375
646
  for (const key in this.attributes) {
@@ -379,37 +650,122 @@ var Model = class {
379
650
  }
380
651
  return dirty;
381
652
  }
653
+ /**
654
+ * Sync the original attributes with the current attributes.
655
+ */
382
656
  syncOriginal() {
383
657
  this.original = { ...this.attributes };
384
658
  }
385
- // Relationships
659
+ /**
660
+ * Define a one-to-one relationship.
661
+ *
662
+ * @param related - The related model class.
663
+ * @param foreignKey - The foreign key on the related table.
664
+ * @param localKey - The local key on the current table.
665
+ * @returns A HasOne relationship instance.
666
+ */
386
667
  hasOne(related, foreignKey, localKey) {
387
668
  const instance = new related();
388
669
  const fk = foreignKey || this.getForeignKey();
389
670
  const lk = localKey || "id";
390
671
  return new HasOne(instance.newQuery(), this, fk, lk);
391
672
  }
673
+ /**
674
+ * Define a one-to-many relationship.
675
+ *
676
+ * @param related - The related model class.
677
+ * @param foreignKey - The foreign key on the related table.
678
+ * @param localKey - The local key on the current table.
679
+ * @returns A HasMany relationship instance.
680
+ */
392
681
  hasMany(related, foreignKey, localKey) {
393
682
  const instance = new related();
394
683
  const fk = foreignKey || this.getForeignKey();
395
684
  const lk = localKey || "id";
396
685
  return new HasMany(instance.newQuery(), this, fk, lk);
397
686
  }
687
+ /**
688
+ * Define an inverse one-to-one or many relationship.
689
+ *
690
+ * @param related - The related model class.
691
+ * @param foreignKey - The foreign key on the current table.
692
+ * @param ownerKey - The owner key on the related table.
693
+ * @returns A BelongsTo relationship instance.
694
+ */
398
695
  belongsTo(related, foreignKey, ownerKey) {
399
696
  const instance = new related();
400
697
  const fk = foreignKey || instance.getForeignKey();
401
698
  const ok = ownerKey || "id";
402
699
  return new BelongsTo(instance.newQuery(), this, fk, ok);
403
700
  }
701
+ /**
702
+ * Get the default foreign key name for the model.
703
+ *
704
+ * @returns The foreign key name.
705
+ */
404
706
  getForeignKey() {
405
707
  return this.getTable().slice(0, -1) + "_id";
406
708
  }
709
+ /**
710
+ * Get a new query builder for the model's table.
711
+ *
712
+ * @returns A ModelQueryBuilder instance.
713
+ */
407
714
  newQuery() {
408
715
  return this.constructor.query();
409
716
  }
410
717
  };
411
718
 
412
719
  // src/database/blueprint.ts
720
+ var Column = class {
721
+ name;
722
+ type;
723
+ isNullable = false;
724
+ isUnique = false;
725
+ defaultValue = null;
726
+ constructor(name, type) {
727
+ this.name = name;
728
+ this.type = type;
729
+ }
730
+ /**
731
+ * Set the column as nullable.
732
+ */
733
+ nullable() {
734
+ this.isNullable = true;
735
+ return this;
736
+ }
737
+ /**
738
+ * Set the column as unique.
739
+ */
740
+ unique() {
741
+ this.isUnique = true;
742
+ return this;
743
+ }
744
+ /**
745
+ * Set the default value for the column.
746
+ */
747
+ default(value) {
748
+ this.defaultValue = value;
749
+ return this;
750
+ }
751
+ /**
752
+ * Convert the column definition to SQL.
753
+ */
754
+ toSql() {
755
+ let sql = `${this.name} ${this.type}`;
756
+ if (this.isUnique) {
757
+ sql += " UNIQUE";
758
+ }
759
+ if (!this.isNullable) {
760
+ sql += " NOT NULL";
761
+ }
762
+ if (this.defaultValue !== null) {
763
+ const formattedValue = typeof this.defaultValue === "string" ? `'${this.defaultValue}'` : this.defaultValue;
764
+ sql += ` DEFAULT ${formattedValue}`;
765
+ }
766
+ return sql;
767
+ }
768
+ };
413
769
  var Blueprint = class {
414
770
  table;
415
771
  columns = [];
@@ -417,30 +773,55 @@ var Blueprint = class {
417
773
  constructor(table) {
418
774
  this.table = table;
419
775
  }
776
+ /**
777
+ * Add an auto-incrementing ID column.
778
+ */
420
779
  id() {
421
780
  this.columns.push("id INTEGER PRIMARY KEY AUTOINCREMENT");
422
781
  return this;
423
782
  }
424
- string(column, length) {
425
- this.columns.push(`${column} TEXT`);
426
- return this;
427
- }
783
+ /**
784
+ * Add a string (TEXT) column.
785
+ */
786
+ string(column) {
787
+ const col = new Column(column, "TEXT");
788
+ this.columns.push(col);
789
+ return col;
790
+ }
791
+ /**
792
+ * Add an integer column.
793
+ */
428
794
  integer(column) {
429
- this.columns.push(`${column} INTEGER`);
430
- return this;
795
+ const col = new Column(column, "INTEGER");
796
+ this.columns.push(col);
797
+ return col;
431
798
  }
799
+ /**
800
+ * Add a boolean column.
801
+ */
432
802
  boolean(column) {
433
- this.columns.push(`${column} BOOLEAN`);
434
- return this;
803
+ const col = new Column(column, "BOOLEAN");
804
+ this.columns.push(col);
805
+ return col;
435
806
  }
807
+ /**
808
+ * Add created_at and updated_at timestamp columns.
809
+ */
436
810
  timestamps() {
437
811
  this.columns.push("created_at DATETIME DEFAULT CURRENT_TIMESTAMP");
438
812
  this.columns.push("updated_at DATETIME DEFAULT CURRENT_TIMESTAMP");
439
813
  return this;
440
814
  }
815
+ /**
816
+ * Convert the blueprint to a set of SQL statements.
817
+ */
441
818
  toSql() {
442
819
  if (this.columns.length > 0) {
443
- const cols = this.columns.join(", ");
820
+ const colDefinitions = this.columns.map((col) => {
821
+ if (typeof col === "string") return col;
822
+ return col.toSql();
823
+ });
824
+ const cols = colDefinitions.join(", ");
444
825
  return [`CREATE TABLE IF NOT EXISTS ${this.table} (${cols})`];
445
826
  }
446
827
  return this.commands;
@@ -462,6 +843,7 @@ var Schema = class {
462
843
  0 && (module.exports = {
463
844
  BelongsTo,
464
845
  Blueprint,
846
+ Column,
465
847
  Connection,
466
848
  Database,
467
849
  HasMany,