@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.
- package/LICENSE +23 -0
- package/README.md +463 -2
- package/THIRD_PARTY_NOTICES.md +36 -0
- package/dist/cjs/components/Database.d.ts +333 -0
- package/dist/cjs/components/Database.js +381 -0
- package/dist/cjs/decorators/orm/AfterInsert.d.ts +1 -0
- package/dist/cjs/decorators/orm/AfterInsert.js +5 -0
- package/dist/cjs/decorators/orm/AfterLoad.d.ts +1 -0
- package/dist/cjs/decorators/orm/AfterLoad.js +5 -0
- package/dist/cjs/decorators/orm/AfterRecover.d.ts +1 -0
- package/dist/cjs/decorators/orm/AfterRecover.js +5 -0
- package/dist/cjs/decorators/orm/AfterRemove.d.ts +1 -0
- package/dist/cjs/decorators/orm/AfterRemove.js +5 -0
- package/dist/cjs/decorators/orm/AfterSoftRemove.d.ts +1 -0
- package/dist/cjs/decorators/orm/AfterSoftRemove.js +5 -0
- package/dist/cjs/decorators/orm/AfterUpdate.d.ts +1 -0
- package/dist/cjs/decorators/orm/AfterUpdate.js +5 -0
- package/dist/cjs/decorators/orm/BeforeInsert.d.ts +1 -0
- package/dist/cjs/decorators/orm/BeforeInsert.js +5 -0
- package/dist/cjs/decorators/orm/BeforeRecover.d.ts +1 -0
- package/dist/cjs/decorators/orm/BeforeRecover.js +5 -0
- package/dist/cjs/decorators/orm/BeforeRemove.d.ts +1 -0
- package/dist/cjs/decorators/orm/BeforeRemove.js +5 -0
- package/dist/cjs/decorators/orm/BeforeSoftRemove.d.ts +1 -0
- package/dist/cjs/decorators/orm/BeforeSoftRemove.js +5 -0
- package/dist/cjs/decorators/orm/BeforeUpdate.d.ts +1 -0
- package/dist/cjs/decorators/orm/BeforeUpdate.js +5 -0
- package/dist/cjs/decorators/orm/Check.d.ts +1 -0
- package/dist/cjs/decorators/orm/Check.js +5 -0
- package/dist/cjs/decorators/orm/ChildEntity.d.ts +1 -0
- package/dist/cjs/decorators/orm/ChildEntity.js +5 -0
- package/dist/cjs/decorators/orm/Column.d.ts +1 -0
- package/dist/cjs/decorators/orm/Column.js +5 -0
- package/dist/cjs/decorators/orm/CreateDateColumn.d.ts +1 -0
- package/dist/cjs/decorators/orm/CreateDateColumn.js +5 -0
- package/dist/cjs/decorators/orm/DeleteDateColumn.d.ts +1 -0
- package/dist/cjs/decorators/orm/DeleteDateColumn.js +5 -0
- package/dist/cjs/decorators/orm/Entity.d.ts +1 -0
- package/dist/cjs/decorators/orm/Entity.js +5 -0
- package/dist/cjs/decorators/orm/EventSubscriber.d.ts +1 -0
- package/dist/cjs/decorators/orm/EventSubscriber.js +5 -0
- package/dist/cjs/decorators/orm/Exclusion.d.ts +1 -0
- package/dist/cjs/decorators/orm/Exclusion.js +5 -0
- package/dist/cjs/decorators/orm/Generated.d.ts +1 -0
- package/dist/cjs/decorators/orm/Generated.js +5 -0
- package/dist/cjs/decorators/orm/Index.d.ts +1 -0
- package/dist/cjs/decorators/orm/Index.js +5 -0
- package/dist/cjs/decorators/orm/JoinColumn.d.ts +1 -0
- package/dist/cjs/decorators/orm/JoinColumn.js +5 -0
- package/dist/cjs/decorators/orm/JoinTable.d.ts +1 -0
- package/dist/cjs/decorators/orm/JoinTable.js +5 -0
- package/dist/cjs/decorators/orm/ManyToMany.d.ts +1 -0
- package/dist/cjs/decorators/orm/ManyToMany.js +5 -0
- package/dist/cjs/decorators/orm/ManyToOne.d.ts +1 -0
- package/dist/cjs/decorators/orm/ManyToOne.js +5 -0
- package/dist/cjs/decorators/orm/ObjectIdColumn.d.ts +1 -0
- package/dist/cjs/decorators/orm/ObjectIdColumn.js +5 -0
- package/dist/cjs/decorators/orm/OneToMany.d.ts +1 -0
- package/dist/cjs/decorators/orm/OneToMany.js +5 -0
- package/dist/cjs/decorators/orm/OneToOne.d.ts +1 -0
- package/dist/cjs/decorators/orm/OneToOne.js +5 -0
- package/dist/cjs/decorators/orm/PrimaryColumn.d.ts +1 -0
- package/dist/cjs/decorators/orm/PrimaryColumn.js +5 -0
- package/dist/cjs/decorators/orm/PrimaryGeneratedColumn.d.ts +1 -0
- package/dist/cjs/decorators/orm/PrimaryGeneratedColumn.js +5 -0
- package/dist/cjs/decorators/orm/RelationId.d.ts +1 -0
- package/dist/cjs/decorators/orm/RelationId.js +5 -0
- package/dist/cjs/decorators/orm/TableInheritance.d.ts +1 -0
- package/dist/cjs/decorators/orm/TableInheritance.js +5 -0
- package/dist/cjs/decorators/orm/Tree.d.ts +1 -0
- package/dist/cjs/decorators/orm/Tree.js +5 -0
- package/dist/cjs/decorators/orm/TreeChildren.d.ts +1 -0
- package/dist/cjs/decorators/orm/TreeChildren.js +5 -0
- package/dist/cjs/decorators/orm/TreeLevelColumn.d.ts +1 -0
- package/dist/cjs/decorators/orm/TreeLevelColumn.js +5 -0
- package/dist/cjs/decorators/orm/TreeParent.d.ts +1 -0
- package/dist/cjs/decorators/orm/TreeParent.js +5 -0
- package/dist/cjs/decorators/orm/Unique.d.ts +1 -0
- package/dist/cjs/decorators/orm/Unique.js +5 -0
- package/dist/cjs/decorators/orm/UpdateDateColumn.d.ts +1 -0
- package/dist/cjs/decorators/orm/UpdateDateColumn.js +5 -0
- package/dist/cjs/decorators/orm/VersionColumn.d.ts +1 -0
- package/dist/cjs/decorators/orm/VersionColumn.js +5 -0
- package/dist/cjs/decorators/orm/ViewColumn.d.ts +1 -0
- package/dist/cjs/decorators/orm/ViewColumn.js +5 -0
- package/dist/cjs/decorators/orm/ViewEntity.d.ts +1 -0
- package/dist/cjs/decorators/orm/ViewEntity.js +5 -0
- package/dist/cjs/decorators/orm/VirtualColumn.d.ts +1 -0
- package/dist/cjs/decorators/orm/VirtualColumn.js +5 -0
- package/dist/cjs/exports/DatabaseComponent.d.ts +3 -0
- package/dist/cjs/exports/DatabaseComponent.js +21 -0
- package/dist/cjs/exports/DatabaseProvider.d.ts +3 -0
- package/dist/cjs/exports/DatabaseProvider.js +21 -0
- package/dist/cjs/exports/ORM.d.ts +193 -0
- package/dist/cjs/exports/ORM.js +166 -0
- package/dist/cjs/exports/ORMDecorators.d.ts +43 -0
- package/dist/cjs/exports/ORMDecorators.js +91 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/providers/Database.d.ts +326 -0
- package/dist/cjs/providers/Database.js +376 -0
- package/dist/cjs/providers/migration/ExecuteMigration.d.ts +40 -0
- package/dist/cjs/providers/migration/ExecuteMigration.js +143 -0
- package/dist/cjs/providers/migration/GenerateMigration.d.ts +59 -0
- package/dist/cjs/providers/migration/GenerateMigration.js +303 -0
- package/dist/cjs/types/MigrationRunOptions.d.ts +19 -0
- package/dist/cjs/types/MigrationRunOptions.js +2 -0
- package/dist/esm/components/Database.js +344 -0
- package/dist/esm/decorators/orm/AfterInsert.js +1 -0
- package/dist/esm/decorators/orm/AfterLoad.js +1 -0
- package/dist/esm/decorators/orm/AfterRecover.js +1 -0
- package/dist/esm/decorators/orm/AfterRemove.js +1 -0
- package/dist/esm/decorators/orm/AfterSoftRemove.js +1 -0
- package/dist/esm/decorators/orm/AfterUpdate.js +1 -0
- package/dist/esm/decorators/orm/BeforeInsert.js +1 -0
- package/dist/esm/decorators/orm/BeforeRecover.js +1 -0
- package/dist/esm/decorators/orm/BeforeRemove.js +1 -0
- package/dist/esm/decorators/orm/BeforeSoftRemove.js +1 -0
- package/dist/esm/decorators/orm/BeforeUpdate.js +1 -0
- package/dist/esm/decorators/orm/Check.js +1 -0
- package/dist/esm/decorators/orm/ChildEntity.js +1 -0
- package/dist/esm/decorators/orm/Column.js +1 -0
- package/dist/esm/decorators/orm/CreateDateColumn.js +1 -0
- package/dist/esm/decorators/orm/DeleteDateColumn.js +1 -0
- package/dist/esm/decorators/orm/Entity.js +1 -0
- package/dist/esm/decorators/orm/EventSubscriber.js +1 -0
- package/dist/esm/decorators/orm/Exclusion.js +1 -0
- package/dist/esm/decorators/orm/Generated.js +1 -0
- package/dist/esm/decorators/orm/Index.js +1 -0
- package/dist/esm/decorators/orm/JoinColumn.js +1 -0
- package/dist/esm/decorators/orm/JoinTable.js +1 -0
- package/dist/esm/decorators/orm/ManyToMany.js +1 -0
- package/dist/esm/decorators/orm/ManyToOne.js +1 -0
- package/dist/esm/decorators/orm/ObjectIdColumn.js +1 -0
- package/dist/esm/decorators/orm/OneToMany.js +1 -0
- package/dist/esm/decorators/orm/OneToOne.js +1 -0
- package/dist/esm/decorators/orm/PrimaryColumn.js +1 -0
- package/dist/esm/decorators/orm/PrimaryGeneratedColumn.js +1 -0
- package/dist/esm/decorators/orm/RelationId.js +1 -0
- package/dist/esm/decorators/orm/TableInheritance.js +1 -0
- package/dist/esm/decorators/orm/Tree.js +1 -0
- package/dist/esm/decorators/orm/TreeChildren.js +1 -0
- package/dist/esm/decorators/orm/TreeLevelColumn.js +1 -0
- package/dist/esm/decorators/orm/TreeParent.js +1 -0
- package/dist/esm/decorators/orm/Unique.js +1 -0
- package/dist/esm/decorators/orm/UpdateDateColumn.js +1 -0
- package/dist/esm/decorators/orm/VersionColumn.js +1 -0
- package/dist/esm/decorators/orm/ViewColumn.js +1 -0
- package/dist/esm/decorators/orm/ViewEntity.js +1 -0
- package/dist/esm/decorators/orm/VirtualColumn.js +1 -0
- package/dist/esm/exports/DatabaseComponent.js +2 -0
- package/dist/esm/exports/DatabaseProvider.js +2 -0
- package/dist/esm/exports/ORM.js +120 -0
- package/dist/esm/exports/ORMDecorators.js +43 -0
- package/dist/esm/providers/Database.js +339 -0
- package/dist/esm/providers/migration/ExecuteMigration.js +137 -0
- package/dist/esm/providers/migration/GenerateMigration.js +297 -0
- package/dist/esm/types/MigrationRunOptions.js +1 -0
- package/dist/types/components/Database.d.ts +333 -0
- package/dist/types/decorators/orm/AfterInsert.d.ts +1 -0
- package/dist/types/decorators/orm/AfterLoad.d.ts +1 -0
- package/dist/types/decorators/orm/AfterRecover.d.ts +1 -0
- package/dist/types/decorators/orm/AfterRemove.d.ts +1 -0
- package/dist/types/decorators/orm/AfterSoftRemove.d.ts +1 -0
- package/dist/types/decorators/orm/AfterUpdate.d.ts +1 -0
- package/dist/types/decorators/orm/BeforeInsert.d.ts +1 -0
- package/dist/types/decorators/orm/BeforeRecover.d.ts +1 -0
- package/dist/types/decorators/orm/BeforeRemove.d.ts +1 -0
- package/dist/types/decorators/orm/BeforeSoftRemove.d.ts +1 -0
- package/dist/types/decorators/orm/BeforeUpdate.d.ts +1 -0
- package/dist/types/decorators/orm/Check.d.ts +1 -0
- package/dist/types/decorators/orm/ChildEntity.d.ts +1 -0
- package/dist/types/decorators/orm/Column.d.ts +1 -0
- package/dist/types/decorators/orm/CreateDateColumn.d.ts +1 -0
- package/dist/types/decorators/orm/DeleteDateColumn.d.ts +1 -0
- package/dist/types/decorators/orm/Entity.d.ts +1 -0
- package/dist/types/decorators/orm/EventSubscriber.d.ts +1 -0
- package/dist/types/decorators/orm/Exclusion.d.ts +1 -0
- package/dist/types/decorators/orm/Generated.d.ts +1 -0
- package/dist/types/decorators/orm/Index.d.ts +1 -0
- package/dist/types/decorators/orm/JoinColumn.d.ts +1 -0
- package/dist/types/decorators/orm/JoinTable.d.ts +1 -0
- package/dist/types/decorators/orm/ManyToMany.d.ts +1 -0
- package/dist/types/decorators/orm/ManyToOne.d.ts +1 -0
- package/dist/types/decorators/orm/ObjectIdColumn.d.ts +1 -0
- package/dist/types/decorators/orm/OneToMany.d.ts +1 -0
- package/dist/types/decorators/orm/OneToOne.d.ts +1 -0
- package/dist/types/decorators/orm/PrimaryColumn.d.ts +1 -0
- package/dist/types/decorators/orm/PrimaryGeneratedColumn.d.ts +1 -0
- package/dist/types/decorators/orm/RelationId.d.ts +1 -0
- package/dist/types/decorators/orm/TableInheritance.d.ts +1 -0
- package/dist/types/decorators/orm/Tree.d.ts +1 -0
- package/dist/types/decorators/orm/TreeChildren.d.ts +1 -0
- package/dist/types/decorators/orm/TreeLevelColumn.d.ts +1 -0
- package/dist/types/decorators/orm/TreeParent.d.ts +1 -0
- package/dist/types/decorators/orm/Unique.d.ts +1 -0
- package/dist/types/decorators/orm/UpdateDateColumn.d.ts +1 -0
- package/dist/types/decorators/orm/VersionColumn.d.ts +1 -0
- package/dist/types/decorators/orm/ViewColumn.d.ts +1 -0
- package/dist/types/decorators/orm/ViewEntity.d.ts +1 -0
- package/dist/types/decorators/orm/VirtualColumn.d.ts +1 -0
- package/dist/types/exports/DatabaseComponent.d.ts +3 -0
- package/dist/types/exports/DatabaseProvider.d.ts +3 -0
- package/dist/types/exports/ORM.d.ts +193 -0
- package/dist/types/exports/ORMDecorators.d.ts +43 -0
- package/dist/types/providers/Database.d.ts +326 -0
- package/dist/types/providers/migration/ExecuteMigration.d.ts +40 -0
- package/dist/types/providers/migration/GenerateMigration.d.ts +59 -0
- package/dist/types/types/MigrationRunOptions.d.ts +19 -0
- 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
|
-
#
|
|
1
|
+
# @lakutata/orm
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
```
|