@uploadista/client-core 0.0.13 → 0.0.14
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/index.d.mts +972 -33
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -4
- package/src/index.ts +2 -0
- package/src/managers/__tests__/event-subscription-manager.test.ts +566 -0
- package/src/managers/__tests__/upload-manager.test.ts +588 -0
- package/src/managers/event-subscription-manager.ts +280 -0
- package/src/managers/flow-manager.ts +614 -0
- package/src/managers/index.ts +28 -0
- package/src/managers/upload-manager.ts +353 -0
- package/src/services/service-container.ts +213 -1
- package/src/testing/index.ts +16 -0
- package/src/testing/mock-service-container.ts +629 -0
- package/src/types/flow-upload-options.ts +29 -4
- package/src/types/index.ts +1 -0
- package/src/types/upload-metrics.ts +130 -0
- package/src/types/upload-options.ts +17 -1
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic event type that the subscription manager can handle
|
|
3
|
+
*/
|
|
4
|
+
export interface GenericEvent {
|
|
5
|
+
type: string;
|
|
6
|
+
data?: unknown;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Event handler callback function
|
|
11
|
+
*/
|
|
12
|
+
export type SubscriptionEventHandler<T = GenericEvent> = (event: T) => void;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Unsubscribe function returned from subscriptions
|
|
16
|
+
*/
|
|
17
|
+
export type UnsubscribeFunction = () => void;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Event source that provides subscription capabilities
|
|
21
|
+
*/
|
|
22
|
+
export interface EventSource<T = GenericEvent> {
|
|
23
|
+
/**
|
|
24
|
+
* Subscribe to events from this source
|
|
25
|
+
* @returns Unsubscribe function to clean up the subscription
|
|
26
|
+
*/
|
|
27
|
+
subscribe(handler: SubscriptionEventHandler<T>): UnsubscribeFunction;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Options for event filtering
|
|
32
|
+
*/
|
|
33
|
+
export interface EventFilterOptions {
|
|
34
|
+
/**
|
|
35
|
+
* Filter events by type (exact match)
|
|
36
|
+
*/
|
|
37
|
+
eventType?: string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Filter events by upload/job ID
|
|
41
|
+
* If provided, only events with matching ID will be passed to the handler
|
|
42
|
+
*/
|
|
43
|
+
uploadId?: string | null;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Custom filter function for advanced filtering
|
|
47
|
+
* Return true to pass the event to the handler
|
|
48
|
+
*/
|
|
49
|
+
customFilter?: (event: GenericEvent) => boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Subscription information for tracking
|
|
54
|
+
*/
|
|
55
|
+
interface SubscriptionInfo<T extends GenericEvent = GenericEvent> {
|
|
56
|
+
unsubscribe: UnsubscribeFunction;
|
|
57
|
+
handler: SubscriptionEventHandler<T>;
|
|
58
|
+
filter?: EventFilterOptions;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Platform-agnostic event subscription manager that handles event filtering,
|
|
63
|
+
* subscription tracking, and automatic cleanup.
|
|
64
|
+
*
|
|
65
|
+
* This manager simplifies event handling by:
|
|
66
|
+
* - Filtering events by type and/or ID
|
|
67
|
+
* - Tracking all active subscriptions
|
|
68
|
+
* - Providing cleanup methods to unsubscribe from all events
|
|
69
|
+
* - Supporting custom filter functions for advanced scenarios
|
|
70
|
+
*
|
|
71
|
+
* @example Basic event subscription
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const manager = new EventSubscriptionManager(eventSource);
|
|
74
|
+
*
|
|
75
|
+
* manager.subscribe(
|
|
76
|
+
* (event) => console.log('Upload progress:', event),
|
|
77
|
+
* { eventType: 'UPLOAD_PROGRESS', uploadId: 'abc123' }
|
|
78
|
+
* );
|
|
79
|
+
*
|
|
80
|
+
* // Clean up all subscriptions when done
|
|
81
|
+
* manager.cleanup();
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @example Multiple filtered subscriptions
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const manager = new EventSubscriptionManager(eventSource);
|
|
87
|
+
*
|
|
88
|
+
* // Subscribe to progress events for specific upload
|
|
89
|
+
* manager.subscribe(
|
|
90
|
+
* onProgress,
|
|
91
|
+
* { eventType: 'UPLOAD_PROGRESS', uploadId: currentUploadId }
|
|
92
|
+
* );
|
|
93
|
+
*
|
|
94
|
+
* // Subscribe to error events for any upload
|
|
95
|
+
* manager.subscribe(
|
|
96
|
+
* onError,
|
|
97
|
+
* { eventType: 'UPLOAD_ERROR' }
|
|
98
|
+
* );
|
|
99
|
+
*
|
|
100
|
+
* // Subscribe to all events with custom filtering
|
|
101
|
+
* manager.subscribe(
|
|
102
|
+
* onEvent,
|
|
103
|
+
* { customFilter: (e) => e.data?.priority === 'high' }
|
|
104
|
+
* );
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export class EventSubscriptionManager<T extends GenericEvent = GenericEvent> {
|
|
108
|
+
private subscriptions: SubscriptionInfo<T>[] = [];
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create a new EventSubscriptionManager
|
|
112
|
+
*
|
|
113
|
+
* @param eventSource - Source to subscribe to for events
|
|
114
|
+
*/
|
|
115
|
+
constructor(private readonly eventSource: EventSource<T>) {}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Subscribe to events with optional filtering
|
|
119
|
+
*
|
|
120
|
+
* @param handler - Callback function to invoke when matching events occur
|
|
121
|
+
* @param filter - Optional filter options to narrow down which events trigger the handler
|
|
122
|
+
* @returns Unsubscribe function to remove this specific subscription
|
|
123
|
+
*
|
|
124
|
+
* @example Subscribe to specific event type
|
|
125
|
+
* ```typescript
|
|
126
|
+
* const unsubscribe = manager.subscribe(
|
|
127
|
+
* (event) => console.log('Progress:', event),
|
|
128
|
+
* { eventType: 'UPLOAD_PROGRESS' }
|
|
129
|
+
* );
|
|
130
|
+
*
|
|
131
|
+
* // Later, unsubscribe
|
|
132
|
+
* unsubscribe();
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
subscribe(
|
|
136
|
+
handler: SubscriptionEventHandler<T>,
|
|
137
|
+
filter?: EventFilterOptions,
|
|
138
|
+
): UnsubscribeFunction {
|
|
139
|
+
// Create a wrapper handler that applies filtering
|
|
140
|
+
const wrappedHandler: SubscriptionEventHandler<T> = (event: T) => {
|
|
141
|
+
if (this.shouldHandleEvent(event, filter)) {
|
|
142
|
+
handler(event);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// Subscribe to the event source with the wrapped handler
|
|
147
|
+
const unsubscribe = this.eventSource.subscribe(wrappedHandler);
|
|
148
|
+
|
|
149
|
+
// Track this subscription
|
|
150
|
+
const subscription: SubscriptionInfo<T> = {
|
|
151
|
+
unsubscribe,
|
|
152
|
+
handler: wrappedHandler,
|
|
153
|
+
filter,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
this.subscriptions.push(subscription);
|
|
157
|
+
|
|
158
|
+
// Return unsubscribe function that also removes from tracking
|
|
159
|
+
return () => {
|
|
160
|
+
const index = this.subscriptions.indexOf(subscription);
|
|
161
|
+
if (index !== -1) {
|
|
162
|
+
this.subscriptions.splice(index, 1);
|
|
163
|
+
}
|
|
164
|
+
unsubscribe();
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Check if an event matches the filter criteria
|
|
170
|
+
*
|
|
171
|
+
* @param event - Event to check
|
|
172
|
+
* @param filter - Filter options to apply
|
|
173
|
+
* @returns True if the event passes all filters
|
|
174
|
+
*/
|
|
175
|
+
private shouldHandleEvent(event: T, filter?: EventFilterOptions): boolean {
|
|
176
|
+
if (!filter) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check event type filter
|
|
181
|
+
if (filter.eventType && event.type !== filter.eventType) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Check upload ID filter
|
|
186
|
+
if (filter.uploadId !== undefined) {
|
|
187
|
+
const eventData = event.data as { id?: string } | undefined;
|
|
188
|
+
const eventId = eventData?.id;
|
|
189
|
+
|
|
190
|
+
// If filter.uploadId is null, only pass events without an ID
|
|
191
|
+
// If filter.uploadId is a string, only pass events with matching ID
|
|
192
|
+
if (filter.uploadId === null) {
|
|
193
|
+
if (eventId !== undefined) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
} else if (eventId !== filter.uploadId) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check custom filter
|
|
202
|
+
if (filter.customFilter) {
|
|
203
|
+
// Cast to GenericEvent for custom filter as it operates on the base interface
|
|
204
|
+
return filter.customFilter(event as unknown as GenericEvent);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get the number of active subscriptions
|
|
212
|
+
*
|
|
213
|
+
* @returns Number of tracked subscriptions
|
|
214
|
+
*/
|
|
215
|
+
getSubscriptionCount(): number {
|
|
216
|
+
return this.subscriptions.length;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Check if there are any active subscriptions
|
|
221
|
+
*
|
|
222
|
+
* @returns True if at least one subscription is active
|
|
223
|
+
*/
|
|
224
|
+
hasSubscriptions(): boolean {
|
|
225
|
+
return this.subscriptions.length > 0;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Unsubscribe from all tracked subscriptions and clear the subscription list
|
|
230
|
+
*
|
|
231
|
+
* This is typically called when disposing of a component or cleaning up resources.
|
|
232
|
+
*
|
|
233
|
+
* @example Cleanup in framework hooks
|
|
234
|
+
* ```typescript
|
|
235
|
+
* // React
|
|
236
|
+
* useEffect(() => {
|
|
237
|
+
* const manager = new EventSubscriptionManager(eventSource);
|
|
238
|
+
* manager.subscribe(handler, filter);
|
|
239
|
+
*
|
|
240
|
+
* return () => manager.cleanup();
|
|
241
|
+
* }, []);
|
|
242
|
+
*
|
|
243
|
+
* // Vue
|
|
244
|
+
* onUnmounted(() => {
|
|
245
|
+
* manager.cleanup();
|
|
246
|
+
* });
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
cleanup(): void {
|
|
250
|
+
for (const subscription of this.subscriptions) {
|
|
251
|
+
subscription.unsubscribe();
|
|
252
|
+
}
|
|
253
|
+
this.subscriptions = [];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Update the upload ID filter for all subscriptions that have an uploadId filter
|
|
258
|
+
*
|
|
259
|
+
* This is useful when the current upload changes and you want to update
|
|
260
|
+
* all subscriptions to listen for the new upload's events.
|
|
261
|
+
*
|
|
262
|
+
* @param newUploadId - New upload ID to filter events by
|
|
263
|
+
*
|
|
264
|
+
* @example Update upload ID when starting new upload
|
|
265
|
+
* ```typescript
|
|
266
|
+
* const manager = new EventSubscriptionManager(eventSource);
|
|
267
|
+
* manager.subscribe(onProgress, { eventType: 'UPLOAD_PROGRESS', uploadId: null });
|
|
268
|
+
*
|
|
269
|
+
* // When upload starts
|
|
270
|
+
* manager.updateUploadIdFilter(uploadId);
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
updateUploadIdFilter(newUploadId: string | null): void {
|
|
274
|
+
for (const subscription of this.subscriptions) {
|
|
275
|
+
if (subscription.filter && subscription.filter.uploadId !== undefined) {
|
|
276
|
+
subscription.filter.uploadId = newUploadId;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|