@mantiq/database 0.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.
Files changed (42) hide show
  1. package/README.md +19 -0
  2. package/package.json +77 -0
  3. package/src/DatabaseManager.ts +115 -0
  4. package/src/DatabaseServiceProvider.ts +39 -0
  5. package/src/contracts/Connection.ts +13 -0
  6. package/src/contracts/Grammar.ts +16 -0
  7. package/src/contracts/MongoConnection.ts +122 -0
  8. package/src/contracts/Paginator.ts +10 -0
  9. package/src/drivers/BaseGrammar.ts +220 -0
  10. package/src/drivers/MSSQLConnection.ts +154 -0
  11. package/src/drivers/MSSQLGrammar.ts +106 -0
  12. package/src/drivers/MongoConnection.ts +298 -0
  13. package/src/drivers/MongoQueryBuilderImpl.ts +77 -0
  14. package/src/drivers/MySQLConnection.ts +120 -0
  15. package/src/drivers/MySQLGrammar.ts +19 -0
  16. package/src/drivers/PostgresConnection.ts +125 -0
  17. package/src/drivers/PostgresGrammar.ts +24 -0
  18. package/src/drivers/SQLiteConnection.ts +125 -0
  19. package/src/drivers/SQLiteGrammar.ts +19 -0
  20. package/src/errors/ConnectionError.ts +10 -0
  21. package/src/errors/ModelNotFoundError.ts +14 -0
  22. package/src/errors/QueryError.ts +11 -0
  23. package/src/events/DatabaseEvents.ts +101 -0
  24. package/src/factories/Factory.ts +170 -0
  25. package/src/factories/Faker.ts +382 -0
  26. package/src/helpers/db.ts +37 -0
  27. package/src/index.ts +100 -0
  28. package/src/migrations/Migration.ts +12 -0
  29. package/src/migrations/MigrationRepository.ts +50 -0
  30. package/src/migrations/Migrator.ts +201 -0
  31. package/src/orm/Collection.ts +236 -0
  32. package/src/orm/Document.ts +202 -0
  33. package/src/orm/Model.ts +775 -0
  34. package/src/orm/ModelQueryBuilder.ts +415 -0
  35. package/src/orm/Scope.ts +39 -0
  36. package/src/orm/eagerLoad.ts +300 -0
  37. package/src/query/Builder.ts +456 -0
  38. package/src/query/Expression.ts +18 -0
  39. package/src/schema/Blueprint.ts +196 -0
  40. package/src/schema/ColumnDefinition.ts +93 -0
  41. package/src/schema/SchemaBuilder.ts +376 -0
  42. package/src/seeders/Seeder.ts +28 -0
@@ -0,0 +1,202 @@
1
+ import type { MongoDatabaseConnection, MongoFilter, MongoUpdateDoc, MongoPipelineStage } from '../contracts/MongoConnection.ts'
2
+ import { ModelNotFoundError } from '../errors/ModelNotFoundError.ts'
3
+
4
+ /**
5
+ * Base class for MongoDB document models. Similar to Model but for document databases.
6
+ *
7
+ * @example
8
+ * class User extends Document {
9
+ * static collection = 'users'
10
+ * static connection = mongoConn
11
+ *
12
+ * get fullName(): string {
13
+ * return `${this.get('firstName')} ${this.get('lastName')}`
14
+ * }
15
+ * }
16
+ *
17
+ * const user = await User.findById('65a...')
18
+ * await user!.update({ $set: { name: 'New Name' } })
19
+ */
20
+ export abstract class Document {
21
+ static connection: MongoDatabaseConnection | null = null
22
+ static collection: string = ''
23
+ static hidden: string[] = []
24
+
25
+ protected _doc: Record<string, any> = {}
26
+ protected _exists = false
27
+
28
+ // ── Static query API ───────────────────────────────────────────────────────
29
+
30
+ static col<T extends Document>(this: new () => T): import('../contracts/MongoConnection.ts').MongoCollectionContract {
31
+ const ctor = this as unknown as typeof Document
32
+ if (!ctor.connection) throw new Error(`No connection set on Document ${ctor.collection}`)
33
+ return ctor.connection.collection(ctor.collection)
34
+ }
35
+
36
+ static async find<T extends Document>(this: new () => T, filter: MongoFilter = {}): Promise<T[]> {
37
+ const rows = await (this as unknown as typeof Document).col<T>().find(filter).get()
38
+ return rows.map((r) => (this as unknown as typeof Document).hydrate<T>(this, r))
39
+ }
40
+
41
+ static async findOne<T extends Document>(this: new () => T, filter: MongoFilter = {}): Promise<T | null> {
42
+ const row = await (this as unknown as typeof Document).col<T>().findOne(filter)
43
+ return row ? (this as unknown as typeof Document).hydrate<T>(this, row) : null
44
+ }
45
+
46
+ static async findById<T extends Document>(this: new () => T, id: any): Promise<T | null> {
47
+ const row = await (this as unknown as typeof Document).col<T>().findById(id)
48
+ return row ? (this as unknown as typeof Document).hydrate<T>(this, row) : null
49
+ }
50
+
51
+ static async findByIdOrFail<T extends Document>(this: new () => T, id: any): Promise<T> {
52
+ const doc = await (this as unknown as typeof Document).findById<T>(id)
53
+ if (!doc) throw new ModelNotFoundError((this as unknown as typeof Document).collection)
54
+ return doc
55
+ }
56
+
57
+ static async create<T extends Document>(this: new () => T, data: Record<string, any>): Promise<T> {
58
+ const col = (this as unknown as typeof Document).col<T>()
59
+ const now = new Date()
60
+ const doc = { ...data, createdAt: now, updatedAt: now }
61
+ const result = await col.insertOne(doc)
62
+ return (this as unknown as typeof Document).hydrate<T>(this, { ...doc, _id: result.insertedId })
63
+ }
64
+
65
+ static async insertMany<T extends Document>(
66
+ this: new () => T,
67
+ docs: Record<string, any>[],
68
+ ): Promise<T[]> {
69
+ const col = (this as unknown as typeof Document).col<T>()
70
+ const now = new Date()
71
+ const withTimestamps = docs.map((d) => ({ ...d, createdAt: now, updatedAt: now }))
72
+ const result = await col.insertMany(withTimestamps)
73
+ return withTimestamps.map((d, i) =>
74
+ (this as unknown as typeof Document).hydrate<T>(this, { ...d, _id: result.insertedIds[i] }),
75
+ )
76
+ }
77
+
78
+ static async updateOne<T extends Document>(
79
+ this: new () => T,
80
+ filter: MongoFilter,
81
+ update: MongoUpdateDoc,
82
+ ) {
83
+ const merged: MongoUpdateDoc = {
84
+ ...update,
85
+ $set: { ...update.$set, updatedAt: new Date() },
86
+ }
87
+ return (this as unknown as typeof Document).col<T>().updateOne(filter, merged)
88
+ }
89
+
90
+ static async updateMany<T extends Document>(
91
+ this: new () => T,
92
+ filter: MongoFilter,
93
+ update: MongoUpdateDoc,
94
+ ) {
95
+ const merged: MongoUpdateDoc = {
96
+ ...update,
97
+ $set: { ...update.$set, updatedAt: new Date() },
98
+ }
99
+ return (this as unknown as typeof Document).col<T>().updateMany(filter, merged)
100
+ }
101
+
102
+ static async deleteOne<T extends Document>(this: new () => T, filter: MongoFilter) {
103
+ return (this as unknown as typeof Document).col<T>().deleteOne(filter)
104
+ }
105
+
106
+ static async deleteMany<T extends Document>(this: new () => T, filter: MongoFilter) {
107
+ return (this as unknown as typeof Document).col<T>().deleteMany(filter)
108
+ }
109
+
110
+ static async aggregate<T extends Document>(
111
+ this: new () => T,
112
+ pipeline: MongoPipelineStage[],
113
+ ): Promise<Record<string, any>[]> {
114
+ return (this as unknown as typeof Document).col<T>().aggregate(pipeline)
115
+ }
116
+
117
+ static async count<T extends Document>(this: new () => T, filter: MongoFilter = {}): Promise<number> {
118
+ return (this as unknown as typeof Document).col<T>().count(filter)
119
+ }
120
+
121
+ /** Set the MongoDB connection for this Document class */
122
+ static setConnection(connection: MongoDatabaseConnection): void {
123
+ this.connection = connection
124
+ }
125
+
126
+ // ── Instance methods ──────────────────────────────────────────────────────
127
+
128
+ get(key: string): any {
129
+ return this._doc[key]
130
+ }
131
+
132
+ set(key: string, value: any): this {
133
+ this._doc[key] = value
134
+ return this
135
+ }
136
+
137
+ getId(): any {
138
+ return this._doc['_id']
139
+ }
140
+
141
+ toObject(): Record<string, any> {
142
+ const ctor = this.constructor as typeof Document
143
+ const obj: Record<string, any> = {}
144
+ for (const [k, v] of Object.entries(this._doc)) {
145
+ if (!ctor.hidden.includes(k)) obj[k] = v
146
+ }
147
+ return obj
148
+ }
149
+
150
+ toJSON(): Record<string, any> {
151
+ return this.toObject()
152
+ }
153
+
154
+ async update(update: MongoUpdateDoc): Promise<this> {
155
+ const ctor = this.constructor as typeof Document
156
+ if (!ctor.connection || !this._exists) return this
157
+
158
+ const merged: MongoUpdateDoc = {
159
+ ...update,
160
+ $set: { ...update.$set, updatedAt: new Date() },
161
+ }
162
+
163
+ await ctor.connection.collection(ctor.collection).updateOne({ _id: this.getId() }, merged)
164
+
165
+ // Apply $set changes locally
166
+ if (merged.$set) {
167
+ for (const [k, v] of Object.entries(merged.$set)) {
168
+ this._doc[k] = v
169
+ }
170
+ }
171
+
172
+ return this
173
+ }
174
+
175
+ async delete(): Promise<boolean> {
176
+ const ctor = this.constructor as typeof Document
177
+ if (!ctor.connection || !this._exists) return false
178
+ await ctor.connection.collection(ctor.collection).deleteOne({ _id: this.getId() })
179
+ this._exists = false
180
+ return true
181
+ }
182
+
183
+ async refresh(): Promise<this> {
184
+ const ctor = this.constructor as typeof Document
185
+ if (!ctor.connection) return this
186
+ const row = await ctor.connection.collection(ctor.collection).findById(this.getId())
187
+ if (row) this._doc = row
188
+ return this
189
+ }
190
+
191
+ // ── Private ────────────────────────────────────────────────────────────────
192
+
193
+ private static hydrate<T extends Document>(
194
+ ctor: new () => T,
195
+ doc: Record<string, any>,
196
+ ): T {
197
+ const instance = new ctor()
198
+ instance._doc = doc
199
+ instance._exists = true
200
+ return instance
201
+ }
202
+ }