@lakutata/orm 0.0.0-stage → 3.0.0-beta.0

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 (209) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +463 -2
  3. package/THIRD_PARTY_NOTICES.md +36 -0
  4. package/dist/cjs/components/Database.d.ts +333 -0
  5. package/dist/cjs/components/Database.js +381 -0
  6. package/dist/cjs/decorators/orm/AfterInsert.d.ts +1 -0
  7. package/dist/cjs/decorators/orm/AfterInsert.js +5 -0
  8. package/dist/cjs/decorators/orm/AfterLoad.d.ts +1 -0
  9. package/dist/cjs/decorators/orm/AfterLoad.js +5 -0
  10. package/dist/cjs/decorators/orm/AfterRecover.d.ts +1 -0
  11. package/dist/cjs/decorators/orm/AfterRecover.js +5 -0
  12. package/dist/cjs/decorators/orm/AfterRemove.d.ts +1 -0
  13. package/dist/cjs/decorators/orm/AfterRemove.js +5 -0
  14. package/dist/cjs/decorators/orm/AfterSoftRemove.d.ts +1 -0
  15. package/dist/cjs/decorators/orm/AfterSoftRemove.js +5 -0
  16. package/dist/cjs/decorators/orm/AfterUpdate.d.ts +1 -0
  17. package/dist/cjs/decorators/orm/AfterUpdate.js +5 -0
  18. package/dist/cjs/decorators/orm/BeforeInsert.d.ts +1 -0
  19. package/dist/cjs/decorators/orm/BeforeInsert.js +5 -0
  20. package/dist/cjs/decorators/orm/BeforeRecover.d.ts +1 -0
  21. package/dist/cjs/decorators/orm/BeforeRecover.js +5 -0
  22. package/dist/cjs/decorators/orm/BeforeRemove.d.ts +1 -0
  23. package/dist/cjs/decorators/orm/BeforeRemove.js +5 -0
  24. package/dist/cjs/decorators/orm/BeforeSoftRemove.d.ts +1 -0
  25. package/dist/cjs/decorators/orm/BeforeSoftRemove.js +5 -0
  26. package/dist/cjs/decorators/orm/BeforeUpdate.d.ts +1 -0
  27. package/dist/cjs/decorators/orm/BeforeUpdate.js +5 -0
  28. package/dist/cjs/decorators/orm/Check.d.ts +1 -0
  29. package/dist/cjs/decorators/orm/Check.js +5 -0
  30. package/dist/cjs/decorators/orm/ChildEntity.d.ts +1 -0
  31. package/dist/cjs/decorators/orm/ChildEntity.js +5 -0
  32. package/dist/cjs/decorators/orm/Column.d.ts +1 -0
  33. package/dist/cjs/decorators/orm/Column.js +5 -0
  34. package/dist/cjs/decorators/orm/CreateDateColumn.d.ts +1 -0
  35. package/dist/cjs/decorators/orm/CreateDateColumn.js +5 -0
  36. package/dist/cjs/decorators/orm/DeleteDateColumn.d.ts +1 -0
  37. package/dist/cjs/decorators/orm/DeleteDateColumn.js +5 -0
  38. package/dist/cjs/decorators/orm/Entity.d.ts +1 -0
  39. package/dist/cjs/decorators/orm/Entity.js +5 -0
  40. package/dist/cjs/decorators/orm/EventSubscriber.d.ts +1 -0
  41. package/dist/cjs/decorators/orm/EventSubscriber.js +5 -0
  42. package/dist/cjs/decorators/orm/Exclusion.d.ts +1 -0
  43. package/dist/cjs/decorators/orm/Exclusion.js +5 -0
  44. package/dist/cjs/decorators/orm/Generated.d.ts +1 -0
  45. package/dist/cjs/decorators/orm/Generated.js +5 -0
  46. package/dist/cjs/decorators/orm/Index.d.ts +1 -0
  47. package/dist/cjs/decorators/orm/Index.js +5 -0
  48. package/dist/cjs/decorators/orm/JoinColumn.d.ts +1 -0
  49. package/dist/cjs/decorators/orm/JoinColumn.js +5 -0
  50. package/dist/cjs/decorators/orm/JoinTable.d.ts +1 -0
  51. package/dist/cjs/decorators/orm/JoinTable.js +5 -0
  52. package/dist/cjs/decorators/orm/ManyToMany.d.ts +1 -0
  53. package/dist/cjs/decorators/orm/ManyToMany.js +5 -0
  54. package/dist/cjs/decorators/orm/ManyToOne.d.ts +1 -0
  55. package/dist/cjs/decorators/orm/ManyToOne.js +5 -0
  56. package/dist/cjs/decorators/orm/ObjectIdColumn.d.ts +1 -0
  57. package/dist/cjs/decorators/orm/ObjectIdColumn.js +5 -0
  58. package/dist/cjs/decorators/orm/OneToMany.d.ts +1 -0
  59. package/dist/cjs/decorators/orm/OneToMany.js +5 -0
  60. package/dist/cjs/decorators/orm/OneToOne.d.ts +1 -0
  61. package/dist/cjs/decorators/orm/OneToOne.js +5 -0
  62. package/dist/cjs/decorators/orm/PrimaryColumn.d.ts +1 -0
  63. package/dist/cjs/decorators/orm/PrimaryColumn.js +5 -0
  64. package/dist/cjs/decorators/orm/PrimaryGeneratedColumn.d.ts +1 -0
  65. package/dist/cjs/decorators/orm/PrimaryGeneratedColumn.js +5 -0
  66. package/dist/cjs/decorators/orm/RelationId.d.ts +1 -0
  67. package/dist/cjs/decorators/orm/RelationId.js +5 -0
  68. package/dist/cjs/decorators/orm/TableInheritance.d.ts +1 -0
  69. package/dist/cjs/decorators/orm/TableInheritance.js +5 -0
  70. package/dist/cjs/decorators/orm/Tree.d.ts +1 -0
  71. package/dist/cjs/decorators/orm/Tree.js +5 -0
  72. package/dist/cjs/decorators/orm/TreeChildren.d.ts +1 -0
  73. package/dist/cjs/decorators/orm/TreeChildren.js +5 -0
  74. package/dist/cjs/decorators/orm/TreeLevelColumn.d.ts +1 -0
  75. package/dist/cjs/decorators/orm/TreeLevelColumn.js +5 -0
  76. package/dist/cjs/decorators/orm/TreeParent.d.ts +1 -0
  77. package/dist/cjs/decorators/orm/TreeParent.js +5 -0
  78. package/dist/cjs/decorators/orm/Unique.d.ts +1 -0
  79. package/dist/cjs/decorators/orm/Unique.js +5 -0
  80. package/dist/cjs/decorators/orm/UpdateDateColumn.d.ts +1 -0
  81. package/dist/cjs/decorators/orm/UpdateDateColumn.js +5 -0
  82. package/dist/cjs/decorators/orm/VersionColumn.d.ts +1 -0
  83. package/dist/cjs/decorators/orm/VersionColumn.js +5 -0
  84. package/dist/cjs/decorators/orm/ViewColumn.d.ts +1 -0
  85. package/dist/cjs/decorators/orm/ViewColumn.js +5 -0
  86. package/dist/cjs/decorators/orm/ViewEntity.d.ts +1 -0
  87. package/dist/cjs/decorators/orm/ViewEntity.js +5 -0
  88. package/dist/cjs/decorators/orm/VirtualColumn.d.ts +1 -0
  89. package/dist/cjs/decorators/orm/VirtualColumn.js +5 -0
  90. package/dist/cjs/exports/DatabaseComponent.d.ts +3 -0
  91. package/dist/cjs/exports/DatabaseComponent.js +21 -0
  92. package/dist/cjs/exports/DatabaseProvider.d.ts +3 -0
  93. package/dist/cjs/exports/DatabaseProvider.js +21 -0
  94. package/dist/cjs/exports/ORM.d.ts +193 -0
  95. package/dist/cjs/exports/ORM.js +166 -0
  96. package/dist/cjs/exports/ORMDecorators.d.ts +43 -0
  97. package/dist/cjs/exports/ORMDecorators.js +91 -0
  98. package/dist/cjs/package.json +1 -0
  99. package/dist/cjs/providers/Database.d.ts +326 -0
  100. package/dist/cjs/providers/Database.js +376 -0
  101. package/dist/cjs/providers/migration/ExecuteMigration.d.ts +40 -0
  102. package/dist/cjs/providers/migration/ExecuteMigration.js +143 -0
  103. package/dist/cjs/providers/migration/GenerateMigration.d.ts +59 -0
  104. package/dist/cjs/providers/migration/GenerateMigration.js +303 -0
  105. package/dist/cjs/types/MigrationRunOptions.d.ts +19 -0
  106. package/dist/cjs/types/MigrationRunOptions.js +2 -0
  107. package/dist/esm/components/Database.js +344 -0
  108. package/dist/esm/decorators/orm/AfterInsert.js +1 -0
  109. package/dist/esm/decorators/orm/AfterLoad.js +1 -0
  110. package/dist/esm/decorators/orm/AfterRecover.js +1 -0
  111. package/dist/esm/decorators/orm/AfterRemove.js +1 -0
  112. package/dist/esm/decorators/orm/AfterSoftRemove.js +1 -0
  113. package/dist/esm/decorators/orm/AfterUpdate.js +1 -0
  114. package/dist/esm/decorators/orm/BeforeInsert.js +1 -0
  115. package/dist/esm/decorators/orm/BeforeRecover.js +1 -0
  116. package/dist/esm/decorators/orm/BeforeRemove.js +1 -0
  117. package/dist/esm/decorators/orm/BeforeSoftRemove.js +1 -0
  118. package/dist/esm/decorators/orm/BeforeUpdate.js +1 -0
  119. package/dist/esm/decorators/orm/Check.js +1 -0
  120. package/dist/esm/decorators/orm/ChildEntity.js +1 -0
  121. package/dist/esm/decorators/orm/Column.js +1 -0
  122. package/dist/esm/decorators/orm/CreateDateColumn.js +1 -0
  123. package/dist/esm/decorators/orm/DeleteDateColumn.js +1 -0
  124. package/dist/esm/decorators/orm/Entity.js +1 -0
  125. package/dist/esm/decorators/orm/EventSubscriber.js +1 -0
  126. package/dist/esm/decorators/orm/Exclusion.js +1 -0
  127. package/dist/esm/decorators/orm/Generated.js +1 -0
  128. package/dist/esm/decorators/orm/Index.js +1 -0
  129. package/dist/esm/decorators/orm/JoinColumn.js +1 -0
  130. package/dist/esm/decorators/orm/JoinTable.js +1 -0
  131. package/dist/esm/decorators/orm/ManyToMany.js +1 -0
  132. package/dist/esm/decorators/orm/ManyToOne.js +1 -0
  133. package/dist/esm/decorators/orm/ObjectIdColumn.js +1 -0
  134. package/dist/esm/decorators/orm/OneToMany.js +1 -0
  135. package/dist/esm/decorators/orm/OneToOne.js +1 -0
  136. package/dist/esm/decorators/orm/PrimaryColumn.js +1 -0
  137. package/dist/esm/decorators/orm/PrimaryGeneratedColumn.js +1 -0
  138. package/dist/esm/decorators/orm/RelationId.js +1 -0
  139. package/dist/esm/decorators/orm/TableInheritance.js +1 -0
  140. package/dist/esm/decorators/orm/Tree.js +1 -0
  141. package/dist/esm/decorators/orm/TreeChildren.js +1 -0
  142. package/dist/esm/decorators/orm/TreeLevelColumn.js +1 -0
  143. package/dist/esm/decorators/orm/TreeParent.js +1 -0
  144. package/dist/esm/decorators/orm/Unique.js +1 -0
  145. package/dist/esm/decorators/orm/UpdateDateColumn.js +1 -0
  146. package/dist/esm/decorators/orm/VersionColumn.js +1 -0
  147. package/dist/esm/decorators/orm/ViewColumn.js +1 -0
  148. package/dist/esm/decorators/orm/ViewEntity.js +1 -0
  149. package/dist/esm/decorators/orm/VirtualColumn.js +1 -0
  150. package/dist/esm/exports/DatabaseComponent.js +2 -0
  151. package/dist/esm/exports/DatabaseProvider.js +2 -0
  152. package/dist/esm/exports/ORM.js +120 -0
  153. package/dist/esm/exports/ORMDecorators.js +43 -0
  154. package/dist/esm/providers/Database.js +339 -0
  155. package/dist/esm/providers/migration/ExecuteMigration.js +137 -0
  156. package/dist/esm/providers/migration/GenerateMigration.js +297 -0
  157. package/dist/esm/types/MigrationRunOptions.js +1 -0
  158. package/dist/types/components/Database.d.ts +333 -0
  159. package/dist/types/decorators/orm/AfterInsert.d.ts +1 -0
  160. package/dist/types/decorators/orm/AfterLoad.d.ts +1 -0
  161. package/dist/types/decorators/orm/AfterRecover.d.ts +1 -0
  162. package/dist/types/decorators/orm/AfterRemove.d.ts +1 -0
  163. package/dist/types/decorators/orm/AfterSoftRemove.d.ts +1 -0
  164. package/dist/types/decorators/orm/AfterUpdate.d.ts +1 -0
  165. package/dist/types/decorators/orm/BeforeInsert.d.ts +1 -0
  166. package/dist/types/decorators/orm/BeforeRecover.d.ts +1 -0
  167. package/dist/types/decorators/orm/BeforeRemove.d.ts +1 -0
  168. package/dist/types/decorators/orm/BeforeSoftRemove.d.ts +1 -0
  169. package/dist/types/decorators/orm/BeforeUpdate.d.ts +1 -0
  170. package/dist/types/decorators/orm/Check.d.ts +1 -0
  171. package/dist/types/decorators/orm/ChildEntity.d.ts +1 -0
  172. package/dist/types/decorators/orm/Column.d.ts +1 -0
  173. package/dist/types/decorators/orm/CreateDateColumn.d.ts +1 -0
  174. package/dist/types/decorators/orm/DeleteDateColumn.d.ts +1 -0
  175. package/dist/types/decorators/orm/Entity.d.ts +1 -0
  176. package/dist/types/decorators/orm/EventSubscriber.d.ts +1 -0
  177. package/dist/types/decorators/orm/Exclusion.d.ts +1 -0
  178. package/dist/types/decorators/orm/Generated.d.ts +1 -0
  179. package/dist/types/decorators/orm/Index.d.ts +1 -0
  180. package/dist/types/decorators/orm/JoinColumn.d.ts +1 -0
  181. package/dist/types/decorators/orm/JoinTable.d.ts +1 -0
  182. package/dist/types/decorators/orm/ManyToMany.d.ts +1 -0
  183. package/dist/types/decorators/orm/ManyToOne.d.ts +1 -0
  184. package/dist/types/decorators/orm/ObjectIdColumn.d.ts +1 -0
  185. package/dist/types/decorators/orm/OneToMany.d.ts +1 -0
  186. package/dist/types/decorators/orm/OneToOne.d.ts +1 -0
  187. package/dist/types/decorators/orm/PrimaryColumn.d.ts +1 -0
  188. package/dist/types/decorators/orm/PrimaryGeneratedColumn.d.ts +1 -0
  189. package/dist/types/decorators/orm/RelationId.d.ts +1 -0
  190. package/dist/types/decorators/orm/TableInheritance.d.ts +1 -0
  191. package/dist/types/decorators/orm/Tree.d.ts +1 -0
  192. package/dist/types/decorators/orm/TreeChildren.d.ts +1 -0
  193. package/dist/types/decorators/orm/TreeLevelColumn.d.ts +1 -0
  194. package/dist/types/decorators/orm/TreeParent.d.ts +1 -0
  195. package/dist/types/decorators/orm/Unique.d.ts +1 -0
  196. package/dist/types/decorators/orm/UpdateDateColumn.d.ts +1 -0
  197. package/dist/types/decorators/orm/VersionColumn.d.ts +1 -0
  198. package/dist/types/decorators/orm/ViewColumn.d.ts +1 -0
  199. package/dist/types/decorators/orm/ViewEntity.d.ts +1 -0
  200. package/dist/types/decorators/orm/VirtualColumn.d.ts +1 -0
  201. package/dist/types/exports/DatabaseComponent.d.ts +3 -0
  202. package/dist/types/exports/DatabaseProvider.d.ts +3 -0
  203. package/dist/types/exports/ORM.d.ts +193 -0
  204. package/dist/types/exports/ORMDecorators.d.ts +43 -0
  205. package/dist/types/providers/Database.d.ts +326 -0
  206. package/dist/types/providers/migration/ExecuteMigration.d.ts +40 -0
  207. package/dist/types/providers/migration/GenerateMigration.d.ts +59 -0
  208. package/dist/types/types/MigrationRunOptions.d.ts +19 -0
  209. package/package.json +81 -4
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-present Lakutata
4
+
5
+ All rights reserved
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,464 @@
1
- # Temporary Holding Version
1
+ # @lakutata/orm
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The ORM of the lakutata framework, built on [TypeORM](https://typeorm.io) 0.3: the `Database` component (a
4
+ connection shared by the application), the `Database` provider (a connection of its own), the entity decorators, the
5
+ TypeORM API (repositories, find operators, query builders, migrations), and the migration commands of the applications
6
+ (`MIGRATION_GENERATE`, `MIGRATION_RUN`, `MIGRATION_REVERT`, `MIGRATION_SHOW`).
7
+
8
+ The umbrella package `lakutata` re-exports it: `lakutata/com/database` (the component), `lakutata/provider/database`
9
+ (the provider), `lakutata/decorator/orm` (the entity decorators) and `lakutata/orm` (the TypeORM API).
10
+
11
+ ## Installation
12
+
13
+ ```shell
14
+ npm install lakutata pg # the framework, and the PostgreSQL driver
15
+ npm install @lakutata/orm # or the ORM only, with @lakutata/core
16
+ ```
17
+
18
+ TypeORM is a dependency of the package; the database driver is not. Install the driver of the `type` of the
19
+ connection options:
20
+
21
+ | Database | `type` | Install |
22
+ |---|---|---|
23
+ | PostgreSQL, CockroachDB | `postgres`, `cockroachdb` | `pg` |
24
+ | MySQL, MariaDB | `mysql`, `mariadb` | `mysql2` |
25
+ | SQLite | `sqlite` / `better-sqlite3` | `sqlite3` / `better-sqlite3` |
26
+ | SQLite in memory (WebAssembly) | `sqljs` | `sql.js` |
27
+ | Microsoft SQL Server | `mssql` | `mssql` |
28
+ | Oracle | `oracle` | `oracledb` |
29
+ | MongoDB | `mongodb` | `mongodb` (5 or 6) |
30
+ | SAP HANA | `sap` | `@sap/hana-client` |
31
+ | Google Cloud Spanner | `spanner` | `@google-cloud/spanner` |
32
+
33
+ The query result cache stored in Redis (`cache: {type: 'redis'}` or `'ioredis'`) needs `redis` or `ioredis`.
34
+
35
+ The entities use the decorators and their metadata: the TypeScript options of the applications (`"extends":
36
+ "lakutata/tsconfig.json"`) enable `experimentalDecorators` and `emitDecoratorMetadata`.
37
+
38
+ ## Usage
39
+
40
+ ### A database in an application
41
+
42
+ `buildDatabaseOptions` registers a `Database` component with the TypeORM connection options (`DataSourceOptions`,
43
+ passed to TypeORM as they are). The component connects when it is created (while the application launches when it is
44
+ listed in `bootstrap`), and disconnects when the application stops. A connection failure stops the launch of the
45
+ application.
46
+
47
+ ```typescript
48
+ import {Application, Component} from 'lakutata'
49
+ import {Inject} from 'lakutata/decorator/di'
50
+ import {buildDatabaseOptions, Database} from 'lakutata/com/database'
51
+ import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn} from 'lakutata/decorator/orm'
52
+
53
+ @Entity('users')
54
+ class User {
55
+ @PrimaryGeneratedColumn()
56
+ public id: number
57
+
58
+ @Column({unique: true})
59
+ public email: string
60
+
61
+ @CreateDateColumn({type: 'timestamptz'})
62
+ public createdAt: Date
63
+ }
64
+
65
+ class Users extends Component {
66
+ //The component registered as db
67
+ @Inject('db')
68
+ protected readonly db: Database
69
+
70
+ public async register(email: string): Promise<User> {
71
+ return await this.db.getRepository(User).save({email: email})
72
+ }
73
+
74
+ public async find(email: string): Promise<User | null> {
75
+ return await this.db.getRepository(User).findOneBy({email: email})
76
+ }
77
+ }
78
+
79
+ Application
80
+ .run(() => ({
81
+ id: 'com.example.users',
82
+ name: 'Users',
83
+ components: {
84
+ db: buildDatabaseOptions({
85
+ type: 'postgres',
86
+ host: process.env.DB_HOST ?? 'localhost',
87
+ port: 5432,
88
+ username: 'postgres',
89
+ password: process.env.DB_PASSWORD,
90
+ database: 'users',
91
+ entities: [User],
92
+ //Development only: the schema follows the entities; the migrations change it in production
93
+ synchronize: true
94
+ }),
95
+ users: {class: Users}
96
+ },
97
+ bootstrap: ['db']
98
+ }))
99
+ .onLaunched(async (app: Application): Promise<void> => {
100
+ const users: Users = await app.getObject('users')
101
+ await users.register('ada@example.com')
102
+ })
103
+ ```
104
+
105
+ The common connection options (see the TypeORM documentation for the options of each driver):
106
+
107
+ | Option | What it sets |
108
+ |---|---|
109
+ | `type` | The driver: `'postgres'`, `'mysql'`, `'sqlite'`… |
110
+ | `url` or `host`, `port`, `username`, `password`, `database` | The server and the database |
111
+ | `schema` | The PostgreSQL schema of the tables (default `public`) |
112
+ | `entities` | The entity classes, `EntitySchema`s, or glob paths of their compiled files (`'dist/entities/*.js'`) |
113
+ | `synchronize` | Update the schema from the entities on each connection (default `false`): development only |
114
+ | `migrations`, `migrationsTableName`, `migrationsRun`, `migrationsTransactionMode` | The migrations, their table (default `migrations`), whether they run on each connection (default `false`), their transactions (default `'all'`) |
115
+ | `logging`, `logger` | What TypeORM logs (`true`, `['error', 'schema']`…) and where |
116
+ | `poolSize`, `extra` | The connection pool size; the options passed to the driver (`extra: {max: 20}` for `pg`) |
117
+ | `subscribers`, `namingStrategy`, `cache` | The entity subscribers, the naming of the tables and columns, the query result cache |
118
+
119
+ ### Entities
120
+
121
+ The decorators of `lakutata/decorator/orm` are the TypeORM decorators: `@Entity`, `@ViewEntity`, the columns
122
+ (`@Column`, `@PrimaryColumn`, `@PrimaryGeneratedColumn`, `@CreateDateColumn`, `@UpdateDateColumn`,
123
+ `@DeleteDateColumn`, `@VersionColumn`, `@VirtualColumn`, `@ViewColumn`, `@ObjectIdColumn`, `@Generated`), the
124
+ relations (`@OneToOne`, `@OneToMany`, `@ManyToOne`, `@ManyToMany`, `@JoinColumn`, `@JoinTable`, `@RelationId`), the
125
+ constraints (`@Index`, `@Unique`, `@Check`, `@Exclusion`), the trees (`@Tree`, `@TreeParent`, `@TreeChildren`,
126
+ `@TreeLevelColumn`), the inheritance (`@TableInheritance`, `@ChildEntity`), the entity listeners (`@BeforeInsert`,
127
+ `@AfterLoad`…) and `@EventSubscriber`.
128
+
129
+ ```typescript
130
+ import {
131
+ BeforeInsert, Column, CreateDateColumn, DeleteDateColumn, Entity, Index, JoinColumn, ManyToOne, OneToMany,
132
+ PrimaryGeneratedColumn, UpdateDateColumn, VersionColumn
133
+ } from 'lakutata/decorator/orm'
134
+
135
+ @Entity('orders')
136
+ export class Order {
137
+ @PrimaryGeneratedColumn('uuid')
138
+ public id: string
139
+
140
+ @Index()
141
+ @Column()
142
+ public customer: string
143
+
144
+ //pg reads the numeric columns back as strings: the transformer converts them
145
+ @Column('numeric', {precision: 12, scale: 2, transformer: {to: (value: number): number => value, from: (value: string): number => Number(value)}})
146
+ public total: number
147
+
148
+ @Column('jsonb', {nullable: true})
149
+ public metadata: Record<string, unknown> | null
150
+
151
+ //Saved with the order
152
+ @OneToMany(() => OrderLine, (line: OrderLine) => line.order, {cascade: true})
153
+ public lines: OrderLine[]
154
+
155
+ @CreateDateColumn({type: 'timestamptz'})
156
+ public createdAt: Date
157
+
158
+ @UpdateDateColumn({type: 'timestamptz'})
159
+ public updatedAt: Date
160
+
161
+ //Set by softDelete(): the finds skip the order unless withDeleted is set
162
+ @DeleteDateColumn({type: 'timestamptz'})
163
+ public deletedAt: Date | null
164
+
165
+ //Incremented by each save
166
+ @VersionColumn()
167
+ public version: number
168
+
169
+ @BeforeInsert()
170
+ protected normalize(): void {
171
+ this.customer = this.customer.trim()
172
+ }
173
+ }
174
+
175
+ @Entity('order_lines')
176
+ export class OrderLine {
177
+ @PrimaryGeneratedColumn()
178
+ public id: number
179
+
180
+ @Column()
181
+ public product: string
182
+
183
+ @Column('integer')
184
+ public quantity: number
185
+
186
+ @ManyToOne(() => Order, (order: Order) => order.lines, {onDelete: 'CASCADE'})
187
+ @JoinColumn({name: 'order_id'})
188
+ public order: Order
189
+ }
190
+ ```
191
+
192
+ An entity may also extend `BaseEntity` (from `lakutata/orm`) for the active record operations (`User.findOneBy()`,
193
+ `user.save()`): they use the last connection which registered the entity.
194
+
195
+ ### Queries
196
+
197
+ `lakutata/orm` exports the TypeORM API: the repositories, the find operators (`In`, `ILike`, `Between`, `MoreThan`,
198
+ `IsNull`, `Not`, `Raw`, `ArrayContains`, `JsonContains`…), the query builders, the entity manager and the errors.
199
+
200
+ ```typescript
201
+ import {Component} from 'lakutata'
202
+ import {Inject} from 'lakutata/decorator/di'
203
+ import {Database} from 'lakutata/com/database'
204
+ import {Column, Entity, PrimaryGeneratedColumn} from 'lakutata/decorator/orm'
205
+ import {Between, ILike, In, type Repository} from 'lakutata/orm'
206
+
207
+ @Entity('orders')
208
+ class Order {
209
+ @PrimaryGeneratedColumn()
210
+ public id: number
211
+
212
+ @Column()
213
+ public customer: string
214
+
215
+ @Column()
216
+ public status: string
217
+
218
+ @Column('integer')
219
+ public total: number
220
+
221
+ @Column({type: 'timestamptz'})
222
+ public createdAt: Date
223
+ }
224
+
225
+ class Orders extends Component {
226
+ @Inject('db')
227
+ protected readonly db: Database
228
+
229
+ protected get orders(): Repository<Order> {
230
+ return this.db.getRepository(Order)
231
+ }
232
+
233
+ //A page of the orders of a period, and their count
234
+ public async search(customer: string, from: Date, to: Date, page: number): Promise<[Order[], number]> {
235
+ return await this.orders.findAndCount({
236
+ where: {customer: ILike(`%${customer}%`), status: In(['paid', 'shipped']), createdAt: Between(from, to)},
237
+ order: {createdAt: 'DESC'},
238
+ skip: (page - 1) * 20,
239
+ take: 20
240
+ })
241
+ }
242
+
243
+ //A query builder for the aggregations
244
+ public async totals(): Promise<{ customer: string, total: string }[]> {
245
+ return await this.db.createQueryBuilder(Order, 'order')
246
+ .select('order.customer', 'customer')
247
+ .addSelect('SUM(order.total)', 'total')
248
+ .groupBy('order.customer')
249
+ .getRawMany()
250
+ }
251
+
252
+ //Raw SQL: the PostgreSQL placeholders are $1, $2…
253
+ public async count(status: string): Promise<number> {
254
+ const rows: { count: string }[] = await this.db.query('SELECT COUNT(*) AS count FROM orders WHERE status = $1', [status])
255
+ return Number(rows[0].count)
256
+ }
257
+ }
258
+ ```
259
+
260
+ ### Transactions
261
+
262
+ `transaction()` runs a function in a transaction: committed when it resolves, rolled back when it rejects (its error
263
+ rethrown). The operations of the transaction go through the entity manager given to the function; the ones made
264
+ through `db.manager` or `db.getRepository()` run outside of it. A query runner controls a transaction by hand.
265
+
266
+ ```typescript
267
+ import {Component} from 'lakutata'
268
+ import {Inject} from 'lakutata/decorator/di'
269
+ import {Database} from 'lakutata/com/database'
270
+ import {Column, Entity, PrimaryGeneratedColumn} from 'lakutata/decorator/orm'
271
+ import {type EntityManager, type QueryRunner} from 'lakutata/orm'
272
+
273
+ @Entity('accounts')
274
+ class Account {
275
+ @PrimaryGeneratedColumn()
276
+ public id: number
277
+
278
+ @Column('integer', {default: 0})
279
+ public balance: number
280
+ }
281
+
282
+ class Bank extends Component {
283
+ @Inject('db')
284
+ protected readonly db: Database
285
+
286
+ //Both updates, or none
287
+ public async transfer(from: number, to: number, amount: number): Promise<void> {
288
+ await this.db.transaction('SERIALIZABLE', async (manager: EntityManager): Promise<void> => {
289
+ const source: Account = await manager.findOneByOrFail(Account, {id: from})
290
+ if (source.balance < amount) throw new Error('Insufficient balance')
291
+ await manager.decrement(Account, {id: from}, 'balance', amount)
292
+ await manager.increment(Account, {id: to}, 'balance', amount)
293
+ })
294
+ }
295
+
296
+ //The same transaction, by hand
297
+ public async deposit(id: number, amount: number): Promise<void> {
298
+ const runner: QueryRunner = this.db.createQueryRunner()
299
+ await runner.connect()
300
+ await runner.startTransaction()
301
+ try {
302
+ await runner.manager.increment(Account, {id: id}, 'balance', amount)
303
+ await runner.commitTransaction()
304
+ } catch (error) {
305
+ await runner.rollbackTransaction()
306
+ throw error
307
+ } finally {
308
+ //The connection goes back to the pool
309
+ await runner.release()
310
+ }
311
+ }
312
+ }
313
+ ```
314
+
315
+ ### The Database provider
316
+
317
+ The provider of `lakutata/provider/database` has the API of the component, but it is transient: each resolution
318
+ (`@Inject`, `getObject()`) creates a new instance, which opens its own connection pool and closes it when it is
319
+ destroyed with the object or the container which created it. It suits a short use (a job, a script, a tenant
320
+ database); the requests of an application share the component. Its `entities` option is added to the entities of its
321
+ connection options.
322
+
323
+ ```typescript
324
+ import {Application} from 'lakutata'
325
+ import {buildDatabaseOptions, Database} from 'lakutata/provider/database'
326
+ import {Column, Entity, PrimaryGeneratedColumn} from 'lakutata/decorator/orm'
327
+
328
+ @Entity('reports')
329
+ class Report {
330
+ @PrimaryGeneratedColumn()
331
+ public id: number
332
+
333
+ @Column('text')
334
+ public content: string
335
+ }
336
+
337
+ Application
338
+ .run(() => ({
339
+ id: 'com.example.reports',
340
+ name: 'Reports',
341
+ providers: {
342
+ reports: {
343
+ ...buildDatabaseOptions({type: 'postgres', url: process.env.REPORTS_URL}),
344
+ entities: [Report]
345
+ }
346
+ }
347
+ }))
348
+ .onLaunched(async (app: Application): Promise<void> => {
349
+ //A connection opened here, closed with the application
350
+ const database: Database = await app.getObject('reports')
351
+ console.log(await database.getRepository(Report).count())
352
+ })
353
+ ```
354
+
355
+ ### Several databases
356
+
357
+ Each database is a component of its own name, injected by it. The migration commands act on all of them, or on the one
358
+ `MIGRATION_DATABASE` names.
359
+
360
+ ```text
361
+ components: {
362
+ db: buildDatabaseOptions({type: 'postgres', url: process.env.DATABASE_URL, entities: [User, Order]}),
363
+ analytics: buildDatabaseOptions({type: 'postgres', url: process.env.ANALYTICS_URL, entities: [Event]})
364
+ }
365
+ ```
366
+
367
+ ### Migrations
368
+
369
+ A migration is a class implementing `MigrationInterface`: its `up()` changes the schema, its `down()` reverts the
370
+ change. The `migrations` connection option lists them (classes, or glob paths of their compiled files); the
371
+ `migrations` table records the ones run.
372
+
373
+ ```typescript
374
+ import {type MigrationInterface, type QueryRunner} from 'lakutata/orm'
375
+
376
+ export class Migrations1760000000000 implements MigrationInterface {
377
+ public name: string = 'Migrations1760000000000'
378
+
379
+ public async up(queryRunner: QueryRunner): Promise<void> {
380
+ await queryRunner.query('ALTER TABLE "users" ADD "name" character varying')
381
+ }
382
+
383
+ public async down(queryRunner: QueryRunner): Promise<void> {
384
+ await queryRunner.query('ALTER TABLE "users" DROP COLUMN "name"')
385
+ }
386
+ }
387
+ ```
388
+
389
+ The migrations run when the database connects with `migrationsRun: true`, by `runMigrations()` and
390
+ `undoLastMigration()` of the `Database` component or provider, or by the commands of the application.
391
+
392
+ The commands are environment variables given to the application itself (no separate CLI): started with one of them,
393
+ the application reads its options, then, instead of its `bootstrap` and its `onLaunched` handler, it does the command
394
+ on each `Database` component and provider of its options, and exits. Each database connects with `synchronize`,
395
+ `migrationsRun` and `dropSchema` forced to `false`. The names of the variables are read in any case; the flags are
396
+ `1`, `true` or `yes`.
397
+
398
+ | Variable | Command |
399
+ |---|---|
400
+ | `MIGRATION_GENERATE=<directory>` | Compare the entities with the schema of the database, and write the SQL of the differences as a migration: `<directory>/<timestamp>-<database name>.ts` (the directory relative to the working directory, created if needed), its class `<Directory><timestamp>` (`Migrations1760000000000` for `src/migrations`), importing its types from `@lakutata/orm` when the project depends on it, from `lakutata/orm` otherwise |
401
+ | `MIGRATION_RUN=1` | Run the pending migrations, printing each one run |
402
+ | `MIGRATION_REVERT=1` | Revert the last migration run (`MIGRATION_DATABASE` required when the application has several databases) |
403
+ | `MIGRATION_SHOW=1` | Print each migration as `[X]` (run) or `[ ]` (pending), and the number pending |
404
+ | `MIGRATION_DATABASE=<name>` | The database of the command: the name of its component or provider (all of them by default) |
405
+ | `MIGRATION_TRANSACTION=all\|each\|none` | The transactions of `MIGRATION_RUN` and `MIGRATION_REVERT` (default: the `migrationsTransactionMode` option of the database, else `all`) |
406
+ | `MIGRATION_FAKE=1` | Record the migrations as run (or reverted) without running them |
407
+
408
+ When several are set, `MIGRATION_GENERATE` comes first, then `MIGRATION_RUN`, `MIGRATION_REVERT` and `MIGRATION_SHOW`.
409
+
410
+ ```shell
411
+ # Generate from the compiled application: the database reachable and up to date with the migrations run
412
+ MIGRATION_GENERATE=src/migrations node dist/App.js
413
+ # Compile the migration, then
414
+ MIGRATION_SHOW=1 node dist/App.js
415
+ MIGRATION_RUN=1 node dist/App.js
416
+ MIGRATION_REVERT=1 MIGRATION_DATABASE=db node dist/App.js
417
+ ```
418
+
419
+ The generated file is written in TypeORM's style (double quotes, semicolons): format it or exclude the migrations from
420
+ the linter. The process exits with the code 0 once the command is done, and 1 when it fails: an invalid
421
+ `MIGRATION_TRANSACTION`, an unknown `MIGRATION_DATABASE`, no database (but for `MIGRATION_GENERATE`, which exits with
422
+ 0), `MIGRATION_REVERT` on several databases, a database which does not connect, a migration which fails (the databases
423
+ after it are skipped), or `MIGRATION_GENERATE` finding no change in any database.
424
+
425
+ The entities of a database are the ones of its connection options, and for a `Database` provider the ones of its
426
+ `entities` option as well.
427
+
428
+ ## API
429
+
430
+ | Export | Subpath | What it is |
431
+ |---|---|---|
432
+ | `Database`, `buildDatabaseOptions` | `lakutata/com/database` | The database component (a shared connection) and its registration |
433
+ | `Database`, `buildDatabaseOptions` | `lakutata/provider/database` | The database provider (a connection per resolution) and its registration |
434
+ | `MigrationRunOptions` | `lakutata/com/database`, `lakutata/provider/database` | The options of `runMigrations()` and `undoLastMigration()` |
435
+ | `Entity`, `Column`, `PrimaryGeneratedColumn`, `OneToMany`, `ManyToOne`, `Index`, `BeforeInsert`… | `lakutata/decorator/orm` | The TypeORM decorators |
436
+ | `Repository`, `EntityManager`, `QueryRunner`, `SelectQueryBuilder`, `DataSource`, `BaseEntity`… | `lakutata/orm` | The TypeORM classes and types |
437
+ | `In`, `ILike`, `Between`, `MoreThan`, `IsNull`, `Not`, `Raw`… | `lakutata/orm` | The TypeORM find operators |
438
+ | `MigrationInterface`, `Migration`, `MigrationExecutor` | `lakutata/orm` | The migrations |
439
+ | `DataSourceOptions`, `PostgresConnectionOptions`, `MysqlConnectionOptions`… | `lakutata/orm` | The connection options of each driver |
440
+ | `QueryFailedError`, `EntityNotFoundError`, `TypeORMError`… | `lakutata/orm` | The TypeORM errors |
441
+
442
+ The typings document the declarations of lakutata with examples; the TypeORM ones are documented by
443
+ [TypeORM](https://typeorm.io).
444
+
445
+ ## Errors
446
+
447
+ The package throws the TypeORM errors, exported by `lakutata/orm`:
448
+
449
+ | Error | Thrown when |
450
+ |---|---|
451
+ | The driver error (`pg`'s `DatabaseError`, `ECONNREFUSED`…) | The `Database` component or provider cannot connect: its creation rejects, and the launch of the application fails when the component is in `bootstrap` |
452
+ | `QueryFailedError` | The database rejects a query (a constraint violated, a syntax error); its `driverError` holds the driver error (`code` `23505` for a PostgreSQL unique violation) |
453
+ | `EntityNotFoundError` | `findOneOrFail()`, `findOneByOrFail()` find no entity |
454
+ | `EntityMetadataNotFoundError` | An entity is not in the `entities` of the connection |
455
+ | `OptimisticLockVersionMismatchError` | A find with `lock: {mode: 'optimistic', version}` reads another version |
456
+ | `CannotExecuteNotConnectedError` | `synchronize()` or the migrations run on a closed connection |
457
+ | `TypeORMError` | The base of the TypeORM errors (a MongoDB method on another database, a relation not found…) |
458
+
459
+ ## See also
460
+
461
+ - The guide: `doc/en/ORMIntegration.md` of the package `lakutata` (Chinese in `doc/zh`).
462
+ - The TypeORM documentation: [typeorm.io](https://typeorm.io) (the entities, the relations, the find options, the
463
+ query builder, the migrations).
464
+ - `@lakutata/core`: the application, the components and providers, the dependency injection.
@@ -0,0 +1,36 @@
1
+ # Third-party notices
2
+
3
+ Parts of `@lakutata/orm` are derived from the third-party projects listed below. They are rewritten in TypeScript and
4
+ modified; the original copyright notices and license terms are reproduced here as required.
5
+
6
+ ## TypeORM 0.3.24
7
+
8
+ The generation of the migrations: `src/providers/migration/GenerateMigration.ts`, from the command
9
+ `typeorm migration:generate` (`src/commands/MigrationGenerateCommand.ts`).
10
+
11
+ Modifications: a provider of the application, run when the environment variable `MIGRATION_GENERATE` is set, for each
12
+ Database component or provider; the migrations import their types from `@lakutata/orm` or `lakutata/orm`.
13
+
14
+ ```
15
+ The MIT License
16
+
17
+ Copyright (c) 2015-2025 TypeORM. http://typeorm.github.io
18
+
19
+ Permission is hereby granted, free of charge, to any person obtaining a copy
20
+ of this software and associated documentation files (the "Software"), to deal
21
+ in the Software without restriction, including without limitation the rights
22
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23
+ copies of the Software, and to permit persons to whom the Software is
24
+ furnished to do so, subject to the following conditions:
25
+
26
+ The above copyright notice and this permission notice shall be included in
27
+ all copies or substantial portions of the Software.
28
+
29
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35
+ THE SOFTWARE.
36
+ ```