@decaf-ts/core 0.5.1 → 0.5.3
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.md +21 -157
- package/README.md +652 -15
- package/dist/core.cjs +2111 -133
- package/dist/core.esm.cjs +2112 -134
- package/lib/esm/identity/decorators.d.ts +52 -7
- package/lib/esm/identity/decorators.js +58 -13
- package/lib/esm/identity/index.js +3 -3
- package/lib/esm/identity/utils.d.ts +19 -0
- package/lib/esm/identity/utils.js +22 -3
- package/lib/esm/index.d.ts +10 -3
- package/lib/esm/index.js +19 -12
- package/lib/esm/interfaces/ErrorParser.d.ts +12 -0
- package/lib/esm/interfaces/ErrorParser.js +1 -1
- package/lib/esm/interfaces/Executor.d.ts +13 -0
- package/lib/esm/interfaces/Executor.js +1 -1
- package/lib/esm/interfaces/Observable.d.ts +27 -0
- package/lib/esm/interfaces/Observable.js +1 -1
- package/lib/esm/interfaces/Observer.d.ts +12 -0
- package/lib/esm/interfaces/Observer.js +1 -1
- package/lib/esm/interfaces/Paginatable.d.ts +15 -0
- package/lib/esm/interfaces/Paginatable.js +1 -1
- package/lib/esm/interfaces/Queriable.d.ts +34 -9
- package/lib/esm/interfaces/Queriable.js +1 -1
- package/lib/esm/interfaces/RawExecutor.d.ts +14 -0
- package/lib/esm/interfaces/RawExecutor.js +1 -1
- package/lib/esm/interfaces/SequenceOptions.d.ts +52 -0
- package/lib/esm/interfaces/SequenceOptions.js +19 -1
- package/lib/esm/interfaces/index.js +8 -8
- package/lib/esm/model/BaseModel.d.ts +31 -0
- package/lib/esm/model/BaseModel.js +24 -1
- package/lib/esm/model/construction.d.ts +433 -0
- package/lib/esm/model/construction.js +444 -5
- package/lib/esm/model/decorators.d.ts +159 -29
- package/lib/esm/model/decorators.js +167 -37
- package/lib/esm/model/index.js +5 -5
- package/lib/esm/model/types.d.ts +9 -0
- package/lib/esm/model/types.js +1 -1
- package/lib/esm/persistence/Adapter.d.ts +358 -17
- package/lib/esm/persistence/Adapter.js +292 -24
- package/lib/esm/persistence/Dispatch.d.ts +114 -1
- package/lib/esm/persistence/Dispatch.js +104 -6
- package/lib/esm/persistence/ObserverHandler.d.ts +95 -0
- package/lib/esm/persistence/ObserverHandler.js +96 -1
- package/lib/esm/persistence/Sequence.d.ts +89 -0
- package/lib/esm/persistence/Sequence.js +71 -2
- package/lib/esm/persistence/constants.d.ts +22 -0
- package/lib/esm/persistence/constants.js +23 -1
- package/lib/esm/persistence/decorators.d.ts +10 -0
- package/lib/esm/persistence/decorators.js +13 -3
- package/lib/esm/persistence/errors.d.ts +23 -0
- package/lib/esm/persistence/errors.js +24 -1
- package/lib/esm/persistence/index.js +9 -9
- package/lib/esm/persistence/types.d.ts +18 -0
- package/lib/esm/persistence/types.js +1 -1
- package/lib/esm/query/Condition.d.ts +78 -31
- package/lib/esm/query/Condition.js +134 -55
- package/lib/esm/query/Paginator.d.ts +56 -0
- package/lib/esm/query/Paginator.js +58 -2
- package/lib/esm/query/Statement.d.ts +51 -0
- package/lib/esm/query/Statement.js +55 -4
- package/lib/esm/query/constants.d.ts +25 -0
- package/lib/esm/query/constants.js +26 -1
- package/lib/esm/query/errors.d.ts +14 -0
- package/lib/esm/query/errors.js +15 -1
- package/lib/esm/query/index.js +8 -8
- package/lib/esm/query/options.d.ts +21 -3
- package/lib/esm/query/options.js +1 -1
- package/lib/esm/query/selectors.d.ts +26 -0
- package/lib/esm/query/selectors.js +1 -1
- package/lib/esm/ram/RamAdapter.d.ts +311 -0
- package/lib/esm/ram/RamAdapter.js +319 -8
- package/lib/esm/ram/RamContext.d.ts +16 -1
- package/lib/esm/ram/RamContext.js +18 -3
- package/lib/esm/ram/RamPaginator.d.ts +43 -0
- package/lib/esm/ram/RamPaginator.js +55 -3
- package/lib/esm/ram/RamSequence.d.ts +61 -0
- package/lib/esm/ram/RamSequence.js +66 -5
- package/lib/esm/ram/RamStatement.d.ts +74 -0
- package/lib/esm/ram/RamStatement.js +78 -4
- package/lib/esm/ram/constants.d.ts +8 -0
- package/lib/esm/ram/constants.js +9 -1
- package/lib/esm/ram/handlers.d.ts +19 -0
- package/lib/esm/ram/handlers.js +21 -2
- package/lib/esm/ram/index.js +11 -11
- package/lib/esm/ram/model/RamSequence.d.ts +25 -0
- package/lib/esm/ram/model/RamSequence.js +21 -3
- package/lib/esm/ram/model/index.js +2 -2
- package/lib/esm/ram/types.d.ts +42 -0
- package/lib/esm/ram/types.js +1 -1
- package/lib/esm/repository/Repository.d.ts +363 -8
- package/lib/esm/repository/Repository.js +369 -24
- package/lib/esm/repository/constants.d.ts +25 -0
- package/lib/esm/repository/constants.js +26 -1
- package/lib/esm/repository/decorators.d.ts +27 -0
- package/lib/esm/repository/decorators.js +29 -2
- package/lib/esm/repository/errors.d.ts +12 -5
- package/lib/esm/repository/errors.js +13 -6
- package/lib/esm/repository/index.js +8 -8
- package/lib/esm/repository/injectables.d.ts +18 -0
- package/lib/esm/repository/injectables.js +23 -5
- package/lib/esm/repository/types.d.ts +15 -0
- package/lib/esm/repository/types.js +1 -1
- package/lib/esm/repository/utils.d.ts +11 -0
- package/lib/esm/repository/utils.js +15 -4
- package/lib/esm/utils/decorators.d.ts +8 -0
- package/lib/esm/utils/decorators.js +9 -1
- package/lib/esm/utils/errors.d.ts +46 -0
- package/lib/esm/utils/errors.js +47 -1
- package/lib/esm/utils/index.js +3 -3
- package/lib/identity/decorators.cjs +53 -8
- package/lib/identity/decorators.d.ts +52 -7
- package/lib/identity/utils.cjs +20 -1
- package/lib/identity/utils.d.ts +19 -0
- package/lib/index.cjs +11 -4
- package/lib/index.d.ts +10 -3
- package/lib/interfaces/ErrorParser.cjs +1 -1
- package/lib/interfaces/ErrorParser.d.ts +12 -0
- package/lib/interfaces/Executor.cjs +1 -1
- package/lib/interfaces/Executor.d.ts +13 -0
- package/lib/interfaces/Observable.cjs +1 -1
- package/lib/interfaces/Observable.d.ts +27 -0
- package/lib/interfaces/Observer.cjs +1 -1
- package/lib/interfaces/Observer.d.ts +12 -0
- package/lib/interfaces/Paginatable.cjs +1 -1
- package/lib/interfaces/Paginatable.d.ts +15 -0
- package/lib/interfaces/Queriable.cjs +1 -1
- package/lib/interfaces/Queriable.d.ts +34 -9
- package/lib/interfaces/RawExecutor.cjs +1 -1
- package/lib/interfaces/RawExecutor.d.ts +14 -0
- package/lib/interfaces/SequenceOptions.cjs +19 -1
- package/lib/interfaces/SequenceOptions.d.ts +52 -0
- package/lib/model/BaseModel.cjs +24 -1
- package/lib/model/BaseModel.d.ts +31 -0
- package/lib/model/construction.cjs +441 -2
- package/lib/model/construction.d.ts +433 -0
- package/lib/model/decorators.cjs +160 -30
- package/lib/model/decorators.d.ts +159 -29
- package/lib/model/types.cjs +1 -1
- package/lib/model/types.d.ts +9 -0
- package/lib/persistence/Adapter.cjs +287 -19
- package/lib/persistence/Adapter.d.ts +358 -17
- package/lib/persistence/Dispatch.cjs +102 -4
- package/lib/persistence/Dispatch.d.ts +114 -1
- package/lib/persistence/ObserverHandler.cjs +96 -1
- package/lib/persistence/ObserverHandler.d.ts +95 -0
- package/lib/persistence/Sequence.cjs +70 -1
- package/lib/persistence/Sequence.d.ts +89 -0
- package/lib/persistence/constants.cjs +23 -1
- package/lib/persistence/constants.d.ts +22 -0
- package/lib/persistence/decorators.cjs +11 -1
- package/lib/persistence/decorators.d.ts +10 -0
- package/lib/persistence/errors.cjs +24 -1
- package/lib/persistence/errors.d.ts +23 -0
- package/lib/persistence/types.cjs +1 -1
- package/lib/persistence/types.d.ts +18 -0
- package/lib/query/Condition.cjs +132 -53
- package/lib/query/Condition.d.ts +78 -31
- package/lib/query/Paginator.cjs +57 -1
- package/lib/query/Paginator.d.ts +56 -0
- package/lib/query/Statement.cjs +52 -1
- package/lib/query/Statement.d.ts +51 -0
- package/lib/query/constants.cjs +26 -1
- package/lib/query/constants.d.ts +25 -0
- package/lib/query/errors.cjs +15 -1
- package/lib/query/errors.d.ts +14 -0
- package/lib/query/options.cjs +1 -1
- package/lib/query/options.d.ts +21 -3
- package/lib/query/selectors.cjs +1 -1
- package/lib/query/selectors.d.ts +26 -0
- package/lib/ram/RamAdapter.cjs +312 -1
- package/lib/ram/RamAdapter.d.ts +311 -0
- package/lib/ram/RamContext.cjs +18 -3
- package/lib/ram/RamContext.d.ts +16 -1
- package/lib/ram/RamPaginator.cjs +54 -2
- package/lib/ram/RamPaginator.d.ts +43 -0
- package/lib/ram/RamSequence.cjs +63 -2
- package/lib/ram/RamSequence.d.ts +61 -0
- package/lib/ram/RamStatement.cjs +75 -1
- package/lib/ram/RamStatement.d.ts +74 -0
- package/lib/ram/constants.cjs +9 -1
- package/lib/ram/constants.d.ts +8 -0
- package/lib/ram/handlers.cjs +20 -1
- package/lib/ram/handlers.d.ts +19 -0
- package/lib/ram/model/RamSequence.cjs +19 -1
- package/lib/ram/model/RamSequence.d.ts +25 -0
- package/lib/ram/types.cjs +1 -1
- package/lib/ram/types.d.ts +42 -0
- package/lib/repository/Repository.cjs +360 -15
- package/lib/repository/Repository.d.ts +363 -8
- package/lib/repository/constants.cjs +26 -1
- package/lib/repository/constants.d.ts +25 -0
- package/lib/repository/decorators.cjs +28 -1
- package/lib/repository/decorators.d.ts +27 -0
- package/lib/repository/errors.cjs +13 -6
- package/lib/repository/errors.d.ts +12 -5
- package/lib/repository/injectables.cjs +19 -1
- package/lib/repository/injectables.d.ts +18 -0
- package/lib/repository/types.cjs +1 -1
- package/lib/repository/types.d.ts +15 -0
- package/lib/repository/utils.cjs +12 -1
- package/lib/repository/utils.d.ts +11 -0
- package/lib/utils/decorators.cjs +9 -1
- package/lib/utils/decorators.d.ts +8 -0
- package/lib/utils/errors.cjs +47 -1
- package/lib/utils/errors.d.ts +46 -0
- package/package.json +5 -5
package/README.md
CHANGED
@@ -2,8 +2,7 @@
|
|
2
2
|
|
3
3
|
## Core Module
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
The Decaf TypeScript Core Module is a comprehensive framework that provides a robust foundation for building TypeScript applications with data persistence capabilities. It offers a flexible model-repository architecture with support for various storage mechanisms, relationship management, querying capabilities, and reactive programming through the Observer pattern. The framework simplifies data handling with decorators for model definition, identity management, and persistence operations while maintaining type safety throughout.
|
7
6
|
|
8
7
|
|
9
8
|

|
@@ -20,12 +19,6 @@ Introduces the Persistence Adapter concept, a Facade implementation managing all
|
|
20
19
|

|
21
20
|

|
22
21
|
|
23
|
-

|
24
|
-

|
25
|
-

|
26
|
-

|
27
|
-
|
28
|
-
|
29
22
|

|
30
23
|

|
31
24
|

|
@@ -37,20 +30,664 @@ Documentation available [here](https://decaf-ts.github.io/core/)
|
|
37
30
|
|
38
31
|
### Description
|
39
32
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
33
|
+
The Decaf TypeScript Core Module is a sophisticated framework designed to streamline data persistence and model management in TypeScript applications. Building upon the foundation of `db-decorators`, `decorator-validation`, and `injectable-decorators`, it provides a comprehensive solution for working with data models across various storage mechanisms.
|
34
|
+
|
35
|
+
#### Architecture Overview
|
36
|
+
|
37
|
+
The framework is organized into several key modules:
|
38
|
+
|
39
|
+
1. **Model System**: At the heart of the framework is the `BaseModel` class, which serves as the foundation for all domain models. It provides automatic timestamp tracking and integrates with the validation system. The model system supports:
|
40
|
+
- Property decorators for defining model attributes
|
41
|
+
- Relationship decorators (`@oneToOne`, `@oneToMany`, `@manyToOne`) for defining associations between models
|
42
|
+
- Table and column mapping through `@table` and `@column` decorators
|
43
|
+
- Indexing capabilities with the `@index` decorator
|
44
|
+
|
45
|
+
2. **Identity Management**: The framework includes robust identity handling with:
|
46
|
+
- Primary key generation through the `@pk` decorator
|
47
|
+
- Sequence generation for automatic ID assignment
|
48
|
+
- Utilities for table name resolution and sequence naming
|
49
|
+
|
50
|
+
3. **Repository Pattern**: The repository module provides a clean abstraction for data access operations:
|
51
|
+
- CRUD operations (create, read, update, delete)
|
52
|
+
- Transaction support
|
53
|
+
- Relationship management with cascade operations
|
54
|
+
- Custom repository implementations through decorators
|
55
|
+
|
56
|
+
4. **Query System**: A flexible query builder allows for:
|
57
|
+
- Condition-based filtering
|
58
|
+
- Property selection
|
59
|
+
- Pagination
|
60
|
+
- Sorting and ordering
|
61
|
+
- Statement execution
|
62
|
+
|
63
|
+
5. **Persistence Layer**: The adapter-based persistence system:
|
64
|
+
- Abstracts away storage implementation details
|
65
|
+
- Supports multiple storage backends
|
66
|
+
- Provides sequence management
|
67
|
+
- Implements the Observer pattern for reactive updates
|
68
|
+
|
69
|
+
6. **RAM Implementation**: An in-memory implementation of the persistence layer for:
|
70
|
+
- Testing purposes
|
71
|
+
- Prototyping
|
72
|
+
- Caching
|
73
|
+
|
74
|
+
#### Key Features
|
75
|
+
|
76
|
+
- **Type Safety**: Leverages TypeScript's type system to provide compile-time checks
|
77
|
+
- **Decorator-Based Configuration**: Uses decorators for clean, declarative model definitions
|
78
|
+
- **Relationship Management**: Handles one-to-one, one-to-many, and many-to-one relationships with automatic cascading
|
79
|
+
- **Flexible Storage**: Works with any storage mechanism through the adapter pattern
|
80
|
+
- **Reactive Updates**: Implements the Observer pattern for reactive programming
|
81
|
+
- **Dependency Injection**: Integrates with dependency injection for flexible component wiring
|
82
|
+
- **Raw Access**: Provides direct access to the underlying storage when needed
|
83
|
+
- **Automatic Timestamps**: Tracks creation and update times automatically
|
84
|
+
|
85
|
+
The Core Module is designed to be extensible and developer-friendly, reducing boilerplate code while providing powerful features for data management in TypeScript applications.
|
86
|
+
|
47
87
|
|
48
88
|
### How to Use
|
49
89
|
|
50
90
|
- [Initial Setup](../../workdocs/tutorials/For%20Developers.md#_initial-setup_)
|
51
91
|
- [Installation](../../workdocs/tutorials/For%20Developers.md#installation)
|
52
92
|
|
93
|
+
## Table of Contents
|
94
|
+
|
95
|
+
- [Model Definition](#model-definition)
|
96
|
+
- [Identity Management](#identity-management)
|
97
|
+
- [Relationships](#relationships)
|
98
|
+
- [Repository Operations](#repository-operations)
|
99
|
+
- [Querying](#querying)
|
100
|
+
- [Persistence](#persistence)
|
101
|
+
- [Observer Pattern](#observer-pattern)
|
102
|
+
|
103
|
+
## Model Definition
|
104
|
+
|
105
|
+
### Creating a Basic Model
|
106
|
+
|
107
|
+
Define a domain model by extending the BaseModel class and using decorators to define properties.
|
108
|
+
|
109
|
+
```typescript
|
110
|
+
import { BaseModel, required, email, pk } from '@decaf-ts/core';
|
111
|
+
|
112
|
+
class User extends BaseModel {
|
113
|
+
@pk()
|
114
|
+
id!: string;
|
115
|
+
|
116
|
+
@required()
|
117
|
+
username!: string;
|
118
|
+
|
119
|
+
@email()
|
120
|
+
email!: string;
|
121
|
+
|
122
|
+
@required()
|
123
|
+
firstName!: string;
|
124
|
+
|
125
|
+
@required()
|
126
|
+
lastName!: string;
|
127
|
+
|
128
|
+
constructor(data?: Partial<User>) {
|
129
|
+
super(data);
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
// Create a new user
|
134
|
+
const user = new User({
|
135
|
+
username: 'johndoe',
|
136
|
+
email: 'john.doe@example.com',
|
137
|
+
firstName: 'John',
|
138
|
+
lastName: 'Doe'
|
139
|
+
});
|
140
|
+
```
|
141
|
+
|
142
|
+
### Customizing Table and Column Names
|
143
|
+
|
144
|
+
Use the `@table` and `@column` decorators to customize database table and column names.
|
145
|
+
|
146
|
+
```typescript
|
147
|
+
import { BaseModel, required, table, column, pk } from '@decaf-ts/core';
|
148
|
+
|
149
|
+
@table('app_users')
|
150
|
+
class User extends BaseModel {
|
151
|
+
@pk()
|
152
|
+
id!: string;
|
153
|
+
|
154
|
+
@required()
|
155
|
+
username!: string;
|
156
|
+
|
157
|
+
@required()
|
158
|
+
@column('user_email')
|
159
|
+
email!: string;
|
160
|
+
|
161
|
+
@column('first_name')
|
162
|
+
firstName!: string;
|
163
|
+
|
164
|
+
@column('last_name')
|
165
|
+
lastName!: string;
|
166
|
+
|
167
|
+
constructor(data?: Partial<User>) {
|
168
|
+
super(data);
|
169
|
+
}
|
170
|
+
}
|
171
|
+
```
|
172
|
+
|
173
|
+
### Creating Indexes
|
174
|
+
|
175
|
+
Use the `@index` decorator to create database indexes for better query performance.
|
176
|
+
|
177
|
+
```typescript
|
178
|
+
import { BaseModel, required, index, pk, OrderDirection } from '@decaf-ts/core';
|
179
|
+
|
180
|
+
class Product extends BaseModel {
|
181
|
+
@pk()
|
182
|
+
id!: string;
|
183
|
+
|
184
|
+
@required()
|
185
|
+
@index([OrderDirection.ASC])
|
186
|
+
name!: string;
|
187
|
+
|
188
|
+
@required()
|
189
|
+
@index([OrderDirection.ASC, OrderDirection.DSC])
|
190
|
+
price!: number;
|
191
|
+
|
192
|
+
@required()
|
193
|
+
category!: string;
|
194
|
+
|
195
|
+
constructor(data?: Partial<Product>) {
|
196
|
+
super(data);
|
197
|
+
}
|
198
|
+
}
|
199
|
+
```
|
200
|
+
|
201
|
+
## Identity Management
|
202
|
+
|
203
|
+
### Using Primary Keys with Automatic Sequence Generation
|
204
|
+
|
205
|
+
The `@pk` decorator marks a property as the primary key and sets up automatic sequence generation.
|
206
|
+
|
207
|
+
```typescript
|
208
|
+
import { BaseModel, pk, required } from '@decaf-ts/core';
|
209
|
+
|
210
|
+
class Order extends BaseModel {
|
211
|
+
@pk({ type: 'Number' })
|
212
|
+
id!: number;
|
213
|
+
|
214
|
+
@required()
|
215
|
+
customerId!: string;
|
216
|
+
|
217
|
+
@required()
|
218
|
+
totalAmount!: number;
|
219
|
+
|
220
|
+
constructor(data?: Partial<Order>) {
|
221
|
+
super(data);
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
// The id will be automatically generated when the order is saved
|
226
|
+
const order = new Order({
|
227
|
+
customerId: 'cust123',
|
228
|
+
totalAmount: 99.99
|
229
|
+
});
|
230
|
+
```
|
231
|
+
|
232
|
+
### Custom Sequence Options
|
233
|
+
|
234
|
+
Customize the sequence generation for primary keys.
|
235
|
+
|
236
|
+
```typescript
|
237
|
+
import { BaseModel, pk, required } from '@decaf-ts/core';
|
238
|
+
|
239
|
+
class Invoice extends BaseModel {
|
240
|
+
@pk({
|
241
|
+
type: 'BigInt',
|
242
|
+
name: 'invoice_sequence',
|
243
|
+
startWith: 1000,
|
244
|
+
incrementBy: 1
|
245
|
+
})
|
246
|
+
invoiceNumber!: bigint;
|
247
|
+
|
248
|
+
@required()
|
249
|
+
orderId!: number;
|
250
|
+
|
251
|
+
@required()
|
252
|
+
amount!: number;
|
253
|
+
|
254
|
+
constructor(data?: Partial<Invoice>) {
|
255
|
+
super(data);
|
256
|
+
}
|
257
|
+
}
|
258
|
+
```
|
259
|
+
|
260
|
+
## Relationships
|
261
|
+
|
262
|
+
### One-to-One Relationships
|
263
|
+
|
264
|
+
Define a one-to-one relationship between models.
|
265
|
+
|
266
|
+
```typescript
|
267
|
+
import { BaseModel, pk, required, oneToOne } from '@decaf-ts/core';
|
268
|
+
|
269
|
+
class User extends BaseModel {
|
270
|
+
@pk()
|
271
|
+
id!: string;
|
272
|
+
|
273
|
+
@required()
|
274
|
+
username!: string;
|
275
|
+
|
276
|
+
@oneToOne(Profile)
|
277
|
+
profile?: Profile;
|
278
|
+
|
279
|
+
constructor(data?: Partial<User>) {
|
280
|
+
super(data);
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
class Profile extends BaseModel {
|
285
|
+
@pk()
|
286
|
+
id!: string;
|
287
|
+
|
288
|
+
@required()
|
289
|
+
userId!: string;
|
290
|
+
|
291
|
+
bio?: string;
|
292
|
+
|
293
|
+
avatarUrl?: string;
|
294
|
+
|
295
|
+
constructor(data?: Partial<Profile>) {
|
296
|
+
super(data);
|
297
|
+
}
|
298
|
+
}
|
299
|
+
```
|
300
|
+
|
301
|
+
### One-to-Many Relationships
|
302
|
+
|
303
|
+
Define a one-to-many relationship between models.
|
304
|
+
|
305
|
+
```typescript
|
306
|
+
import { BaseModel, pk, required, oneToMany } from '@decaf-ts/core';
|
307
|
+
|
308
|
+
class Author extends BaseModel {
|
309
|
+
@pk()
|
310
|
+
id!: string;
|
311
|
+
|
312
|
+
@required()
|
313
|
+
name!: string;
|
314
|
+
|
315
|
+
@oneToMany(Book)
|
316
|
+
books?: Book[];
|
317
|
+
|
318
|
+
constructor(data?: Partial<Author>) {
|
319
|
+
super(data);
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
class Book extends BaseModel {
|
324
|
+
@pk()
|
325
|
+
id!: string;
|
326
|
+
|
327
|
+
@required()
|
328
|
+
title!: string;
|
329
|
+
|
330
|
+
@required()
|
331
|
+
authorId!: string;
|
332
|
+
|
333
|
+
constructor(data?: Partial<Book>) {
|
334
|
+
super(data);
|
335
|
+
}
|
336
|
+
}
|
337
|
+
```
|
338
|
+
|
339
|
+
### Many-to-One Relationships
|
340
|
+
|
341
|
+
Define a many-to-one relationship between models.
|
342
|
+
|
343
|
+
```typescript
|
344
|
+
import { BaseModel, pk, required, manyToOne } from '@decaf-ts/core';
|
345
|
+
|
346
|
+
class Book extends BaseModel {
|
347
|
+
@pk()
|
348
|
+
id!: string;
|
349
|
+
|
350
|
+
@required()
|
351
|
+
title!: string;
|
352
|
+
|
353
|
+
@required()
|
354
|
+
authorId!: string;
|
355
|
+
|
356
|
+
@manyToOne(Author)
|
357
|
+
author?: Author;
|
358
|
+
|
359
|
+
constructor(data?: Partial<Book>) {
|
360
|
+
super(data);
|
361
|
+
}
|
362
|
+
}
|
363
|
+
|
364
|
+
class Author extends BaseModel {
|
365
|
+
@pk()
|
366
|
+
id!: string;
|
367
|
+
|
368
|
+
@required()
|
369
|
+
name!: string;
|
370
|
+
|
371
|
+
constructor(data?: Partial<Author>) {
|
372
|
+
super(data);
|
373
|
+
}
|
374
|
+
}
|
375
|
+
```
|
376
|
+
|
377
|
+
## Repository Operations
|
378
|
+
|
379
|
+
### Basic CRUD Operations
|
380
|
+
|
381
|
+
Perform basic CRUD operations using a repository.
|
382
|
+
|
383
|
+
```typescript
|
384
|
+
import { Repository, BaseModel, pk, required } from '@decaf-ts/core';
|
385
|
+
|
386
|
+
class User extends BaseModel {
|
387
|
+
@pk({ type: 'Number' })
|
388
|
+
id!: string;
|
389
|
+
|
390
|
+
@required()
|
391
|
+
username!: string;
|
392
|
+
|
393
|
+
@required()
|
394
|
+
email!: string;
|
395
|
+
|
396
|
+
constructor(data?: Partial<User>) {
|
397
|
+
super(data);
|
398
|
+
}
|
399
|
+
}
|
400
|
+
|
401
|
+
// Create a repository for the User model
|
402
|
+
const userRepository = new Repository(User);
|
403
|
+
|
404
|
+
// Create a new user
|
405
|
+
async function createUser() {
|
406
|
+
const user = new User({
|
407
|
+
username: 'johndoe',
|
408
|
+
email: 'john.doe@example.com'
|
409
|
+
});
|
410
|
+
|
411
|
+
const createdUser = await userRepository.create(user);
|
412
|
+
console.log('User created with ID:', createdUser.id);
|
413
|
+
return createdUser;
|
414
|
+
}
|
415
|
+
|
416
|
+
// Read a user by ID
|
417
|
+
async function getUserById(id: string) {
|
418
|
+
const user = await userRepository.read(id);
|
419
|
+
console.log('User found:', user);
|
420
|
+
return user;
|
421
|
+
}
|
422
|
+
|
423
|
+
// Update a user
|
424
|
+
async function updateUser(user: User) {
|
425
|
+
user.email = 'new.email@example.com';
|
426
|
+
const updatedUser = await userRepository.update(user);
|
427
|
+
console.log('User updated');
|
428
|
+
return updatedUser;
|
429
|
+
}
|
430
|
+
|
431
|
+
// Delete a user
|
432
|
+
async function deleteUser(user: User) {
|
433
|
+
await userRepository.delete(user);
|
434
|
+
console.log('User deleted');
|
435
|
+
}
|
436
|
+
```
|
437
|
+
|
438
|
+
## Querying
|
439
|
+
|
440
|
+
### Basic Queries
|
441
|
+
|
442
|
+
Perform basic queries using conditions.
|
443
|
+
|
444
|
+
```typescript
|
445
|
+
import { Repository, BaseModel, pk, required, Condition } from '@decaf-ts/core';
|
446
|
+
|
447
|
+
class Product extends BaseModel {
|
448
|
+
@pk()
|
449
|
+
id!: string;
|
450
|
+
|
451
|
+
@required()
|
452
|
+
name!: string;
|
453
|
+
|
454
|
+
@required()
|
455
|
+
price!: number;
|
456
|
+
|
457
|
+
@required()
|
458
|
+
category!: string;
|
459
|
+
|
460
|
+
constructor(data?: Partial<Product>) {
|
461
|
+
super(data);
|
462
|
+
}
|
463
|
+
}
|
464
|
+
|
465
|
+
const productRepository = new Repository(Product);
|
466
|
+
|
467
|
+
// Find products by category
|
468
|
+
async function findProductsByCategory(category: string) {
|
469
|
+
const condition = Condition.eq('category', category);
|
470
|
+
const products = await productRepository.find(condition);
|
471
|
+
console.log(`Found ${products.length} products in category ${category}`);
|
472
|
+
return products;
|
473
|
+
}
|
474
|
+
|
475
|
+
// Find products with price greater than a value
|
476
|
+
async function findExpensiveProducts(minPrice: number) {
|
477
|
+
const condition = Condition.gt('price', minPrice);
|
478
|
+
const products = await productRepository.find(condition);
|
479
|
+
console.log(`Found ${products.length} products with price > ${minPrice}`);
|
480
|
+
return products;
|
481
|
+
}
|
482
|
+
|
483
|
+
// Find products with complex conditions
|
484
|
+
async function findSpecificProducts() {
|
485
|
+
const condition = Condition.and(
|
486
|
+
Condition.eq('category', 'electronics'),
|
487
|
+
Condition.or(
|
488
|
+
Condition.lt('price', 500),
|
489
|
+
Condition.gt('price', 1000)
|
490
|
+
)
|
491
|
+
);
|
492
|
+
const products = await productRepository.find(condition);
|
493
|
+
console.log(`Found ${products.length} specific products`);
|
494
|
+
return products;
|
495
|
+
}
|
496
|
+
```
|
497
|
+
|
498
|
+
### Pagination
|
499
|
+
|
500
|
+
Use pagination to handle large result sets.
|
501
|
+
|
502
|
+
```typescript
|
503
|
+
import { Repository, BaseModel, pk, required, OrderDirection } from '@decaf-ts/core';
|
504
|
+
|
505
|
+
class Product extends BaseModel {
|
506
|
+
@pk()
|
507
|
+
id!: string;
|
508
|
+
|
509
|
+
@required()
|
510
|
+
name!: string;
|
511
|
+
|
512
|
+
@required()
|
513
|
+
price!: number;
|
514
|
+
|
515
|
+
constructor(data?: Partial<Product>) {
|
516
|
+
super(data);
|
517
|
+
}
|
518
|
+
}
|
519
|
+
|
520
|
+
const productRepository = new Repository(Product);
|
521
|
+
|
522
|
+
// Get paginated results
|
523
|
+
async function getProductsPage(pageNumber: number, pageSize: number) {
|
524
|
+
const result = await productRepository.select()
|
525
|
+
.orderBy('name', OrderDirection.ASC)
|
526
|
+
.paginate(pageSize)
|
527
|
+
.page()
|
528
|
+
|
529
|
+
console.log(`Page ${pageNumber}: ${result.length} products`);
|
530
|
+
console.log(`Total pages: ${result.totalPages}`);
|
531
|
+
console.log(`Total items: ${result.totalItems}`);
|
532
|
+
|
533
|
+
return result;
|
534
|
+
}
|
535
|
+
```
|
536
|
+
|
537
|
+
### Property Selection
|
53
538
|
|
539
|
+
Select specific properties from models.
|
540
|
+
|
541
|
+
```typescript
|
542
|
+
import { Repository, BaseModel, pk, required } from '@decaf-ts/core';
|
543
|
+
|
544
|
+
class User extends BaseModel {
|
545
|
+
@pk()
|
546
|
+
id!: string;
|
547
|
+
|
548
|
+
@required()
|
549
|
+
username!: string;
|
550
|
+
|
551
|
+
@required()
|
552
|
+
email!: string;
|
553
|
+
|
554
|
+
@required()
|
555
|
+
password!: string;
|
556
|
+
|
557
|
+
constructor(data?: Partial<User>) {
|
558
|
+
super(data);
|
559
|
+
}
|
560
|
+
}
|
561
|
+
|
562
|
+
const userRepository = new Repository(User);
|
563
|
+
|
564
|
+
// Select only specific properties
|
565
|
+
async function getUsersPublicInfo() {
|
566
|
+
const users = await userRepository
|
567
|
+
.select(['id', 'username', 'email'])
|
568
|
+
.execute();
|
569
|
+
|
570
|
+
// The returned objects will only have id, username, and email properties
|
571
|
+
console.log('Users public info:', users);
|
572
|
+
return users;
|
573
|
+
}
|
574
|
+
```
|
575
|
+
|
576
|
+
## Persistence
|
577
|
+
|
578
|
+
### Using Different Adapters
|
579
|
+
|
580
|
+
Configure and use different persistence adapters.
|
581
|
+
|
582
|
+
```typescript
|
583
|
+
import {
|
584
|
+
Adapter,
|
585
|
+
RamAdapter,
|
586
|
+
Repository,
|
587
|
+
BaseModel,
|
588
|
+
pk,
|
589
|
+
required
|
590
|
+
} from '@decaf-ts/core';
|
591
|
+
|
592
|
+
class User extends BaseModel {
|
593
|
+
@pk()
|
594
|
+
id!: string;
|
595
|
+
|
596
|
+
@required()
|
597
|
+
username!: string;
|
598
|
+
|
599
|
+
constructor(data?: Partial<User>) {
|
600
|
+
super(data);
|
601
|
+
}
|
602
|
+
}
|
603
|
+
|
604
|
+
// Use RAM adapter for in-memory storage (useful for testing)
|
605
|
+
const ramAdapter = new RamAdapter();
|
606
|
+
const userRepository = new Repository(User, { adapter: ramAdapter });
|
607
|
+
|
608
|
+
// Example with a hypothetical SQL adapter
|
609
|
+
// const sqlAdapter = new SqlAdapter({
|
610
|
+
// host: 'localhost',
|
611
|
+
// port: 5432,
|
612
|
+
// database: 'myapp',
|
613
|
+
// username: 'user',
|
614
|
+
// password: 'password'
|
615
|
+
// });
|
616
|
+
// const userRepository = new Repository(User, { adapter: sqlAdapter });
|
617
|
+
|
618
|
+
async function testRepository() {
|
619
|
+
// Create a user
|
620
|
+
const user = new User({ username: 'testuser' });
|
621
|
+
await userRepository.create(user);
|
622
|
+
|
623
|
+
// Read the user
|
624
|
+
const retrievedUser = await userRepository.read(user.id);
|
625
|
+
console.log('Retrieved user:', retrievedUser);
|
626
|
+
}
|
627
|
+
```
|
628
|
+
|
629
|
+
## Observer Pattern
|
630
|
+
|
631
|
+
### Implementing an Observer
|
632
|
+
|
633
|
+
Create an observer to react to changes in observable objects.
|
634
|
+
|
635
|
+
```typescript
|
636
|
+
import { Observer, Observable } from '@decaf-ts/core';
|
637
|
+
|
638
|
+
// Create a custom observer
|
639
|
+
class LoggingObserver implements Observer {
|
640
|
+
async refresh(...args: any[]): Promise<void> {
|
641
|
+
console.log('Observable was updated with args:', args);
|
642
|
+
}
|
643
|
+
}
|
644
|
+
|
645
|
+
// Example usage with a hypothetical observable repository
|
646
|
+
class ObservableRepository implements Observable {
|
647
|
+
private observers: Observer[] = [];
|
648
|
+
|
649
|
+
observe(observer: Observer): void {
|
650
|
+
this.observers.push(observer);
|
651
|
+
console.log('Observer registered');
|
652
|
+
}
|
653
|
+
|
654
|
+
unObserve(observer: Observer): void {
|
655
|
+
this.observers = this.observers.filter(obs => obs !== observer);
|
656
|
+
console.log('Observer unregistered');
|
657
|
+
}
|
658
|
+
|
659
|
+
async updateObservers(...args: any[]): Promise<void> {
|
660
|
+
console.log('Notifying observers...');
|
661
|
+
for (const observer of this.observers) {
|
662
|
+
await observer.refresh(...args);
|
663
|
+
}
|
664
|
+
}
|
665
|
+
|
666
|
+
// Example method that triggers an update
|
667
|
+
async performAction(action: string): Promise<void> {
|
668
|
+
console.log(`Performing action: ${action}`);
|
669
|
+
await this.updateObservers(action, new Date());
|
670
|
+
}
|
671
|
+
}
|
672
|
+
|
673
|
+
// Usage
|
674
|
+
async function demonstrateObserverPattern() {
|
675
|
+
const repository = new ObservableRepository();
|
676
|
+
const logger = new LoggingObserver();
|
677
|
+
|
678
|
+
// Register the observer
|
679
|
+
repository.observe(logger);
|
680
|
+
|
681
|
+
// Perform an action that will notify the observer
|
682
|
+
await repository.performAction('save');
|
683
|
+
|
684
|
+
// Unregister the observer
|
685
|
+
repository.unObserve(logger);
|
686
|
+
|
687
|
+
// This action won't be logged by the observer
|
688
|
+
await repository.performAction('delete');
|
689
|
+
}
|
690
|
+
```
|
54
691
|
|
55
692
|
|
56
693
|
### Related
|