@shuji-bonji/rxjs-mcp 0.1.1 → 0.1.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/dist/data/cleanup-examples.d.ts +9 -0
- package/dist/data/cleanup-examples.d.ts.map +1 -0
- package/dist/data/cleanup-examples.js +87 -0
- package/dist/data/cleanup-examples.js.map +1 -0
- package/dist/data/creation-functions.d.ts +7 -0
- package/dist/data/creation-functions.d.ts.map +1 -0
- package/dist/data/creation-functions.js +135 -0
- package/dist/data/creation-functions.js.map +1 -0
- package/dist/data/operators.d.ts +7 -0
- package/dist/data/operators.d.ts.map +1 -0
- package/dist/data/operators.js +500 -0
- package/dist/data/operators.js.map +1 -0
- package/dist/data/patterns.d.ts +10 -0
- package/dist/data/patterns.d.ts.map +1 -0
- package/dist/data/patterns.js +482 -0
- package/dist/data/patterns.js.map +1 -0
- package/dist/data/rxjs-context.d.ts +169 -0
- package/dist/data/rxjs-context.d.ts.map +1 -0
- package/dist/data/rxjs-context.js +209 -0
- package/dist/data/rxjs-context.js.map +1 -0
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/analyze-operators.d.ts.map +1 -1
- package/dist/tools/analyze-operators.js +2 -121
- package/dist/tools/analyze-operators.js.map +1 -1
- package/dist/tools/execute-stream-worker.js +7 -149
- package/dist/tools/execute-stream-worker.js.map +1 -1
- package/dist/tools/execute-stream.d.ts.map +1 -1
- package/dist/tools/execute-stream.js +33 -67
- package/dist/tools/execute-stream.js.map +1 -1
- package/dist/tools/memory-leak.d.ts.map +1 -1
- package/dist/tools/memory-leak.js +2 -82
- package/dist/tools/memory-leak.js.map +1 -1
- package/dist/tools/suggest-pattern.d.ts.map +1 -1
- package/dist/tools/suggest-pattern.js +1 -477
- package/dist/tools/suggest-pattern.js.map +1 -1
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RxJS Pattern Templates Database
|
|
3
|
+
*/
|
|
4
|
+
export const patterns = {
|
|
5
|
+
'http-retry': {
|
|
6
|
+
name: 'HTTP Request with Retry',
|
|
7
|
+
description: 'Resilient HTTP requests with exponential backoff retry strategy',
|
|
8
|
+
useCase: 'Making API calls that may fail due to network issues or server errors',
|
|
9
|
+
operators: ['retry', 'retryWhen', 'delay', 'catchError', 'timer'],
|
|
10
|
+
code: `// HTTP request with exponential backoff retry
|
|
11
|
+
import { throwError, timer, of } from 'rxjs';
|
|
12
|
+
import { ajax } from 'rxjs/ajax';
|
|
13
|
+
import { retryWhen, mergeMap, catchError, finalize } from 'rxjs/operators';
|
|
14
|
+
|
|
15
|
+
const apiCall$ = ajax.getJSON('/api/data').pipe(
|
|
16
|
+
retryWhen(errors =>
|
|
17
|
+
errors.pipe(
|
|
18
|
+
mergeMap((error, index) => {
|
|
19
|
+
const retryAttempt = index + 1;
|
|
20
|
+
if (retryAttempt > 3) {
|
|
21
|
+
return throwError(() => error);
|
|
22
|
+
}
|
|
23
|
+
console.log(\`Retry attempt \${retryAttempt} after \${retryAttempt * 1000}ms\`);
|
|
24
|
+
return timer(retryAttempt * 1000); // Exponential backoff
|
|
25
|
+
})
|
|
26
|
+
)
|
|
27
|
+
),
|
|
28
|
+
catchError(error => {
|
|
29
|
+
console.error('Failed after 3 retries:', error);
|
|
30
|
+
return of({ error: true, message: 'Service unavailable' });
|
|
31
|
+
}),
|
|
32
|
+
finalize(() => console.log('Request completed'))
|
|
33
|
+
);`,
|
|
34
|
+
considerations: [
|
|
35
|
+
'Set a maximum retry count to prevent infinite retries',
|
|
36
|
+
'Use exponential backoff to avoid overwhelming the server',
|
|
37
|
+
'Provide fallback data on final failure',
|
|
38
|
+
'Log retry attempts for debugging',
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
'search-typeahead': {
|
|
42
|
+
name: 'Search Typeahead with Debounce',
|
|
43
|
+
description: 'Efficient search implementation with debouncing and cancellation',
|
|
44
|
+
useCase: 'Real-time search suggestions as user types, minimizing API calls',
|
|
45
|
+
operators: ['debounceTime', 'distinctUntilChanged', 'switchMap', 'catchError', 'filter'],
|
|
46
|
+
code: `// Search typeahead implementation
|
|
47
|
+
import { fromEvent, of, EMPTY } from 'rxjs';
|
|
48
|
+
import { ajax } from 'rxjs/ajax';
|
|
49
|
+
import { debounceTime, distinctUntilChanged, switchMap, catchError, filter, map } from 'rxjs/operators';
|
|
50
|
+
|
|
51
|
+
const searchBox = document.getElementById('search');
|
|
52
|
+
const search$ = fromEvent(searchBox, 'input').pipe(
|
|
53
|
+
map(event => (event.target as HTMLInputElement).value),
|
|
54
|
+
filter(query => query.length > 2), // Min 3 characters
|
|
55
|
+
debounceTime(300), // Wait for pause in typing
|
|
56
|
+
distinctUntilChanged(), // Ignore if same as previous
|
|
57
|
+
switchMap(query =>
|
|
58
|
+
ajax.getJSON(\`/api/search?q=\${query}\`).pipe(
|
|
59
|
+
catchError(error => {
|
|
60
|
+
console.error('Search failed:', error);
|
|
61
|
+
return of([]); // Return empty results on error
|
|
62
|
+
})
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
search$.subscribe(results => {
|
|
68
|
+
displaySearchResults(results);
|
|
69
|
+
});`,
|
|
70
|
+
considerations: [
|
|
71
|
+
'Use debounceTime to wait for typing pause',
|
|
72
|
+
'distinctUntilChanged prevents duplicate searches',
|
|
73
|
+
'switchMap cancels previous requests automatically',
|
|
74
|
+
'Handle errors gracefully with empty results',
|
|
75
|
+
'Set minimum query length to reduce noise',
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
'polling': {
|
|
79
|
+
name: 'Smart Polling with Backoff',
|
|
80
|
+
description: 'Periodic data fetching with error handling and dynamic intervals',
|
|
81
|
+
useCase: 'Regularly checking for updates while being respectful of server resources',
|
|
82
|
+
operators: ['interval', 'switchMap', 'retry', 'catchError', 'takeWhile', 'expand'],
|
|
83
|
+
code: `// Smart polling with exponential backoff on errors
|
|
84
|
+
import { interval, timer, throwError, EMPTY } from 'rxjs';
|
|
85
|
+
import { ajax } from 'rxjs/ajax';
|
|
86
|
+
import { switchMap, retry, catchError, takeWhile, expand, tap } from 'rxjs/operators';
|
|
87
|
+
|
|
88
|
+
let errorCount = 0;
|
|
89
|
+
const maxErrors = 3;
|
|
90
|
+
|
|
91
|
+
const polling$ = timer(0, 5000).pipe( // Start immediately, then every 5s
|
|
92
|
+
switchMap(() =>
|
|
93
|
+
ajax.getJSON('/api/status').pipe(
|
|
94
|
+
tap(() => errorCount = 0), // Reset on success
|
|
95
|
+
catchError(error => {
|
|
96
|
+
errorCount++;
|
|
97
|
+
if (errorCount >= maxErrors) {
|
|
98
|
+
console.error('Max errors reached, stopping polling');
|
|
99
|
+
return throwError(() => error);
|
|
100
|
+
}
|
|
101
|
+
console.log(\`Error \${errorCount}, backing off...\`);
|
|
102
|
+
return EMPTY; // Skip this emission
|
|
103
|
+
})
|
|
104
|
+
)
|
|
105
|
+
),
|
|
106
|
+
takeWhile(() => errorCount < maxErrors)
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Alternative: Dynamic polling interval
|
|
110
|
+
const dynamicPolling$ = ajax.getJSON('/api/data').pipe(
|
|
111
|
+
expand(data =>
|
|
112
|
+
timer(data.nextPollDelay || 5000).pipe(
|
|
113
|
+
switchMap(() => ajax.getJSON('/api/data'))
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
);`,
|
|
117
|
+
considerations: [
|
|
118
|
+
'Implement backoff strategy for errors',
|
|
119
|
+
'Allow server to control polling rate',
|
|
120
|
+
'Stop polling after consecutive failures',
|
|
121
|
+
'Consider WebSockets for real-time requirements',
|
|
122
|
+
'Add jitter to prevent thundering herd',
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
'websocket-reconnect': {
|
|
126
|
+
name: 'WebSocket with Auto-Reconnect',
|
|
127
|
+
description: 'Resilient WebSocket connection with automatic reconnection',
|
|
128
|
+
useCase: 'Maintaining persistent real-time connections with fallback',
|
|
129
|
+
operators: ['webSocket', 'retryWhen', 'delay', 'tap', 'catchError'],
|
|
130
|
+
code: `// WebSocket with automatic reconnection
|
|
131
|
+
import { webSocket } from 'rxjs/webSocket';
|
|
132
|
+
import { retry, retryWhen, delay, tap, catchError } from 'rxjs/operators';
|
|
133
|
+
import { EMPTY } from 'rxjs';
|
|
134
|
+
|
|
135
|
+
const createWebSocketSubject = () =>
|
|
136
|
+
webSocket({
|
|
137
|
+
url: 'ws://localhost:8080',
|
|
138
|
+
openObserver: {
|
|
139
|
+
next: () => console.log('WebSocket connected')
|
|
140
|
+
},
|
|
141
|
+
closeObserver: {
|
|
142
|
+
next: () => console.log('WebSocket disconnected')
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
let socket$ = createWebSocketSubject();
|
|
147
|
+
|
|
148
|
+
const resilientSocket$ = socket$.pipe(
|
|
149
|
+
retryWhen(errors =>
|
|
150
|
+
errors.pipe(
|
|
151
|
+
tap(err => console.log('WebSocket error, reconnecting...', err)),
|
|
152
|
+
delay(1000) // Wait before reconnecting
|
|
153
|
+
)
|
|
154
|
+
),
|
|
155
|
+
catchError(err => {
|
|
156
|
+
console.error('WebSocket failed permanently:', err);
|
|
157
|
+
return EMPTY;
|
|
158
|
+
})
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// Send messages
|
|
162
|
+
const sendMessage = (message: any) => {
|
|
163
|
+
socket$.next(message);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Subscribe to messages
|
|
167
|
+
resilientSocket$.subscribe({
|
|
168
|
+
next: msg => console.log('Received:', msg),
|
|
169
|
+
error: err => console.error('Fatal error:', err),
|
|
170
|
+
complete: () => console.log('Connection closed')
|
|
171
|
+
});`,
|
|
172
|
+
considerations: [
|
|
173
|
+
'Implement exponential backoff for reconnection',
|
|
174
|
+
'Queue messages during disconnection',
|
|
175
|
+
'Handle connection state in UI',
|
|
176
|
+
'Consider fallback to HTTP polling',
|
|
177
|
+
'Implement heartbeat/ping mechanism',
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
'form-validation': {
|
|
181
|
+
name: 'Reactive Form Validation',
|
|
182
|
+
description: 'Real-time form validation with debouncing and async validators',
|
|
183
|
+
useCase: 'Validating form inputs with instant feedback and async checks',
|
|
184
|
+
operators: ['combineLatest', 'debounceTime', 'distinctUntilChanged', 'switchMap', 'map'],
|
|
185
|
+
code: `// Reactive form validation
|
|
186
|
+
import { fromEvent, combineLatest, of, timer } from 'rxjs';
|
|
187
|
+
import { debounceTime, distinctUntilChanged, switchMap, map, startWith } from 'rxjs/operators';
|
|
188
|
+
import { ajax } from 'rxjs/ajax';
|
|
189
|
+
|
|
190
|
+
const emailInput = document.getElementById('email') as HTMLInputElement;
|
|
191
|
+
const passwordInput = document.getElementById('password') as HTMLInputElement;
|
|
192
|
+
|
|
193
|
+
const email$ = fromEvent(emailInput, 'input').pipe(
|
|
194
|
+
map(e => (e.target as HTMLInputElement).value),
|
|
195
|
+
startWith(''),
|
|
196
|
+
debounceTime(300),
|
|
197
|
+
distinctUntilChanged(),
|
|
198
|
+
switchMap(email => {
|
|
199
|
+
if (!email) return of({ valid: false, error: 'Email required' });
|
|
200
|
+
if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) {
|
|
201
|
+
return of({ valid: false, error: 'Invalid email format' });
|
|
202
|
+
}
|
|
203
|
+
// Async validation - check if email exists
|
|
204
|
+
return ajax.getJSON(\`/api/check-email?email=\${email}\`).pipe(
|
|
205
|
+
map(result => ({
|
|
206
|
+
valid: !(result as any).exists,
|
|
207
|
+
error: (result as any).exists ? 'Email already taken' : null
|
|
208
|
+
}))
|
|
209
|
+
);
|
|
210
|
+
})
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const password$ = fromEvent(passwordInput, 'input').pipe(
|
|
214
|
+
map(e => (e.target as HTMLInputElement).value),
|
|
215
|
+
startWith(''),
|
|
216
|
+
map(password => ({
|
|
217
|
+
valid: password.length >= 8,
|
|
218
|
+
error: password.length < 8 ? 'Password must be 8+ characters' : null
|
|
219
|
+
}))
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const formValid$ = combineLatest([email$, password$]).pipe(
|
|
223
|
+
map(([email, password]) => ({
|
|
224
|
+
valid: email.valid && password.valid,
|
|
225
|
+
errors: {
|
|
226
|
+
email: email.error,
|
|
227
|
+
password: password.error
|
|
228
|
+
}
|
|
229
|
+
}))
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
formValid$.subscribe(validation => {
|
|
233
|
+
updateFormUI(validation);
|
|
234
|
+
});`,
|
|
235
|
+
considerations: [
|
|
236
|
+
'Debounce async validators to reduce API calls',
|
|
237
|
+
'Show loading states during validation',
|
|
238
|
+
'Cache validation results when appropriate',
|
|
239
|
+
'Validate on blur for better UX',
|
|
240
|
+
'Consider field dependencies for complex forms',
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
'state-management': {
|
|
244
|
+
name: 'Simple State Management',
|
|
245
|
+
description: 'Centralized state management using RxJS subjects',
|
|
246
|
+
useCase: 'Managing application state without external libraries',
|
|
247
|
+
operators: ['scan', 'shareReplay', 'distinctUntilChanged', 'pluck'],
|
|
248
|
+
code: `// Simple state management with RxJS
|
|
249
|
+
import { BehaviorSubject, Subject, merge } from 'rxjs';
|
|
250
|
+
import { scan, shareReplay, distinctUntilChanged, map } from 'rxjs/operators';
|
|
251
|
+
|
|
252
|
+
interface AppState {
|
|
253
|
+
user: { id: string; name: string } | null;
|
|
254
|
+
items: Array<{ id: string; title: string }>;
|
|
255
|
+
loading: boolean;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Initial state
|
|
259
|
+
const initialState: AppState = {
|
|
260
|
+
user: null,
|
|
261
|
+
items: [],
|
|
262
|
+
loading: false
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// Actions
|
|
266
|
+
interface Action {
|
|
267
|
+
type: string;
|
|
268
|
+
payload?: any;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Action creators
|
|
272
|
+
const actions$ = new Subject<Action>();
|
|
273
|
+
|
|
274
|
+
const setUser = (user: AppState['user']) =>
|
|
275
|
+
actions$.next({ type: 'SET_USER', payload: user });
|
|
276
|
+
|
|
277
|
+
const addItem = (item: { id: string; title: string }) =>
|
|
278
|
+
actions$.next({ type: 'ADD_ITEM', payload: item });
|
|
279
|
+
|
|
280
|
+
const setLoading = (loading: boolean) =>
|
|
281
|
+
actions$.next({ type: 'SET_LOADING', payload: loading });
|
|
282
|
+
|
|
283
|
+
// Reducer
|
|
284
|
+
const reducer = (state: AppState, action: Action): AppState => {
|
|
285
|
+
switch (action.type) {
|
|
286
|
+
case 'SET_USER':
|
|
287
|
+
return { ...state, user: action.payload };
|
|
288
|
+
case 'ADD_ITEM':
|
|
289
|
+
return { ...state, items: [...state.items, action.payload] };
|
|
290
|
+
case 'SET_LOADING':
|
|
291
|
+
return { ...state, loading: action.payload };
|
|
292
|
+
default:
|
|
293
|
+
return state;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// State stream
|
|
298
|
+
const state$ = actions$.pipe(
|
|
299
|
+
scan(reducer, initialState),
|
|
300
|
+
shareReplay(1)
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
// Selectors
|
|
304
|
+
const user$ = state$.pipe(
|
|
305
|
+
map(state => state.user),
|
|
306
|
+
distinctUntilChanged()
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const items$ = state$.pipe(
|
|
310
|
+
map(state => state.items),
|
|
311
|
+
distinctUntilChanged()
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const loading$ = state$.pipe(
|
|
315
|
+
map(state => state.loading),
|
|
316
|
+
distinctUntilChanged()
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
// Subscribe to state changes
|
|
320
|
+
state$.subscribe(state => console.log('State updated:', state));`,
|
|
321
|
+
considerations: [
|
|
322
|
+
'Use BehaviorSubject for synchronous state access',
|
|
323
|
+
'Implement selectors with distinctUntilChanged',
|
|
324
|
+
'Consider immutability for predictable updates',
|
|
325
|
+
'Add middleware for side effects',
|
|
326
|
+
'Use shareReplay for multiple subscribers',
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
'cache-refresh': {
|
|
330
|
+
name: 'Cache with Refresh Strategy',
|
|
331
|
+
description: 'Cached data with manual and automatic refresh capabilities',
|
|
332
|
+
useCase: 'Serving cached data instantly while fetching fresh data in background',
|
|
333
|
+
operators: ['shareReplay', 'merge', 'switchMap', 'startWith'],
|
|
334
|
+
code: `// Cache with refresh strategy
|
|
335
|
+
import { BehaviorSubject, Subject, timer, merge } from 'rxjs';
|
|
336
|
+
import { switchMap, shareReplay, startWith, tap } from 'rxjs/operators';
|
|
337
|
+
import { ajax } from 'rxjs/ajax';
|
|
338
|
+
|
|
339
|
+
class CachedDataService {
|
|
340
|
+
private refreshSubject = new Subject<void>();
|
|
341
|
+
private cache$ = new BehaviorSubject<any>(null);
|
|
342
|
+
|
|
343
|
+
// Automatic refresh every 30 seconds
|
|
344
|
+
private autoRefresh$ = timer(0, 30000);
|
|
345
|
+
|
|
346
|
+
// Combine manual and auto refresh triggers
|
|
347
|
+
private dataSource$ = merge(
|
|
348
|
+
this.autoRefresh$,
|
|
349
|
+
this.refreshSubject
|
|
350
|
+
).pipe(
|
|
351
|
+
switchMap(() => ajax.getJSON('/api/data')),
|
|
352
|
+
tap(data => this.cache$.next(data)),
|
|
353
|
+
shareReplay({
|
|
354
|
+
bufferSize: 1,
|
|
355
|
+
refCount: true
|
|
356
|
+
})
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
// Public API
|
|
360
|
+
getData() {
|
|
361
|
+
const cached = this.cache$.value;
|
|
362
|
+
if (cached) {
|
|
363
|
+
// Return cached data immediately, fetch fresh in background
|
|
364
|
+
return merge(
|
|
365
|
+
of(cached),
|
|
366
|
+
this.dataSource$
|
|
367
|
+
).pipe(
|
|
368
|
+
distinctUntilChanged((a, b) =>
|
|
369
|
+
JSON.stringify(a) === JSON.stringify(b)
|
|
370
|
+
)
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
return this.dataSource$;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
refresh() {
|
|
377
|
+
this.refreshSubject.next();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Cache invalidation
|
|
381
|
+
invalidate() {
|
|
382
|
+
this.cache$.next(null);
|
|
383
|
+
this.refresh();
|
|
384
|
+
}
|
|
385
|
+
}`,
|
|
386
|
+
considerations: [
|
|
387
|
+
'Implement cache expiration policies',
|
|
388
|
+
'Handle stale-while-revalidate pattern',
|
|
389
|
+
'Consider memory limits for cache size',
|
|
390
|
+
'Add cache versioning for updates',
|
|
391
|
+
'Implement offline support with persistence',
|
|
392
|
+
],
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
/**
|
|
396
|
+
* Adapt pattern for specific framework
|
|
397
|
+
*/
|
|
398
|
+
export function adaptPatternForFramework(pattern, framework) {
|
|
399
|
+
const adapted = { ...pattern };
|
|
400
|
+
if (framework === 'angular') {
|
|
401
|
+
adapted.code = `// Angular-specific implementation
|
|
402
|
+
@Injectable({ providedIn: 'root' })
|
|
403
|
+
export class RxJSPatternService implements OnDestroy {
|
|
404
|
+
private destroy$ = new Subject<void>();
|
|
405
|
+
|
|
406
|
+
${pattern.code}
|
|
407
|
+
|
|
408
|
+
ngOnDestroy() {
|
|
409
|
+
this.destroy$.next();
|
|
410
|
+
this.destroy$.complete();
|
|
411
|
+
}
|
|
412
|
+
}`;
|
|
413
|
+
adapted.considerations = [
|
|
414
|
+
...pattern.considerations,
|
|
415
|
+
'Use async pipe in templates for auto-unsubscribe',
|
|
416
|
+
'Inject HttpClient instead of ajax',
|
|
417
|
+
'Consider using NgRx for complex state management',
|
|
418
|
+
];
|
|
419
|
+
}
|
|
420
|
+
else if (framework === 'react') {
|
|
421
|
+
adapted.code = `// React Hook implementation
|
|
422
|
+
import { useEffect, useState, useRef } from 'react';
|
|
423
|
+
import { Subscription } from 'rxjs';
|
|
424
|
+
|
|
425
|
+
export function useRxJSPattern() {
|
|
426
|
+
const [data, setData] = useState(null);
|
|
427
|
+
const subscription = useRef<Subscription>();
|
|
428
|
+
|
|
429
|
+
useEffect(() => {
|
|
430
|
+
${pattern.code.split('\n').map(line => ' ' + line).join('\n')}
|
|
431
|
+
|
|
432
|
+
subscription.current = stream$.subscribe(setData);
|
|
433
|
+
|
|
434
|
+
return () => {
|
|
435
|
+
subscription.current?.unsubscribe();
|
|
436
|
+
};
|
|
437
|
+
}, []);
|
|
438
|
+
|
|
439
|
+
return data;
|
|
440
|
+
}`;
|
|
441
|
+
adapted.considerations = [
|
|
442
|
+
...pattern.considerations,
|
|
443
|
+
'Clean up subscriptions in useEffect return',
|
|
444
|
+
'Consider using a state management library',
|
|
445
|
+
'Be careful with closure stale values',
|
|
446
|
+
];
|
|
447
|
+
}
|
|
448
|
+
else if (framework === 'vue') {
|
|
449
|
+
adapted.code = `// Vue 3 Composition API implementation
|
|
450
|
+
import { ref, onBeforeUnmount } from 'vue';
|
|
451
|
+
import { Subject } from 'rxjs';
|
|
452
|
+
import { takeUntil } from 'rxjs/operators';
|
|
453
|
+
|
|
454
|
+
export function useRxJSPattern() {
|
|
455
|
+
const destroy$ = new Subject();
|
|
456
|
+
const data = ref(null);
|
|
457
|
+
|
|
458
|
+
${pattern.code}
|
|
459
|
+
|
|
460
|
+
stream$.pipe(
|
|
461
|
+
takeUntil(destroy$)
|
|
462
|
+
).subscribe(value => {
|
|
463
|
+
data.value = value;
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
onBeforeUnmount(() => {
|
|
467
|
+
destroy$.next();
|
|
468
|
+
destroy$.complete();
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
return { data };
|
|
472
|
+
}`;
|
|
473
|
+
adapted.considerations = [
|
|
474
|
+
...pattern.considerations,
|
|
475
|
+
'Use Composition API for better TypeScript support',
|
|
476
|
+
'Clean up in onBeforeUnmount',
|
|
477
|
+
'Consider Pinia for state management',
|
|
478
|
+
];
|
|
479
|
+
}
|
|
480
|
+
return adapted;
|
|
481
|
+
}
|
|
482
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/data/patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RxJS execution context for Worker thread
|
|
3
|
+
* Centralizes all RxJS imports and provides a safe execution context
|
|
4
|
+
*/
|
|
5
|
+
import { Observable, Subject, BehaviorSubject, ReplaySubject, AsyncSubject, connectable, of, from, interval, timer, range, generate, concat, merge, combineLatest, zip, forkJoin, race, partition, iif, defer, scheduled, throwError, firstValueFrom, lastValueFrom, map, scan, mergeScan, reduce, pairwise, groupBy, mergeMap, switchMap, concatMap, exhaustMap, expand, buffer, bufferTime, bufferCount, bufferWhen, bufferToggle, windowTime, window as windowOp, windowCount, windowToggle, windowWhen, filter, take, takeLast, takeWhile, skip, skipLast, skipWhile, skipUntil, first, last, elementAt, find, findIndex, debounceTime, throttleTime, auditTime, audit, sampleTime, sample, ignoreElements, distinct, distinctUntilChanged, distinctUntilKeyChanged, concatWith, mergeWith, combineLatestWith, zipWith, raceWith, withLatestFrom, mergeAll, concatAll, switchAll, exhaustAll, combineLatestAll, zipAll, tap, delay, delayWhen, timeout, takeUntil, finalize, repeat, retry, startWith, endWith, toArray, materialize, dematerialize, observeOn, subscribeOn, timestamp, defaultIfEmpty, every, isEmpty, catchError, retryWhen, share, shareReplay, connect, refCount, pluck, mapTo, switchMapTo, mergeMapTo, concatMapTo, count, max, min, single, throwIfEmpty } from 'rxjs';
|
|
6
|
+
/**
|
|
7
|
+
* RxJS symbols available in the execution context
|
|
8
|
+
*/
|
|
9
|
+
export declare const rxjsSymbols: {
|
|
10
|
+
Observable: typeof Observable;
|
|
11
|
+
Subject: typeof Subject;
|
|
12
|
+
BehaviorSubject: typeof BehaviorSubject;
|
|
13
|
+
ReplaySubject: typeof ReplaySubject;
|
|
14
|
+
AsyncSubject: typeof AsyncSubject;
|
|
15
|
+
connectable: typeof connectable;
|
|
16
|
+
of: typeof of;
|
|
17
|
+
from: typeof from;
|
|
18
|
+
interval: typeof interval;
|
|
19
|
+
timer: typeof timer;
|
|
20
|
+
range: typeof range;
|
|
21
|
+
generate: typeof generate;
|
|
22
|
+
concat: typeof concat;
|
|
23
|
+
merge: typeof merge;
|
|
24
|
+
combineLatest: typeof combineLatest;
|
|
25
|
+
zip: typeof zip;
|
|
26
|
+
forkJoin: typeof forkJoin;
|
|
27
|
+
race: typeof race;
|
|
28
|
+
partition: typeof partition;
|
|
29
|
+
iif: typeof iif;
|
|
30
|
+
defer: typeof defer;
|
|
31
|
+
scheduled: typeof scheduled;
|
|
32
|
+
throwError: typeof throwError;
|
|
33
|
+
EMPTY: Observable<never>;
|
|
34
|
+
NEVER: Observable<never>;
|
|
35
|
+
firstValueFrom: typeof firstValueFrom;
|
|
36
|
+
lastValueFrom: typeof lastValueFrom;
|
|
37
|
+
map: typeof map;
|
|
38
|
+
scan: typeof scan;
|
|
39
|
+
mergeScan: typeof mergeScan;
|
|
40
|
+
reduce: typeof reduce;
|
|
41
|
+
pairwise: typeof pairwise;
|
|
42
|
+
groupBy: typeof groupBy;
|
|
43
|
+
mergeMap: typeof mergeMap;
|
|
44
|
+
switchMap: typeof switchMap;
|
|
45
|
+
concatMap: typeof concatMap;
|
|
46
|
+
exhaustMap: typeof exhaustMap;
|
|
47
|
+
expand: typeof expand;
|
|
48
|
+
buffer: typeof buffer;
|
|
49
|
+
bufferTime: typeof bufferTime;
|
|
50
|
+
bufferCount: typeof bufferCount;
|
|
51
|
+
bufferWhen: typeof bufferWhen;
|
|
52
|
+
bufferToggle: typeof bufferToggle;
|
|
53
|
+
windowTime: typeof windowTime;
|
|
54
|
+
window: typeof windowOp;
|
|
55
|
+
windowCount: typeof windowCount;
|
|
56
|
+
windowToggle: typeof windowToggle;
|
|
57
|
+
windowWhen: typeof windowWhen;
|
|
58
|
+
filter: typeof filter;
|
|
59
|
+
take: typeof take;
|
|
60
|
+
takeLast: typeof takeLast;
|
|
61
|
+
takeWhile: typeof takeWhile;
|
|
62
|
+
skip: typeof skip;
|
|
63
|
+
skipLast: typeof skipLast;
|
|
64
|
+
skipWhile: typeof skipWhile;
|
|
65
|
+
skipUntil: typeof skipUntil;
|
|
66
|
+
first: typeof first;
|
|
67
|
+
last: typeof last;
|
|
68
|
+
elementAt: typeof elementAt;
|
|
69
|
+
find: typeof find;
|
|
70
|
+
findIndex: typeof findIndex;
|
|
71
|
+
debounceTime: typeof debounceTime;
|
|
72
|
+
throttleTime: typeof throttleTime;
|
|
73
|
+
auditTime: typeof auditTime;
|
|
74
|
+
audit: typeof audit;
|
|
75
|
+
sampleTime: typeof sampleTime;
|
|
76
|
+
sample: typeof sample;
|
|
77
|
+
ignoreElements: typeof ignoreElements;
|
|
78
|
+
distinct: typeof distinct;
|
|
79
|
+
distinctUntilChanged: typeof distinctUntilChanged;
|
|
80
|
+
distinctUntilKeyChanged: typeof distinctUntilKeyChanged;
|
|
81
|
+
concatWith: typeof concatWith;
|
|
82
|
+
mergeWith: typeof mergeWith;
|
|
83
|
+
combineLatestWith: typeof combineLatestWith;
|
|
84
|
+
zipWith: typeof zipWith;
|
|
85
|
+
raceWith: typeof raceWith;
|
|
86
|
+
withLatestFrom: typeof withLatestFrom;
|
|
87
|
+
mergeAll: typeof mergeAll;
|
|
88
|
+
concatAll: typeof concatAll;
|
|
89
|
+
switchAll: typeof switchAll;
|
|
90
|
+
exhaustAll: typeof exhaustAll;
|
|
91
|
+
combineLatestAll: typeof combineLatestAll;
|
|
92
|
+
zipAll: typeof zipAll;
|
|
93
|
+
tap: typeof tap;
|
|
94
|
+
delay: typeof delay;
|
|
95
|
+
delayWhen: typeof delayWhen;
|
|
96
|
+
timeout: typeof timeout;
|
|
97
|
+
takeUntil: typeof takeUntil;
|
|
98
|
+
finalize: typeof finalize;
|
|
99
|
+
repeat: typeof repeat;
|
|
100
|
+
retry: typeof retry;
|
|
101
|
+
startWith: typeof startWith;
|
|
102
|
+
endWith: typeof endWith;
|
|
103
|
+
toArray: typeof toArray;
|
|
104
|
+
materialize: typeof materialize;
|
|
105
|
+
dematerialize: typeof dematerialize;
|
|
106
|
+
observeOn: typeof observeOn;
|
|
107
|
+
subscribeOn: typeof subscribeOn;
|
|
108
|
+
timestamp: typeof timestamp;
|
|
109
|
+
defaultIfEmpty: typeof defaultIfEmpty;
|
|
110
|
+
every: typeof every;
|
|
111
|
+
isEmpty: typeof isEmpty;
|
|
112
|
+
catchError: typeof catchError;
|
|
113
|
+
retryWhen: typeof retryWhen;
|
|
114
|
+
share: typeof share;
|
|
115
|
+
shareReplay: typeof shareReplay;
|
|
116
|
+
connect: typeof connect;
|
|
117
|
+
refCount: typeof refCount;
|
|
118
|
+
pluck: typeof pluck;
|
|
119
|
+
mapTo: typeof mapTo;
|
|
120
|
+
switchMapTo: typeof switchMapTo;
|
|
121
|
+
mergeMapTo: typeof mergeMapTo;
|
|
122
|
+
concatMapTo: typeof concatMapTo;
|
|
123
|
+
count: typeof count;
|
|
124
|
+
max: typeof max;
|
|
125
|
+
min: typeof min;
|
|
126
|
+
single: typeof single;
|
|
127
|
+
throwIfEmpty: typeof throwIfEmpty;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Safe globals for the execution context
|
|
131
|
+
* These are the only non-RxJS APIs available in user code
|
|
132
|
+
*/
|
|
133
|
+
export declare const safeGlobals: {
|
|
134
|
+
console: {
|
|
135
|
+
log: (..._args: unknown[]) => void;
|
|
136
|
+
error: (..._args: unknown[]) => void;
|
|
137
|
+
warn: (..._args: unknown[]) => void;
|
|
138
|
+
};
|
|
139
|
+
setTimeout: typeof setTimeout;
|
|
140
|
+
clearTimeout: typeof clearTimeout;
|
|
141
|
+
Date: DateConstructor;
|
|
142
|
+
Math: Math;
|
|
143
|
+
JSON: JSON;
|
|
144
|
+
Array: ArrayConstructor;
|
|
145
|
+
Object: ObjectConstructor;
|
|
146
|
+
String: StringConstructor;
|
|
147
|
+
Number: NumberConstructor;
|
|
148
|
+
Boolean: BooleanConstructor;
|
|
149
|
+
Promise: PromiseConstructor;
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Dangerous globals that must be explicitly undefined
|
|
153
|
+
*/
|
|
154
|
+
export declare const blockedGlobals: {
|
|
155
|
+
process: undefined;
|
|
156
|
+
require: undefined;
|
|
157
|
+
module: undefined;
|
|
158
|
+
exports: undefined;
|
|
159
|
+
__dirname: undefined;
|
|
160
|
+
__filename: undefined;
|
|
161
|
+
global: undefined;
|
|
162
|
+
globalThis: undefined;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Creates the complete execution context for user code
|
|
166
|
+
*/
|
|
167
|
+
export declare function createExecutionContext(): Record<string, unknown>;
|
|
168
|
+
export { Observable, EMPTY } from 'rxjs';
|
|
169
|
+
//# sourceMappingURL=rxjs-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rxjs-context.d.ts","sourceRoot":"","sources":["../../src/data/rxjs-context.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAEL,UAAU,EACV,OAAO,EACP,eAAe,EACf,aAAa,EACb,YAAY,EACZ,WAAW,EAEX,EAAE,EACF,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,EACN,KAAK,EACL,aAAa,EACb,GAAG,EACH,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,GAAG,EACH,KAAK,EACL,SAAS,EACT,UAAU,EAGV,cAAc,EACd,aAAa,EAEb,GAAG,EACH,IAAI,EACJ,SAAS,EACT,MAAM,EACN,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,EACV,MAAM,EACN,MAAM,EACN,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,EACZ,UAAU,EACV,MAAM,IAAI,QAAQ,EAClB,WAAW,EACX,YAAY,EACZ,UAAU,EAEV,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,SAAS,EACT,KAAK,EACL,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,KAAK,EACL,UAAU,EACV,MAAM,EACN,cAAc,EACd,QAAQ,EACR,oBAAoB,EACpB,uBAAuB,EAEvB,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,OAAO,EACP,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,MAAM,EAEN,GAAG,EACH,KAAK,EACL,SAAS,EACT,OAAO,EACP,SAAS,EACT,QAAQ,EACR,MAAM,EACN,KAAK,EACL,SAAS,EACT,OAAO,EACP,OAAO,EACP,WAAW,EACX,aAAa,EACb,SAAS,EACT,WAAW,EACX,SAAS,EAET,cAAc,EACd,KAAK,EACL,OAAO,EAEP,UAAU,EACV,SAAS,EAET,KAAK,EACL,WAAW,EACX,OAAO,EACP,QAAQ,EAER,KAAK,EACL,KAAK,EACL,WAAW,EACX,UAAU,EACV,WAAW,EAEX,KAAK,EACL,GAAG,EACH,GAAG,EACH,MAAM,EACN,YAAY,EACb,MAAM,MAAM,CAAC;AAEd;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkIvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW;;wBAEJ,OAAO,EAAE;0BACP,OAAO,EAAE;yBACV,OAAO,EAAE;;;;;;;;;;;;;CAa7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;CAS1B,CAAC;AAEF;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMhE;AAGD,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC"}
|