@markwharton/pwa-core 1.7.0 → 2.0.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/{client/api.d.ts → client.d.ts} +85 -9
- package/dist/{client/api.js → client.js} +159 -56
- package/dist/index.d.ts +10 -2
- package/dist/index.js +14 -6
- package/dist/server.d.ts +283 -0
- package/dist/server.js +476 -0
- package/dist/shared.d.ts +150 -0
- package/dist/shared.js +124 -0
- package/package.json +11 -12
- package/dist/__tests__/auth/apiKey.test.d.ts +0 -1
- package/dist/__tests__/auth/apiKey.test.js +0 -80
- package/dist/__tests__/auth/token.test.d.ts +0 -1
- package/dist/__tests__/auth/token.test.js +0 -212
- package/dist/__tests__/auth/types.test.d.ts +0 -1
- package/dist/__tests__/auth/types.test.js +0 -77
- package/dist/__tests__/client/api.test.d.ts +0 -1
- package/dist/__tests__/client/api.test.js +0 -369
- package/dist/__tests__/client/apiError.test.d.ts +0 -1
- package/dist/__tests__/client/apiError.test.js +0 -91
- package/dist/__tests__/http/responses.test.d.ts +0 -1
- package/dist/__tests__/http/responses.test.js +0 -112
- package/dist/__tests__/http/status.test.d.ts +0 -1
- package/dist/__tests__/http/status.test.js +0 -27
- package/dist/__tests__/server/auth/apiKey.test.d.ts +0 -1
- package/dist/__tests__/server/auth/apiKey.test.js +0 -80
- package/dist/__tests__/server/auth/token.test.d.ts +0 -1
- package/dist/__tests__/server/auth/token.test.js +0 -299
- package/dist/__tests__/server/http/responses.test.d.ts +0 -1
- package/dist/__tests__/server/http/responses.test.js +0 -112
- package/dist/__tests__/server/storage/client.test.d.ts +0 -1
- package/dist/__tests__/server/storage/client.test.js +0 -173
- package/dist/__tests__/server/storage/keys.test.d.ts +0 -1
- package/dist/__tests__/server/storage/keys.test.js +0 -47
- package/dist/__tests__/shared/auth/types.test.d.ts +0 -1
- package/dist/__tests__/shared/auth/types.test.js +0 -77
- package/dist/__tests__/shared/http/status.test.d.ts +0 -1
- package/dist/__tests__/shared/http/status.test.js +0 -29
- package/dist/__tests__/storage/client.test.d.ts +0 -1
- package/dist/__tests__/storage/client.test.js +0 -173
- package/dist/__tests__/storage/keys.test.d.ts +0 -1
- package/dist/__tests__/storage/keys.test.js +0 -47
- package/dist/__tests__/types.test.d.ts +0 -1
- package/dist/__tests__/types.test.js +0 -56
- package/dist/auth/apiKey.d.ts +0 -44
- package/dist/auth/apiKey.js +0 -59
- package/dist/auth/index.d.ts +0 -3
- package/dist/auth/index.js +0 -22
- package/dist/auth/token.d.ts +0 -56
- package/dist/auth/token.js +0 -104
- package/dist/auth/types.d.ts +0 -63
- package/dist/auth/types.js +0 -41
- package/dist/client/apiError.d.ts +0 -48
- package/dist/client/apiError.js +0 -65
- package/dist/client/index.d.ts +0 -3
- package/dist/client/index.js +0 -14
- package/dist/client/types.d.ts +0 -12
- package/dist/client/types.js +0 -5
- package/dist/http/index.d.ts +0 -3
- package/dist/http/index.js +0 -14
- package/dist/http/responses.d.ts +0 -82
- package/dist/http/responses.js +0 -132
- package/dist/http/status.d.ts +0 -17
- package/dist/http/status.js +0 -19
- package/dist/http/types.d.ts +0 -10
- package/dist/http/types.js +0 -5
- package/dist/server/auth/apiKey.d.ts +0 -44
- package/dist/server/auth/apiKey.js +0 -59
- package/dist/server/auth/index.d.ts +0 -3
- package/dist/server/auth/index.js +0 -19
- package/dist/server/auth/token.d.ts +0 -102
- package/dist/server/auth/token.js +0 -158
- package/dist/server/http/index.d.ts +0 -1
- package/dist/server/http/index.js +0 -12
- package/dist/server/http/responses.d.ts +0 -82
- package/dist/server/http/responses.js +0 -132
- package/dist/server/index.d.ts +0 -4
- package/dist/server/index.js +0 -37
- package/dist/server/storage/client.d.ts +0 -48
- package/dist/server/storage/client.js +0 -107
- package/dist/server/storage/index.d.ts +0 -2
- package/dist/server/storage/index.js +0 -11
- package/dist/server/storage/keys.d.ts +0 -8
- package/dist/server/storage/keys.js +0 -14
- package/dist/shared/auth/index.d.ts +0 -2
- package/dist/shared/auth/index.js +0 -7
- package/dist/shared/auth/types.d.ts +0 -63
- package/dist/shared/auth/types.js +0 -41
- package/dist/shared/http/index.d.ts +0 -3
- package/dist/shared/http/index.js +0 -5
- package/dist/shared/http/status.d.ts +0 -19
- package/dist/shared/http/status.js +0 -21
- package/dist/shared/http/types.d.ts +0 -10
- package/dist/shared/http/types.js +0 -5
- package/dist/shared/index.d.ts +0 -5
- package/dist/shared/index.js +0 -10
- package/dist/storage/client.d.ts +0 -48
- package/dist/storage/client.js +0 -107
- package/dist/storage/index.d.ts +0 -2
- package/dist/storage/index.js +0 -11
- package/dist/storage/keys.d.ts +0 -8
- package/dist/storage/keys.js +0 -14
- package/dist/types.d.ts +0 -48
- package/dist/types.js +0 -41
|
@@ -1,11 +1,80 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* pwa-core/client - Browser-side API client utilities
|
|
3
|
+
*
|
|
4
|
+
* This module has NO Node.js dependencies and is designed for browser use.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* API response wrapper for safe calls
|
|
8
|
+
*/
|
|
9
|
+
export interface ApiResponse<T> {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
status: number;
|
|
12
|
+
data?: T;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Configuration for initApiClient
|
|
17
|
+
*/
|
|
18
|
+
export interface ApiClientConfig {
|
|
19
|
+
/** Function that returns the current auth token (or null) */
|
|
20
|
+
getToken: () => string | null;
|
|
21
|
+
/** Optional callback for 401 responses (e.g., redirect to login) */
|
|
22
|
+
onUnauthorized?: () => void;
|
|
23
|
+
/** Optional request timeout in milliseconds (default: 30000) */
|
|
24
|
+
timeout?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Custom error class for API errors.
|
|
28
|
+
* Preserves HTTP status code and error message from the server.
|
|
29
|
+
* @example
|
|
30
|
+
* try {
|
|
31
|
+
* await apiGet('/users/123');
|
|
32
|
+
* } catch (error) {
|
|
33
|
+
* if (error instanceof ApiError && error.isNotFound()) {
|
|
34
|
+
* console.log('User not found');
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
*/
|
|
38
|
+
export declare class ApiError extends Error {
|
|
39
|
+
status: number;
|
|
40
|
+
details?: string | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Creates a new ApiError instance.
|
|
43
|
+
* @param status - The HTTP status code
|
|
44
|
+
* @param message - The error message
|
|
45
|
+
* @param details - Optional additional error details
|
|
46
|
+
*/
|
|
47
|
+
constructor(status: number, message: string, details?: string | undefined);
|
|
48
|
+
/**
|
|
49
|
+
* Checks if this is a 401 Unauthorized error.
|
|
50
|
+
* @returns True if status is 401
|
|
51
|
+
*/
|
|
52
|
+
isUnauthorized(): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Checks if this is a 404 Not Found error.
|
|
55
|
+
* @returns True if status is 404
|
|
56
|
+
*/
|
|
57
|
+
isNotFound(): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Checks if this is a 400 Bad Request error.
|
|
60
|
+
* @returns True if status is 400
|
|
61
|
+
*/
|
|
62
|
+
isBadRequest(): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Checks if this is a client error (4xx status).
|
|
65
|
+
* @returns True if status is 400-499
|
|
66
|
+
*/
|
|
67
|
+
isClientError(): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Checks if this is a server error (5xx status).
|
|
70
|
+
* @returns True if status is 500-599
|
|
71
|
+
*/
|
|
72
|
+
isServerError(): boolean;
|
|
73
|
+
}
|
|
2
74
|
/**
|
|
3
75
|
* Initializes the API client with token retrieval and optional configuration.
|
|
4
76
|
* Call once at application startup.
|
|
5
77
|
* @param config - Client configuration
|
|
6
|
-
* @param config.getToken - Function that returns the current auth token (or null)
|
|
7
|
-
* @param config.onUnauthorized - Optional callback for 401 responses (e.g., redirect to login)
|
|
8
|
-
* @param config.timeout - Optional request timeout in milliseconds (default: 30000)
|
|
9
78
|
* @example
|
|
10
79
|
* initApiClient({
|
|
11
80
|
* getToken: () => localStorage.getItem('token'),
|
|
@@ -13,11 +82,7 @@ import { ApiResponse } from './types';
|
|
|
13
82
|
* timeout: 60000 // 60 seconds
|
|
14
83
|
* });
|
|
15
84
|
*/
|
|
16
|
-
export declare function initApiClient(config:
|
|
17
|
-
getToken: () => string | null;
|
|
18
|
-
onUnauthorized?: () => void;
|
|
19
|
-
timeout?: number;
|
|
20
|
-
}): void;
|
|
85
|
+
export declare function initApiClient(config: ApiClientConfig): void;
|
|
21
86
|
/**
|
|
22
87
|
* Makes an authenticated API call. Throws ApiError on non-2xx responses.
|
|
23
88
|
* @typeParam T - The expected response data type
|
|
@@ -82,6 +147,17 @@ export declare function apiPatch<T>(url: string, body?: unknown): Promise<T>;
|
|
|
82
147
|
* await apiDelete('/api/users/123');
|
|
83
148
|
*/
|
|
84
149
|
export declare function apiDelete<T>(url: string): Promise<T>;
|
|
150
|
+
/**
|
|
151
|
+
* Makes an authenticated API call expecting no response body.
|
|
152
|
+
* Use for DELETE, POST, or PUT actions that return 204 No Content.
|
|
153
|
+
* @param url - The API endpoint URL
|
|
154
|
+
* @param options - Optional fetch options (method, body, headers)
|
|
155
|
+
* @throws ApiError on non-2xx HTTP status or timeout
|
|
156
|
+
* @example
|
|
157
|
+
* await apiCallVoid('/api/users/123', { method: 'DELETE' });
|
|
158
|
+
* await apiCallVoid('/api/action', { method: 'POST' });
|
|
159
|
+
*/
|
|
160
|
+
export declare function apiCallVoid(url: string, options?: RequestInit): Promise<void>;
|
|
85
161
|
/**
|
|
86
162
|
* Makes an authenticated API call with Result-style error handling.
|
|
87
163
|
* Unlike apiCall, this never throws - errors are returned in the response.
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pwa-core/client - Browser-side API client utilities
|
|
4
|
+
*
|
|
5
|
+
* This module has NO Node.js dependencies and is designed for browser use.
|
|
6
|
+
*/
|
|
2
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.ApiError = void 0;
|
|
3
9
|
exports.initApiClient = initApiClient;
|
|
4
10
|
exports.apiCall = apiCall;
|
|
5
11
|
exports.apiGet = apiGet;
|
|
@@ -7,24 +13,130 @@ exports.apiPost = apiPost;
|
|
|
7
13
|
exports.apiPut = apiPut;
|
|
8
14
|
exports.apiPatch = apiPatch;
|
|
9
15
|
exports.apiDelete = apiDelete;
|
|
16
|
+
exports.apiCallVoid = apiCallVoid;
|
|
10
17
|
exports.apiCallSafe = apiCallSafe;
|
|
11
|
-
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// ApiError Class
|
|
20
|
+
// =============================================================================
|
|
12
21
|
/**
|
|
13
|
-
*
|
|
22
|
+
* Custom error class for API errors.
|
|
23
|
+
* Preserves HTTP status code and error message from the server.
|
|
24
|
+
* @example
|
|
25
|
+
* try {
|
|
26
|
+
* await apiGet('/users/123');
|
|
27
|
+
* } catch (error) {
|
|
28
|
+
* if (error instanceof ApiError && error.isNotFound()) {
|
|
29
|
+
* console.log('User not found');
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
14
32
|
*/
|
|
33
|
+
class ApiError extends Error {
|
|
34
|
+
/**
|
|
35
|
+
* Creates a new ApiError instance.
|
|
36
|
+
* @param status - The HTTP status code
|
|
37
|
+
* @param message - The error message
|
|
38
|
+
* @param details - Optional additional error details
|
|
39
|
+
*/
|
|
40
|
+
constructor(status, message, details) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.status = status;
|
|
43
|
+
this.details = details;
|
|
44
|
+
this.name = 'ApiError';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Checks if this is a 401 Unauthorized error.
|
|
48
|
+
* @returns True if status is 401
|
|
49
|
+
*/
|
|
50
|
+
isUnauthorized() {
|
|
51
|
+
return this.status === 401;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Checks if this is a 404 Not Found error.
|
|
55
|
+
* @returns True if status is 404
|
|
56
|
+
*/
|
|
57
|
+
isNotFound() {
|
|
58
|
+
return this.status === 404;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Checks if this is a 400 Bad Request error.
|
|
62
|
+
* @returns True if status is 400
|
|
63
|
+
*/
|
|
64
|
+
isBadRequest() {
|
|
65
|
+
return this.status === 400;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Checks if this is a client error (4xx status).
|
|
69
|
+
* @returns True if status is 400-499
|
|
70
|
+
*/
|
|
71
|
+
isClientError() {
|
|
72
|
+
return this.status >= 400 && this.status < 500;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Checks if this is a server error (5xx status).
|
|
76
|
+
* @returns True if status is 500-599
|
|
77
|
+
*/
|
|
78
|
+
isServerError() {
|
|
79
|
+
return this.status >= 500 && this.status < 600;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.ApiError = ApiError;
|
|
83
|
+
// =============================================================================
|
|
84
|
+
// Module State
|
|
85
|
+
// =============================================================================
|
|
15
86
|
// Token getter function - set by consuming app
|
|
16
87
|
let getToken = null;
|
|
17
88
|
// Callback for 401 responses (e.g., redirect to login)
|
|
18
89
|
let onUnauthorized = null;
|
|
19
90
|
// Request timeout in milliseconds (default: 30 seconds)
|
|
20
91
|
let requestTimeout = 30000;
|
|
92
|
+
// =============================================================================
|
|
93
|
+
// Internal Helpers
|
|
94
|
+
// =============================================================================
|
|
95
|
+
/** Prepare headers with optional auth token */
|
|
96
|
+
function prepareHeaders(options) {
|
|
97
|
+
const token = getToken?.();
|
|
98
|
+
const headers = {
|
|
99
|
+
'Content-Type': 'application/json',
|
|
100
|
+
...options.headers
|
|
101
|
+
};
|
|
102
|
+
if (token) {
|
|
103
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
104
|
+
}
|
|
105
|
+
return headers;
|
|
106
|
+
}
|
|
107
|
+
/** Extract error message from failed response */
|
|
108
|
+
async function extractErrorMessage(response) {
|
|
109
|
+
try {
|
|
110
|
+
const errorData = (await response.json());
|
|
111
|
+
return errorData.error || 'Request failed';
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return 'Request failed';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Handle 401 unauthorized callback */
|
|
118
|
+
function handleUnauthorized(status) {
|
|
119
|
+
if (status === 401 && onUnauthorized) {
|
|
120
|
+
onUnauthorized();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** Handle catch block for throwing API functions */
|
|
124
|
+
function handleApiError(error) {
|
|
125
|
+
if (error instanceof ApiError) {
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
129
|
+
throw new ApiError(0, 'Request timeout');
|
|
130
|
+
}
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
// =============================================================================
|
|
134
|
+
// Public API
|
|
135
|
+
// =============================================================================
|
|
21
136
|
/**
|
|
22
137
|
* Initializes the API client with token retrieval and optional configuration.
|
|
23
138
|
* Call once at application startup.
|
|
24
139
|
* @param config - Client configuration
|
|
25
|
-
* @param config.getToken - Function that returns the current auth token (or null)
|
|
26
|
-
* @param config.onUnauthorized - Optional callback for 401 responses (e.g., redirect to login)
|
|
27
|
-
* @param config.timeout - Optional request timeout in milliseconds (default: 30000)
|
|
28
140
|
* @example
|
|
29
141
|
* initApiClient({
|
|
30
142
|
* getToken: () => localStorage.getItem('token'),
|
|
@@ -48,15 +160,7 @@ function initApiClient(config) {
|
|
|
48
160
|
* const user = await apiCall<User>('/api/users/123');
|
|
49
161
|
*/
|
|
50
162
|
async function apiCall(url, options = {}) {
|
|
51
|
-
const
|
|
52
|
-
const headers = {
|
|
53
|
-
'Content-Type': 'application/json',
|
|
54
|
-
...options.headers
|
|
55
|
-
};
|
|
56
|
-
if (token) {
|
|
57
|
-
headers['Authorization'] = `Bearer ${token}`;
|
|
58
|
-
}
|
|
59
|
-
// Setup timeout with AbortController
|
|
163
|
+
const headers = prepareHeaders(options);
|
|
60
164
|
const controller = new AbortController();
|
|
61
165
|
const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
|
|
62
166
|
try {
|
|
@@ -66,34 +170,18 @@ async function apiCall(url, options = {}) {
|
|
|
66
170
|
signal: controller.signal
|
|
67
171
|
});
|
|
68
172
|
if (!response.ok) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
let errorMessage = 'Request failed';
|
|
73
|
-
try {
|
|
74
|
-
const errorData = (await response.json());
|
|
75
|
-
errorMessage = errorData.error || errorMessage;
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
// Ignore JSON parse errors
|
|
79
|
-
}
|
|
80
|
-
throw new apiError_1.ApiError(response.status, errorMessage);
|
|
173
|
+
handleUnauthorized(response.status);
|
|
174
|
+
const errorMessage = await extractErrorMessage(response);
|
|
175
|
+
throw new ApiError(response.status, errorMessage);
|
|
81
176
|
}
|
|
82
|
-
// Handle empty responses
|
|
83
177
|
const text = await response.text();
|
|
84
178
|
if (!text) {
|
|
85
|
-
|
|
179
|
+
throw new ApiError(response.status, 'Empty response body');
|
|
86
180
|
}
|
|
87
181
|
return JSON.parse(text);
|
|
88
182
|
}
|
|
89
183
|
catch (error) {
|
|
90
|
-
|
|
91
|
-
throw error;
|
|
92
|
-
}
|
|
93
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
94
|
-
throw new apiError_1.ApiError(0, 'Request timeout');
|
|
95
|
-
}
|
|
96
|
-
throw error;
|
|
184
|
+
handleApiError(error);
|
|
97
185
|
}
|
|
98
186
|
finally {
|
|
99
187
|
clearTimeout(timeoutId);
|
|
@@ -171,6 +259,39 @@ async function apiPatch(url, body) {
|
|
|
171
259
|
async function apiDelete(url) {
|
|
172
260
|
return apiCall(url, { method: 'DELETE' });
|
|
173
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Makes an authenticated API call expecting no response body.
|
|
264
|
+
* Use for DELETE, POST, or PUT actions that return 204 No Content.
|
|
265
|
+
* @param url - The API endpoint URL
|
|
266
|
+
* @param options - Optional fetch options (method, body, headers)
|
|
267
|
+
* @throws ApiError on non-2xx HTTP status or timeout
|
|
268
|
+
* @example
|
|
269
|
+
* await apiCallVoid('/api/users/123', { method: 'DELETE' });
|
|
270
|
+
* await apiCallVoid('/api/action', { method: 'POST' });
|
|
271
|
+
*/
|
|
272
|
+
async function apiCallVoid(url, options = {}) {
|
|
273
|
+
const headers = prepareHeaders(options);
|
|
274
|
+
const controller = new AbortController();
|
|
275
|
+
const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
|
|
276
|
+
try {
|
|
277
|
+
const response = await fetch(url, {
|
|
278
|
+
...options,
|
|
279
|
+
headers,
|
|
280
|
+
signal: controller.signal
|
|
281
|
+
});
|
|
282
|
+
if (!response.ok) {
|
|
283
|
+
handleUnauthorized(response.status);
|
|
284
|
+
const errorMessage = await extractErrorMessage(response);
|
|
285
|
+
throw new ApiError(response.status, errorMessage);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
handleApiError(error);
|
|
290
|
+
}
|
|
291
|
+
finally {
|
|
292
|
+
clearTimeout(timeoutId);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
174
295
|
/**
|
|
175
296
|
* Makes an authenticated API call with Result-style error handling.
|
|
176
297
|
* Unlike apiCall, this never throws - errors are returned in the response.
|
|
@@ -188,15 +309,7 @@ async function apiDelete(url) {
|
|
|
188
309
|
* }
|
|
189
310
|
*/
|
|
190
311
|
async function apiCallSafe(url, options = {}) {
|
|
191
|
-
const
|
|
192
|
-
const headers = {
|
|
193
|
-
'Content-Type': 'application/json',
|
|
194
|
-
...options.headers
|
|
195
|
-
};
|
|
196
|
-
if (token) {
|
|
197
|
-
headers['Authorization'] = `Bearer ${token}`;
|
|
198
|
-
}
|
|
199
|
-
// Setup timeout with AbortController
|
|
312
|
+
const headers = prepareHeaders(options);
|
|
200
313
|
const controller = new AbortController();
|
|
201
314
|
const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
|
|
202
315
|
try {
|
|
@@ -206,20 +319,10 @@ async function apiCallSafe(url, options = {}) {
|
|
|
206
319
|
signal: controller.signal
|
|
207
320
|
});
|
|
208
321
|
if (!response.ok) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
let errorMessage = 'Request failed';
|
|
213
|
-
try {
|
|
214
|
-
const errorData = (await response.json());
|
|
215
|
-
errorMessage = errorData.error || errorMessage;
|
|
216
|
-
}
|
|
217
|
-
catch {
|
|
218
|
-
// Ignore JSON parse errors
|
|
219
|
-
}
|
|
322
|
+
handleUnauthorized(response.status);
|
|
323
|
+
const errorMessage = await extractErrorMessage(response);
|
|
220
324
|
return { ok: false, status: response.status, error: errorMessage };
|
|
221
325
|
}
|
|
222
|
-
// Handle empty responses
|
|
223
326
|
const text = await response.text();
|
|
224
327
|
const data = text ? JSON.parse(text) : undefined;
|
|
225
328
|
return { ok: true, status: response.status, data };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* pwa-core - Shared patterns for Azure PWA projects
|
|
3
|
+
*
|
|
4
|
+
* Import paths:
|
|
5
|
+
* - '@markwharton/pwa-core/shared' - Types, HTTP_STATUS, Result pattern (browser-safe)
|
|
6
|
+
* - '@markwharton/pwa-core/server' - JWT, storage, HTTP responses (Node.js only)
|
|
7
|
+
* - '@markwharton/pwa-core/client' - API client utilities (browser only)
|
|
8
|
+
* - '@markwharton/pwa-core' - Server + shared (for convenience in Node.js)
|
|
9
|
+
*/
|
|
3
10
|
export * from './shared';
|
|
11
|
+
export * from './server';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pwa-core - Shared patterns for Azure PWA projects
|
|
4
|
+
*
|
|
5
|
+
* Import paths:
|
|
6
|
+
* - '@markwharton/pwa-core/shared' - Types, HTTP_STATUS, Result pattern (browser-safe)
|
|
7
|
+
* - '@markwharton/pwa-core/server' - JWT, storage, HTTP responses (Node.js only)
|
|
8
|
+
* - '@markwharton/pwa-core/client' - API client utilities (browser only)
|
|
9
|
+
* - '@markwharton/pwa-core' - Server + shared (for convenience in Node.js)
|
|
10
|
+
*/
|
|
2
11
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
12
|
if (k2 === undefined) k2 = k;
|
|
4
13
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -14,10 +23,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
23
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
24
|
};
|
|
16
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
//
|
|
18
|
-
__exportStar(require("./types"), exports);
|
|
19
|
-
// Server utilities (for convenience, but prefer ./server)
|
|
20
|
-
__exportStar(require("./server"), exports);
|
|
21
|
-
// Shared utilities
|
|
26
|
+
// Re-export shared (types, constants, utilities)
|
|
22
27
|
__exportStar(require("./shared"), exports);
|
|
23
|
-
//
|
|
28
|
+
// Re-export server utilities (for backend convenience)
|
|
29
|
+
__exportStar(require("./server"), exports);
|
|
30
|
+
// Note: Client utilities should be imported from '@markwharton/pwa-core/client'
|
|
31
|
+
// to avoid including browser-only code in Node.js bundles
|