@decaf-ts/transactional-decorators 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -188
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/index.cjs +1 -1
- package/lib/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,14 @@ A comprehensive TypeScript library providing transaction management capabilities
|
|
|
6
6
|
|
|
7
7
|
> Release docs refreshed on 2025-11-26. See [workdocs/reports/RELEASE_NOTES.md](./workdocs/reports/RELEASE_NOTES.md) for ticket summaries.
|
|
8
8
|
|
|
9
|
+
### Core Concepts
|
|
10
|
+
|
|
11
|
+
* **`@transactional`**: A method decorator that wraps a method in a transaction, ensuring that it is executed atomically.
|
|
12
|
+
* **`Transaction` Class**: The core class for managing transaction lifecycle, including creation, execution, and cleanup.
|
|
13
|
+
* **Locks**: The library provides different lock implementations to control concurrency.
|
|
14
|
+
* **`SynchronousLock`**: A simple lock that allows only one transaction to execute at a time.
|
|
15
|
+
* **`MultiLock`**: A more advanced lock that allows multiple transactions to execute concurrently, with a configurable limit.
|
|
16
|
+
|
|
9
17
|
|
|
10
18
|

|
|
11
19
|

|
|
@@ -60,226 +68,67 @@ The Transactional Decorators library is a standalone module that provides a robu
|
|
|
60
68
|
This library is ideal for applications that need to ensure data consistency and handle concurrent operations safely, such as database applications, financial systems, or any application where atomic operations are important.
|
|
61
69
|
|
|
62
70
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
- [Initial Setup](./tutorials/For%20Developers.md#_initial-setup_)
|
|
66
|
-
- [Installation](./tutorials/For%20Developers.md#installation)
|
|
71
|
+
# How to Use
|
|
67
72
|
|
|
68
|
-
|
|
73
|
+
This guide provides examples of how to use the main features of the `@decaf-ts/transactional-decorators` library.
|
|
69
74
|
|
|
70
|
-
|
|
75
|
+
## Transactional Decorator
|
|
71
76
|
|
|
72
|
-
|
|
77
|
+
The `@transactional` decorator ensures that a method is executed within a transaction.
|
|
73
78
|
|
|
74
79
|
```typescript
|
|
75
80
|
import { transactional } from '@decaf-ts/transactional-decorators';
|
|
76
81
|
|
|
77
|
-
class
|
|
82
|
+
class MyService {
|
|
78
83
|
@transactional()
|
|
79
|
-
async
|
|
80
|
-
// This method will be executed within a transaction
|
|
81
|
-
// If an error occurs, the transaction will be released with the error
|
|
82
|
-
const user = await this.userRepository.save(userData);
|
|
83
|
-
return user;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
@transactional(['custom', 'metadata'])
|
|
87
|
-
async updateUser(userId: string, userData: any): Promise<any> {
|
|
88
|
-
// You can pass custom metadata to the transaction
|
|
89
|
-
const user = await this.userRepository.findById(userId);
|
|
90
|
-
Object.assign(user, userData);
|
|
91
|
-
return await this.userRepository.save(user);
|
|
84
|
+
async myTransactionalMethod() {
|
|
85
|
+
// This method will be executed within a transaction.
|
|
92
86
|
}
|
|
93
87
|
}
|
|
94
|
-
|
|
95
|
-
// Using the transactional method
|
|
96
|
-
const userService = new UserService();
|
|
97
|
-
const newUser = await userService.createUser({ name: 'John Doe' });
|
|
98
88
|
```
|
|
99
89
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
For more control over the transaction lifecycle, you can use the Transaction class directly.
|
|
103
|
-
|
|
104
|
-
**Description**: Create and manage transactions manually for complex scenarios or when you need fine-grained control.
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
import { Transaction } from '@decaf-ts/transactional-decorators';
|
|
108
|
-
|
|
109
|
-
// Creating a transaction
|
|
110
|
-
const transaction = new Transaction(
|
|
111
|
-
'UserService', // Source
|
|
112
|
-
'createUser', // Method name
|
|
113
|
-
async () => {
|
|
114
|
-
// Transaction logic here
|
|
115
|
-
const user = await userRepository.save({ name: 'John Doe' });
|
|
116
|
-
return user;
|
|
117
|
-
}
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
// Submitting the transaction for execution
|
|
121
|
-
await Transaction.submit(transaction);
|
|
122
|
-
|
|
123
|
-
// Using the Transaction.push method for callback-style APIs
|
|
124
|
-
Transaction.push(
|
|
125
|
-
userService, // The object instance
|
|
126
|
-
userService.createUserWithCallback, // The method to call
|
|
127
|
-
{ name: 'John Doe' }, // Arguments
|
|
128
|
-
(err, user) => {
|
|
129
|
-
if (err) {
|
|
130
|
-
console.error('Error creating user:', err);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
console.log('User created:', user);
|
|
134
|
-
}
|
|
135
|
-
);
|
|
136
|
-
```
|
|
90
|
+
## Locks
|
|
137
91
|
|
|
138
|
-
|
|
92
|
+
The library provides different lock implementations to control concurrency.
|
|
139
93
|
|
|
140
|
-
|
|
94
|
+
### SynchronousLock
|
|
141
95
|
|
|
142
|
-
|
|
96
|
+
The `SynchronousLock` allows only one transaction to execute at a time. This is the default lock.
|
|
143
97
|
|
|
144
98
|
```typescript
|
|
145
|
-
import {
|
|
146
|
-
|
|
147
|
-
class BaseRepository {
|
|
148
|
-
@transactional()
|
|
149
|
-
async save(entity: any): Promise<any> {
|
|
150
|
-
// Base save implementation
|
|
151
|
-
return entity;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
99
|
+
import { Transaction, SynchronousLock } from '@decaf-ts/transactional-decorators';
|
|
154
100
|
|
|
155
|
-
|
|
156
|
-
@transactional()
|
|
157
|
-
async save(user: any): Promise<any> {
|
|
158
|
-
// Pre-processing
|
|
159
|
-
user.updatedAt = new Date();
|
|
160
|
-
|
|
161
|
-
// Call the super method with transaction context
|
|
162
|
-
const result = await transactionalSuperCall(super.save.bind(this), user);
|
|
163
|
-
|
|
164
|
-
// Post-processing
|
|
165
|
-
console.log('User saved:', result);
|
|
166
|
-
return result;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
101
|
+
Transaction.setLock(new SynchronousLock());
|
|
169
102
|
```
|
|
170
103
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
You can implement your own TransactionLock to customize how transactions are processed.
|
|
104
|
+
### MultiLock
|
|
174
105
|
|
|
175
|
-
|
|
106
|
+
The `MultiLock` allows multiple transactions to execute concurrently, with a configurable limit.
|
|
176
107
|
|
|
177
108
|
```typescript
|
|
178
|
-
import {
|
|
179
|
-
|
|
180
|
-
// Custom transaction lock that logs transactions
|
|
181
|
-
class LoggingTransactionLock implements TransactionLock {
|
|
182
|
-
currentTransaction?: Transaction;
|
|
183
|
-
private pendingTransactions: Transaction[] = [];
|
|
184
|
-
|
|
185
|
-
submit(transaction: Transaction): void {
|
|
186
|
-
console.log(`Submitting transaction: ${transaction.toString()}`);
|
|
187
|
-
|
|
188
|
-
if (this.currentTransaction) {
|
|
189
|
-
this.pendingTransactions.push(transaction);
|
|
190
|
-
console.log(`Transaction queued. Queue length: ${this.pendingTransactions.length}`);
|
|
191
|
-
} else {
|
|
192
|
-
this.currentTransaction = transaction;
|
|
193
|
-
console.log(`Executing transaction immediately`);
|
|
194
|
-
transaction.fire();
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
async release(err?: Error): Promise<void> {
|
|
199
|
-
if (err) {
|
|
200
|
-
console.error(`Transaction error: ${err.message}`);
|
|
201
|
-
} else {
|
|
202
|
-
console.log(`Transaction completed successfully`);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
this.currentTransaction = undefined;
|
|
109
|
+
import { Transaction, MultiLock } from '@decaf-ts/transactional-decorators';
|
|
206
110
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
console.log(`Processing next transaction: ${nextTransaction.toString()}`);
|
|
210
|
-
this.currentTransaction = nextTransaction;
|
|
211
|
-
nextTransaction.fire();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return Promise.resolve();
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Set the custom lock as the default
|
|
219
|
-
Transaction.setLock(new LoggingTransactionLock());
|
|
111
|
+
// Allow up to 5 transactions to execute concurrently
|
|
112
|
+
Transaction.setLock(new MultiLock(5));
|
|
220
113
|
```
|
|
221
114
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
The Lock class provides a basic locking mechanism that you can use independently of the transaction system.
|
|
115
|
+
## Manual Transaction Management
|
|
225
116
|
|
|
226
|
-
|
|
117
|
+
You can also manage transactions manually using the `Transaction` class.
|
|
227
118
|
|
|
228
119
|
```typescript
|
|
229
|
-
import {
|
|
230
|
-
|
|
231
|
-
// Create a lock for a shared resource
|
|
232
|
-
const resourceLock = new Lock();
|
|
233
|
-
|
|
234
|
-
// Execute a function with exclusive access to the resource
|
|
235
|
-
async function accessSharedResource() {
|
|
236
|
-
const result = await resourceLock.execute(async () => {
|
|
237
|
-
// This code will run with exclusive access to the resource
|
|
238
|
-
const data = await fetchDataFromDatabase();
|
|
239
|
-
const processedData = processData(data);
|
|
240
|
-
await saveDataToDatabase(processedData);
|
|
241
|
-
return processedData;
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
return result;
|
|
245
|
-
}
|
|
120
|
+
import { Transaction } from '@decaf-ts/transactional-decorators';
|
|
246
121
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
//
|
|
252
|
-
const data = await fetchDataFromDatabase();
|
|
253
|
-
const processedData = processData(data);
|
|
254
|
-
await saveDataToDatabase(processedData);
|
|
255
|
-
return processedData;
|
|
256
|
-
} finally {
|
|
257
|
-
// Always release the lock, even if an error occurs
|
|
258
|
-
resourceLock.release();
|
|
122
|
+
const myTransaction = new Transaction(
|
|
123
|
+
'MyManualTransaction',
|
|
124
|
+
'myAction',
|
|
125
|
+
async () => {
|
|
126
|
+
// Transaction logic here
|
|
259
127
|
}
|
|
260
|
-
|
|
261
|
-
```
|
|
262
|
-
|
|
128
|
+
);
|
|
263
129
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
- group similar functionality in folders (analog to namespaces but without any namespace declaration)
|
|
267
|
-
- one class per file;
|
|
268
|
-
- one interface per file (unless interface is just used as a type);
|
|
269
|
-
- group types as other interfaces in a types.ts file per folder;
|
|
270
|
-
- group constants or enums in a constants.ts file per folder;
|
|
271
|
-
- group decorators in a decorators.ts file per folder;
|
|
272
|
-
- always import from the specific file, never from a folder or index file (exceptions for dependencies on other packages);
|
|
273
|
-
- prefer the usage of established design patters where applicable:
|
|
274
|
-
- Singleton (can be an anti-pattern. use with care);
|
|
275
|
-
- factory;
|
|
276
|
-
- observer;
|
|
277
|
-
- strategy;
|
|
278
|
-
- builder;
|
|
279
|
-
- etc;
|
|
280
|
-
|
|
281
|
-
## Release Documentation Hooks
|
|
282
|
-
Stay aligned with the automated release pipeline by reviewing [Release Notes](./workdocs/reports/RELEASE_NOTES.md) and [Dependencies](./workdocs/reports/DEPENDENCIES.md) after trying these recipes (updated on 2025-11-26).
|
|
130
|
+
Transaction.submit(myTransaction);
|
|
131
|
+
```
|
|
283
132
|
|
|
284
133
|
|
|
285
134
|
### Related
|
package/lib/esm/index.d.ts
CHANGED
package/lib/esm/index.js
CHANGED
|
@@ -18,7 +18,7 @@ export * from "./types.js";
|
|
|
18
18
|
* @const VERSION
|
|
19
19
|
* @memberOf module:transactions
|
|
20
20
|
*/
|
|
21
|
-
export const VERSION = "0.3.
|
|
21
|
+
export const VERSION = "0.3.5";
|
|
22
22
|
export const PACKAGE_NAME = "@decaf-ts/transactional-decorators";
|
|
23
23
|
Metadata.registerLibrary(PACKAGE_NAME, VERSION);
|
|
24
24
|
//# sourceMappingURL=index.js.map
|
package/lib/index.cjs
CHANGED
|
@@ -35,7 +35,7 @@ __exportStar(require("./types.cjs"), exports);
|
|
|
35
35
|
* @const VERSION
|
|
36
36
|
* @memberOf module:transactions
|
|
37
37
|
*/
|
|
38
|
-
exports.VERSION = "0.3.
|
|
38
|
+
exports.VERSION = "0.3.5";
|
|
39
39
|
exports.PACKAGE_NAME = "@decaf-ts/transactional-decorators";
|
|
40
40
|
decoration_1.Metadata.registerLibrary(exports.PACKAGE_NAME, exports.VERSION);
|
|
41
41
|
//# sourceMappingURL=index.js.map
|
package/lib/index.d.ts
CHANGED