@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.
Files changed (206) hide show
  1. package/LICENSE.md +21 -157
  2. package/README.md +652 -15
  3. package/dist/core.cjs +2111 -133
  4. package/dist/core.esm.cjs +2112 -134
  5. package/lib/esm/identity/decorators.d.ts +52 -7
  6. package/lib/esm/identity/decorators.js +58 -13
  7. package/lib/esm/identity/index.js +3 -3
  8. package/lib/esm/identity/utils.d.ts +19 -0
  9. package/lib/esm/identity/utils.js +22 -3
  10. package/lib/esm/index.d.ts +10 -3
  11. package/lib/esm/index.js +19 -12
  12. package/lib/esm/interfaces/ErrorParser.d.ts +12 -0
  13. package/lib/esm/interfaces/ErrorParser.js +1 -1
  14. package/lib/esm/interfaces/Executor.d.ts +13 -0
  15. package/lib/esm/interfaces/Executor.js +1 -1
  16. package/lib/esm/interfaces/Observable.d.ts +27 -0
  17. package/lib/esm/interfaces/Observable.js +1 -1
  18. package/lib/esm/interfaces/Observer.d.ts +12 -0
  19. package/lib/esm/interfaces/Observer.js +1 -1
  20. package/lib/esm/interfaces/Paginatable.d.ts +15 -0
  21. package/lib/esm/interfaces/Paginatable.js +1 -1
  22. package/lib/esm/interfaces/Queriable.d.ts +34 -9
  23. package/lib/esm/interfaces/Queriable.js +1 -1
  24. package/lib/esm/interfaces/RawExecutor.d.ts +14 -0
  25. package/lib/esm/interfaces/RawExecutor.js +1 -1
  26. package/lib/esm/interfaces/SequenceOptions.d.ts +52 -0
  27. package/lib/esm/interfaces/SequenceOptions.js +19 -1
  28. package/lib/esm/interfaces/index.js +8 -8
  29. package/lib/esm/model/BaseModel.d.ts +31 -0
  30. package/lib/esm/model/BaseModel.js +24 -1
  31. package/lib/esm/model/construction.d.ts +433 -0
  32. package/lib/esm/model/construction.js +444 -5
  33. package/lib/esm/model/decorators.d.ts +159 -29
  34. package/lib/esm/model/decorators.js +167 -37
  35. package/lib/esm/model/index.js +5 -5
  36. package/lib/esm/model/types.d.ts +9 -0
  37. package/lib/esm/model/types.js +1 -1
  38. package/lib/esm/persistence/Adapter.d.ts +358 -17
  39. package/lib/esm/persistence/Adapter.js +292 -24
  40. package/lib/esm/persistence/Dispatch.d.ts +114 -1
  41. package/lib/esm/persistence/Dispatch.js +104 -6
  42. package/lib/esm/persistence/ObserverHandler.d.ts +95 -0
  43. package/lib/esm/persistence/ObserverHandler.js +96 -1
  44. package/lib/esm/persistence/Sequence.d.ts +89 -0
  45. package/lib/esm/persistence/Sequence.js +71 -2
  46. package/lib/esm/persistence/constants.d.ts +22 -0
  47. package/lib/esm/persistence/constants.js +23 -1
  48. package/lib/esm/persistence/decorators.d.ts +10 -0
  49. package/lib/esm/persistence/decorators.js +13 -3
  50. package/lib/esm/persistence/errors.d.ts +23 -0
  51. package/lib/esm/persistence/errors.js +24 -1
  52. package/lib/esm/persistence/index.js +9 -9
  53. package/lib/esm/persistence/types.d.ts +18 -0
  54. package/lib/esm/persistence/types.js +1 -1
  55. package/lib/esm/query/Condition.d.ts +78 -31
  56. package/lib/esm/query/Condition.js +134 -55
  57. package/lib/esm/query/Paginator.d.ts +56 -0
  58. package/lib/esm/query/Paginator.js +58 -2
  59. package/lib/esm/query/Statement.d.ts +51 -0
  60. package/lib/esm/query/Statement.js +55 -4
  61. package/lib/esm/query/constants.d.ts +25 -0
  62. package/lib/esm/query/constants.js +26 -1
  63. package/lib/esm/query/errors.d.ts +14 -0
  64. package/lib/esm/query/errors.js +15 -1
  65. package/lib/esm/query/index.js +8 -8
  66. package/lib/esm/query/options.d.ts +21 -3
  67. package/lib/esm/query/options.js +1 -1
  68. package/lib/esm/query/selectors.d.ts +26 -0
  69. package/lib/esm/query/selectors.js +1 -1
  70. package/lib/esm/ram/RamAdapter.d.ts +311 -0
  71. package/lib/esm/ram/RamAdapter.js +319 -8
  72. package/lib/esm/ram/RamContext.d.ts +16 -1
  73. package/lib/esm/ram/RamContext.js +18 -3
  74. package/lib/esm/ram/RamPaginator.d.ts +43 -0
  75. package/lib/esm/ram/RamPaginator.js +55 -3
  76. package/lib/esm/ram/RamSequence.d.ts +61 -0
  77. package/lib/esm/ram/RamSequence.js +66 -5
  78. package/lib/esm/ram/RamStatement.d.ts +74 -0
  79. package/lib/esm/ram/RamStatement.js +78 -4
  80. package/lib/esm/ram/constants.d.ts +8 -0
  81. package/lib/esm/ram/constants.js +9 -1
  82. package/lib/esm/ram/handlers.d.ts +19 -0
  83. package/lib/esm/ram/handlers.js +21 -2
  84. package/lib/esm/ram/index.js +11 -11
  85. package/lib/esm/ram/model/RamSequence.d.ts +25 -0
  86. package/lib/esm/ram/model/RamSequence.js +21 -3
  87. package/lib/esm/ram/model/index.js +2 -2
  88. package/lib/esm/ram/types.d.ts +42 -0
  89. package/lib/esm/ram/types.js +1 -1
  90. package/lib/esm/repository/Repository.d.ts +363 -8
  91. package/lib/esm/repository/Repository.js +369 -24
  92. package/lib/esm/repository/constants.d.ts +25 -0
  93. package/lib/esm/repository/constants.js +26 -1
  94. package/lib/esm/repository/decorators.d.ts +27 -0
  95. package/lib/esm/repository/decorators.js +29 -2
  96. package/lib/esm/repository/errors.d.ts +12 -5
  97. package/lib/esm/repository/errors.js +13 -6
  98. package/lib/esm/repository/index.js +8 -8
  99. package/lib/esm/repository/injectables.d.ts +18 -0
  100. package/lib/esm/repository/injectables.js +23 -5
  101. package/lib/esm/repository/types.d.ts +15 -0
  102. package/lib/esm/repository/types.js +1 -1
  103. package/lib/esm/repository/utils.d.ts +11 -0
  104. package/lib/esm/repository/utils.js +15 -4
  105. package/lib/esm/utils/decorators.d.ts +8 -0
  106. package/lib/esm/utils/decorators.js +9 -1
  107. package/lib/esm/utils/errors.d.ts +46 -0
  108. package/lib/esm/utils/errors.js +47 -1
  109. package/lib/esm/utils/index.js +3 -3
  110. package/lib/identity/decorators.cjs +53 -8
  111. package/lib/identity/decorators.d.ts +52 -7
  112. package/lib/identity/utils.cjs +20 -1
  113. package/lib/identity/utils.d.ts +19 -0
  114. package/lib/index.cjs +11 -4
  115. package/lib/index.d.ts +10 -3
  116. package/lib/interfaces/ErrorParser.cjs +1 -1
  117. package/lib/interfaces/ErrorParser.d.ts +12 -0
  118. package/lib/interfaces/Executor.cjs +1 -1
  119. package/lib/interfaces/Executor.d.ts +13 -0
  120. package/lib/interfaces/Observable.cjs +1 -1
  121. package/lib/interfaces/Observable.d.ts +27 -0
  122. package/lib/interfaces/Observer.cjs +1 -1
  123. package/lib/interfaces/Observer.d.ts +12 -0
  124. package/lib/interfaces/Paginatable.cjs +1 -1
  125. package/lib/interfaces/Paginatable.d.ts +15 -0
  126. package/lib/interfaces/Queriable.cjs +1 -1
  127. package/lib/interfaces/Queriable.d.ts +34 -9
  128. package/lib/interfaces/RawExecutor.cjs +1 -1
  129. package/lib/interfaces/RawExecutor.d.ts +14 -0
  130. package/lib/interfaces/SequenceOptions.cjs +19 -1
  131. package/lib/interfaces/SequenceOptions.d.ts +52 -0
  132. package/lib/model/BaseModel.cjs +24 -1
  133. package/lib/model/BaseModel.d.ts +31 -0
  134. package/lib/model/construction.cjs +441 -2
  135. package/lib/model/construction.d.ts +433 -0
  136. package/lib/model/decorators.cjs +160 -30
  137. package/lib/model/decorators.d.ts +159 -29
  138. package/lib/model/types.cjs +1 -1
  139. package/lib/model/types.d.ts +9 -0
  140. package/lib/persistence/Adapter.cjs +287 -19
  141. package/lib/persistence/Adapter.d.ts +358 -17
  142. package/lib/persistence/Dispatch.cjs +102 -4
  143. package/lib/persistence/Dispatch.d.ts +114 -1
  144. package/lib/persistence/ObserverHandler.cjs +96 -1
  145. package/lib/persistence/ObserverHandler.d.ts +95 -0
  146. package/lib/persistence/Sequence.cjs +70 -1
  147. package/lib/persistence/Sequence.d.ts +89 -0
  148. package/lib/persistence/constants.cjs +23 -1
  149. package/lib/persistence/constants.d.ts +22 -0
  150. package/lib/persistence/decorators.cjs +11 -1
  151. package/lib/persistence/decorators.d.ts +10 -0
  152. package/lib/persistence/errors.cjs +24 -1
  153. package/lib/persistence/errors.d.ts +23 -0
  154. package/lib/persistence/types.cjs +1 -1
  155. package/lib/persistence/types.d.ts +18 -0
  156. package/lib/query/Condition.cjs +132 -53
  157. package/lib/query/Condition.d.ts +78 -31
  158. package/lib/query/Paginator.cjs +57 -1
  159. package/lib/query/Paginator.d.ts +56 -0
  160. package/lib/query/Statement.cjs +52 -1
  161. package/lib/query/Statement.d.ts +51 -0
  162. package/lib/query/constants.cjs +26 -1
  163. package/lib/query/constants.d.ts +25 -0
  164. package/lib/query/errors.cjs +15 -1
  165. package/lib/query/errors.d.ts +14 -0
  166. package/lib/query/options.cjs +1 -1
  167. package/lib/query/options.d.ts +21 -3
  168. package/lib/query/selectors.cjs +1 -1
  169. package/lib/query/selectors.d.ts +26 -0
  170. package/lib/ram/RamAdapter.cjs +312 -1
  171. package/lib/ram/RamAdapter.d.ts +311 -0
  172. package/lib/ram/RamContext.cjs +18 -3
  173. package/lib/ram/RamContext.d.ts +16 -1
  174. package/lib/ram/RamPaginator.cjs +54 -2
  175. package/lib/ram/RamPaginator.d.ts +43 -0
  176. package/lib/ram/RamSequence.cjs +63 -2
  177. package/lib/ram/RamSequence.d.ts +61 -0
  178. package/lib/ram/RamStatement.cjs +75 -1
  179. package/lib/ram/RamStatement.d.ts +74 -0
  180. package/lib/ram/constants.cjs +9 -1
  181. package/lib/ram/constants.d.ts +8 -0
  182. package/lib/ram/handlers.cjs +20 -1
  183. package/lib/ram/handlers.d.ts +19 -0
  184. package/lib/ram/model/RamSequence.cjs +19 -1
  185. package/lib/ram/model/RamSequence.d.ts +25 -0
  186. package/lib/ram/types.cjs +1 -1
  187. package/lib/ram/types.d.ts +42 -0
  188. package/lib/repository/Repository.cjs +360 -15
  189. package/lib/repository/Repository.d.ts +363 -8
  190. package/lib/repository/constants.cjs +26 -1
  191. package/lib/repository/constants.d.ts +25 -0
  192. package/lib/repository/decorators.cjs +28 -1
  193. package/lib/repository/decorators.d.ts +27 -0
  194. package/lib/repository/errors.cjs +13 -6
  195. package/lib/repository/errors.d.ts +12 -5
  196. package/lib/repository/injectables.cjs +19 -1
  197. package/lib/repository/injectables.d.ts +18 -0
  198. package/lib/repository/types.cjs +1 -1
  199. package/lib/repository/types.d.ts +15 -0
  200. package/lib/repository/utils.cjs +12 -1
  201. package/lib/repository/utils.d.ts +11 -0
  202. package/lib/utils/decorators.cjs +9 -1
  203. package/lib/utils/decorators.d.ts +8 -0
  204. package/lib/utils/errors.cjs +47 -1
  205. package/lib/utils/errors.d.ts +46 -0
  206. package/package.json +5 -5
package/README.md CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  ## Core Module
4
4
 
5
- Introduces the Persistence Adapter concept, a Facade implementation managing all the particularities of each persistence layer.
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
  ![Licence](https://img.shields.io/github/license/decaf-ts/core.svg?style=plastic)
@@ -20,12 +19,6 @@ Introduces the Persistence Adapter concept, a Facade implementation managing all
20
19
  ![Pull Requests](https://img.shields.io/github/issues-pr-closed/decaf-ts/core.svg)
21
20
  ![Maintained](https://img.shields.io/badge/Maintained%3F-yes-green.svg)
22
21
 
23
- ![Line Coverage](workdocs/reports/coverage/badge-lines.svg)
24
- ![Function Coverage](workdocs/reports/coverage/badge-functions.svg)
25
- ![Statement Coverage](workdocs/reports/coverage/badge-statements.svg)
26
- ![Branch Coverage](workdocs/reports/coverage/badge-branches.svg)
27
-
28
-
29
22
  ![Forks](https://img.shields.io/github/forks/decaf-ts/core.svg)
30
23
  ![Stars](https://img.shields.io/github/stars/decaf-ts/core.svg)
31
24
  ![Watchers](https://img.shields.io/github/watchers/decaf-ts/core.svg)
@@ -37,20 +30,664 @@ Documentation available [here](https://decaf-ts.github.io/core/)
37
30
 
38
31
  ### Description
39
32
 
40
- More opinionated (but very convenient) extension of `db-decorators`, and exposes all the functionality from the previous modules in a very extensible and developer friendly way:
41
- - wraps any storage (blockchain, relational/non-relational databases and any other storage mechanism);
42
- - automates the boiled plate code from `decorator-validation`, `db-decorators` and `injectable-decorators`;
43
- - provides raw access to the storage;
44
- - provides querying capabilities;
45
- - Provides Repository apis for all selector Models;
46
- - Initializes the storage according to the selected Models;
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