@markwharton/pwa-core 1.2.0 → 1.3.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/__tests__/auth/apiKey.test.d.ts +1 -0
- package/dist/__tests__/auth/apiKey.test.js +80 -0
- package/dist/__tests__/auth/token.test.d.ts +1 -0
- package/dist/__tests__/auth/token.test.js +212 -0
- package/dist/__tests__/auth/types.test.d.ts +1 -0
- package/dist/__tests__/auth/types.test.js +77 -0
- package/dist/__tests__/client/api.test.d.ts +1 -0
- package/dist/__tests__/client/api.test.js +334 -0
- package/dist/__tests__/client/apiError.test.d.ts +1 -0
- package/dist/__tests__/client/apiError.test.js +58 -0
- package/dist/__tests__/http/responses.test.d.ts +1 -0
- package/dist/__tests__/http/responses.test.js +112 -0
- package/dist/__tests__/http/status.test.d.ts +1 -0
- package/dist/__tests__/http/status.test.js +27 -0
- package/dist/__tests__/server/auth/apiKey.test.d.ts +1 -0
- package/dist/__tests__/server/auth/apiKey.test.js +80 -0
- package/dist/__tests__/server/auth/token.test.d.ts +1 -0
- package/dist/__tests__/server/auth/token.test.js +212 -0
- package/dist/__tests__/server/http/responses.test.d.ts +1 -0
- package/dist/__tests__/server/http/responses.test.js +112 -0
- package/dist/__tests__/server/storage/client.test.d.ts +1 -0
- package/dist/__tests__/server/storage/client.test.js +173 -0
- package/dist/__tests__/server/storage/keys.test.d.ts +1 -0
- package/dist/__tests__/server/storage/keys.test.js +47 -0
- package/dist/__tests__/shared/auth/types.test.d.ts +1 -0
- package/dist/__tests__/shared/auth/types.test.js +77 -0
- package/dist/__tests__/shared/http/status.test.d.ts +1 -0
- package/dist/__tests__/shared/http/status.test.js +27 -0
- package/dist/__tests__/storage/client.test.d.ts +1 -0
- package/dist/__tests__/storage/client.test.js +173 -0
- package/dist/__tests__/storage/keys.test.d.ts +1 -0
- package/dist/__tests__/storage/keys.test.js +47 -0
- package/dist/__tests__/types.test.d.ts +1 -0
- package/dist/__tests__/types.test.js +56 -0
- package/dist/auth/apiKey.d.ts +24 -7
- package/dist/auth/apiKey.js +24 -7
- package/dist/auth/token.d.ts +37 -10
- package/dist/auth/token.js +37 -10
- package/dist/auth/types.d.ts +21 -3
- package/dist/auth/types.js +21 -3
- package/dist/client/api.d.ts +70 -9
- package/dist/client/api.js +70 -9
- package/dist/client/apiError.d.ts +22 -5
- package/dist/client/apiError.js +22 -5
- package/dist/http/responses.d.ts +57 -8
- package/dist/http/responses.js +57 -8
- package/dist/index.d.ts +2 -4
- package/dist/index.js +6 -5
- package/dist/server/auth/apiKey.d.ts +44 -0
- package/dist/server/auth/apiKey.js +59 -0
- package/dist/server/auth/index.d.ts +2 -0
- package/dist/server/auth/index.js +15 -0
- package/dist/server/auth/token.d.ts +56 -0
- package/dist/server/auth/token.js +104 -0
- package/dist/server/http/index.d.ts +1 -0
- package/dist/server/http/index.js +12 -0
- package/dist/server/http/responses.d.ts +82 -0
- package/dist/server/http/responses.js +132 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +33 -0
- package/dist/server/storage/client.d.ts +48 -0
- package/dist/server/storage/client.js +107 -0
- package/dist/server/storage/index.d.ts +2 -0
- package/dist/server/storage/index.js +11 -0
- package/dist/server/storage/keys.d.ts +8 -0
- package/dist/server/storage/keys.js +14 -0
- package/dist/shared/auth/index.d.ts +2 -0
- package/dist/shared/auth/index.js +7 -0
- package/dist/shared/auth/types.d.ts +63 -0
- package/dist/shared/auth/types.js +41 -0
- package/dist/shared/http/index.d.ts +3 -0
- package/dist/shared/http/index.js +5 -0
- package/dist/shared/http/status.d.ts +17 -0
- package/dist/shared/http/status.js +19 -0
- package/dist/shared/http/types.d.ts +10 -0
- package/dist/shared/http/types.js +5 -0
- package/dist/shared/index.d.ts +5 -0
- package/dist/shared/index.js +10 -0
- package/dist/storage/client.d.ts +29 -6
- package/dist/storage/client.js +29 -6
- package/dist/types.d.ts +16 -3
- package/dist/types.js +16 -3
- package/package.json +9 -13
package/dist/client/api.d.ts
CHANGED
|
@@ -1,37 +1,98 @@
|
|
|
1
1
|
import { ApiResponse } from './types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Initializes the API client with token retrieval and optional 401 handler.
|
|
4
|
+
* Call once at application startup.
|
|
5
|
+
* @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
|
+
* @example
|
|
9
|
+
* initApiClient({
|
|
10
|
+
* getToken: () => localStorage.getItem('token'),
|
|
11
|
+
* onUnauthorized: () => window.location.href = '/login'
|
|
12
|
+
* });
|
|
4
13
|
*/
|
|
5
14
|
export declare function initApiClient(config: {
|
|
6
15
|
getToken: () => string | null;
|
|
7
16
|
onUnauthorized?: () => void;
|
|
8
17
|
}): void;
|
|
9
18
|
/**
|
|
10
|
-
*
|
|
19
|
+
* Makes an authenticated API call. Throws ApiError on non-2xx responses.
|
|
20
|
+
* @typeParam T - The expected response data type
|
|
21
|
+
* @param url - The API endpoint URL
|
|
22
|
+
* @param options - Optional fetch options (method, body, headers)
|
|
23
|
+
* @returns The parsed JSON response
|
|
24
|
+
* @throws ApiError on non-2xx HTTP status
|
|
25
|
+
* @example
|
|
26
|
+
* const user = await apiCall<User>('/api/users/123');
|
|
11
27
|
*/
|
|
12
28
|
export declare function apiCall<T>(url: string, options?: RequestInit): Promise<T>;
|
|
13
29
|
/**
|
|
14
|
-
* GET request
|
|
30
|
+
* Makes an authenticated GET request.
|
|
31
|
+
* @typeParam T - The expected response data type
|
|
32
|
+
* @param url - The API endpoint URL
|
|
33
|
+
* @returns The parsed JSON response
|
|
34
|
+
* @throws ApiError on non-2xx HTTP status
|
|
35
|
+
* @example
|
|
36
|
+
* const users = await apiGet<User[]>('/api/users');
|
|
15
37
|
*/
|
|
16
38
|
export declare function apiGet<T>(url: string): Promise<T>;
|
|
17
39
|
/**
|
|
18
|
-
* POST request
|
|
40
|
+
* Makes an authenticated POST request.
|
|
41
|
+
* @typeParam T - The expected response data type
|
|
42
|
+
* @param url - The API endpoint URL
|
|
43
|
+
* @param body - Optional request body (will be JSON stringified)
|
|
44
|
+
* @returns The parsed JSON response
|
|
45
|
+
* @throws ApiError on non-2xx HTTP status
|
|
46
|
+
* @example
|
|
47
|
+
* const user = await apiPost<User>('/api/users', { name: 'John' });
|
|
19
48
|
*/
|
|
20
49
|
export declare function apiPost<T>(url: string, body?: unknown): Promise<T>;
|
|
21
50
|
/**
|
|
22
|
-
* PUT request
|
|
51
|
+
* Makes an authenticated PUT request.
|
|
52
|
+
* @typeParam T - The expected response data type
|
|
53
|
+
* @param url - The API endpoint URL
|
|
54
|
+
* @param body - Optional request body (will be JSON stringified)
|
|
55
|
+
* @returns The parsed JSON response
|
|
56
|
+
* @throws ApiError on non-2xx HTTP status
|
|
57
|
+
* @example
|
|
58
|
+
* const user = await apiPut<User>('/api/users/123', { name: 'Jane' });
|
|
23
59
|
*/
|
|
24
60
|
export declare function apiPut<T>(url: string, body?: unknown): Promise<T>;
|
|
25
61
|
/**
|
|
26
|
-
* PATCH request
|
|
62
|
+
* Makes an authenticated PATCH request.
|
|
63
|
+
* @typeParam T - The expected response data type
|
|
64
|
+
* @param url - The API endpoint URL
|
|
65
|
+
* @param body - Optional request body (will be JSON stringified)
|
|
66
|
+
* @returns The parsed JSON response
|
|
67
|
+
* @throws ApiError on non-2xx HTTP status
|
|
68
|
+
* @example
|
|
69
|
+
* const user = await apiPatch<User>('/api/users/123', { status: 'active' });
|
|
27
70
|
*/
|
|
28
71
|
export declare function apiPatch<T>(url: string, body?: unknown): Promise<T>;
|
|
29
72
|
/**
|
|
30
|
-
* DELETE request
|
|
73
|
+
* Makes an authenticated DELETE request.
|
|
74
|
+
* @typeParam T - The expected response data type
|
|
75
|
+
* @param url - The API endpoint URL
|
|
76
|
+
* @returns The parsed JSON response
|
|
77
|
+
* @throws ApiError on non-2xx HTTP status
|
|
78
|
+
* @example
|
|
79
|
+
* await apiDelete('/api/users/123');
|
|
31
80
|
*/
|
|
32
81
|
export declare function apiDelete<T>(url: string): Promise<T>;
|
|
33
82
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
83
|
+
* Makes an authenticated API call with Result-style error handling.
|
|
84
|
+
* Unlike apiCall, this never throws - errors are returned in the response.
|
|
85
|
+
* Preserves actual HTTP status code on success (not always 200).
|
|
86
|
+
* @typeParam T - The expected response data type
|
|
87
|
+
* @param url - The API endpoint URL
|
|
88
|
+
* @param options - Optional fetch options (method, body, headers)
|
|
89
|
+
* @returns ApiResponse with ok, status, data (on success), or error (on failure)
|
|
90
|
+
* @example
|
|
91
|
+
* const response = await apiCallSafe<User>('/api/users/123');
|
|
92
|
+
* if (response.ok) {
|
|
93
|
+
* console.log(response.data);
|
|
94
|
+
* } else {
|
|
95
|
+
* console.error(response.error);
|
|
96
|
+
* }
|
|
36
97
|
*/
|
|
37
98
|
export declare function apiCallSafe<T>(url: string, options?: RequestInit): Promise<ApiResponse<T>>;
|
package/dist/client/api.js
CHANGED
|
@@ -17,14 +17,30 @@ let getToken = null;
|
|
|
17
17
|
// Callback for 401 responses (e.g., redirect to login)
|
|
18
18
|
let onUnauthorized = null;
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* Initializes the API client with token retrieval and optional 401 handler.
|
|
21
|
+
* Call once at application startup.
|
|
22
|
+
* @param config - Client configuration
|
|
23
|
+
* @param config.getToken - Function that returns the current auth token (or null)
|
|
24
|
+
* @param config.onUnauthorized - Optional callback for 401 responses (e.g., redirect to login)
|
|
25
|
+
* @example
|
|
26
|
+
* initApiClient({
|
|
27
|
+
* getToken: () => localStorage.getItem('token'),
|
|
28
|
+
* onUnauthorized: () => window.location.href = '/login'
|
|
29
|
+
* });
|
|
21
30
|
*/
|
|
22
31
|
function initApiClient(config) {
|
|
23
32
|
getToken = config.getToken;
|
|
24
33
|
onUnauthorized = config.onUnauthorized ?? null;
|
|
25
34
|
}
|
|
26
35
|
/**
|
|
27
|
-
*
|
|
36
|
+
* Makes an authenticated API call. Throws ApiError on non-2xx responses.
|
|
37
|
+
* @typeParam T - The expected response data type
|
|
38
|
+
* @param url - The API endpoint URL
|
|
39
|
+
* @param options - Optional fetch options (method, body, headers)
|
|
40
|
+
* @returns The parsed JSON response
|
|
41
|
+
* @throws ApiError on non-2xx HTTP status
|
|
42
|
+
* @example
|
|
43
|
+
* const user = await apiCall<User>('/api/users/123');
|
|
28
44
|
*/
|
|
29
45
|
async function apiCall(url, options = {}) {
|
|
30
46
|
const token = getToken?.();
|
|
@@ -61,13 +77,26 @@ async function apiCall(url, options = {}) {
|
|
|
61
77
|
return JSON.parse(text);
|
|
62
78
|
}
|
|
63
79
|
/**
|
|
64
|
-
* GET request
|
|
80
|
+
* Makes an authenticated GET request.
|
|
81
|
+
* @typeParam T - The expected response data type
|
|
82
|
+
* @param url - The API endpoint URL
|
|
83
|
+
* @returns The parsed JSON response
|
|
84
|
+
* @throws ApiError on non-2xx HTTP status
|
|
85
|
+
* @example
|
|
86
|
+
* const users = await apiGet<User[]>('/api/users');
|
|
65
87
|
*/
|
|
66
88
|
async function apiGet(url) {
|
|
67
89
|
return apiCall(url, { method: 'GET' });
|
|
68
90
|
}
|
|
69
91
|
/**
|
|
70
|
-
* POST request
|
|
92
|
+
* Makes an authenticated POST request.
|
|
93
|
+
* @typeParam T - The expected response data type
|
|
94
|
+
* @param url - The API endpoint URL
|
|
95
|
+
* @param body - Optional request body (will be JSON stringified)
|
|
96
|
+
* @returns The parsed JSON response
|
|
97
|
+
* @throws ApiError on non-2xx HTTP status
|
|
98
|
+
* @example
|
|
99
|
+
* const user = await apiPost<User>('/api/users', { name: 'John' });
|
|
71
100
|
*/
|
|
72
101
|
async function apiPost(url, body) {
|
|
73
102
|
return apiCall(url, {
|
|
@@ -76,7 +105,14 @@ async function apiPost(url, body) {
|
|
|
76
105
|
});
|
|
77
106
|
}
|
|
78
107
|
/**
|
|
79
|
-
* PUT request
|
|
108
|
+
* Makes an authenticated PUT request.
|
|
109
|
+
* @typeParam T - The expected response data type
|
|
110
|
+
* @param url - The API endpoint URL
|
|
111
|
+
* @param body - Optional request body (will be JSON stringified)
|
|
112
|
+
* @returns The parsed JSON response
|
|
113
|
+
* @throws ApiError on non-2xx HTTP status
|
|
114
|
+
* @example
|
|
115
|
+
* const user = await apiPut<User>('/api/users/123', { name: 'Jane' });
|
|
80
116
|
*/
|
|
81
117
|
async function apiPut(url, body) {
|
|
82
118
|
return apiCall(url, {
|
|
@@ -85,7 +121,14 @@ async function apiPut(url, body) {
|
|
|
85
121
|
});
|
|
86
122
|
}
|
|
87
123
|
/**
|
|
88
|
-
* PATCH request
|
|
124
|
+
* Makes an authenticated PATCH request.
|
|
125
|
+
* @typeParam T - The expected response data type
|
|
126
|
+
* @param url - The API endpoint URL
|
|
127
|
+
* @param body - Optional request body (will be JSON stringified)
|
|
128
|
+
* @returns The parsed JSON response
|
|
129
|
+
* @throws ApiError on non-2xx HTTP status
|
|
130
|
+
* @example
|
|
131
|
+
* const user = await apiPatch<User>('/api/users/123', { status: 'active' });
|
|
89
132
|
*/
|
|
90
133
|
async function apiPatch(url, body) {
|
|
91
134
|
return apiCall(url, {
|
|
@@ -94,14 +137,32 @@ async function apiPatch(url, body) {
|
|
|
94
137
|
});
|
|
95
138
|
}
|
|
96
139
|
/**
|
|
97
|
-
* DELETE request
|
|
140
|
+
* Makes an authenticated DELETE request.
|
|
141
|
+
* @typeParam T - The expected response data type
|
|
142
|
+
* @param url - The API endpoint URL
|
|
143
|
+
* @returns The parsed JSON response
|
|
144
|
+
* @throws ApiError on non-2xx HTTP status
|
|
145
|
+
* @example
|
|
146
|
+
* await apiDelete('/api/users/123');
|
|
98
147
|
*/
|
|
99
148
|
async function apiDelete(url) {
|
|
100
149
|
return apiCall(url, { method: 'DELETE' });
|
|
101
150
|
}
|
|
102
151
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
152
|
+
* Makes an authenticated API call with Result-style error handling.
|
|
153
|
+
* Unlike apiCall, this never throws - errors are returned in the response.
|
|
154
|
+
* Preserves actual HTTP status code on success (not always 200).
|
|
155
|
+
* @typeParam T - The expected response data type
|
|
156
|
+
* @param url - The API endpoint URL
|
|
157
|
+
* @param options - Optional fetch options (method, body, headers)
|
|
158
|
+
* @returns ApiResponse with ok, status, data (on success), or error (on failure)
|
|
159
|
+
* @example
|
|
160
|
+
* const response = await apiCallSafe<User>('/api/users/123');
|
|
161
|
+
* if (response.ok) {
|
|
162
|
+
* console.log(response.data);
|
|
163
|
+
* } else {
|
|
164
|
+
* console.error(response.error);
|
|
165
|
+
* }
|
|
105
166
|
*/
|
|
106
167
|
async function apiCallSafe(url, options = {}) {
|
|
107
168
|
const token = getToken?.();
|
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Custom error class for API errors
|
|
3
|
-
* Preserves status code and error message from server
|
|
2
|
+
* Custom error class for API errors.
|
|
3
|
+
* Preserves HTTP status code and error message from the server.
|
|
4
|
+
* @example
|
|
5
|
+
* try {
|
|
6
|
+
* await apiGet('/users/123');
|
|
7
|
+
* } catch (error) {
|
|
8
|
+
* if (error instanceof ApiError && error.isNotFound()) {
|
|
9
|
+
* console.log('User not found');
|
|
10
|
+
* }
|
|
11
|
+
* }
|
|
4
12
|
*/
|
|
5
13
|
export declare class ApiError extends Error {
|
|
6
14
|
status: number;
|
|
7
15
|
details?: string | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new ApiError instance.
|
|
18
|
+
* @param status - The HTTP status code
|
|
19
|
+
* @param message - The error message
|
|
20
|
+
* @param details - Optional additional error details
|
|
21
|
+
*/
|
|
8
22
|
constructor(status: number, message: string, details?: string | undefined);
|
|
9
23
|
/**
|
|
10
|
-
*
|
|
24
|
+
* Checks if this is a 401 Unauthorized error.
|
|
25
|
+
* @returns True if status is 401
|
|
11
26
|
*/
|
|
12
27
|
isUnauthorized(): boolean;
|
|
13
28
|
/**
|
|
14
|
-
*
|
|
29
|
+
* Checks if this is a 404 Not Found error.
|
|
30
|
+
* @returns True if status is 404
|
|
15
31
|
*/
|
|
16
32
|
isNotFound(): boolean;
|
|
17
33
|
/**
|
|
18
|
-
*
|
|
34
|
+
* Checks if this is a 400 Bad Request error.
|
|
35
|
+
* @returns True if status is 400
|
|
19
36
|
*/
|
|
20
37
|
isBadRequest(): boolean;
|
|
21
38
|
}
|
package/dist/client/apiError.js
CHANGED
|
@@ -2,10 +2,24 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ApiError = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* Custom error class for API errors
|
|
6
|
-
* Preserves status code and error message from server
|
|
5
|
+
* Custom error class for API errors.
|
|
6
|
+
* Preserves HTTP status code and error message from the server.
|
|
7
|
+
* @example
|
|
8
|
+
* try {
|
|
9
|
+
* await apiGet('/users/123');
|
|
10
|
+
* } catch (error) {
|
|
11
|
+
* if (error instanceof ApiError && error.isNotFound()) {
|
|
12
|
+
* console.log('User not found');
|
|
13
|
+
* }
|
|
14
|
+
* }
|
|
7
15
|
*/
|
|
8
16
|
class ApiError extends Error {
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new ApiError instance.
|
|
19
|
+
* @param status - The HTTP status code
|
|
20
|
+
* @param message - The error message
|
|
21
|
+
* @param details - Optional additional error details
|
|
22
|
+
*/
|
|
9
23
|
constructor(status, message, details) {
|
|
10
24
|
super(message);
|
|
11
25
|
this.status = status;
|
|
@@ -13,19 +27,22 @@ class ApiError extends Error {
|
|
|
13
27
|
this.name = 'ApiError';
|
|
14
28
|
}
|
|
15
29
|
/**
|
|
16
|
-
*
|
|
30
|
+
* Checks if this is a 401 Unauthorized error.
|
|
31
|
+
* @returns True if status is 401
|
|
17
32
|
*/
|
|
18
33
|
isUnauthorized() {
|
|
19
34
|
return this.status === 401;
|
|
20
35
|
}
|
|
21
36
|
/**
|
|
22
|
-
*
|
|
37
|
+
* Checks if this is a 404 Not Found error.
|
|
38
|
+
* @returns True if status is 404
|
|
23
39
|
*/
|
|
24
40
|
isNotFound() {
|
|
25
41
|
return this.status === 404;
|
|
26
42
|
}
|
|
27
43
|
/**
|
|
28
|
-
*
|
|
44
|
+
* Checks if this is a 400 Bad Request error.
|
|
45
|
+
* @returns True if status is 400
|
|
29
46
|
*/
|
|
30
47
|
isBadRequest() {
|
|
31
48
|
return this.status === 400;
|
package/dist/http/responses.d.ts
CHANGED
|
@@ -1,33 +1,82 @@
|
|
|
1
1
|
import { HttpResponseInit, InvocationContext } from '@azure/functions';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Creates a 400 Bad Request response.
|
|
4
|
+
* @param message - The error message to return
|
|
5
|
+
* @returns Azure Functions HttpResponseInit object
|
|
6
|
+
* @example
|
|
7
|
+
* if (!body.email) return badRequestResponse('Email is required');
|
|
4
8
|
*/
|
|
5
9
|
export declare function badRequestResponse(message: string): HttpResponseInit;
|
|
6
10
|
/**
|
|
7
|
-
*
|
|
11
|
+
* Creates a 401 Unauthorized response.
|
|
12
|
+
* @param message - The error message (default: 'Unauthorized')
|
|
13
|
+
* @returns Azure Functions HttpResponseInit object
|
|
14
|
+
* @example
|
|
15
|
+
* if (!token) return unauthorizedResponse();
|
|
8
16
|
*/
|
|
9
17
|
export declare function unauthorizedResponse(message?: string): HttpResponseInit;
|
|
10
18
|
/**
|
|
11
|
-
*
|
|
19
|
+
* Creates a 403 Forbidden response.
|
|
20
|
+
* @param message - The error message (default: 'Forbidden')
|
|
21
|
+
* @returns Azure Functions HttpResponseInit object
|
|
22
|
+
* @example
|
|
23
|
+
* if (!isAdmin(payload)) return forbiddenResponse('Admin access required');
|
|
12
24
|
*/
|
|
13
25
|
export declare function forbiddenResponse(message?: string): HttpResponseInit;
|
|
14
26
|
/**
|
|
15
|
-
*
|
|
27
|
+
* Creates a 404 Not Found response.
|
|
28
|
+
* @param resource - The name of the resource that wasn't found
|
|
29
|
+
* @returns Azure Functions HttpResponseInit object
|
|
30
|
+
* @example
|
|
31
|
+
* if (!user) return notFoundResponse('User');
|
|
32
|
+
* // Returns: { error: 'User not found' }
|
|
16
33
|
*/
|
|
17
34
|
export declare function notFoundResponse(resource: string): HttpResponseInit;
|
|
18
35
|
/**
|
|
19
|
-
*
|
|
36
|
+
* Creates a 409 Conflict response.
|
|
37
|
+
* @param message - The conflict error message
|
|
38
|
+
* @returns Azure Functions HttpResponseInit object
|
|
39
|
+
* @example
|
|
40
|
+
* if (existingUser) return conflictResponse('Email already registered');
|
|
20
41
|
*/
|
|
21
42
|
export declare function conflictResponse(message: string): HttpResponseInit;
|
|
22
43
|
/**
|
|
23
|
-
*
|
|
44
|
+
* Handles unexpected errors safely by logging details and returning a generic message.
|
|
45
|
+
* Use in catch blocks to avoid exposing internal error details to clients.
|
|
46
|
+
* @param error - The caught error
|
|
47
|
+
* @param context - Azure Functions InvocationContext for logging
|
|
48
|
+
* @returns Azure Functions HttpResponseInit with 500 status
|
|
49
|
+
* @example
|
|
50
|
+
* try {
|
|
51
|
+
* await riskyOperation();
|
|
52
|
+
* } catch (error) {
|
|
53
|
+
* return handleFunctionError(error, context);
|
|
54
|
+
* }
|
|
24
55
|
*/
|
|
25
56
|
export declare function handleFunctionError(error: unknown, context: InvocationContext): HttpResponseInit;
|
|
26
57
|
/**
|
|
27
|
-
*
|
|
58
|
+
* Checks if an error is an Azure Table Storage "not found" error.
|
|
59
|
+
* @param error - The caught error
|
|
60
|
+
* @returns True if error has statusCode 404
|
|
61
|
+
* @example
|
|
62
|
+
* try {
|
|
63
|
+
* await tableClient.getEntity(pk, rk);
|
|
64
|
+
* } catch (error) {
|
|
65
|
+
* if (isNotFoundError(error)) return notFoundResponse('Entity');
|
|
66
|
+
* throw error;
|
|
67
|
+
* }
|
|
28
68
|
*/
|
|
29
69
|
export declare function isNotFoundError(error: unknown): boolean;
|
|
30
70
|
/**
|
|
31
|
-
*
|
|
71
|
+
* Checks if an error is an Azure Table Storage "conflict" error.
|
|
72
|
+
* @param error - The caught error
|
|
73
|
+
* @returns True if error has statusCode 409
|
|
74
|
+
* @example
|
|
75
|
+
* try {
|
|
76
|
+
* await tableClient.createEntity(entity);
|
|
77
|
+
* } catch (error) {
|
|
78
|
+
* if (isConflictError(error)) return conflictResponse('Entity already exists');
|
|
79
|
+
* throw error;
|
|
80
|
+
* }
|
|
32
81
|
*/
|
|
33
82
|
export declare function isConflictError(error: unknown): boolean;
|
package/dist/http/responses.js
CHANGED
|
@@ -10,7 +10,11 @@ exports.isNotFoundError = isNotFoundError;
|
|
|
10
10
|
exports.isConflictError = isConflictError;
|
|
11
11
|
const status_1 = require("./status");
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Creates a 400 Bad Request response.
|
|
14
|
+
* @param message - The error message to return
|
|
15
|
+
* @returns Azure Functions HttpResponseInit object
|
|
16
|
+
* @example
|
|
17
|
+
* if (!body.email) return badRequestResponse('Email is required');
|
|
14
18
|
*/
|
|
15
19
|
function badRequestResponse(message) {
|
|
16
20
|
return {
|
|
@@ -19,7 +23,11 @@ function badRequestResponse(message) {
|
|
|
19
23
|
};
|
|
20
24
|
}
|
|
21
25
|
/**
|
|
22
|
-
*
|
|
26
|
+
* Creates a 401 Unauthorized response.
|
|
27
|
+
* @param message - The error message (default: 'Unauthorized')
|
|
28
|
+
* @returns Azure Functions HttpResponseInit object
|
|
29
|
+
* @example
|
|
30
|
+
* if (!token) return unauthorizedResponse();
|
|
23
31
|
*/
|
|
24
32
|
function unauthorizedResponse(message = 'Unauthorized') {
|
|
25
33
|
return {
|
|
@@ -28,7 +36,11 @@ function unauthorizedResponse(message = 'Unauthorized') {
|
|
|
28
36
|
};
|
|
29
37
|
}
|
|
30
38
|
/**
|
|
31
|
-
*
|
|
39
|
+
* Creates a 403 Forbidden response.
|
|
40
|
+
* @param message - The error message (default: 'Forbidden')
|
|
41
|
+
* @returns Azure Functions HttpResponseInit object
|
|
42
|
+
* @example
|
|
43
|
+
* if (!isAdmin(payload)) return forbiddenResponse('Admin access required');
|
|
32
44
|
*/
|
|
33
45
|
function forbiddenResponse(message = 'Forbidden') {
|
|
34
46
|
return {
|
|
@@ -37,7 +49,12 @@ function forbiddenResponse(message = 'Forbidden') {
|
|
|
37
49
|
};
|
|
38
50
|
}
|
|
39
51
|
/**
|
|
40
|
-
*
|
|
52
|
+
* Creates a 404 Not Found response.
|
|
53
|
+
* @param resource - The name of the resource that wasn't found
|
|
54
|
+
* @returns Azure Functions HttpResponseInit object
|
|
55
|
+
* @example
|
|
56
|
+
* if (!user) return notFoundResponse('User');
|
|
57
|
+
* // Returns: { error: 'User not found' }
|
|
41
58
|
*/
|
|
42
59
|
function notFoundResponse(resource) {
|
|
43
60
|
return {
|
|
@@ -46,7 +63,11 @@ function notFoundResponse(resource) {
|
|
|
46
63
|
};
|
|
47
64
|
}
|
|
48
65
|
/**
|
|
49
|
-
*
|
|
66
|
+
* Creates a 409 Conflict response.
|
|
67
|
+
* @param message - The conflict error message
|
|
68
|
+
* @returns Azure Functions HttpResponseInit object
|
|
69
|
+
* @example
|
|
70
|
+
* if (existingUser) return conflictResponse('Email already registered');
|
|
50
71
|
*/
|
|
51
72
|
function conflictResponse(message) {
|
|
52
73
|
return {
|
|
@@ -55,7 +76,17 @@ function conflictResponse(message) {
|
|
|
55
76
|
};
|
|
56
77
|
}
|
|
57
78
|
/**
|
|
58
|
-
*
|
|
79
|
+
* Handles unexpected errors safely by logging details and returning a generic message.
|
|
80
|
+
* Use in catch blocks to avoid exposing internal error details to clients.
|
|
81
|
+
* @param error - The caught error
|
|
82
|
+
* @param context - Azure Functions InvocationContext for logging
|
|
83
|
+
* @returns Azure Functions HttpResponseInit with 500 status
|
|
84
|
+
* @example
|
|
85
|
+
* try {
|
|
86
|
+
* await riskyOperation();
|
|
87
|
+
* } catch (error) {
|
|
88
|
+
* return handleFunctionError(error, context);
|
|
89
|
+
* }
|
|
59
90
|
*/
|
|
60
91
|
function handleFunctionError(error, context) {
|
|
61
92
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
@@ -66,7 +97,16 @@ function handleFunctionError(error, context) {
|
|
|
66
97
|
};
|
|
67
98
|
}
|
|
68
99
|
/**
|
|
69
|
-
*
|
|
100
|
+
* Checks if an error is an Azure Table Storage "not found" error.
|
|
101
|
+
* @param error - The caught error
|
|
102
|
+
* @returns True if error has statusCode 404
|
|
103
|
+
* @example
|
|
104
|
+
* try {
|
|
105
|
+
* await tableClient.getEntity(pk, rk);
|
|
106
|
+
* } catch (error) {
|
|
107
|
+
* if (isNotFoundError(error)) return notFoundResponse('Entity');
|
|
108
|
+
* throw error;
|
|
109
|
+
* }
|
|
70
110
|
*/
|
|
71
111
|
function isNotFoundError(error) {
|
|
72
112
|
return (error instanceof Error &&
|
|
@@ -74,7 +114,16 @@ function isNotFoundError(error) {
|
|
|
74
114
|
error.statusCode === 404);
|
|
75
115
|
}
|
|
76
116
|
/**
|
|
77
|
-
*
|
|
117
|
+
* Checks if an error is an Azure Table Storage "conflict" error.
|
|
118
|
+
* @param error - The caught error
|
|
119
|
+
* @returns True if error has statusCode 409
|
|
120
|
+
* @example
|
|
121
|
+
* try {
|
|
122
|
+
* await tableClient.createEntity(entity);
|
|
123
|
+
* } catch (error) {
|
|
124
|
+
* if (isConflictError(error)) return conflictResponse('Entity already exists');
|
|
125
|
+
* throw error;
|
|
126
|
+
* }
|
|
78
127
|
*/
|
|
79
128
|
function isConflictError(error) {
|
|
80
129
|
return (error instanceof Error &&
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -14,9 +14,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
//
|
|
17
|
+
// Core types (universal)
|
|
18
18
|
__exportStar(require("./types"), exports);
|
|
19
|
-
|
|
20
|
-
__exportStar(require("./
|
|
21
|
-
|
|
22
|
-
__exportStar(require("./
|
|
19
|
+
// Server utilities (for convenience, but prefer ./server)
|
|
20
|
+
__exportStar(require("./server"), exports);
|
|
21
|
+
// Shared utilities
|
|
22
|
+
__exportStar(require("./shared"), exports);
|
|
23
|
+
// Note: Client utilities available via ./client subpath
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Result } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* API Key utilities for machine-to-machine authentication
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Extracts API key from the X-API-Key header.
|
|
7
|
+
* @param request - Request object with headers.get() method
|
|
8
|
+
* @returns The API key string, or null if not present
|
|
9
|
+
* @example
|
|
10
|
+
* const apiKey = extractApiKey(request);
|
|
11
|
+
*/
|
|
12
|
+
export declare function extractApiKey(request: {
|
|
13
|
+
headers: {
|
|
14
|
+
get(name: string): string | null;
|
|
15
|
+
};
|
|
16
|
+
}): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Hashes an API key using SHA-256 for secure storage.
|
|
19
|
+
* Store this hash in your database, never the raw key.
|
|
20
|
+
* @param apiKey - The raw API key to hash
|
|
21
|
+
* @returns The SHA-256 hash as a hex string
|
|
22
|
+
* @example
|
|
23
|
+
* const hash = hashApiKey(rawKey);
|
|
24
|
+
* await db.save({ apiKeyHash: hash });
|
|
25
|
+
*/
|
|
26
|
+
export declare function hashApiKey(apiKey: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Validates an API key against a stored hash.
|
|
29
|
+
* @param apiKey - The API key from the request
|
|
30
|
+
* @param storedHash - The hash stored in your database
|
|
31
|
+
* @returns Result with ok=true if valid, or error message if invalid
|
|
32
|
+
* @example
|
|
33
|
+
* const result = validateApiKey(apiKey, user.apiKeyHash);
|
|
34
|
+
* if (!result.ok) return httpUnauthorized();
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateApiKey(apiKey: string, storedHash: string): Result<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Generates a cryptographically secure API key.
|
|
39
|
+
* @returns A random 64-character hex string (32 bytes)
|
|
40
|
+
* @example
|
|
41
|
+
* const apiKey = generateApiKey();
|
|
42
|
+
* // Return to user once, store hash in database
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateApiKey(): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractApiKey = extractApiKey;
|
|
4
|
+
exports.hashApiKey = hashApiKey;
|
|
5
|
+
exports.validateApiKey = validateApiKey;
|
|
6
|
+
exports.generateApiKey = generateApiKey;
|
|
7
|
+
const crypto_1 = require("crypto");
|
|
8
|
+
const types_1 = require("../../types");
|
|
9
|
+
/**
|
|
10
|
+
* API Key utilities for machine-to-machine authentication
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Extracts API key from the X-API-Key header.
|
|
14
|
+
* @param request - Request object with headers.get() method
|
|
15
|
+
* @returns The API key string, or null if not present
|
|
16
|
+
* @example
|
|
17
|
+
* const apiKey = extractApiKey(request);
|
|
18
|
+
*/
|
|
19
|
+
function extractApiKey(request) {
|
|
20
|
+
return request.headers.get('X-API-Key');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Hashes an API key using SHA-256 for secure storage.
|
|
24
|
+
* Store this hash in your database, never the raw key.
|
|
25
|
+
* @param apiKey - The raw API key to hash
|
|
26
|
+
* @returns The SHA-256 hash as a hex string
|
|
27
|
+
* @example
|
|
28
|
+
* const hash = hashApiKey(rawKey);
|
|
29
|
+
* await db.save({ apiKeyHash: hash });
|
|
30
|
+
*/
|
|
31
|
+
function hashApiKey(apiKey) {
|
|
32
|
+
return (0, crypto_1.createHash)('sha256').update(apiKey).digest('hex');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Validates an API key against a stored hash.
|
|
36
|
+
* @param apiKey - The API key from the request
|
|
37
|
+
* @param storedHash - The hash stored in your database
|
|
38
|
+
* @returns Result with ok=true if valid, or error message if invalid
|
|
39
|
+
* @example
|
|
40
|
+
* const result = validateApiKey(apiKey, user.apiKeyHash);
|
|
41
|
+
* if (!result.ok) return httpUnauthorized();
|
|
42
|
+
*/
|
|
43
|
+
function validateApiKey(apiKey, storedHash) {
|
|
44
|
+
const keyHash = hashApiKey(apiKey);
|
|
45
|
+
if (keyHash === storedHash) {
|
|
46
|
+
return (0, types_1.okVoid)();
|
|
47
|
+
}
|
|
48
|
+
return (0, types_1.err)('Invalid API key');
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Generates a cryptographically secure API key.
|
|
52
|
+
* @returns A random 64-character hex string (32 bytes)
|
|
53
|
+
* @example
|
|
54
|
+
* const apiKey = generateApiKey();
|
|
55
|
+
* // Return to user once, store hash in database
|
|
56
|
+
*/
|
|
57
|
+
function generateApiKey() {
|
|
58
|
+
return (0, crypto_1.randomBytes)(32).toString('hex');
|
|
59
|
+
}
|