@markwharton/api-core 1.0.0 → 1.1.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/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # @markwharton/api-core
2
+
3
+ Shared utilities for API client packages.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @markwharton/api-core
9
+ ```
10
+
11
+ ## API Reference
12
+
13
+ ### TTLCache
14
+
15
+ In-memory TTL cache with request coalescing. When multiple concurrent callers request the same expired key, only one factory call is made.
16
+
17
+ ```typescript
18
+ import { TTLCache } from '@markwharton/api-core';
19
+
20
+ const cache = new TTLCache();
21
+
22
+ // Get or populate a cached value (5 minute TTL)
23
+ const data = await cache.get('employees', 5 * 60_000, () => fetchEmployees());
24
+
25
+ // Invalidate by key prefix
26
+ cache.invalidate('timesheet:');
27
+
28
+ // Clear all cached data
29
+ cache.clear();
30
+ ```
31
+
32
+ ### fetchWithRetry
33
+
34
+ Automatic retry on HTTP 429 and 503 with exponential backoff. Respects the `Retry-After` header.
35
+
36
+ ```typescript
37
+ import { fetchWithRetry } from '@markwharton/api-core';
38
+
39
+ const response = await fetchWithRetry(url, init, {
40
+ retry: { maxRetries: 3, initialDelayMs: 1000, maxDelayMs: 10000 },
41
+ onRetry: ({ attempt, delayMs, status }) => {
42
+ console.log(`Retry ${attempt} after ${delayMs}ms (HTTP ${status})`);
43
+ },
44
+ });
45
+ ```
46
+
47
+ ### batchMap
48
+
49
+ Map over items with bounded concurrency.
50
+
51
+ ```typescript
52
+ import { batchMap } from '@markwharton/api-core';
53
+
54
+ const results = await batchMap(items, 5, async (item) => fetchItem(item.id));
55
+ ```
56
+
57
+ ### getErrorMessage
58
+
59
+ Extract a safe error message from any error type.
60
+
61
+ ```typescript
62
+ import { getErrorMessage } from '@markwharton/api-core';
63
+
64
+ try { ... } catch (err) {
65
+ console.error(getErrorMessage(err));
66
+ }
67
+ ```
68
+
69
+ ## License
70
+
71
+ MIT
package/dist/errors.d.ts CHANGED
@@ -3,5 +3,28 @@
3
3
  */
4
4
  /**
5
5
  * Get a safe error message from any error type
6
+ *
7
+ * @param error - The caught error value
8
+ * @param fallback - Custom fallback message (default: 'Unknown error')
6
9
  */
7
- export declare function getErrorMessage(error: unknown): string;
10
+ export declare function getErrorMessage(error: unknown, fallback?: string): string;
11
+ /**
12
+ * Parsed error from a JSON API response
13
+ */
14
+ export interface ParsedError {
15
+ /** Human-readable error message */
16
+ message: string;
17
+ }
18
+ /**
19
+ * Parse a JSON error response body into a human-readable message.
20
+ *
21
+ * Handles common API error formats:
22
+ * - `{ "message": "..." }`
23
+ * - `{ "error": "..." }`
24
+ * - Plain text
25
+ *
26
+ * @param errorText - Raw error response text
27
+ * @param statusCode - HTTP status code (used as fallback message)
28
+ * @returns Parsed error with message
29
+ */
30
+ export declare function parseJsonErrorResponse(errorText: string, statusCode: number): ParsedError;
package/dist/errors.js CHANGED
@@ -5,10 +5,41 @@
5
5
  const DEFAULT_ERROR_MESSAGE = 'Unknown error';
6
6
  /**
7
7
  * Get a safe error message from any error type
8
+ *
9
+ * @param error - The caught error value
10
+ * @param fallback - Custom fallback message (default: 'Unknown error')
8
11
  */
9
- export function getErrorMessage(error) {
12
+ export function getErrorMessage(error, fallback) {
10
13
  if (error instanceof Error) {
11
14
  return error.message;
12
15
  }
13
- return DEFAULT_ERROR_MESSAGE;
16
+ return fallback ?? DEFAULT_ERROR_MESSAGE;
17
+ }
18
+ /**
19
+ * Parse a JSON error response body into a human-readable message.
20
+ *
21
+ * Handles common API error formats:
22
+ * - `{ "message": "..." }`
23
+ * - `{ "error": "..." }`
24
+ * - Plain text
25
+ *
26
+ * @param errorText - Raw error response text
27
+ * @param statusCode - HTTP status code (used as fallback message)
28
+ * @returns Parsed error with message
29
+ */
30
+ export function parseJsonErrorResponse(errorText, statusCode) {
31
+ try {
32
+ const errorJson = JSON.parse(errorText);
33
+ if (errorJson.message) {
34
+ return { message: errorJson.message };
35
+ }
36
+ if (errorJson.error) {
37
+ return { message: errorJson.error };
38
+ }
39
+ return { message: `HTTP ${statusCode}` };
40
+ }
41
+ catch {
42
+ // Not JSON, return as-is or fallback
43
+ return { message: errorText || `HTTP ${statusCode}` };
44
+ }
14
45
  }
package/dist/index.d.ts CHANGED
@@ -3,8 +3,11 @@
3
3
  *
4
4
  * Shared utilities for API client packages.
5
5
  */
6
+ export { ok, okVoid, err } from './result.js';
7
+ export type { Result } from './result.js';
6
8
  export { TTLCache } from './cache.js';
7
9
  export { batchMap } from './utils.js';
8
- export { getErrorMessage } from './errors.js';
10
+ export { getErrorMessage, parseJsonErrorResponse } from './errors.js';
11
+ export type { ParsedError } from './errors.js';
9
12
  export { fetchWithRetry } from './retry.js';
10
13
  export type { RetryConfig, FetchWithRetryOptions } from './retry.js';
package/dist/index.js CHANGED
@@ -3,11 +3,13 @@
3
3
  *
4
4
  * Shared utilities for API client packages.
5
5
  */
6
+ // Result
7
+ export { ok, okVoid, err } from './result.js';
6
8
  // Cache
7
9
  export { TTLCache } from './cache.js';
8
10
  // Utilities
9
11
  export { batchMap } from './utils.js';
10
12
  // Errors
11
- export { getErrorMessage } from './errors.js';
13
+ export { getErrorMessage, parseJsonErrorResponse } from './errors.js';
12
14
  // Retry
13
15
  export { fetchWithRetry } from './retry.js';
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Universal Result Type
3
+ *
4
+ * Standard return type for all fallible API operations.
5
+ * Aligned with @markwharton/pwa-core Result<T>.
6
+ */
7
+ /**
8
+ * Result of a fallible operation
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * // Success
13
+ * const result: Result<User> = { ok: true, data: user };
14
+ *
15
+ * // Error
16
+ * const result: Result<User> = { ok: false, error: 'Not found', status: 404 };
17
+ * ```
18
+ */
19
+ export interface Result<T> {
20
+ /** Whether the operation succeeded */
21
+ ok: boolean;
22
+ /** The data if successful */
23
+ data?: T;
24
+ /** Error message if failed */
25
+ error?: string;
26
+ /** HTTP status code (present on error, sometimes on success) */
27
+ status?: number;
28
+ }
29
+ /**
30
+ * Create a successful Result with data
31
+ */
32
+ export declare function ok<T>(data: T): Result<T>;
33
+ /**
34
+ * Create a successful Result with no data
35
+ */
36
+ export declare function okVoid(): Result<void>;
37
+ /**
38
+ * Create a failed Result with an error message and optional status code
39
+ */
40
+ export declare function err<T = never>(error: string, status?: number): Result<T>;
package/dist/result.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Universal Result Type
3
+ *
4
+ * Standard return type for all fallible API operations.
5
+ * Aligned with @markwharton/pwa-core Result<T>.
6
+ */
7
+ /**
8
+ * Create a successful Result with data
9
+ */
10
+ export function ok(data) {
11
+ return { ok: true, data };
12
+ }
13
+ /**
14
+ * Create a successful Result with no data
15
+ */
16
+ export function okVoid() {
17
+ return { ok: true };
18
+ }
19
+ /**
20
+ * Create a failed Result with an error message and optional status code
21
+ */
22
+ export function err(error, status) {
23
+ const result = { ok: false, error };
24
+ if (status !== undefined)
25
+ result.status = status;
26
+ return result;
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markwharton/api-core",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Shared utilities for API client packages",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",