@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.
Files changed (83) hide show
  1. package/dist/__tests__/auth/apiKey.test.d.ts +1 -0
  2. package/dist/__tests__/auth/apiKey.test.js +80 -0
  3. package/dist/__tests__/auth/token.test.d.ts +1 -0
  4. package/dist/__tests__/auth/token.test.js +212 -0
  5. package/dist/__tests__/auth/types.test.d.ts +1 -0
  6. package/dist/__tests__/auth/types.test.js +77 -0
  7. package/dist/__tests__/client/api.test.d.ts +1 -0
  8. package/dist/__tests__/client/api.test.js +334 -0
  9. package/dist/__tests__/client/apiError.test.d.ts +1 -0
  10. package/dist/__tests__/client/apiError.test.js +58 -0
  11. package/dist/__tests__/http/responses.test.d.ts +1 -0
  12. package/dist/__tests__/http/responses.test.js +112 -0
  13. package/dist/__tests__/http/status.test.d.ts +1 -0
  14. package/dist/__tests__/http/status.test.js +27 -0
  15. package/dist/__tests__/server/auth/apiKey.test.d.ts +1 -0
  16. package/dist/__tests__/server/auth/apiKey.test.js +80 -0
  17. package/dist/__tests__/server/auth/token.test.d.ts +1 -0
  18. package/dist/__tests__/server/auth/token.test.js +212 -0
  19. package/dist/__tests__/server/http/responses.test.d.ts +1 -0
  20. package/dist/__tests__/server/http/responses.test.js +112 -0
  21. package/dist/__tests__/server/storage/client.test.d.ts +1 -0
  22. package/dist/__tests__/server/storage/client.test.js +173 -0
  23. package/dist/__tests__/server/storage/keys.test.d.ts +1 -0
  24. package/dist/__tests__/server/storage/keys.test.js +47 -0
  25. package/dist/__tests__/shared/auth/types.test.d.ts +1 -0
  26. package/dist/__tests__/shared/auth/types.test.js +77 -0
  27. package/dist/__tests__/shared/http/status.test.d.ts +1 -0
  28. package/dist/__tests__/shared/http/status.test.js +27 -0
  29. package/dist/__tests__/storage/client.test.d.ts +1 -0
  30. package/dist/__tests__/storage/client.test.js +173 -0
  31. package/dist/__tests__/storage/keys.test.d.ts +1 -0
  32. package/dist/__tests__/storage/keys.test.js +47 -0
  33. package/dist/__tests__/types.test.d.ts +1 -0
  34. package/dist/__tests__/types.test.js +56 -0
  35. package/dist/auth/apiKey.d.ts +24 -7
  36. package/dist/auth/apiKey.js +24 -7
  37. package/dist/auth/token.d.ts +37 -10
  38. package/dist/auth/token.js +37 -10
  39. package/dist/auth/types.d.ts +21 -3
  40. package/dist/auth/types.js +21 -3
  41. package/dist/client/api.d.ts +70 -9
  42. package/dist/client/api.js +70 -9
  43. package/dist/client/apiError.d.ts +22 -5
  44. package/dist/client/apiError.js +22 -5
  45. package/dist/http/responses.d.ts +57 -8
  46. package/dist/http/responses.js +57 -8
  47. package/dist/index.d.ts +2 -4
  48. package/dist/index.js +6 -5
  49. package/dist/server/auth/apiKey.d.ts +44 -0
  50. package/dist/server/auth/apiKey.js +59 -0
  51. package/dist/server/auth/index.d.ts +2 -0
  52. package/dist/server/auth/index.js +15 -0
  53. package/dist/server/auth/token.d.ts +56 -0
  54. package/dist/server/auth/token.js +104 -0
  55. package/dist/server/http/index.d.ts +1 -0
  56. package/dist/server/http/index.js +12 -0
  57. package/dist/server/http/responses.d.ts +82 -0
  58. package/dist/server/http/responses.js +132 -0
  59. package/dist/server/index.d.ts +3 -0
  60. package/dist/server/index.js +33 -0
  61. package/dist/server/storage/client.d.ts +48 -0
  62. package/dist/server/storage/client.js +107 -0
  63. package/dist/server/storage/index.d.ts +2 -0
  64. package/dist/server/storage/index.js +11 -0
  65. package/dist/server/storage/keys.d.ts +8 -0
  66. package/dist/server/storage/keys.js +14 -0
  67. package/dist/shared/auth/index.d.ts +2 -0
  68. package/dist/shared/auth/index.js +7 -0
  69. package/dist/shared/auth/types.d.ts +63 -0
  70. package/dist/shared/auth/types.js +41 -0
  71. package/dist/shared/http/index.d.ts +3 -0
  72. package/dist/shared/http/index.js +5 -0
  73. package/dist/shared/http/status.d.ts +17 -0
  74. package/dist/shared/http/status.js +19 -0
  75. package/dist/shared/http/types.d.ts +10 -0
  76. package/dist/shared/http/types.js +5 -0
  77. package/dist/shared/index.d.ts +5 -0
  78. package/dist/shared/index.js +10 -0
  79. package/dist/storage/client.d.ts +29 -6
  80. package/dist/storage/client.js +29 -6
  81. package/dist/types.d.ts +16 -3
  82. package/dist/types.js +16 -3
  83. package/package.json +9 -13
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Base JWT payload - all tokens include these fields
3
+ * Projects extend this with their specific fields
4
+ */
5
+ export interface BaseJwtPayload {
6
+ iat: number;
7
+ exp: number;
8
+ }
9
+ /**
10
+ * Standard user token payload
11
+ * Used by: azure-pwa-starter, azure-alert-service (admin), onsite-monitor
12
+ */
13
+ export interface UserTokenPayload extends BaseJwtPayload {
14
+ authenticated: true;
15
+ tokenType: 'user' | 'machine';
16
+ }
17
+ /**
18
+ * Username-based token payload
19
+ * Used by: financial-tracker
20
+ */
21
+ export interface UsernameTokenPayload extends BaseJwtPayload {
22
+ username: string;
23
+ }
24
+ /**
25
+ * Role-based token payload
26
+ * Used by: azure-alert-service
27
+ */
28
+ export interface RoleTokenPayload extends BaseJwtPayload {
29
+ authenticated: true;
30
+ tokenType: 'user' | 'machine';
31
+ role: 'admin' | 'viewer';
32
+ viewerTokenId?: string;
33
+ }
34
+ /**
35
+ * Type guard to check if a JWT payload contains a username field.
36
+ * @param payload - The JWT payload to check
37
+ * @returns True if payload has a string username field
38
+ * @example
39
+ * if (hasUsername(payload)) {
40
+ * console.log(payload.username); // TypeScript knows username exists
41
+ * }
42
+ */
43
+ export declare function hasUsername(payload: BaseJwtPayload): payload is UsernameTokenPayload;
44
+ /**
45
+ * Type guard to check if a JWT payload contains a role field.
46
+ * @param payload - The JWT payload to check
47
+ * @returns True if payload has a role field
48
+ * @example
49
+ * if (hasRole(payload)) {
50
+ * console.log(payload.role); // 'admin' | 'viewer'
51
+ * }
52
+ */
53
+ export declare function hasRole(payload: BaseJwtPayload): payload is RoleTokenPayload;
54
+ /**
55
+ * Checks if a JWT payload represents an admin user.
56
+ * @param payload - The JWT payload to check
57
+ * @returns True if payload has role='admin'
58
+ * @example
59
+ * if (!isAdmin(payload)) {
60
+ * return httpForbidden('Admin access required');
61
+ * }
62
+ */
63
+ export declare function isAdmin(payload: BaseJwtPayload): boolean;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasUsername = hasUsername;
4
+ exports.hasRole = hasRole;
5
+ exports.isAdmin = isAdmin;
6
+ /**
7
+ * Type guard to check if a JWT payload contains a username field.
8
+ * @param payload - The JWT payload to check
9
+ * @returns True if payload has a string username field
10
+ * @example
11
+ * if (hasUsername(payload)) {
12
+ * console.log(payload.username); // TypeScript knows username exists
13
+ * }
14
+ */
15
+ function hasUsername(payload) {
16
+ return 'username' in payload && typeof payload.username === 'string';
17
+ }
18
+ /**
19
+ * Type guard to check if a JWT payload contains a role field.
20
+ * @param payload - The JWT payload to check
21
+ * @returns True if payload has a role field
22
+ * @example
23
+ * if (hasRole(payload)) {
24
+ * console.log(payload.role); // 'admin' | 'viewer'
25
+ * }
26
+ */
27
+ function hasRole(payload) {
28
+ return 'role' in payload;
29
+ }
30
+ /**
31
+ * Checks if a JWT payload represents an admin user.
32
+ * @param payload - The JWT payload to check
33
+ * @returns True if payload has role='admin'
34
+ * @example
35
+ * if (!isAdmin(payload)) {
36
+ * return httpForbidden('Admin access required');
37
+ * }
38
+ */
39
+ function isAdmin(payload) {
40
+ return hasRole(payload) && payload.role === 'admin';
41
+ }
@@ -0,0 +1,3 @@
1
+ export { HTTP_STATUS } from './status';
2
+ export type { HttpStatus } from './status';
3
+ export type { ErrorResponse } from './types';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HTTP_STATUS = void 0;
4
+ var status_1 = require("./status");
5
+ Object.defineProperty(exports, "HTTP_STATUS", { enumerable: true, get: function () { return status_1.HTTP_STATUS; } });
@@ -0,0 +1,17 @@
1
+ /**
2
+ * HTTP status codes - use instead of magic numbers
3
+ */
4
+ export declare const HTTP_STATUS: {
5
+ readonly OK: 200;
6
+ readonly CREATED: 201;
7
+ readonly NO_CONTENT: 204;
8
+ readonly BAD_REQUEST: 400;
9
+ readonly UNAUTHORIZED: 401;
10
+ readonly FORBIDDEN: 403;
11
+ readonly NOT_FOUND: 404;
12
+ readonly CONFLICT: 409;
13
+ readonly GONE: 410;
14
+ readonly INTERNAL_ERROR: 500;
15
+ readonly SERVICE_UNAVAILABLE: 503;
16
+ };
17
+ export type HttpStatus = typeof HTTP_STATUS[keyof typeof HTTP_STATUS];
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HTTP_STATUS = void 0;
4
+ /**
5
+ * HTTP status codes - use instead of magic numbers
6
+ */
7
+ exports.HTTP_STATUS = {
8
+ OK: 200,
9
+ CREATED: 201,
10
+ NO_CONTENT: 204,
11
+ BAD_REQUEST: 400,
12
+ UNAUTHORIZED: 401,
13
+ FORBIDDEN: 403,
14
+ NOT_FOUND: 404,
15
+ CONFLICT: 409,
16
+ GONE: 410,
17
+ INTERNAL_ERROR: 500,
18
+ SERVICE_UNAVAILABLE: 503
19
+ };
@@ -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 });
@@ -0,0 +1,5 @@
1
+ export type { BaseJwtPayload, UserTokenPayload, UsernameTokenPayload, RoleTokenPayload } from './auth';
2
+ export { hasUsername, hasRole, isAdmin } from './auth';
3
+ export { HTTP_STATUS } from './http';
4
+ export type { HttpStatus } from './http';
5
+ export type { ErrorResponse } from './http';
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HTTP_STATUS = exports.isAdmin = exports.hasRole = exports.hasUsername = void 0;
4
+ var auth_1 = require("./auth");
5
+ Object.defineProperty(exports, "hasUsername", { enumerable: true, get: function () { return auth_1.hasUsername; } });
6
+ Object.defineProperty(exports, "hasRole", { enumerable: true, get: function () { return auth_1.hasRole; } });
7
+ Object.defineProperty(exports, "isAdmin", { enumerable: true, get: function () { return auth_1.isAdmin; } });
8
+ // HTTP
9
+ var http_1 = require("./http");
10
+ Object.defineProperty(exports, "HTTP_STATUS", { enumerable: true, get: function () { return http_1.HTTP_STATUS; } });
@@ -1,25 +1,48 @@
1
1
  import { TableClient } from '@azure/data-tables';
2
2
  /**
3
- * Initialize storage configuration - call once at startup
3
+ * Initializes Azure Table Storage configuration. Call once at application startup.
4
+ * @param config - Storage configuration options
5
+ * @param config.accountName - Azure Storage account name (for managed identity)
6
+ * @param config.connectionString - Connection string (for local development)
7
+ * @example
8
+ * // Production (Azure with managed identity)
9
+ * initStorage({ accountName: 'mystorageaccount' });
10
+ *
11
+ * // Local development (Azurite)
12
+ * initStorage({ connectionString: 'UseDevelopmentStorage=true' });
4
13
  */
5
14
  export declare function initStorage(config: {
6
15
  accountName?: string;
7
16
  connectionString?: string;
8
17
  }): void;
9
18
  /**
10
- * Auto-initialize from environment variables
19
+ * Initializes storage from environment variables.
20
+ * Reads STORAGE_ACCOUNT_NAME and AzureWebJobsStorage.
21
+ * @example
22
+ * initStorageFromEnv(); // Uses process.env automatically
11
23
  */
12
24
  export declare function initStorageFromEnv(): void;
13
25
  /**
14
- * Check if using managed identity (Azure) vs connection string (local)
26
+ * Checks if using Azure managed identity vs local connection string.
27
+ * @returns True if using managed identity (production), false for connection string (local)
15
28
  */
16
29
  export declare function useManagedIdentity(): boolean;
17
30
  /**
18
- * Get a TableClient for the specified table
19
- * Creates table if it doesn't exist, caches client for reuse
31
+ * Gets a TableClient for the specified table, creating the table if needed.
32
+ * Clients are cached for reuse across requests.
33
+ * @param tableName - The Azure Table Storage table name
34
+ * @returns A configured TableClient instance
35
+ * @throws Error if storage is not configured
36
+ * @example
37
+ * const client = await getTableClient('users');
38
+ * await client.createEntity({ partitionKey: 'pk', rowKey: 'rk', name: 'John' });
20
39
  */
21
40
  export declare function getTableClient(tableName: string): Promise<TableClient>;
22
41
  /**
23
- * Clear the client cache (useful for testing)
42
+ * Clears the TableClient cache. Primarily useful for testing.
43
+ * @example
44
+ * afterEach(() => {
45
+ * clearTableClientCache();
46
+ * });
24
47
  */
25
48
  export declare function clearTableClientCache(): void;
@@ -16,14 +16,26 @@ const tableClients = new Map();
16
16
  let storageAccountName;
17
17
  let connectionString;
18
18
  /**
19
- * Initialize storage configuration - call once at startup
19
+ * Initializes Azure Table Storage configuration. Call once at application startup.
20
+ * @param config - Storage configuration options
21
+ * @param config.accountName - Azure Storage account name (for managed identity)
22
+ * @param config.connectionString - Connection string (for local development)
23
+ * @example
24
+ * // Production (Azure with managed identity)
25
+ * initStorage({ accountName: 'mystorageaccount' });
26
+ *
27
+ * // Local development (Azurite)
28
+ * initStorage({ connectionString: 'UseDevelopmentStorage=true' });
20
29
  */
21
30
  function initStorage(config) {
22
31
  storageAccountName = config.accountName;
23
32
  connectionString = config.connectionString;
24
33
  }
25
34
  /**
26
- * Auto-initialize from environment variables
35
+ * Initializes storage from environment variables.
36
+ * Reads STORAGE_ACCOUNT_NAME and AzureWebJobsStorage.
37
+ * @example
38
+ * initStorageFromEnv(); // Uses process.env automatically
27
39
  */
28
40
  function initStorageFromEnv() {
29
41
  initStorage({
@@ -32,14 +44,21 @@ function initStorageFromEnv() {
32
44
  });
33
45
  }
34
46
  /**
35
- * Check if using managed identity (Azure) vs connection string (local)
47
+ * Checks if using Azure managed identity vs local connection string.
48
+ * @returns True if using managed identity (production), false for connection string (local)
36
49
  */
37
50
  function useManagedIdentity() {
38
51
  return !!storageAccountName && !connectionString?.includes('UseDevelopmentStorage');
39
52
  }
40
53
  /**
41
- * Get a TableClient for the specified table
42
- * Creates table if it doesn't exist, caches client for reuse
54
+ * Gets a TableClient for the specified table, creating the table if needed.
55
+ * Clients are cached for reuse across requests.
56
+ * @param tableName - The Azure Table Storage table name
57
+ * @returns A configured TableClient instance
58
+ * @throws Error if storage is not configured
59
+ * @example
60
+ * const client = await getTableClient('users');
61
+ * await client.createEntity({ partitionKey: 'pk', rowKey: 'rk', name: 'John' });
43
62
  */
44
63
  async function getTableClient(tableName) {
45
64
  // Return cached client if available
@@ -77,7 +96,11 @@ async function getTableClient(tableName) {
77
96
  return client;
78
97
  }
79
98
  /**
80
- * Clear the client cache (useful for testing)
99
+ * Clears the TableClient cache. Primarily useful for testing.
100
+ * @example
101
+ * afterEach(() => {
102
+ * clearTableClientCache();
103
+ * });
81
104
  */
82
105
  function clearTableClientCache() {
83
106
  tableClients.clear();
package/dist/types.d.ts CHANGED
@@ -22,14 +22,27 @@ export interface Result<T> {
22
22
  statusCode?: number;
23
23
  }
24
24
  /**
25
- * Helper to create a success result
25
+ * Creates a success result with data.
26
+ * @param data - The success payload
27
+ * @returns A Result with ok=true and the provided data
28
+ * @example
29
+ * return ok({ user: 'john', role: 'admin' });
26
30
  */
27
31
  export declare function ok<T>(data: T): Result<T>;
28
32
  /**
29
- * Helper to create a success result with no data
33
+ * Creates a success result with no data.
34
+ * @returns A Result with ok=true and no data
35
+ * @example
36
+ * return okVoid(); // { ok: true }
30
37
  */
31
38
  export declare function okVoid(): Result<void>;
32
39
  /**
33
- * Helper to create a failure result
40
+ * Creates a failure result with an error message.
41
+ * @param error - The error message
42
+ * @param statusCode - Optional HTTP status code for API/push operations
43
+ * @returns A Result with ok=false and the error details
44
+ * @example
45
+ * return err('Token expired');
46
+ * return err('Subscription gone', 410);
34
47
  */
35
48
  export declare function err<T>(error: string, statusCode?: number): Result<T>;
package/dist/types.js CHANGED
@@ -7,19 +7,32 @@ exports.ok = ok;
7
7
  exports.okVoid = okVoid;
8
8
  exports.err = err;
9
9
  /**
10
- * Helper to create a success result
10
+ * Creates a success result with data.
11
+ * @param data - The success payload
12
+ * @returns A Result with ok=true and the provided data
13
+ * @example
14
+ * return ok({ user: 'john', role: 'admin' });
11
15
  */
12
16
  function ok(data) {
13
17
  return { ok: true, data };
14
18
  }
15
19
  /**
16
- * Helper to create a success result with no data
20
+ * Creates a success result with no data.
21
+ * @returns A Result with ok=true and no data
22
+ * @example
23
+ * return okVoid(); // { ok: true }
17
24
  */
18
25
  function okVoid() {
19
26
  return { ok: true };
20
27
  }
21
28
  /**
22
- * Helper to create a failure result
29
+ * Creates a failure result with an error message.
30
+ * @param error - The error message
31
+ * @param statusCode - Optional HTTP status code for API/push operations
32
+ * @returns A Result with ok=false and the error details
33
+ * @example
34
+ * return err('Token expired');
35
+ * return err('Subscription gone', 410);
23
36
  */
24
37
  function err(error, statusCode) {
25
38
  return statusCode !== undefined
package/package.json CHANGED
@@ -1,33 +1,29 @@
1
1
  {
2
2
  "name": "@markwharton/pwa-core",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Shared patterns for Azure PWA projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
8
8
  ".": "./dist/index.js",
9
9
  "./types": "./dist/types.js",
10
- "./auth": "./dist/auth/index.js",
11
- "./http": "./dist/http/index.js",
12
- "./storage": "./dist/storage/index.js",
13
- "./client": "./dist/client/index.js"
10
+ "./server": "./dist/server/index.js",
11
+ "./client": "./dist/client/index.js",
12
+ "./shared": "./dist/shared/index.js"
14
13
  },
15
14
  "typesVersions": {
16
15
  "*": {
17
16
  "types": [
18
17
  "dist/types.d.ts"
19
18
  ],
20
- "auth": [
21
- "dist/auth/index.d.ts"
22
- ],
23
- "http": [
24
- "dist/http/index.d.ts"
25
- ],
26
- "storage": [
27
- "dist/storage/index.d.ts"
19
+ "server": [
20
+ "dist/server/index.d.ts"
28
21
  ],
29
22
  "client": [
30
23
  "dist/client/index.d.ts"
24
+ ],
25
+ "shared": [
26
+ "dist/shared/index.d.ts"
31
27
  ]
32
28
  }
33
29
  },