@signaltree/events 7.3.6 → 7.4.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/dist/angular/handlers.cjs +38 -0
- package/dist/angular/handlers.js +35 -0
- package/dist/angular/index.cjs +15 -0
- package/dist/angular/index.js +3 -0
- package/dist/angular/optimistic-updates.cjs +161 -0
- package/dist/angular/optimistic-updates.js +159 -0
- package/{angular.cjs.js → dist/angular/websocket.service.cjs} +0 -194
- package/{angular.esm.js → dist/angular/websocket.service.js} +1 -191
- package/dist/core/error-classification.cjs +282 -0
- package/dist/core/error-classification.js +276 -0
- package/{factory.cjs.js → dist/core/factory.cjs} +3 -40
- package/{factory.esm.js → dist/core/factory.js} +2 -37
- package/dist/core/idempotency.cjs +252 -0
- package/dist/core/idempotency.js +247 -0
- package/dist/core/registry.cjs +183 -0
- package/dist/core/registry.js +180 -0
- package/dist/core/types.cjs +41 -0
- package/dist/core/types.js +38 -0
- package/{index.cjs.js → dist/core/validation.cjs} +1 -23
- package/{index.esm.js → dist/core/validation.js} +1 -4
- package/dist/index.cjs +43 -0
- package/dist/index.js +7 -0
- package/dist/nestjs/base.subscriber.cjs +287 -0
- package/dist/nestjs/base.subscriber.js +287 -0
- package/dist/nestjs/decorators.cjs +35 -0
- package/dist/nestjs/decorators.js +32 -0
- package/dist/nestjs/dlq.service.cjs +249 -0
- package/dist/nestjs/dlq.service.js +249 -0
- package/dist/nestjs/event-bus.module.cjs +152 -0
- package/dist/nestjs/event-bus.module.js +152 -0
- package/dist/nestjs/event-bus.service.cjs +243 -0
- package/dist/nestjs/event-bus.service.js +243 -0
- package/dist/nestjs/index.cjs +33 -0
- package/dist/nestjs/index.js +6 -0
- package/dist/nestjs/tokens.cjs +14 -0
- package/dist/nestjs/tokens.js +9 -0
- package/dist/testing/assertions.cjs +172 -0
- package/dist/testing/assertions.js +169 -0
- package/dist/testing/factories.cjs +122 -0
- package/dist/testing/factories.js +119 -0
- package/dist/testing/helpers.cjs +233 -0
- package/dist/testing/helpers.js +227 -0
- package/dist/testing/index.cjs +20 -0
- package/dist/testing/index.js +4 -0
- package/dist/testing/mock-event-bus.cjs +237 -0
- package/dist/testing/mock-event-bus.js +234 -0
- package/package.json +22 -23
- package/angular.d.ts +0 -1
- package/idempotency.cjs.js +0 -713
- package/idempotency.esm.js +0 -701
- package/index.d.ts +0 -1
- package/nestjs.cjs.js +0 -951
- package/nestjs.d.ts +0 -1
- package/nestjs.esm.js +0 -944
- package/testing.cjs.js +0 -755
- package/testing.d.ts +0 -1
- package/testing.esm.js +0 -743
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a simple event handler
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* const handler = createEventHandler<TradeProposalCreated>(event => {
|
|
9
|
+
* store.$.trades.entities.upsertOne(event.data);
|
|
10
|
+
* });
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
function createEventHandler(handler) {
|
|
14
|
+
return handler;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create a typed event handler with metadata
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const handler = createTypedHandler('TradeProposalCreated', {
|
|
22
|
+
* handle: (event) => {
|
|
23
|
+
* store.$.trades.entities.upsertOne(event.data);
|
|
24
|
+
* },
|
|
25
|
+
* priority: 1,
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
function createTypedHandler(eventType, options) {
|
|
30
|
+
return {
|
|
31
|
+
eventType,
|
|
32
|
+
handle: options.handle,
|
|
33
|
+
priority: options.priority ?? 10
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
exports.createEventHandler = createEventHandler;
|
|
38
|
+
exports.createTypedHandler = createTypedHandler;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a simple event handler
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* const handler = createEventHandler<TradeProposalCreated>(event => {
|
|
7
|
+
* store.$.trades.entities.upsertOne(event.data);
|
|
8
|
+
* });
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
function createEventHandler(handler) {
|
|
12
|
+
return handler;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create a typed event handler with metadata
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const handler = createTypedHandler('TradeProposalCreated', {
|
|
20
|
+
* handle: (event) => {
|
|
21
|
+
* store.$.trades.entities.upsertOne(event.data);
|
|
22
|
+
* },
|
|
23
|
+
* priority: 1,
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function createTypedHandler(eventType, options) {
|
|
28
|
+
return {
|
|
29
|
+
eventType,
|
|
30
|
+
handle: options.handle,
|
|
31
|
+
priority: options.priority ?? 10
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { createEventHandler, createTypedHandler };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var websocket_service = require('./websocket.service.cjs');
|
|
4
|
+
var optimisticUpdates = require('./optimistic-updates.cjs');
|
|
5
|
+
var handlers = require('./handlers.cjs');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Object.defineProperty(exports, "WebSocketService", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function () { return websocket_service.WebSocketService; }
|
|
12
|
+
});
|
|
13
|
+
exports.OptimisticUpdateManager = optimisticUpdates.OptimisticUpdateManager;
|
|
14
|
+
exports.createEventHandler = handlers.createEventHandler;
|
|
15
|
+
exports.createTypedHandler = handlers.createTypedHandler;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@angular/core');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Optimistic Update Manager
|
|
7
|
+
*
|
|
8
|
+
* Tracks optimistic updates and handles confirmation/rollback.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const manager = new OptimisticUpdateManager();
|
|
13
|
+
*
|
|
14
|
+
* // Apply optimistic update
|
|
15
|
+
* manager.apply({
|
|
16
|
+
* id: 'update-1',
|
|
17
|
+
* correlationId: 'corr-123',
|
|
18
|
+
* type: 'UpdateTradeStatus',
|
|
19
|
+
* data: { status: 'accepted' },
|
|
20
|
+
* previousData: { status: 'pending' },
|
|
21
|
+
* timeoutMs: 5000,
|
|
22
|
+
* rollback: () => store.$.trade.status.set('pending'),
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // When server confirms
|
|
26
|
+
* manager.confirm('corr-123');
|
|
27
|
+
*
|
|
28
|
+
* // Or when server rejects
|
|
29
|
+
* manager.rollback('corr-123', new Error('Server rejected'));
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
class OptimisticUpdateManager {
|
|
33
|
+
_updates = core.signal(new Map());
|
|
34
|
+
timeouts = new Map();
|
|
35
|
+
/**
|
|
36
|
+
* Number of pending updates
|
|
37
|
+
*/
|
|
38
|
+
pendingCount = core.computed(() => this._updates().size);
|
|
39
|
+
/**
|
|
40
|
+
* Whether there are any pending updates
|
|
41
|
+
*/
|
|
42
|
+
hasPending = core.computed(() => this._updates().size > 0);
|
|
43
|
+
/**
|
|
44
|
+
* Get all pending updates
|
|
45
|
+
*/
|
|
46
|
+
pending = core.computed(() => Array.from(this._updates().values()));
|
|
47
|
+
/**
|
|
48
|
+
* Apply an optimistic update
|
|
49
|
+
*/
|
|
50
|
+
apply(update) {
|
|
51
|
+
// Store the update
|
|
52
|
+
this._updates.update(map => {
|
|
53
|
+
const newMap = new Map(map);
|
|
54
|
+
newMap.set(update.correlationId, update);
|
|
55
|
+
return newMap;
|
|
56
|
+
});
|
|
57
|
+
// Set timeout for automatic rollback
|
|
58
|
+
const timeout = setTimeout(() => {
|
|
59
|
+
this.rollback(update.correlationId, new Error(`Optimistic update timeout after ${update.timeoutMs}ms`));
|
|
60
|
+
}, update.timeoutMs);
|
|
61
|
+
this.timeouts.set(update.correlationId, timeout);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Confirm an optimistic update (server accepted)
|
|
65
|
+
*/
|
|
66
|
+
confirm(correlationId) {
|
|
67
|
+
const update = this._updates().get(correlationId);
|
|
68
|
+
if (!update) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
// Clear timeout
|
|
72
|
+
const timeout = this.timeouts.get(correlationId);
|
|
73
|
+
if (timeout) {
|
|
74
|
+
clearTimeout(timeout);
|
|
75
|
+
this.timeouts.delete(correlationId);
|
|
76
|
+
}
|
|
77
|
+
// Remove from pending
|
|
78
|
+
this._updates.update(map => {
|
|
79
|
+
const newMap = new Map(map);
|
|
80
|
+
newMap.delete(correlationId);
|
|
81
|
+
return newMap;
|
|
82
|
+
});
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Rollback an optimistic update (server rejected or timeout)
|
|
87
|
+
*/
|
|
88
|
+
rollback(correlationId, error) {
|
|
89
|
+
const update = this._updates().get(correlationId);
|
|
90
|
+
if (!update) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
// Clear timeout
|
|
94
|
+
const timeout = this.timeouts.get(correlationId);
|
|
95
|
+
if (timeout) {
|
|
96
|
+
clearTimeout(timeout);
|
|
97
|
+
this.timeouts.delete(correlationId);
|
|
98
|
+
}
|
|
99
|
+
// Execute rollback
|
|
100
|
+
try {
|
|
101
|
+
update.rollback();
|
|
102
|
+
} catch (rollbackError) {
|
|
103
|
+
console.error('Rollback failed:', rollbackError);
|
|
104
|
+
}
|
|
105
|
+
// Remove from pending
|
|
106
|
+
this._updates.update(map => {
|
|
107
|
+
const newMap = new Map(map);
|
|
108
|
+
newMap.delete(correlationId);
|
|
109
|
+
return newMap;
|
|
110
|
+
});
|
|
111
|
+
if (error) {
|
|
112
|
+
console.warn(`Optimistic update rolled back: ${error.message}`);
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Rollback all pending updates
|
|
118
|
+
*/
|
|
119
|
+
rollbackAll(error) {
|
|
120
|
+
const updates = Array.from(this._updates().keys());
|
|
121
|
+
let count = 0;
|
|
122
|
+
for (const correlationId of updates) {
|
|
123
|
+
if (this.rollback(correlationId, error)) {
|
|
124
|
+
count++;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return count;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get update by correlation ID
|
|
131
|
+
*/
|
|
132
|
+
get(correlationId) {
|
|
133
|
+
return this._updates().get(correlationId);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if an update is pending
|
|
137
|
+
*/
|
|
138
|
+
isPending(correlationId) {
|
|
139
|
+
return this._updates().has(correlationId);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Clear all updates without rollback (use with caution)
|
|
143
|
+
*/
|
|
144
|
+
clear() {
|
|
145
|
+
// Clear all timeouts
|
|
146
|
+
for (const timeout of this.timeouts.values()) {
|
|
147
|
+
clearTimeout(timeout);
|
|
148
|
+
}
|
|
149
|
+
this.timeouts.clear();
|
|
150
|
+
// Clear updates
|
|
151
|
+
this._updates.set(new Map());
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Dispose the manager
|
|
155
|
+
*/
|
|
156
|
+
dispose() {
|
|
157
|
+
this.clear();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
exports.OptimisticUpdateManager = OptimisticUpdateManager;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { signal, computed } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Optimistic Update Manager
|
|
5
|
+
*
|
|
6
|
+
* Tracks optimistic updates and handles confirmation/rollback.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const manager = new OptimisticUpdateManager();
|
|
11
|
+
*
|
|
12
|
+
* // Apply optimistic update
|
|
13
|
+
* manager.apply({
|
|
14
|
+
* id: 'update-1',
|
|
15
|
+
* correlationId: 'corr-123',
|
|
16
|
+
* type: 'UpdateTradeStatus',
|
|
17
|
+
* data: { status: 'accepted' },
|
|
18
|
+
* previousData: { status: 'pending' },
|
|
19
|
+
* timeoutMs: 5000,
|
|
20
|
+
* rollback: () => store.$.trade.status.set('pending'),
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // When server confirms
|
|
24
|
+
* manager.confirm('corr-123');
|
|
25
|
+
*
|
|
26
|
+
* // Or when server rejects
|
|
27
|
+
* manager.rollback('corr-123', new Error('Server rejected'));
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
class OptimisticUpdateManager {
|
|
31
|
+
_updates = signal(new Map());
|
|
32
|
+
timeouts = new Map();
|
|
33
|
+
/**
|
|
34
|
+
* Number of pending updates
|
|
35
|
+
*/
|
|
36
|
+
pendingCount = computed(() => this._updates().size);
|
|
37
|
+
/**
|
|
38
|
+
* Whether there are any pending updates
|
|
39
|
+
*/
|
|
40
|
+
hasPending = computed(() => this._updates().size > 0);
|
|
41
|
+
/**
|
|
42
|
+
* Get all pending updates
|
|
43
|
+
*/
|
|
44
|
+
pending = computed(() => Array.from(this._updates().values()));
|
|
45
|
+
/**
|
|
46
|
+
* Apply an optimistic update
|
|
47
|
+
*/
|
|
48
|
+
apply(update) {
|
|
49
|
+
// Store the update
|
|
50
|
+
this._updates.update(map => {
|
|
51
|
+
const newMap = new Map(map);
|
|
52
|
+
newMap.set(update.correlationId, update);
|
|
53
|
+
return newMap;
|
|
54
|
+
});
|
|
55
|
+
// Set timeout for automatic rollback
|
|
56
|
+
const timeout = setTimeout(() => {
|
|
57
|
+
this.rollback(update.correlationId, new Error(`Optimistic update timeout after ${update.timeoutMs}ms`));
|
|
58
|
+
}, update.timeoutMs);
|
|
59
|
+
this.timeouts.set(update.correlationId, timeout);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Confirm an optimistic update (server accepted)
|
|
63
|
+
*/
|
|
64
|
+
confirm(correlationId) {
|
|
65
|
+
const update = this._updates().get(correlationId);
|
|
66
|
+
if (!update) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
// Clear timeout
|
|
70
|
+
const timeout = this.timeouts.get(correlationId);
|
|
71
|
+
if (timeout) {
|
|
72
|
+
clearTimeout(timeout);
|
|
73
|
+
this.timeouts.delete(correlationId);
|
|
74
|
+
}
|
|
75
|
+
// Remove from pending
|
|
76
|
+
this._updates.update(map => {
|
|
77
|
+
const newMap = new Map(map);
|
|
78
|
+
newMap.delete(correlationId);
|
|
79
|
+
return newMap;
|
|
80
|
+
});
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Rollback an optimistic update (server rejected or timeout)
|
|
85
|
+
*/
|
|
86
|
+
rollback(correlationId, error) {
|
|
87
|
+
const update = this._updates().get(correlationId);
|
|
88
|
+
if (!update) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
// Clear timeout
|
|
92
|
+
const timeout = this.timeouts.get(correlationId);
|
|
93
|
+
if (timeout) {
|
|
94
|
+
clearTimeout(timeout);
|
|
95
|
+
this.timeouts.delete(correlationId);
|
|
96
|
+
}
|
|
97
|
+
// Execute rollback
|
|
98
|
+
try {
|
|
99
|
+
update.rollback();
|
|
100
|
+
} catch (rollbackError) {
|
|
101
|
+
console.error('Rollback failed:', rollbackError);
|
|
102
|
+
}
|
|
103
|
+
// Remove from pending
|
|
104
|
+
this._updates.update(map => {
|
|
105
|
+
const newMap = new Map(map);
|
|
106
|
+
newMap.delete(correlationId);
|
|
107
|
+
return newMap;
|
|
108
|
+
});
|
|
109
|
+
if (error) {
|
|
110
|
+
console.warn(`Optimistic update rolled back: ${error.message}`);
|
|
111
|
+
}
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Rollback all pending updates
|
|
116
|
+
*/
|
|
117
|
+
rollbackAll(error) {
|
|
118
|
+
const updates = Array.from(this._updates().keys());
|
|
119
|
+
let count = 0;
|
|
120
|
+
for (const correlationId of updates) {
|
|
121
|
+
if (this.rollback(correlationId, error)) {
|
|
122
|
+
count++;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return count;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get update by correlation ID
|
|
129
|
+
*/
|
|
130
|
+
get(correlationId) {
|
|
131
|
+
return this._updates().get(correlationId);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Check if an update is pending
|
|
135
|
+
*/
|
|
136
|
+
isPending(correlationId) {
|
|
137
|
+
return this._updates().has(correlationId);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Clear all updates without rollback (use with caution)
|
|
141
|
+
*/
|
|
142
|
+
clear() {
|
|
143
|
+
// Clear all timeouts
|
|
144
|
+
for (const timeout of this.timeouts.values()) {
|
|
145
|
+
clearTimeout(timeout);
|
|
146
|
+
}
|
|
147
|
+
this.timeouts.clear();
|
|
148
|
+
// Clear updates
|
|
149
|
+
this._updates.set(new Map());
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Dispose the manager
|
|
153
|
+
*/
|
|
154
|
+
dispose() {
|
|
155
|
+
this.clear();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export { OptimisticUpdateManager };
|
|
@@ -355,197 +355,3 @@ exports.WebSocketService = class WebSocketService {
|
|
|
355
355
|
}
|
|
356
356
|
};
|
|
357
357
|
exports.WebSocketService = tslib.__decorate([core.Injectable(), tslib.__metadata("design:paramtypes", [Object])], exports.WebSocketService);
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Optimistic Update Manager
|
|
361
|
-
*
|
|
362
|
-
* Tracks optimistic updates and handles confirmation/rollback.
|
|
363
|
-
*
|
|
364
|
-
* @example
|
|
365
|
-
* ```typescript
|
|
366
|
-
* const manager = new OptimisticUpdateManager();
|
|
367
|
-
*
|
|
368
|
-
* // Apply optimistic update
|
|
369
|
-
* manager.apply({
|
|
370
|
-
* id: 'update-1',
|
|
371
|
-
* correlationId: 'corr-123',
|
|
372
|
-
* type: 'UpdateTradeStatus',
|
|
373
|
-
* data: { status: 'accepted' },
|
|
374
|
-
* previousData: { status: 'pending' },
|
|
375
|
-
* timeoutMs: 5000,
|
|
376
|
-
* rollback: () => store.$.trade.status.set('pending'),
|
|
377
|
-
* });
|
|
378
|
-
*
|
|
379
|
-
* // When server confirms
|
|
380
|
-
* manager.confirm('corr-123');
|
|
381
|
-
*
|
|
382
|
-
* // Or when server rejects
|
|
383
|
-
* manager.rollback('corr-123', new Error('Server rejected'));
|
|
384
|
-
* ```
|
|
385
|
-
*/
|
|
386
|
-
class OptimisticUpdateManager {
|
|
387
|
-
_updates = core.signal(new Map());
|
|
388
|
-
timeouts = new Map();
|
|
389
|
-
/**
|
|
390
|
-
* Number of pending updates
|
|
391
|
-
*/
|
|
392
|
-
pendingCount = core.computed(() => this._updates().size);
|
|
393
|
-
/**
|
|
394
|
-
* Whether there are any pending updates
|
|
395
|
-
*/
|
|
396
|
-
hasPending = core.computed(() => this._updates().size > 0);
|
|
397
|
-
/**
|
|
398
|
-
* Get all pending updates
|
|
399
|
-
*/
|
|
400
|
-
pending = core.computed(() => Array.from(this._updates().values()));
|
|
401
|
-
/**
|
|
402
|
-
* Apply an optimistic update
|
|
403
|
-
*/
|
|
404
|
-
apply(update) {
|
|
405
|
-
// Store the update
|
|
406
|
-
this._updates.update(map => {
|
|
407
|
-
const newMap = new Map(map);
|
|
408
|
-
newMap.set(update.correlationId, update);
|
|
409
|
-
return newMap;
|
|
410
|
-
});
|
|
411
|
-
// Set timeout for automatic rollback
|
|
412
|
-
const timeout = setTimeout(() => {
|
|
413
|
-
this.rollback(update.correlationId, new Error(`Optimistic update timeout after ${update.timeoutMs}ms`));
|
|
414
|
-
}, update.timeoutMs);
|
|
415
|
-
this.timeouts.set(update.correlationId, timeout);
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Confirm an optimistic update (server accepted)
|
|
419
|
-
*/
|
|
420
|
-
confirm(correlationId) {
|
|
421
|
-
const update = this._updates().get(correlationId);
|
|
422
|
-
if (!update) {
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
|
-
// Clear timeout
|
|
426
|
-
const timeout = this.timeouts.get(correlationId);
|
|
427
|
-
if (timeout) {
|
|
428
|
-
clearTimeout(timeout);
|
|
429
|
-
this.timeouts.delete(correlationId);
|
|
430
|
-
}
|
|
431
|
-
// Remove from pending
|
|
432
|
-
this._updates.update(map => {
|
|
433
|
-
const newMap = new Map(map);
|
|
434
|
-
newMap.delete(correlationId);
|
|
435
|
-
return newMap;
|
|
436
|
-
});
|
|
437
|
-
return true;
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* Rollback an optimistic update (server rejected or timeout)
|
|
441
|
-
*/
|
|
442
|
-
rollback(correlationId, error) {
|
|
443
|
-
const update = this._updates().get(correlationId);
|
|
444
|
-
if (!update) {
|
|
445
|
-
return false;
|
|
446
|
-
}
|
|
447
|
-
// Clear timeout
|
|
448
|
-
const timeout = this.timeouts.get(correlationId);
|
|
449
|
-
if (timeout) {
|
|
450
|
-
clearTimeout(timeout);
|
|
451
|
-
this.timeouts.delete(correlationId);
|
|
452
|
-
}
|
|
453
|
-
// Execute rollback
|
|
454
|
-
try {
|
|
455
|
-
update.rollback();
|
|
456
|
-
} catch (rollbackError) {
|
|
457
|
-
console.error('Rollback failed:', rollbackError);
|
|
458
|
-
}
|
|
459
|
-
// Remove from pending
|
|
460
|
-
this._updates.update(map => {
|
|
461
|
-
const newMap = new Map(map);
|
|
462
|
-
newMap.delete(correlationId);
|
|
463
|
-
return newMap;
|
|
464
|
-
});
|
|
465
|
-
if (error) {
|
|
466
|
-
console.warn(`Optimistic update rolled back: ${error.message}`);
|
|
467
|
-
}
|
|
468
|
-
return true;
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* Rollback all pending updates
|
|
472
|
-
*/
|
|
473
|
-
rollbackAll(error) {
|
|
474
|
-
const updates = Array.from(this._updates().keys());
|
|
475
|
-
let count = 0;
|
|
476
|
-
for (const correlationId of updates) {
|
|
477
|
-
if (this.rollback(correlationId, error)) {
|
|
478
|
-
count++;
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
return count;
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* Get update by correlation ID
|
|
485
|
-
*/
|
|
486
|
-
get(correlationId) {
|
|
487
|
-
return this._updates().get(correlationId);
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Check if an update is pending
|
|
491
|
-
*/
|
|
492
|
-
isPending(correlationId) {
|
|
493
|
-
return this._updates().has(correlationId);
|
|
494
|
-
}
|
|
495
|
-
/**
|
|
496
|
-
* Clear all updates without rollback (use with caution)
|
|
497
|
-
*/
|
|
498
|
-
clear() {
|
|
499
|
-
// Clear all timeouts
|
|
500
|
-
for (const timeout of this.timeouts.values()) {
|
|
501
|
-
clearTimeout(timeout);
|
|
502
|
-
}
|
|
503
|
-
this.timeouts.clear();
|
|
504
|
-
// Clear updates
|
|
505
|
-
this._updates.set(new Map());
|
|
506
|
-
}
|
|
507
|
-
/**
|
|
508
|
-
* Dispose the manager
|
|
509
|
-
*/
|
|
510
|
-
dispose() {
|
|
511
|
-
this.clear();
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* Create a simple event handler
|
|
517
|
-
*
|
|
518
|
-
* @example
|
|
519
|
-
* ```typescript
|
|
520
|
-
* const handler = createEventHandler<TradeProposalCreated>(event => {
|
|
521
|
-
* store.$.trades.entities.upsertOne(event.data);
|
|
522
|
-
* });
|
|
523
|
-
* ```
|
|
524
|
-
*/
|
|
525
|
-
function createEventHandler(handler) {
|
|
526
|
-
return handler;
|
|
527
|
-
}
|
|
528
|
-
/**
|
|
529
|
-
* Create a typed event handler with metadata
|
|
530
|
-
*
|
|
531
|
-
* @example
|
|
532
|
-
* ```typescript
|
|
533
|
-
* const handler = createTypedHandler('TradeProposalCreated', {
|
|
534
|
-
* handle: (event) => {
|
|
535
|
-
* store.$.trades.entities.upsertOne(event.data);
|
|
536
|
-
* },
|
|
537
|
-
* priority: 1,
|
|
538
|
-
* });
|
|
539
|
-
* ```
|
|
540
|
-
*/
|
|
541
|
-
function createTypedHandler(eventType, options) {
|
|
542
|
-
return {
|
|
543
|
-
eventType,
|
|
544
|
-
handle: options.handle,
|
|
545
|
-
priority: options.priority ?? 10
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
exports.OptimisticUpdateManager = OptimisticUpdateManager;
|
|
550
|
-
exports.createEventHandler = createEventHandler;
|
|
551
|
-
exports.createTypedHandler = createTypedHandler;
|