@markwharton/pwa-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.
@@ -1,3 +1,4 @@
1
+ import { Result } from '../types';
1
2
  /**
2
3
  * API Key utilities for machine-to-machine authentication
3
4
  */
@@ -16,8 +17,10 @@ export declare function extractApiKey(request: {
16
17
  export declare function hashApiKey(apiKey: string): string;
17
18
  /**
18
19
  * Validate API key against stored hash
20
+ *
21
+ * @returns Result indicating success or failure with error message
19
22
  */
20
- export declare function validateApiKey(apiKey: string, storedHash: string): boolean;
23
+ export declare function validateApiKey(apiKey: string, storedHash: string): Result<void>;
21
24
  /**
22
25
  * Generate a new API key (random 32-byte hex string)
23
26
  */
@@ -5,6 +5,7 @@ exports.hashApiKey = hashApiKey;
5
5
  exports.validateApiKey = validateApiKey;
6
6
  exports.generateApiKey = generateApiKey;
7
7
  const crypto_1 = require("crypto");
8
+ const types_1 = require("../types");
8
9
  /**
9
10
  * API Key utilities for machine-to-machine authentication
10
11
  */
@@ -23,10 +24,15 @@ function hashApiKey(apiKey) {
23
24
  }
24
25
  /**
25
26
  * Validate API key against stored hash
27
+ *
28
+ * @returns Result indicating success or failure with error message
26
29
  */
27
30
  function validateApiKey(apiKey, storedHash) {
28
31
  const keyHash = hashApiKey(apiKey);
29
- return keyHash === storedHash;
32
+ if (keyHash === storedHash) {
33
+ return (0, types_1.okVoid)();
34
+ }
35
+ return (0, types_1.err)('Invalid API key');
30
36
  }
31
37
  /**
32
38
  * Generate a new API key (random 32-byte hex string)
@@ -1,3 +1,4 @@
1
+ import { Result } from '../types';
1
2
  /**
2
3
  * Initialize JWT secret - call once at startup
3
4
  * Fails fast if secret is missing or too short
@@ -14,8 +15,10 @@ export declare function extractToken(authHeader: string | null): string | null;
14
15
  /**
15
16
  * Validate and decode a JWT token
16
17
  * Generic type allows project-specific payload shapes
18
+ *
19
+ * @returns Result with decoded payload or error message
17
20
  */
18
- export declare function validateToken<T extends object>(token: string): T | null;
21
+ export declare function validateToken<T extends object>(token: string): Result<T>;
19
22
  /**
20
23
  * Generate a JWT token with custom payload
21
24
  */
@@ -10,6 +10,7 @@ exports.validateToken = validateToken;
10
10
  exports.generateToken = generateToken;
11
11
  exports.generateLongLivedToken = generateLongLivedToken;
12
12
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
13
+ const types_1 = require("../types");
13
14
  /**
14
15
  * JWT token utilities - works with any payload structure
15
16
  * Use BaseJwtPayload or extend it for type safety
@@ -46,17 +47,20 @@ function extractToken(authHeader) {
46
47
  /**
47
48
  * Validate and decode a JWT token
48
49
  * Generic type allows project-specific payload shapes
50
+ *
51
+ * @returns Result with decoded payload or error message
49
52
  */
50
53
  function validateToken(token) {
51
54
  try {
52
55
  const payload = jsonwebtoken_1.default.verify(token, getJwtSecret());
53
56
  if (typeof payload === 'object' && payload !== null) {
54
- return payload;
57
+ return (0, types_1.ok)(payload);
55
58
  }
56
- return null;
59
+ return (0, types_1.err)('Invalid token payload');
57
60
  }
58
- catch {
59
- return null;
61
+ catch (error) {
62
+ const message = error instanceof Error ? error.message : 'Token validation failed';
63
+ return (0, types_1.err)(message);
60
64
  }
61
65
  }
62
66
  /**
@@ -1,3 +1,4 @@
1
+ import { ApiResponse } from './types';
1
2
  /**
2
3
  * Initialize the API client
3
4
  */
@@ -5,15 +6,6 @@ export declare function initApiClient(config: {
5
6
  getToken: () => string | null;
6
7
  onUnauthorized?: () => void;
7
8
  }): void;
8
- /**
9
- * API response wrapper
10
- */
11
- export interface ApiResponse<T> {
12
- ok: boolean;
13
- status: number;
14
- data?: T;
15
- error?: string;
16
- }
17
9
  /**
18
10
  * Make an authenticated API call
19
11
  */
@@ -40,5 +32,6 @@ export declare function apiPatch<T>(url: string, body?: unknown): Promise<T>;
40
32
  export declare function apiDelete<T>(url: string): Promise<T>;
41
33
  /**
42
34
  * Alternative: Response wrapper style (like financial-tracker's authJsonFetch)
35
+ * Preserves actual HTTP status code on success
43
36
  */
44
37
  export declare function apiCallSafe<T>(url: string, options?: RequestInit): Promise<ApiResponse<T>>;
@@ -8,7 +8,7 @@ exports.apiPut = apiPut;
8
8
  exports.apiPatch = apiPatch;
9
9
  exports.apiDelete = apiDelete;
10
10
  exports.apiCallSafe = apiCallSafe;
11
- const ApiError_1 = require("./ApiError");
11
+ const apiError_1 = require("./apiError");
12
12
  /**
13
13
  * Client-side API utilities for authenticated requests
14
14
  */
@@ -51,7 +51,7 @@ async function apiCall(url, options = {}) {
51
51
  catch {
52
52
  // Ignore JSON parse errors
53
53
  }
54
- throw new ApiError_1.ApiError(response.status, errorMessage);
54
+ throw new apiError_1.ApiError(response.status, errorMessage);
55
55
  }
56
56
  // Handle empty responses
57
57
  const text = await response.text();
@@ -101,16 +101,42 @@ async function apiDelete(url) {
101
101
  }
102
102
  /**
103
103
  * Alternative: Response wrapper style (like financial-tracker's authJsonFetch)
104
+ * Preserves actual HTTP status code on success
104
105
  */
105
106
  async function apiCallSafe(url, options = {}) {
106
- try {
107
- const data = await apiCall(url, options);
108
- return { ok: true, status: 200, data };
107
+ const token = getToken?.();
108
+ const headers = {
109
+ 'Content-Type': 'application/json',
110
+ ...options.headers
111
+ };
112
+ if (token) {
113
+ headers['Authorization'] = `Bearer ${token}`;
109
114
  }
110
- catch (error) {
111
- if (error instanceof ApiError_1.ApiError) {
112
- return { ok: false, status: error.status, error: error.message };
115
+ try {
116
+ const response = await fetch(url, {
117
+ ...options,
118
+ headers
119
+ });
120
+ if (!response.ok) {
121
+ if (response.status === 401 && onUnauthorized) {
122
+ onUnauthorized();
123
+ }
124
+ let errorMessage = 'Request failed';
125
+ try {
126
+ const errorData = (await response.json());
127
+ errorMessage = errorData.error || errorMessage;
128
+ }
129
+ catch {
130
+ // Ignore JSON parse errors
131
+ }
132
+ return { ok: false, status: response.status, error: errorMessage };
113
133
  }
134
+ // Handle empty responses
135
+ const text = await response.text();
136
+ const data = text ? JSON.parse(text) : undefined;
137
+ return { ok: true, status: response.status, data };
138
+ }
139
+ catch {
114
140
  return { ok: false, status: 0, error: 'Network error' };
115
141
  }
116
142
  }
@@ -1,2 +1,3 @@
1
- export { ApiError } from './ApiError';
2
- export { initApiClient, type ApiResponse, apiCall, apiGet, apiPost, apiPut, apiPatch, apiDelete, apiCallSafe } from './api';
1
+ export { ApiError } from './apiError';
2
+ export { type ApiResponse } from './types';
3
+ export { initApiClient, apiCall, apiGet, apiPost, apiPut, apiPatch, apiDelete, apiCallSafe } from './api';
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.apiCallSafe = exports.apiDelete = exports.apiPatch = exports.apiPut = exports.apiPost = exports.apiGet = exports.apiCall = exports.initApiClient = exports.ApiError = void 0;
4
- var ApiError_1 = require("./ApiError");
5
- Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return ApiError_1.ApiError; } });
4
+ var apiError_1 = require("./apiError");
5
+ Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return apiError_1.ApiError; } });
6
6
  var api_1 = require("./api");
7
7
  Object.defineProperty(exports, "initApiClient", { enumerable: true, get: function () { return api_1.initApiClient; } });
8
8
  Object.defineProperty(exports, "apiCall", { enumerable: true, get: function () { return api_1.apiCall; } });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Client module type definitions
3
+ */
4
+ /**
5
+ * API response wrapper for safe calls
6
+ */
7
+ export interface ApiResponse<T> {
8
+ ok: boolean;
9
+ status: number;
10
+ data?: T;
11
+ error?: string;
12
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * Client module type definitions
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,2 +1,3 @@
1
1
  export { HTTP_STATUS, type HttpStatus } from './status';
2
- export { type ErrorResponse, badRequestResponse, unauthorizedResponse, forbiddenResponse, notFoundResponse, conflictResponse, handleFunctionError, isNotFoundError, isConflictError } from './responses';
2
+ export { type ErrorResponse } from './types';
3
+ export { badRequestResponse, unauthorizedResponse, forbiddenResponse, notFoundResponse, conflictResponse, handleFunctionError, isNotFoundError, isConflictError } from './responses';
@@ -1,11 +1,4 @@
1
1
  import { HttpResponseInit, InvocationContext } from '@azure/functions';
2
- /**
3
- * Standard error response structure
4
- */
5
- export interface ErrorResponse {
6
- error: string;
7
- details?: string;
8
- }
9
2
  /**
10
3
  * Create a 400 Bad Request response
11
4
  */
@@ -0,0 +1,10 @@
1
+ /**
2
+ * HTTP module type definitions
3
+ */
4
+ /**
5
+ * Standard error response structure
6
+ */
7
+ export interface ErrorResponse {
8
+ error: string;
9
+ details?: string;
10
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * HTTP module type definitions
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './types';
1
2
  export * from './auth';
2
3
  export * from './http';
3
4
  export * from './storage';
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  // Main entry point - re-export everything
18
+ __exportStar(require("./types"), exports);
18
19
  __exportStar(require("./auth"), exports);
19
20
  __exportStar(require("./http"), exports);
20
21
  __exportStar(require("./storage"), exports);
@@ -1,2 +1,2 @@
1
1
  export { initStorage, initStorageFromEnv, useManagedIdentity, getTableClient, clearTableClientCache } from './client';
2
- export { generateRowKey } from './helpers';
2
+ export { generateRowKey } from './keys';
@@ -7,5 +7,5 @@ Object.defineProperty(exports, "initStorageFromEnv", { enumerable: true, get: fu
7
7
  Object.defineProperty(exports, "useManagedIdentity", { enumerable: true, get: function () { return client_1.useManagedIdentity; } });
8
8
  Object.defineProperty(exports, "getTableClient", { enumerable: true, get: function () { return client_1.getTableClient; } });
9
9
  Object.defineProperty(exports, "clearTableClientCache", { enumerable: true, get: function () { return client_1.clearTableClientCache; } });
10
- var helpers_1 = require("./helpers");
11
- Object.defineProperty(exports, "generateRowKey", { enumerable: true, get: function () { return helpers_1.generateRowKey; } });
10
+ var keys_1 = require("./keys");
11
+ Object.defineProperty(exports, "generateRowKey", { enumerable: true, get: function () { return keys_1.generateRowKey; } });
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Shared types for pwa-core
3
+ */
4
+ /**
5
+ * Standard result type for operations that can fail.
6
+ * Used consistently across all modules (auth, push, client).
7
+ *
8
+ * @example
9
+ * // Success
10
+ * { ok: true, data: user }
11
+ *
12
+ * // Failure
13
+ * { ok: false, error: 'Token expired' }
14
+ *
15
+ * // With status code (HTTP/push operations)
16
+ * { ok: false, error: 'Subscription expired', statusCode: 410 }
17
+ */
18
+ export interface Result<T> {
19
+ ok: boolean;
20
+ data?: T;
21
+ error?: string;
22
+ statusCode?: number;
23
+ }
24
+ /**
25
+ * Helper to create a success result
26
+ */
27
+ export declare function ok<T>(data: T): Result<T>;
28
+ /**
29
+ * Helper to create a success result with no data
30
+ */
31
+ export declare function okVoid(): Result<void>;
32
+ /**
33
+ * Helper to create a failure result
34
+ */
35
+ export declare function err<T>(error: string, statusCode?: number): Result<T>;
package/dist/types.js ADDED
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ /**
3
+ * Shared types for pwa-core
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ok = ok;
7
+ exports.okVoid = okVoid;
8
+ exports.err = err;
9
+ /**
10
+ * Helper to create a success result
11
+ */
12
+ function ok(data) {
13
+ return { ok: true, data };
14
+ }
15
+ /**
16
+ * Helper to create a success result with no data
17
+ */
18
+ function okVoid() {
19
+ return { ok: true };
20
+ }
21
+ /**
22
+ * Helper to create a failure result
23
+ */
24
+ function err(error, statusCode) {
25
+ return statusCode !== undefined
26
+ ? { ok: false, error, statusCode }
27
+ : { ok: false, error };
28
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markwharton/pwa-core",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Shared patterns for Azure PWA projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
File without changes
File without changes
File without changes
File without changes