@fjell/registry 4.4.52 → 4.4.54

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 (51) hide show
  1. package/package.json +4 -4
  2. package/API.md +0 -62
  3. package/GETTING_STARTED.md +0 -69
  4. package/build.js +0 -38
  5. package/docs/README.md +0 -74
  6. package/docs/TIMING_NODE_OPTIMIZATION.md +0 -207
  7. package/docs/TIMING_README.md +0 -170
  8. package/docs/docs.config.ts +0 -114
  9. package/docs/index.html +0 -26
  10. package/docs/package-lock.json +0 -5129
  11. package/docs/package.json +0 -34
  12. package/docs/public/404.html +0 -53
  13. package/docs/public/GETTING_STARTED.md +0 -69
  14. package/docs/public/README.md +0 -623
  15. package/docs/public/TIMING_NODE_OPTIMIZATION.md +0 -207
  16. package/docs/public/api.md +0 -62
  17. package/docs/public/client-api-overview.md +0 -137
  18. package/docs/public/configuration.md +0 -759
  19. package/docs/public/error-handling/README.md +0 -356
  20. package/docs/public/error-handling/network-errors.md +0 -485
  21. package/docs/public/examples/coordinates-example.ts +0 -253
  22. package/docs/public/examples/multi-level-keys.ts +0 -374
  23. package/docs/public/examples/registry-hub-coordinates-example.ts +0 -370
  24. package/docs/public/examples/registry-hub-types.ts +0 -437
  25. package/docs/public/examples/simple-example.ts +0 -250
  26. package/docs/public/examples-README.md +0 -241
  27. package/docs/public/fjell-icon.svg +0 -1
  28. package/docs/public/icon.png +0 -0
  29. package/docs/public/icon2.png +0 -0
  30. package/docs/public/memory-overhead.svg +0 -120
  31. package/docs/public/memory.md +0 -430
  32. package/docs/public/operations/README.md +0 -121
  33. package/docs/public/operations/all.md +0 -325
  34. package/docs/public/operations/create.md +0 -415
  35. package/docs/public/operations/get.md +0 -389
  36. package/docs/public/package.json +0 -63
  37. package/docs/public/pano.png +0 -0
  38. package/docs/public/pano2.png +0 -0
  39. package/docs/public/timing-range.svg +0 -176
  40. package/docs/public/timing.md +0 -483
  41. package/docs/timing-range.svg +0 -174
  42. package/docs/vitest.config.ts +0 -14
  43. package/examples/README.md +0 -241
  44. package/examples/coordinates-example.ts +0 -253
  45. package/examples/multi-level-keys.ts +0 -382
  46. package/examples/registry-hub-coordinates-example.ts +0 -370
  47. package/examples/registry-hub-types.ts +0 -437
  48. package/examples/registry-statistics-example.ts +0 -264
  49. package/examples/simple-example.ts +0 -250
  50. package/tsconfig.docs.json +0 -28
  51. package/vitest.config.ts +0 -22
@@ -1,485 +0,0 @@
1
- # Network Errors and Timeouts
2
-
3
- Handle connection failures, DNS issues, and request timeouts with automatic retry logic.
4
-
5
- ## Overview
6
-
7
- Network errors are among the most common issues in distributed systems. The Fjell Client API automatically handles these transient failures with intelligent retry logic and exponential backoff.
8
-
9
- ## Error Types
10
-
11
- ### NetworkError
12
- - **Code**: `NETWORK_ERROR`
13
- - **Retryable**: ✅ Yes
14
- - **Common Causes**: Connection refused, DNS resolution failure, network unreachable
15
- - **HTTP Equivalents**: Connection timeouts, DNS errors, network unavailable
16
-
17
- ### TimeoutError
18
- - **Code**: `TIMEOUT_ERROR`
19
- - **Retryable**: ✅ Yes
20
- - **Common Causes**: Request takes longer than configured timeout
21
- - **HTTP Equivalents**: Request timeout, gateway timeout
22
-
23
- ## Automatic Retry Behavior
24
-
25
- Network errors are automatically retried with exponential backoff:
26
-
27
- ```typescript
28
- // This will automatically retry on network failures
29
- try {
30
- const user = await userApi.get(userKey);
31
- console.log('Success (possibly after retries):', user.name);
32
- } catch (error) {
33
- // Only reaches here after all retry attempts failed
34
- console.error('Network completely unavailable:', error.message);
35
- }
36
- ```
37
-
38
- ### Default Retry Configuration
39
-
40
- ```typescript
41
- const defaultRetryConfig = {
42
- maxRetries: 3, // Maximum retry attempts
43
- initialDelayMs: 1000, // Initial delay: 1 second
44
- maxDelayMs: 30000, // Maximum delay: 30 seconds
45
- backoffMultiplier: 2, // Double delay each time
46
- enableJitter: true // Add randomness to prevent thundering herd
47
- };
48
- ```
49
-
50
- ### Retry Sequence Example
51
-
52
- For a network error with default configuration:
53
-
54
- 1. **Initial attempt**: Fails with `ECONNREFUSED`
55
- 2. **Retry 1**: Wait ~1000ms (with jitter: 500-1000ms), retry
56
- 3. **Retry 2**: Wait ~2000ms (with jitter: 1000-2000ms), retry
57
- 4. **Retry 3**: Wait ~4000ms (with jitter: 2000-4000ms), retry
58
- 5. **Final failure**: If still failing, throw error
59
-
60
- ## Configuration
61
-
62
- ### Basic Network Resilience
63
-
64
- ```typescript
65
- const config = {
66
- baseUrl: 'https://api.example.com',
67
- retryConfig: {
68
- maxRetries: 5, // More retries for critical operations
69
- initialDelayMs: 500, // Faster initial retry
70
- maxDelayMs: 60000, // Allow longer delays
71
- enableJitter: true // Prevent retry storms
72
- }
73
- };
74
-
75
- const userApi = createPItemApi<User, 'user'>('user', ['users'], config);
76
- ```
77
-
78
- ### Custom Network Error Handling
79
-
80
- ```typescript
81
- const networkResilientConfig = {
82
- baseUrl: 'https://api.example.com',
83
- retryConfig: {
84
- maxRetries: 3,
85
- initialDelayMs: 1000,
86
- maxDelayMs: 30000,
87
- backoffMultiplier: 2,
88
- enableJitter: true,
89
-
90
- // Custom retry logic for network errors
91
- shouldRetry: (error, attemptNumber) => {
92
- // Always retry network and timeout errors
93
- if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT_ERROR') {
94
- return attemptNumber < 5; // Allow up to 5 retries for network issues
95
- }
96
-
97
- // Default retry logic for other errors
98
- return error.isRetryable && attemptNumber < 3;
99
- },
100
-
101
- // Monitor retry attempts
102
- onRetry: (error, attemptNumber, delay) => {
103
- console.log(`Network retry ${attemptNumber + 1}: ${error.message} (delay: ${delay}ms)`);
104
-
105
- // Send metrics to monitoring service
106
- metrics.increment('api.network.retry', {
107
- error_code: error.code,
108
- attempt: attemptNumber + 1
109
- });
110
- }
111
- }
112
- };
113
- ```
114
-
115
- ## Error Handling Patterns
116
-
117
- ### Basic Network Error Handling
118
-
119
- ```typescript
120
- async function fetchUserSafely(userKey: PriKey<'user'>): Promise<User | null> {
121
- try {
122
- return await userApi.get(userKey);
123
- } catch (error) {
124
- if (error.code === 'NETWORK_ERROR') {
125
- console.log('Network issue resolved automatically or exhausted retries');
126
-
127
- // Could show user-friendly message
128
- showNotification('Network connection issues. Please try again later.');
129
- return null;
130
- }
131
-
132
- if (error.code === 'TIMEOUT_ERROR') {
133
- console.log('Request timed out after retries');
134
-
135
- // Could suggest checking connection
136
- showNotification('Request timed out. Please check your connection.');
137
- return null;
138
- }
139
-
140
- // Re-throw unexpected errors
141
- throw error;
142
- }
143
- }
144
- ```
145
-
146
- ### Graceful Degradation
147
-
148
- ```typescript
149
- async function getUserWithFallback(userKey: PriKey<'user'>): Promise<User | null> {
150
- try {
151
- // Try primary API with retry
152
- return await userApi.get(userKey);
153
-
154
- } catch (error) {
155
- if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT_ERROR') {
156
- console.log('Primary API unavailable, trying alternatives...');
157
-
158
- // Try cached data
159
- try {
160
- const cachedUser = await cache.get(`user:${userKey.pk}`);
161
- if (cachedUser) {
162
- console.log('Serving from cache');
163
- return cachedUser;
164
- }
165
- } catch (cacheError) {
166
- console.warn('Cache also unavailable:', cacheError.message);
167
- }
168
-
169
- // Try backup API
170
- try {
171
- const backupUser = await backupUserApi.get(userKey);
172
- console.log('Served from backup API');
173
- return backupUser;
174
- } catch (backupError) {
175
- console.warn('Backup API also failed:', backupError.message);
176
- }
177
-
178
- // All sources failed
179
- console.error('All data sources unavailable');
180
- return null;
181
- }
182
-
183
- // Non-network errors
184
- throw error;
185
- }
186
- }
187
- ```
188
-
189
- ### Circuit Breaker Pattern
190
-
191
- ```typescript
192
- class CircuitBreaker {
193
- private failures = 0;
194
- private lastFailureTime = 0;
195
- private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
196
-
197
- constructor(
198
- private failureThreshold = 5,
199
- private recoveryTimeMs = 30000
200
- ) {}
201
-
202
- async execute<T>(operation: () => Promise<T>): Promise<T> {
203
- if (this.state === 'OPEN') {
204
- if (Date.now() - this.lastFailureTime > this.recoveryTimeMs) {
205
- this.state = 'HALF_OPEN';
206
- } else {
207
- throw new Error('Circuit breaker is OPEN');
208
- }
209
- }
210
-
211
- try {
212
- const result = await operation();
213
-
214
- // Success - reset circuit breaker
215
- if (this.state === 'HALF_OPEN') {
216
- this.state = 'CLOSED';
217
- this.failures = 0;
218
- }
219
-
220
- return result;
221
-
222
- } catch (error) {
223
- this.failures++;
224
- this.lastFailureTime = Date.now();
225
-
226
- if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT_ERROR') {
227
- if (this.failures >= this.failureThreshold) {
228
- this.state = 'OPEN';
229
- console.log('Circuit breaker opened due to network failures');
230
- }
231
- }
232
-
233
- throw error;
234
- }
235
- }
236
- }
237
-
238
- // Usage
239
- const circuitBreaker = new CircuitBreaker(3, 10000); // 3 failures, 10s recovery
240
-
241
- async function resilientApiCall(userKey: PriKey<'user'>): Promise<User | null> {
242
- try {
243
- return await circuitBreaker.execute(() => userApi.get(userKey));
244
- } catch (error) {
245
- if (error.message === 'Circuit breaker is OPEN') {
246
- console.log('Circuit breaker preventing calls, using fallback');
247
- return await getFallbackUser(userKey);
248
- }
249
- throw error;
250
- }
251
- }
252
- ```
253
-
254
- ## Monitoring and Observability
255
-
256
- ### Network Error Metrics
257
-
258
- ```typescript
259
- const networkMetrics = {
260
- // Track network error rates
261
- recordNetworkError: (error: NetworkError, context: any) => {
262
- metrics.increment('api.network.errors', {
263
- error_type: error.code,
264
- endpoint: context.url,
265
- method: context.method
266
- });
267
- },
268
-
269
- // Track retry success rates
270
- recordRetrySuccess: (attemptNumber: number, context: any) => {
271
- metrics.increment('api.network.retry_success', {
272
- attempt_number: attemptNumber,
273
- endpoint: context.url
274
- });
275
-
276
- metrics.timing('api.network.recovery_time', context.duration, {
277
- attempts: attemptNumber
278
- });
279
- },
280
-
281
- // Track connection health
282
- recordConnectionHealth: (isHealthy: boolean) => {
283
- metrics.gauge('api.network.health', isHealthy ? 1 : 0);
284
- }
285
- };
286
-
287
- // Custom error handler with metrics
288
- const config = {
289
- errorHandler: (error, context) => {
290
- if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT_ERROR') {
291
- networkMetrics.recordNetworkError(error, context);
292
-
293
- // Alert on high network error rates
294
- if (context.totalAttempts >= 3) {
295
- alerting.sendAlert({
296
- severity: 'warning',
297
- message: `High network error rate: ${error.message}`,
298
- context
299
- });
300
- }
301
- }
302
- }
303
- };
304
- ```
305
-
306
- ### Health Checks
307
-
308
- ```typescript
309
- async function performHealthCheck(): Promise<boolean> {
310
- try {
311
- // Simple health check endpoint
312
- await healthApi.get({ keyType: 'health', pk: 'status' });
313
- networkMetrics.recordConnectionHealth(true);
314
- return true;
315
- } catch (error) {
316
- if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT_ERROR') {
317
- networkMetrics.recordConnectionHealth(false);
318
- console.warn('Health check failed:', error.message);
319
- return false;
320
- }
321
-
322
- // API is reachable but returned an error
323
- networkMetrics.recordConnectionHealth(true);
324
- return true;
325
- }
326
- }
327
-
328
- // Run health checks periodically
329
- setInterval(performHealthCheck, 30000); // Every 30 seconds
330
- ```
331
-
332
- ## Testing Network Errors
333
-
334
- ### Unit Testing
335
-
336
- ```typescript
337
- describe('Network Error Handling', () => {
338
- it('should retry on network errors', async () => {
339
- const mockApi = createMockApi();
340
-
341
- // First call fails, second succeeds
342
- mockApi.httpGet
343
- .mockRejectedValueOnce(new NetworkError('ECONNREFUSED'))
344
- .mockResolvedValueOnce({ id: 'user-123' });
345
-
346
- const user = await userApi.get(userKey);
347
-
348
- expect(user.id).toBe('user-123');
349
- expect(mockApi.httpGet).toHaveBeenCalledTimes(2);
350
- });
351
-
352
- it('should respect max retries', async () => {
353
- const mockApi = createMockApi();
354
- mockApi.httpGet.mockRejectedValue(new NetworkError('ECONNREFUSED'));
355
-
356
- const config = { retryConfig: { maxRetries: 2 } };
357
- const api = createPItemApi<User, 'user'>('user', ['users'], config);
358
-
359
- await expect(api.get(userKey)).rejects.toThrow('ECONNREFUSED');
360
- expect(mockApi.httpGet).toHaveBeenCalledTimes(3); // 1 initial + 2 retries
361
- });
362
-
363
- it('should apply exponential backoff', async () => {
364
- const delays: number[] = [];
365
- const mockSetTimeout = jest.spyOn(global, 'setTimeout').mockImplementation((fn, delay) => {
366
- delays.push(delay);
367
- return setTimeout(fn, 0); // Execute immediately for testing
368
- });
369
-
370
- const mockApi = createMockApi();
371
- mockApi.httpGet.mockRejectedValue(new NetworkError('ECONNREFUSED'));
372
-
373
- try {
374
- await userApi.get(userKey);
375
- } catch (error) {
376
- // Expected to fail after retries
377
- }
378
-
379
- expect(delays).toHaveLength(3);
380
- expect(delays[1]).toBeGreaterThan(delays[0]); // Exponential increase
381
- expect(delays[2]).toBeGreaterThan(delays[1]);
382
-
383
- mockSetTimeout.mockRestore();
384
- });
385
- });
386
- ```
387
-
388
- ### Integration Testing
389
-
390
- ```typescript
391
- describe('Network Resilience Integration', () => {
392
- it('should handle intermittent network issues', async () => {
393
- let callCount = 0;
394
-
395
- server.use(
396
- rest.get('/api/users/:id', (req, res, ctx) => {
397
- callCount++;
398
-
399
- // Fail first two calls, succeed on third
400
- if (callCount <= 2) {
401
- return res.networkError('Connection failed');
402
- }
403
-
404
- return res(ctx.json({ id: 'user-123' }));
405
- })
406
- );
407
-
408
- const user = await userApi.get(userKey);
409
- expect(user.id).toBe('user-123');
410
- expect(callCount).toBe(3);
411
- });
412
- });
413
- ```
414
-
415
- ## Best Practices
416
-
417
- ### 1. **Configure Appropriate Timeouts**
418
-
419
- ```typescript
420
- const config = {
421
- baseUrl: 'https://api.example.com',
422
- timeout: 10000, // 10 second timeout
423
- retryConfig: {
424
- maxRetries: 3,
425
- initialDelayMs: 1000,
426
- maxDelayMs: 15000
427
- }
428
- };
429
- ```
430
-
431
- ### 2. **Implement Progressive Degradation**
432
-
433
- ```typescript
434
- async function getDataWithDegradation(key: string) {
435
- try {
436
- // Try full data
437
- return await api.getFullData(key);
438
- } catch (error) {
439
- if (error.code === 'NETWORK_ERROR') {
440
- try {
441
- // Try summary data (smaller payload)
442
- return await api.getSummaryData(key);
443
- } catch (summaryError) {
444
- // Use cached minimal data
445
- return await cache.getMinimalData(key);
446
- }
447
- }
448
- throw error;
449
- }
450
- }
451
- ```
452
-
453
- ### 3. **User Experience Considerations**
454
-
455
- ```typescript
456
- async function fetchDataWithUI(key: string) {
457
- const loadingIndicator = showLoadingSpinner();
458
-
459
- try {
460
- const data = await api.getData(key);
461
- hideLoadingSpinner(loadingIndicator);
462
- return data;
463
-
464
- } catch (error) {
465
- hideLoadingSpinner(loadingIndicator);
466
-
467
- if (error.code === 'NETWORK_ERROR') {
468
- showNotification('Connection issue. Retrying automatically...', 'info');
469
-
470
- // Give user option to retry manually
471
- showRetryButton(() => fetchDataWithUI(key));
472
- } else if (error.code === 'TIMEOUT_ERROR') {
473
- showNotification('Request timed out. Please try again.', 'warning');
474
- }
475
-
476
- throw error;
477
- }
478
- }
479
- ```
480
-
481
- ## Related Documentation
482
-
483
- - [Error Handling Overview](./README.md) - Complete error handling guide
484
- - [Server Errors](./server-errors.md) - Handle 5xx server errors
485
- - [Configuration](../configuration.md) - Configure retry behavior