tspace-mysql 1.9.1 → 1.9.3-beta.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 +388 -40
- package/dist/lib/core/Abstracts/AbstractBuilder.d.ts +16 -17
- package/dist/lib/core/Abstracts/AbstractBuilder.js.map +1 -1
- package/dist/lib/core/Abstracts/AbstractDB.d.ts +1 -1
- package/dist/lib/core/Abstracts/AbstractModel.d.ts +7 -2
- package/dist/lib/core/Abstracts/AbstractModel.js.map +1 -1
- package/dist/lib/core/Blueprint.d.ts +56 -23
- package/dist/lib/core/Blueprint.js +45 -6
- package/dist/lib/core/Blueprint.js.map +1 -1
- package/dist/lib/core/Builder.d.ts +57 -39
- package/dist/lib/core/Builder.js +91 -56
- package/dist/lib/core/Builder.js.map +1 -1
- package/dist/lib/core/Cache/MemoryCache.js +9 -4
- package/dist/lib/core/Cache/MemoryCache.js.map +1 -1
- package/dist/lib/core/Cache/index.js +32 -13
- package/dist/lib/core/Cache/index.js.map +1 -1
- package/dist/lib/core/Contracts/Audit.d.ts +6 -6
- package/dist/lib/core/Contracts/Audit.js.map +1 -1
- package/dist/lib/core/Contracts/Logger.d.ts +5 -5
- package/dist/lib/core/Contracts/Logger.js.map +1 -1
- package/dist/lib/core/DB.d.ts +2 -2
- package/dist/lib/core/DB.js +6 -19
- package/dist/lib/core/DB.js.map +1 -1
- package/dist/lib/core/Driver/index.d.ts +7 -5
- package/dist/lib/core/Driver/index.js +4 -2
- package/dist/lib/core/Driver/index.js.map +1 -1
- package/dist/lib/core/Driver/mariadb/MariadbDriver.js +14 -6
- package/dist/lib/core/Driver/mariadb/MariadbDriver.js.map +1 -1
- package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.d.ts +3 -3
- package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.js +24 -7
- package/dist/lib/core/Driver/mariadb/MariadbQueryBuilder.js.map +1 -1
- package/dist/lib/core/Driver/mongodb/MongodbDriver.js +18 -12
- package/dist/lib/core/Driver/mongodb/MongodbDriver.js.map +1 -1
- package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.d.ts +3 -3
- package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.js +9 -1
- package/dist/lib/core/Driver/mongodb/MongodbQueryBuilder.js.map +1 -1
- package/dist/lib/core/Driver/mysql/MysqlDriver.js +15 -7
- package/dist/lib/core/Driver/mysql/MysqlDriver.js.map +1 -1
- package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.d.ts +3 -3
- package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.js +24 -7
- package/dist/lib/core/Driver/mysql/MysqlQueryBuilder.js.map +1 -1
- package/dist/lib/core/Driver/postgres/PostgresDriver.js +14 -6
- package/dist/lib/core/Driver/postgres/PostgresDriver.js.map +1 -1
- package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.d.ts +3 -3
- package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.js +22 -7
- package/dist/lib/core/Driver/postgres/PostgresQueryBuilder.js.map +1 -1
- package/dist/lib/core/Driver/sqlite/SqliteDriver.d.ts +1 -0
- package/dist/lib/core/Driver/sqlite/SqliteDriver.js +25 -4
- package/dist/lib/core/Driver/sqlite/SqliteDriver.js.map +1 -1
- package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.d.ts +2 -0
- package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.js +27 -21
- package/dist/lib/core/Driver/sqlite/SqliteQueryBuilder.js.map +1 -1
- package/dist/lib/core/Join.d.ts +63 -5
- package/dist/lib/core/Join.js +92 -15
- package/dist/lib/core/Join.js.map +1 -1
- package/dist/lib/core/JoinModel.d.ts +65 -9
- package/dist/lib/core/JoinModel.js +79 -50
- package/dist/lib/core/JoinModel.js.map +1 -1
- package/dist/lib/core/Meta.d.ts +7 -7
- package/dist/lib/core/Meta.js +10 -9
- package/dist/lib/core/Meta.js.map +1 -1
- package/dist/lib/core/Model.d.ts +377 -69
- package/dist/lib/core/Model.js +528 -93
- package/dist/lib/core/Model.js.map +1 -1
- package/dist/lib/core/{Queue.d.ts → Queue/index.d.ts} +96 -90
- package/dist/lib/core/Queue/index.js +282 -0
- package/dist/lib/core/Queue/index.js.map +1 -0
- package/dist/lib/core/Queue/types.d.ts +31 -0
- package/dist/lib/core/Queue/types.js +3 -0
- package/dist/lib/core/Queue/types.js.map +1 -0
- package/dist/lib/core/Queue/worker.d.ts +102 -0
- package/dist/lib/core/{Queue.js → Queue/worker.js} +165 -260
- package/dist/lib/core/Queue/worker.js.map +1 -0
- package/dist/lib/core/RelationManager.d.ts +4 -4
- package/dist/lib/core/RelationManager.js +141 -16
- package/dist/lib/core/RelationManager.js.map +1 -1
- package/dist/lib/core/Repository.d.ts +41 -21
- package/dist/lib/core/Repository.js +40 -15
- package/dist/lib/core/Repository.js.map +1 -1
- package/dist/lib/core/Schema.d.ts +13 -13
- package/dist/lib/core/Schema.js +12 -0
- package/dist/lib/core/Schema.js.map +1 -1
- package/dist/lib/core/StateManager.d.ts +6 -0
- package/dist/lib/core/StateManager.js +1 -0
- package/dist/lib/core/StateManager.js.map +1 -1
- package/dist/lib/core/UtilityTypes.d.ts +75 -78
- package/dist/lib/core/UtilityTypes.js.map +1 -1
- package/dist/lib/types/decorator/index.d.ts +3 -0
- package/dist/lib/types/index.d.ts +53 -9
- package/dist/lib/types/repository/index.d.ts +3 -3
- package/dist/lib/utils/index.d.ts +6 -3
- package/dist/lib/utils/index.js +42 -3
- package/dist/lib/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/lib/core/Queue.js.map +0 -1
package/README.md
CHANGED
|
@@ -43,7 +43,29 @@ npm install -g tspace-mysql
|
|
|
43
43
|
|
|
44
44
|
## Documentation
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
### For LLMs & AI Assistants
|
|
47
|
+
|
|
48
|
+
📚 **Complete Skills Documentation** is available in the [`skills/`](./skills) folder. This includes comprehensive, verified guides for:
|
|
49
|
+
|
|
50
|
+
- Model Setup & Blueprint
|
|
51
|
+
- Query Builder & SQL Operations
|
|
52
|
+
- Relationships (hasOne, hasMany, belongsTo, belongsToMany)
|
|
53
|
+
- Repository Pattern
|
|
54
|
+
- Decorators
|
|
55
|
+
- Type Safety (T namespace)
|
|
56
|
+
- Transactions
|
|
57
|
+
- Caching (DB_CACHE, .cache())
|
|
58
|
+
- Queue System
|
|
59
|
+
- CLI Commands
|
|
60
|
+
- Complete Real-World Examples
|
|
61
|
+
|
|
62
|
+
All skills documentation is **100% verified** against the source code.
|
|
63
|
+
|
|
64
|
+
### Human-Readable Documentation
|
|
65
|
+
|
|
66
|
+
See the [`docs/`](./docs) directory for full documentation with examples.
|
|
67
|
+
|
|
68
|
+
Online: https://thanathip41.github.io/tspace-mysql
|
|
47
69
|
|
|
48
70
|
## TypeScript
|
|
49
71
|
|
|
@@ -66,6 +88,8 @@ npm install -D typescript@5.9.3
|
|
|
66
88
|
- [MySQL Database](#mysql-database)
|
|
67
89
|
- [Mariadb Database](#mariadb-database)
|
|
68
90
|
- [Postgres Database](#postgres-database)
|
|
91
|
+
- [SQLite Database](#sqlite-database)
|
|
92
|
+
- [Mongodb Database](#mongodb-database)
|
|
69
93
|
- [Cluster Database](#cluster-database)
|
|
70
94
|
- [SQL Like](#sql-Like)
|
|
71
95
|
- [Query Builder](#query-builder)
|
|
@@ -107,6 +131,7 @@ npm install -D typescript@5.9.3
|
|
|
107
131
|
- [More Methods](#more-methods)
|
|
108
132
|
- [Database Transactions](#database-transactions)
|
|
109
133
|
- [Race Condition](#race-condition)
|
|
134
|
+
- [Lock Table](#lock-table)
|
|
110
135
|
- [Connection](#connection)
|
|
111
136
|
- [Backup](#backup)
|
|
112
137
|
- [Injection](#injection)
|
|
@@ -165,6 +190,8 @@ npm install -D typescript@5.9.3
|
|
|
165
190
|
- [Repository Transactions](#repository-transactions)
|
|
166
191
|
- [Repository Relations](#repository-relations)
|
|
167
192
|
- [Queue](#queue)
|
|
193
|
+
- [Queue Events](#queue-events)
|
|
194
|
+
- [Queue Type-safe Events and Jobs](#queue-type-safe-events-and-jobs)
|
|
168
195
|
- [View](#view)
|
|
169
196
|
- [Stored Procedure](#stored-procedure)
|
|
170
197
|
- [Blueprint](#blueprint)
|
|
@@ -247,6 +274,50 @@ DB_PASSWORD = password
|
|
|
247
274
|
DB_DATABASE = database
|
|
248
275
|
```
|
|
249
276
|
|
|
277
|
+
### SQLite Database
|
|
278
|
+
|
|
279
|
+
To connect the application to a SQLite database, using the following:
|
|
280
|
+
|
|
281
|
+
```sh
|
|
282
|
+
npm install better-sqlite3 --save
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
```js
|
|
286
|
+
DB_DRIVER = sqlite
|
|
287
|
+
DB_DATABASE = app.db
|
|
288
|
+
```
|
|
289
|
+
⚠️ Requirements for better-sqlite3
|
|
290
|
+
Node.js 22 or higher is required
|
|
291
|
+
|
|
292
|
+
### Mongodb Database
|
|
293
|
+
|
|
294
|
+
To connect the application to a Mongodb database, using the following:
|
|
295
|
+
```sh
|
|
296
|
+
npm install mongodb --save
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
```js
|
|
300
|
+
DB_DRIVER = mongodb
|
|
301
|
+
DB_HOST = localhost
|
|
302
|
+
DB_PORT = 27017
|
|
303
|
+
DB_USERNAME = root
|
|
304
|
+
DB_PASSWORD = password
|
|
305
|
+
DB_DATABASE = database
|
|
306
|
+
```
|
|
307
|
+
✅ Supported Features
|
|
308
|
+
|
|
309
|
+
CRUD operations (create, read, update, delete)
|
|
310
|
+
Basic joins & relations (via abstraction layer)
|
|
311
|
+
|
|
312
|
+
⚠️ Limitations
|
|
313
|
+
|
|
314
|
+
MongoDB support is partially implemented and may not fully match SQL-based drivers like MySQL or PostgreSQL
|
|
315
|
+
|
|
316
|
+
* Advanced ORM features may be limited
|
|
317
|
+
* Complex joins rely on abstraction (not native MongoDB behavior)
|
|
318
|
+
* Some features may behave differently compared to relational databases
|
|
319
|
+
* Transactions / advanced optimizations may be limited
|
|
320
|
+
|
|
250
321
|
### Cluster Database
|
|
251
322
|
If you need strict race condition control, it is required to use multiple nodes for write and read. <br>
|
|
252
323
|
Avoid using a node load balancer in this case, as it may bypass proper write/read distribution and compromise consistency.<br>
|
|
@@ -1251,7 +1322,7 @@ whereNotIn(column , [])
|
|
|
1251
1322
|
whereNull(column)
|
|
1252
1323
|
whereNotNull(column)
|
|
1253
1324
|
whereBetween (column , [value1 , value2])
|
|
1254
|
-
whereQuery(
|
|
1325
|
+
whereQuery(query)
|
|
1255
1326
|
whereJson(column, { targetKey, value , OP })
|
|
1256
1327
|
whereRaw(sql)
|
|
1257
1328
|
whereExists(sql)
|
|
@@ -1261,7 +1332,7 @@ orWhere(column , OP , value)
|
|
|
1261
1332
|
orWhereRaw(sql)
|
|
1262
1333
|
orWhereIn(column , [])
|
|
1263
1334
|
orWhereSubQuery(colmn , rawSQL)
|
|
1264
|
-
when(contition ,
|
|
1335
|
+
when(contition , query)
|
|
1265
1336
|
select(column1 ,column2 ,...N)
|
|
1266
1337
|
distinct()
|
|
1267
1338
|
selectRaw(column1 ,column2 ,...N)
|
|
@@ -1294,7 +1365,7 @@ onlyTrashed()
|
|
|
1294
1365
|
connection(options)
|
|
1295
1366
|
backup({ database , connection })
|
|
1296
1367
|
backupToFile({ filePath, database , connection })
|
|
1297
|
-
hook((result) => ...) //
|
|
1368
|
+
hook((result) => ...) // query result to function
|
|
1298
1369
|
sleep(seconds)
|
|
1299
1370
|
|
|
1300
1371
|
/**
|
|
@@ -1322,9 +1393,9 @@ relationsExists(name1 , name2,...nameN) // withExists(name1, name2,...nameN)
|
|
|
1322
1393
|
*/
|
|
1323
1394
|
relationsTrashed(name1 , name2,...nameN) // withTrashed(name1, name2,...nameN)
|
|
1324
1395
|
/**
|
|
1325
|
-
* @relation call a name of relation in registry,
|
|
1396
|
+
* @relation call a name of relation in registry, query query of data
|
|
1326
1397
|
*/
|
|
1327
|
-
relationQuery(name, (
|
|
1398
|
+
relationQuery(name, (query) ) // withQuery(name1, (query))
|
|
1328
1399
|
|
|
1329
1400
|
|
|
1330
1401
|
/**
|
|
@@ -1603,6 +1674,148 @@ async function simulateRaceConnection(): Promise<void> {
|
|
|
1603
1674
|
|
|
1604
1675
|
simulateRaceConnection();
|
|
1605
1676
|
|
|
1677
|
+
```
|
|
1678
|
+
## Lock Table
|
|
1679
|
+
|
|
1680
|
+
Within a lock table, you can utilize the following:
|
|
1681
|
+
|
|
1682
|
+
This approach is not recommended in clustered or load-balance environments,
|
|
1683
|
+
because locks are session-bound and requests may be routed to different database connections.
|
|
1684
|
+
|
|
1685
|
+
READ:
|
|
1686
|
+
- Other sessions can read.
|
|
1687
|
+
- Other sessions must wait before writing.
|
|
1688
|
+
- SELECT: allowed
|
|
1689
|
+
- INSERT: waits
|
|
1690
|
+
- UPDATE: waits
|
|
1691
|
+
- DELETE: waits
|
|
1692
|
+
|
|
1693
|
+
WRITE:
|
|
1694
|
+
- Other sessions must wait before reading or writing.
|
|
1695
|
+
- SELECT: waits
|
|
1696
|
+
- INSERT: waits
|
|
1697
|
+
- UPDATE: waits
|
|
1698
|
+
- DELETE: waits
|
|
1699
|
+
|
|
1700
|
+
The lock is released automatically after the query completes.
|
|
1701
|
+
|
|
1702
|
+
```js
|
|
1703
|
+
import { Model, Blueprint } from 'tspace-mysql'
|
|
1704
|
+
|
|
1705
|
+
class User extends Model {
|
|
1706
|
+
protected boot() {
|
|
1707
|
+
this.useSchema({
|
|
1708
|
+
id : Blueprint.int().notNull().primary().autoIncrement(),
|
|
1709
|
+
email : Blueprint.varchar(20).notNull()
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
async function requestA() {
|
|
1715
|
+
|
|
1716
|
+
try {
|
|
1717
|
+
|
|
1718
|
+
console.log("A: locking table...");
|
|
1719
|
+
|
|
1720
|
+
// READ | WRITE
|
|
1721
|
+
await User.lockTable('WRITE', async (query) => {
|
|
1722
|
+
console.log("A: table LOCKED");
|
|
1723
|
+
|
|
1724
|
+
// Simulate a long-running write transaction.
|
|
1725
|
+
// WRITE operations from other sessions will wait
|
|
1726
|
+
// until the table lock is released.
|
|
1727
|
+
//
|
|
1728
|
+
// READ behavior depends on the selected lock mode:
|
|
1729
|
+
// - READ lock: other readers are allowed
|
|
1730
|
+
// - WRITE lock: readers/writers may be blocked
|
|
1731
|
+
await new Promise((r) => setTimeout(r, 1000 * 10));
|
|
1732
|
+
|
|
1733
|
+
// IMPORTANT:
|
|
1734
|
+
// While the table is locked, all database operations
|
|
1735
|
+
// must use the provided `query` instance.
|
|
1736
|
+
//
|
|
1737
|
+
// This guarantees the operation runs on the same
|
|
1738
|
+
// database session/connection that owns the lock.
|
|
1739
|
+
//
|
|
1740
|
+
// ❌ May fail because it can use a different connection:
|
|
1741
|
+
// await new User().insert({...}).save();
|
|
1742
|
+
//
|
|
1743
|
+
// ✅ Uses the lock owner session:
|
|
1744
|
+
await query.insert({...}).save();
|
|
1745
|
+
|
|
1746
|
+
// In READ mode this write operation will throw,
|
|
1747
|
+
// because writes are not permitted under a READ lock.
|
|
1748
|
+
// In WRITE mode it executes successfully.
|
|
1749
|
+
|
|
1750
|
+
console.log("A: unlocking table...");
|
|
1751
|
+
});
|
|
1752
|
+
|
|
1753
|
+
console.log("A: done");
|
|
1754
|
+
|
|
1755
|
+
} catch (err:any) {
|
|
1756
|
+
|
|
1757
|
+
console.error('❌ SHOULD NOT FAIL:', err.message);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
async function requestB(n: number = 1) {
|
|
1762
|
+
console.log(`B(${n}): waiting 1s before query...`);
|
|
1763
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
1764
|
+
|
|
1765
|
+
console.log(`B(${n}): trying SELECT...`);
|
|
1766
|
+
|
|
1767
|
+
const start = Date.now();
|
|
1768
|
+
|
|
1769
|
+
try {
|
|
1770
|
+
|
|
1771
|
+
const users = await new User().select('id').get();
|
|
1772
|
+
console.log(`B(${n}): result`, users.length);
|
|
1773
|
+
|
|
1774
|
+
console.log(`B(${n}): finished in`, Date.now() - start, "ms");
|
|
1775
|
+
} catch (err:any) {
|
|
1776
|
+
console.error(`B(${n}): ERROR`, err.message);
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
async function requestC(n: number = 1) {
|
|
1781
|
+
console.log(`C(${n}): waiting 1s before query...`);
|
|
1782
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
1783
|
+
|
|
1784
|
+
console.log(`C(${n}): trying Insert...Select`);
|
|
1785
|
+
|
|
1786
|
+
const start = Date.now();
|
|
1787
|
+
|
|
1788
|
+
try {
|
|
1789
|
+
|
|
1790
|
+
await new User().faker(1);
|
|
1791
|
+
|
|
1792
|
+
const users = await new User().select('id').get();
|
|
1793
|
+
console.log(`C(${n}): result`, users.length);
|
|
1794
|
+
|
|
1795
|
+
console.log(`C(${n}): finished in`, Date.now() - start, "ms");
|
|
1796
|
+
} catch (err:any) {
|
|
1797
|
+
console.error(`C(${n}): ERROR`, err.message);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
(async () => {
|
|
1802
|
+
// Test scenario:
|
|
1803
|
+
//
|
|
1804
|
+
// 1. Request A acquires a table lock.
|
|
1805
|
+
// 2. Requests B perform concurrent reads.
|
|
1806
|
+
// 3. Request C performs a concurrent write.
|
|
1807
|
+
// 4. Observe whether operations are blocked, queued, or rejected
|
|
1808
|
+
// according to the selected lock mode.
|
|
1809
|
+
await Promise.all([
|
|
1810
|
+
requestA(),
|
|
1811
|
+
requestB(1),
|
|
1812
|
+
requestB(2),
|
|
1813
|
+
requestC(3)
|
|
1814
|
+
])
|
|
1815
|
+
.then(r => process.exit(1))
|
|
1816
|
+
|
|
1817
|
+
})()
|
|
1818
|
+
|
|
1606
1819
|
```
|
|
1607
1820
|
|
|
1608
1821
|
## Connection
|
|
@@ -1757,6 +1970,26 @@ class User extends Model {
|
|
|
1757
1970
|
* updated_at : Blueprint.timestamp().null(),
|
|
1758
1971
|
* deleted_at : Blueprint.timestamp().null()
|
|
1759
1972
|
* }) // auto-generated table when table is not exists and auto-create column when column not exists
|
|
1973
|
+
*
|
|
1974
|
+
* this.useLifecycle('beforeInsert', () => console.log('beforeInsert'))
|
|
1975
|
+
* this.useLifecycle('afterInsert', () => console.log('afterInsert'))
|
|
1976
|
+
* this.useLifecycle('beforeUpdate', () => console.log('beforeUpdate'))
|
|
1977
|
+
* this.useLifecycle('afterUpdate', () => console.log('afterUpdate'))
|
|
1978
|
+
* this.useLifecycle('beforeRemove', () => console.log('beforeRemove'))
|
|
1979
|
+
* this.useLifecycle('afterRemove', () => console.log('afterRemove'))
|
|
1980
|
+
*
|
|
1981
|
+
* this.useTransform({
|
|
1982
|
+
* name : {
|
|
1983
|
+
* to : async (name) => `${name}-> transform@before`,
|
|
1984
|
+
* from : async (name) => `${name}-> transform@after`,
|
|
1985
|
+
* }
|
|
1986
|
+
* })
|
|
1987
|
+
*
|
|
1988
|
+
* this.useHooks([
|
|
1989
|
+
* (r:any) => console.log(r,'hook1'),
|
|
1990
|
+
* (r:any) => console.log(r,'hook2'),
|
|
1991
|
+
* (r:any) => console.log(r,'hook3')
|
|
1992
|
+
* ])
|
|
1760
1993
|
*
|
|
1761
1994
|
* // validate input when create or update reference to the schema in 'this.useSchema'
|
|
1762
1995
|
* this.useValidateSchema({
|
|
@@ -2053,7 +2286,8 @@ Let's example a basic relationship:
|
|
|
2053
2286
|
A one-to-one relationship is used to define relationships where a single model is the parent to one child models
|
|
2054
2287
|
|
|
2055
2288
|
```js
|
|
2056
|
-
import { Model } from 'tspace-mysql'
|
|
2289
|
+
import { Model , type T } from 'tspace-mysql';
|
|
2290
|
+
|
|
2057
2291
|
import Phone from '../Phone'
|
|
2058
2292
|
class User extends Model {
|
|
2059
2293
|
constructor(){
|
|
@@ -2068,12 +2302,15 @@ class User extends Model {
|
|
|
2068
2302
|
}
|
|
2069
2303
|
/**
|
|
2070
2304
|
* Mark a method for relationship
|
|
2071
|
-
* @hasOne Get the phone associated with the user. using function
|
|
2305
|
+
* @hasOne Get the phone associated with the user. using function query
|
|
2072
2306
|
* @function
|
|
2073
2307
|
*/
|
|
2074
|
-
phone (
|
|
2075
|
-
return this.hasOneBuilder({
|
|
2308
|
+
public phone (query ?: T.QueryModifier<Phone>) {
|
|
2309
|
+
return this.hasOneBuilder({ model : Phone } , query)
|
|
2076
2310
|
}
|
|
2311
|
+
|
|
2312
|
+
// or
|
|
2313
|
+
public phone = this.createRelation(Phone,{ type : 'hasOne'})
|
|
2077
2314
|
}
|
|
2078
2315
|
export default User
|
|
2079
2316
|
|
|
@@ -2091,8 +2328,8 @@ const userUsingFunction = await new User().phone().findOne()
|
|
|
2091
2328
|
A one-to-many relationship is used to define relationships where a single model is the parent to one or more child models.
|
|
2092
2329
|
|
|
2093
2330
|
```js
|
|
2094
|
-
import { Model } from 'tspace-mysql'
|
|
2095
|
-
import Comment from '../Comment'
|
|
2331
|
+
import { Model, type T } from 'tspace-mysql';
|
|
2332
|
+
import Comment from '../Comment';
|
|
2096
2333
|
class Post extends Model {
|
|
2097
2334
|
constructor(){
|
|
2098
2335
|
super()
|
|
@@ -2106,12 +2343,15 @@ class Post extends Model {
|
|
|
2106
2343
|
}
|
|
2107
2344
|
/**
|
|
2108
2345
|
*
|
|
2109
|
-
* @hasManyQuery Get the comments for the post. using function
|
|
2346
|
+
* @hasManyQuery Get the comments for the post. using function query
|
|
2110
2347
|
* @function
|
|
2111
2348
|
*/
|
|
2112
|
-
comments (
|
|
2113
|
-
return this.hasManyBuilder({
|
|
2349
|
+
public comments (query?: T.QueryModifier<Comment>) {
|
|
2350
|
+
return this.hasManyBuilder({ model : Comment } , query)
|
|
2114
2351
|
}
|
|
2352
|
+
|
|
2353
|
+
// or
|
|
2354
|
+
public comments = this.createRelation(Comment,{ type : 'hasMany'})
|
|
2115
2355
|
}
|
|
2116
2356
|
export default Post
|
|
2117
2357
|
|
|
@@ -2129,7 +2369,7 @@ const postsUsingFunction = await new Post().comments().findOne()
|
|
|
2129
2369
|
A belongsto relationship is used to define relationships where a single model is the child to parent models.
|
|
2130
2370
|
|
|
2131
2371
|
```js
|
|
2132
|
-
import { Model } from 'tspace-mysql'
|
|
2372
|
+
import { Model, type T } from 'tspace-mysql'
|
|
2133
2373
|
import User from '../User'
|
|
2134
2374
|
class Phone extends Model {
|
|
2135
2375
|
constructor(){
|
|
@@ -2144,12 +2384,15 @@ class Phone extends Model {
|
|
|
2144
2384
|
}
|
|
2145
2385
|
/**
|
|
2146
2386
|
*
|
|
2147
|
-
* @belongsToBuilder Get the user that owns the phone.. using function
|
|
2387
|
+
* @belongsToBuilder Get the user that owns the phone.. using function query
|
|
2148
2388
|
* @function
|
|
2149
2389
|
*/
|
|
2150
|
-
user (
|
|
2151
|
-
|
|
2390
|
+
public user (query?: T.QueryModifier<user>) {
|
|
2391
|
+
return this.belongsToBuilder({ model : User }, query)
|
|
2152
2392
|
}
|
|
2393
|
+
|
|
2394
|
+
// or
|
|
2395
|
+
public user = this.createRelation(User,{ type : 'belognsTo'})
|
|
2153
2396
|
}
|
|
2154
2397
|
export default Phone
|
|
2155
2398
|
|
|
@@ -2167,7 +2410,7 @@ const phoneUsingFunction = await new Phone().user().findOne()
|
|
|
2167
2410
|
Many-to-many relations are slightly more complicated than hasOne and hasMany relationships.
|
|
2168
2411
|
|
|
2169
2412
|
```js
|
|
2170
|
-
import { Model } from 'tspace-mysql'
|
|
2413
|
+
import { Model, type T } from 'tspace-mysql'
|
|
2171
2414
|
import Role from '../Role'
|
|
2172
2415
|
class User extends Model {
|
|
2173
2416
|
constructor(){
|
|
@@ -2181,12 +2424,15 @@ class User extends Model {
|
|
|
2181
2424
|
this.belognsToMany({ name : 'roles' , model : Role })
|
|
2182
2425
|
}
|
|
2183
2426
|
/**
|
|
2184
|
-
* @belongsToBuilder Get the user that owns the phone.. using function
|
|
2427
|
+
* @belongsToBuilder Get the user that owns the phone.. using function query
|
|
2185
2428
|
* @function
|
|
2186
2429
|
*/
|
|
2187
|
-
roles (
|
|
2188
|
-
|
|
2430
|
+
public roles (query?: T.QueryModifier<Role>) {
|
|
2431
|
+
return this.belognsToManyBuilder({ model : Role } , query)
|
|
2189
2432
|
}
|
|
2433
|
+
|
|
2434
|
+
// or
|
|
2435
|
+
public roles = this.createRelation(Role, { type : 'belognsToMany'})
|
|
2190
2436
|
}
|
|
2191
2437
|
export default User
|
|
2192
2438
|
|
|
@@ -2700,7 +2946,7 @@ Cache can be used in a Model.
|
|
|
2700
2946
|
Let's illustrate this with an example of a cache:
|
|
2701
2947
|
|
|
2702
2948
|
```js
|
|
2703
|
-
// support memory db
|
|
2949
|
+
// support memory , db , redis
|
|
2704
2950
|
// set cache in file config .env , .env.development ... etc
|
|
2705
2951
|
DB_CACHE = memory // by default
|
|
2706
2952
|
|
|
@@ -2708,6 +2954,7 @@ DB_CACHE = memory // by default
|
|
|
2708
2954
|
DB_CACHE = db
|
|
2709
2955
|
|
|
2710
2956
|
// for redis
|
|
2957
|
+
// npm install redis@5.6.0
|
|
2711
2958
|
DB_CACHE = redis://username:password@server:6379
|
|
2712
2959
|
|
|
2713
2960
|
const users = await new User()
|
|
@@ -2743,22 +2990,27 @@ import { PostUser } from './PostUser'
|
|
|
2743
2990
|
|
|
2744
2991
|
class UserObserve {
|
|
2745
2992
|
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2993
|
+
public selected(results) {
|
|
2994
|
+
console.log({ results , selected : true })
|
|
2995
|
+
}
|
|
2749
2996
|
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2997
|
+
public created(results) {
|
|
2998
|
+
console.log({ results , created : true })
|
|
2999
|
+
}
|
|
2753
3000
|
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
3001
|
+
public updated(results) {
|
|
3002
|
+
console.log({ results , updated : true })
|
|
3003
|
+
}
|
|
2757
3004
|
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
}
|
|
3005
|
+
public deleted(results) {
|
|
3006
|
+
console.log({ results , deleted : true })
|
|
2761
3007
|
}
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
enum UserRole {
|
|
3011
|
+
Admin = 'admin',
|
|
3012
|
+
User = 'user',
|
|
3013
|
+
}
|
|
2762
3014
|
|
|
2763
3015
|
@Pattern('camelCase')
|
|
2764
3016
|
@Observer(UserObserve)
|
|
@@ -2794,6 +3046,9 @@ class User extends Model {
|
|
|
2794
3046
|
@Column(() => Blueprint.varchar(50).null())
|
|
2795
3047
|
public password !: string
|
|
2796
3048
|
|
|
3049
|
+
@Column(() => Blueprint.enum(UserRole).default(UserRole.Admin))
|
|
3050
|
+
public role !: `${UserRole}`
|
|
3051
|
+
|
|
2797
3052
|
@Column(() => Blueprint.timestamp().null())
|
|
2798
3053
|
public createdAt!: Date
|
|
2799
3054
|
|
|
@@ -3939,9 +4194,14 @@ const fakeSendEmail = async (job: Job) => {
|
|
|
3939
4194
|
|
|
3940
4195
|
// start the Queue
|
|
3941
4196
|
await Queue.start({
|
|
3942
|
-
inspect
|
|
3943
|
-
flush
|
|
3944
|
-
hostname: 'pod1'
|
|
4197
|
+
inspect : true, // @default false, enable queue workflow inspection
|
|
4198
|
+
flush : false, // @default false, true -> remove all jobs
|
|
4199
|
+
hostname : 'pod1', // @default null, worker hostname
|
|
4200
|
+
maxIdleRetries : 8, // @default 5, maximum retries when no jobs are available
|
|
4201
|
+
poll : {
|
|
4202
|
+
enabled : true, // @default false, enable periodic job checking
|
|
4203
|
+
timeout : 10_000 // @default 60_000, polling interval
|
|
4204
|
+
};
|
|
3945
4205
|
});
|
|
3946
4206
|
|
|
3947
4207
|
const worker = 20;
|
|
@@ -3981,6 +4241,83 @@ for(let j = 1; j <= worker * 500; j++) {
|
|
|
3981
4241
|
// if you want to end the Queue
|
|
3982
4242
|
// await Queue.end()
|
|
3983
4243
|
|
|
4244
|
+
```
|
|
4245
|
+
### Queue Events
|
|
4246
|
+
|
|
4247
|
+
Queue also supports an event-based publish/subscribe pattern.
|
|
4248
|
+
|
|
4249
|
+
One event can have multiple subscribers.
|
|
4250
|
+
Use Queue.subscribe() to subscribe a job handler to an event
|
|
4251
|
+
```js
|
|
4252
|
+
// user.created
|
|
4253
|
+
// │
|
|
4254
|
+
// ├── email
|
|
4255
|
+
// ├── notify
|
|
4256
|
+
// └── analytics
|
|
4257
|
+
|
|
4258
|
+
Queue.subscribe(
|
|
4259
|
+
'user.created',
|
|
4260
|
+
'email',
|
|
4261
|
+
async (job) => {
|
|
4262
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
4263
|
+
console.log('Send e-mail : ' + job.payload.email)
|
|
4264
|
+
return 'Done!';
|
|
4265
|
+
},
|
|
4266
|
+
{ concurrency: 2 }
|
|
4267
|
+
);
|
|
4268
|
+
|
|
4269
|
+
Queue.subscribe(
|
|
4270
|
+
'user.created',
|
|
4271
|
+
'notify',
|
|
4272
|
+
async (job) => {
|
|
4273
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
4274
|
+
console.log('notify : ' + job.payload.email)
|
|
4275
|
+
return 'Done!';
|
|
4276
|
+
},
|
|
4277
|
+
{ concurrency: 3 }
|
|
4278
|
+
);
|
|
4279
|
+
```
|
|
4280
|
+
Use Queue.publish() to publish an event.
|
|
4281
|
+
```js
|
|
4282
|
+
for (let i = 1; i <= 5; i++) {
|
|
4283
|
+
// will send to jobs ['email','notify'];
|
|
4284
|
+
Queue.publish(
|
|
4285
|
+
'user.created',
|
|
4286
|
+
{ id : i , email : `user${i}@gmail.com` },
|
|
4287
|
+
{
|
|
4288
|
+
priority: i
|
|
4289
|
+
}
|
|
4290
|
+
);
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
```
|
|
4294
|
+
### Queue Type-safe Events and Jobs
|
|
4295
|
+
Queue optionally supports type-safe event and job names through `QueueContract`.
|
|
4296
|
+
|
|
4297
|
+
The feature is optional.
|
|
4298
|
+
|
|
4299
|
+
If the application does not define a contract, the Queue API can continue to accept normal string names.
|
|
4300
|
+
|
|
4301
|
+
Create a `types.d.ts` file in your application:
|
|
4302
|
+
```js
|
|
4303
|
+
import { QueueContract } from 'tspace-mysql';
|
|
4304
|
+
|
|
4305
|
+
declare module 'tspace-mysql' {
|
|
4306
|
+
interface QueueContract {
|
|
4307
|
+
events: {
|
|
4308
|
+
'user.created': [
|
|
4309
|
+
'email',
|
|
4310
|
+
'notify',
|
|
4311
|
+
];
|
|
4312
|
+
};
|
|
4313
|
+
|
|
4314
|
+
jobs: [
|
|
4315
|
+
'resize-video',
|
|
4316
|
+
'cleanup',
|
|
4317
|
+
];
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
|
|
3984
4321
|
```
|
|
3985
4322
|
|
|
3986
4323
|
## View
|
|
@@ -4170,7 +4507,13 @@ Blueprint is a tool used for defining database schemas programmatically.
|
|
|
4170
4507
|
It allows developers to describe the structure of their database tables using a simple and intuitive syntax rather than writing SQL queries directly., you may use the:
|
|
4171
4508
|
|
|
4172
4509
|
```js
|
|
4173
|
-
import { Schema , Blueprint , DB } from 'tspace-mysql'
|
|
4510
|
+
import { Schema , Blueprint , DB } from 'tspace-mysql';
|
|
4511
|
+
|
|
4512
|
+
enum UserRole {
|
|
4513
|
+
Admin = 'admin',
|
|
4514
|
+
User = 'user',
|
|
4515
|
+
}
|
|
4516
|
+
|
|
4174
4517
|
(async () => {
|
|
4175
4518
|
await new Schema().table('users', {
|
|
4176
4519
|
id : Blueprint.int().notNull().primary().autoIncrement(),
|
|
@@ -4181,6 +4524,7 @@ import { Schema , Blueprint , DB } from 'tspace-mysql'
|
|
|
4181
4524
|
email_verify : Blueprint.tinyInt(),
|
|
4182
4525
|
password : Blueprint.varchar(255),
|
|
4183
4526
|
json : Blueprint.json(),
|
|
4527
|
+
role : Blueprint.enum(UserRole).default(UserRole.Admin)
|
|
4184
4528
|
created_at : Blueprint.null().timestamp(),
|
|
4185
4529
|
updated_at : Blueprint.null().timestamp(),
|
|
4186
4530
|
deleted_at : Blueprint.null().timestamp()
|
|
@@ -4213,6 +4557,10 @@ enum(...n)
|
|
|
4213
4557
|
date()
|
|
4214
4558
|
dateTime()
|
|
4215
4559
|
timestamp ()
|
|
4560
|
+
raw({
|
|
4561
|
+
type : 'DECIMAL(10, 2)',
|
|
4562
|
+
attribute : 'DEFAULT(1)'
|
|
4563
|
+
})
|
|
4216
4564
|
|
|
4217
4565
|
/**
|
|
4218
4566
|
* To add attributes of the schema to the database
|