@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,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const status_1 = require("../../../shared/http/status");
5
+ (0, vitest_1.describe)('HTTP_STATUS', () => {
6
+ (0, vitest_1.it)('has correct status codes', () => {
7
+ (0, vitest_1.expect)(status_1.HTTP_STATUS.OK).toBe(200);
8
+ (0, vitest_1.expect)(status_1.HTTP_STATUS.CREATED).toBe(201);
9
+ (0, vitest_1.expect)(status_1.HTTP_STATUS.NO_CONTENT).toBe(204);
10
+ (0, vitest_1.expect)(status_1.HTTP_STATUS.BAD_REQUEST).toBe(400);
11
+ (0, vitest_1.expect)(status_1.HTTP_STATUS.UNAUTHORIZED).toBe(401);
12
+ (0, vitest_1.expect)(status_1.HTTP_STATUS.FORBIDDEN).toBe(403);
13
+ (0, vitest_1.expect)(status_1.HTTP_STATUS.NOT_FOUND).toBe(404);
14
+ (0, vitest_1.expect)(status_1.HTTP_STATUS.CONFLICT).toBe(409);
15
+ (0, vitest_1.expect)(status_1.HTTP_STATUS.GONE).toBe(410);
16
+ (0, vitest_1.expect)(status_1.HTTP_STATUS.INTERNAL_ERROR).toBe(500);
17
+ (0, vitest_1.expect)(status_1.HTTP_STATUS.SERVICE_UNAVAILABLE).toBe(503);
18
+ });
19
+ (0, vitest_1.it)('is immutable (const assertion)', () => {
20
+ // TypeScript const assertion makes the object readonly
21
+ // This test verifies the values haven't been accidentally modified
22
+ const statusCodes = Object.values(status_1.HTTP_STATUS);
23
+ (0, vitest_1.expect)(statusCodes).toContain(200);
24
+ (0, vitest_1.expect)(statusCodes).toContain(404);
25
+ (0, vitest_1.expect)(statusCodes).toContain(500);
26
+ });
27
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const vitest_1 = require("vitest");
37
+ // Create mock functions
38
+ const mockCreateTable = vitest_1.vi.fn();
39
+ const mockFromConnectionString = vitest_1.vi.fn();
40
+ // Mock @azure/data-tables
41
+ vitest_1.vi.mock('@azure/data-tables', () => {
42
+ class MockTableClient {
43
+ constructor() {
44
+ this.createTable = mockCreateTable;
45
+ }
46
+ }
47
+ MockTableClient.fromConnectionString = mockFromConnectionString;
48
+ return {
49
+ TableClient: MockTableClient,
50
+ TableServiceClient: class MockTableServiceClient {
51
+ }
52
+ };
53
+ });
54
+ // Mock @azure/identity
55
+ vitest_1.vi.mock('@azure/identity', () => ({
56
+ DefaultAzureCredential: class MockDefaultAzureCredential {
57
+ }
58
+ }));
59
+ (0, vitest_1.describe)('Storage client', () => {
60
+ (0, vitest_1.beforeEach)(() => {
61
+ vitest_1.vi.resetModules();
62
+ vitest_1.vi.clearAllMocks();
63
+ mockCreateTable.mockResolvedValue(undefined);
64
+ mockFromConnectionString.mockReturnValue({
65
+ createTable: vitest_1.vi.fn().mockResolvedValue(undefined)
66
+ });
67
+ // Reset environment
68
+ delete process.env.STORAGE_ACCOUNT_NAME;
69
+ delete process.env.AzureWebJobsStorage;
70
+ });
71
+ (0, vitest_1.describe)('initStorage', () => {
72
+ (0, vitest_1.it)('accepts configuration object', async () => {
73
+ const { initStorage, useManagedIdentity } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
74
+ initStorage({ accountName: 'myaccount' });
75
+ (0, vitest_1.expect)(useManagedIdentity()).toBe(true);
76
+ });
77
+ (0, vitest_1.it)('accepts connection string', async () => {
78
+ const { initStorage, useManagedIdentity } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
79
+ initStorage({ connectionString: 'DefaultEndpointsProtocol=https;AccountName=test' });
80
+ (0, vitest_1.expect)(useManagedIdentity()).toBe(false);
81
+ });
82
+ });
83
+ (0, vitest_1.describe)('initStorageFromEnv', () => {
84
+ (0, vitest_1.it)('reads STORAGE_ACCOUNT_NAME', async () => {
85
+ process.env.STORAGE_ACCOUNT_NAME = 'testaccount';
86
+ const { initStorageFromEnv, useManagedIdentity } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
87
+ initStorageFromEnv();
88
+ (0, vitest_1.expect)(useManagedIdentity()).toBe(true);
89
+ });
90
+ (0, vitest_1.it)('reads AzureWebJobsStorage', async () => {
91
+ process.env.AzureWebJobsStorage = 'UseDevelopmentStorage=true';
92
+ const { initStorageFromEnv, useManagedIdentity } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
93
+ initStorageFromEnv();
94
+ (0, vitest_1.expect)(useManagedIdentity()).toBe(false);
95
+ });
96
+ });
97
+ (0, vitest_1.describe)('useManagedIdentity', () => {
98
+ (0, vitest_1.it)('returns true when accountName is set without development storage', async () => {
99
+ const { initStorage, useManagedIdentity } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
100
+ initStorage({ accountName: 'prodaccount' });
101
+ (0, vitest_1.expect)(useManagedIdentity()).toBe(true);
102
+ });
103
+ (0, vitest_1.it)('returns false when using development storage', async () => {
104
+ const { initStorage, useManagedIdentity } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
105
+ initStorage({
106
+ accountName: 'devaccount',
107
+ connectionString: 'UseDevelopmentStorage=true'
108
+ });
109
+ (0, vitest_1.expect)(useManagedIdentity()).toBe(false);
110
+ });
111
+ (0, vitest_1.it)('returns false when only connection string is set', async () => {
112
+ const { initStorage, useManagedIdentity } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
113
+ initStorage({ connectionString: 'DefaultEndpointsProtocol=https' });
114
+ (0, vitest_1.expect)(useManagedIdentity()).toBe(false);
115
+ });
116
+ });
117
+ (0, vitest_1.describe)('getTableClient', () => {
118
+ (0, vitest_1.it)('throws if storage not configured', async () => {
119
+ const { getTableClient } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
120
+ await (0, vitest_1.expect)(getTableClient('test')).rejects.toThrow('Storage not configured. Set STORAGE_ACCOUNT_NAME or AzureWebJobsStorage.');
121
+ });
122
+ (0, vitest_1.it)('creates table client with managed identity', async () => {
123
+ const { initStorage, getTableClient, clearTableClientCache } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
124
+ initStorage({ accountName: 'testaccount' });
125
+ clearTableClientCache();
126
+ const client = await getTableClient('mytable');
127
+ (0, vitest_1.expect)(client).toBeDefined();
128
+ });
129
+ (0, vitest_1.it)('creates table client with connection string', async () => {
130
+ const { initStorage, getTableClient, clearTableClientCache } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
131
+ initStorage({ connectionString: 'DefaultEndpointsProtocol=https;AccountName=test' });
132
+ clearTableClientCache();
133
+ await getTableClient('mytable');
134
+ (0, vitest_1.expect)(mockFromConnectionString).toHaveBeenCalledWith('DefaultEndpointsProtocol=https;AccountName=test', 'mytable');
135
+ });
136
+ (0, vitest_1.it)('caches table client', async () => {
137
+ const { initStorage, getTableClient, clearTableClientCache } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
138
+ initStorage({ accountName: 'testaccount' });
139
+ clearTableClientCache();
140
+ const client1 = await getTableClient('mytable');
141
+ const client2 = await getTableClient('mytable');
142
+ (0, vitest_1.expect)(client1).toBe(client2);
143
+ });
144
+ (0, vitest_1.it)('handles 409 conflict (table exists)', async () => {
145
+ mockCreateTable.mockRejectedValue({ statusCode: 409 });
146
+ const { initStorage, getTableClient, clearTableClientCache } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
147
+ initStorage({ accountName: 'testaccount' });
148
+ clearTableClientCache();
149
+ // Should not throw
150
+ const client = await getTableClient('existingtable');
151
+ (0, vitest_1.expect)(client).toBeDefined();
152
+ });
153
+ (0, vitest_1.it)('throws on other errors', async () => {
154
+ mockCreateTable.mockRejectedValue({ statusCode: 500, message: 'Server error' });
155
+ const { initStorage, getTableClient, clearTableClientCache } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
156
+ initStorage({ accountName: 'testaccount' });
157
+ clearTableClientCache();
158
+ await (0, vitest_1.expect)(getTableClient('mytable')).rejects.toMatchObject({ statusCode: 500 });
159
+ });
160
+ });
161
+ (0, vitest_1.describe)('clearTableClientCache', () => {
162
+ (0, vitest_1.it)('clears cached clients', async () => {
163
+ const { initStorage, getTableClient, clearTableClientCache } = await Promise.resolve().then(() => __importStar(require('../../storage/client')));
164
+ initStorage({ accountName: 'testaccount' });
165
+ clearTableClientCache();
166
+ await getTableClient('mytable');
167
+ clearTableClientCache();
168
+ // After clearing, a new call should create a new client
169
+ const client2 = await getTableClient('mytable');
170
+ (0, vitest_1.expect)(client2).toBeDefined();
171
+ });
172
+ });
173
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const keys_1 = require("../../storage/keys");
5
+ (0, vitest_1.describe)('generateRowKey', () => {
6
+ (0, vitest_1.it)('returns 32-character hex string', () => {
7
+ const key = (0, keys_1.generateRowKey)('test-identifier');
8
+ (0, vitest_1.expect)(key).toHaveLength(32);
9
+ (0, vitest_1.expect)(key).toMatch(/^[a-f0-9]+$/);
10
+ });
11
+ (0, vitest_1.it)('produces consistent hash for same input', () => {
12
+ const key1 = (0, keys_1.generateRowKey)('same-input');
13
+ const key2 = (0, keys_1.generateRowKey)('same-input');
14
+ (0, vitest_1.expect)(key1).toBe(key2);
15
+ });
16
+ (0, vitest_1.it)('produces different hashes for different inputs', () => {
17
+ const key1 = (0, keys_1.generateRowKey)('input-1');
18
+ const key2 = (0, keys_1.generateRowKey)('input-2');
19
+ (0, vitest_1.expect)(key1).not.toBe(key2);
20
+ });
21
+ (0, vitest_1.it)('handles empty string', () => {
22
+ const key = (0, keys_1.generateRowKey)('');
23
+ (0, vitest_1.expect)(key).toHaveLength(32);
24
+ (0, vitest_1.expect)(key).toMatch(/^[a-f0-9]+$/);
25
+ });
26
+ (0, vitest_1.it)('handles special characters', () => {
27
+ const key = (0, keys_1.generateRowKey)('https://example.com/path?query=value&foo=bar');
28
+ (0, vitest_1.expect)(key).toHaveLength(32);
29
+ (0, vitest_1.expect)(key).toMatch(/^[a-f0-9]+$/);
30
+ });
31
+ (0, vitest_1.it)('handles unicode characters', () => {
32
+ const key = (0, keys_1.generateRowKey)('日本語テスト');
33
+ (0, vitest_1.expect)(key).toHaveLength(32);
34
+ (0, vitest_1.expect)(key).toMatch(/^[a-f0-9]+$/);
35
+ });
36
+ (0, vitest_1.it)('handles long strings', () => {
37
+ const longString = 'x'.repeat(10000);
38
+ const key = (0, keys_1.generateRowKey)(longString);
39
+ (0, vitest_1.expect)(key).toHaveLength(32);
40
+ (0, vitest_1.expect)(key).toMatch(/^[a-f0-9]+$/);
41
+ });
42
+ (0, vitest_1.it)('produces URL-safe keys', () => {
43
+ const key = (0, keys_1.generateRowKey)('some-identifier');
44
+ // Row keys should not contain these characters
45
+ (0, vitest_1.expect)(key).not.toMatch(/[\/\\#?\s]/);
46
+ });
47
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const types_1 = require("../types");
5
+ (0, vitest_1.describe)('Result helpers', () => {
6
+ (0, vitest_1.describe)('ok', () => {
7
+ (0, vitest_1.it)('creates success result with data', () => {
8
+ const result = (0, types_1.ok)({ name: 'test' });
9
+ (0, vitest_1.expect)(result.ok).toBe(true);
10
+ (0, vitest_1.expect)(result.data).toEqual({ name: 'test' });
11
+ (0, vitest_1.expect)(result.error).toBeUndefined();
12
+ });
13
+ (0, vitest_1.it)('works with primitive values', () => {
14
+ (0, vitest_1.expect)((0, types_1.ok)(42).data).toBe(42);
15
+ (0, vitest_1.expect)((0, types_1.ok)('hello').data).toBe('hello');
16
+ (0, vitest_1.expect)((0, types_1.ok)(true).data).toBe(true);
17
+ });
18
+ (0, vitest_1.it)('works with null', () => {
19
+ const result = (0, types_1.ok)(null);
20
+ (0, vitest_1.expect)(result.ok).toBe(true);
21
+ (0, vitest_1.expect)(result.data).toBeNull();
22
+ });
23
+ });
24
+ (0, vitest_1.describe)('okVoid', () => {
25
+ (0, vitest_1.it)('creates success result without data', () => {
26
+ const result = (0, types_1.okVoid)();
27
+ (0, vitest_1.expect)(result.ok).toBe(true);
28
+ (0, vitest_1.expect)(result.data).toBeUndefined();
29
+ (0, vitest_1.expect)(result.error).toBeUndefined();
30
+ });
31
+ });
32
+ (0, vitest_1.describe)('err', () => {
33
+ (0, vitest_1.it)('creates failure result with error message', () => {
34
+ const result = (0, types_1.err)('Something went wrong');
35
+ (0, vitest_1.expect)(result.ok).toBe(false);
36
+ (0, vitest_1.expect)(result.error).toBe('Something went wrong');
37
+ (0, vitest_1.expect)(result.data).toBeUndefined();
38
+ (0, vitest_1.expect)(result.statusCode).toBeUndefined();
39
+ });
40
+ (0, vitest_1.it)('creates failure result with status code', () => {
41
+ const result = (0, types_1.err)('Not found', 404);
42
+ (0, vitest_1.expect)(result.ok).toBe(false);
43
+ (0, vitest_1.expect)(result.error).toBe('Not found');
44
+ (0, vitest_1.expect)(result.statusCode).toBe(404);
45
+ });
46
+ (0, vitest_1.it)('excludes statusCode when not provided', () => {
47
+ const result = (0, types_1.err)('Error');
48
+ (0, vitest_1.expect)('statusCode' in result).toBe(false);
49
+ });
50
+ (0, vitest_1.it)('includes statusCode when provided', () => {
51
+ const result = (0, types_1.err)('Error', 500);
52
+ (0, vitest_1.expect)('statusCode' in result).toBe(true);
53
+ (0, vitest_1.expect)(result.statusCode).toBe(500);
54
+ });
55
+ });
56
+ });
@@ -3,7 +3,11 @@ import { Result } from '../types';
3
3
  * API Key utilities for machine-to-machine authentication
4
4
  */
5
5
  /**
6
- * Extract API key from X-API-Key header
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);
7
11
  */
8
12
  export declare function extractApiKey(request: {
9
13
  headers: {
@@ -11,17 +15,30 @@ export declare function extractApiKey(request: {
11
15
  };
12
16
  }): string | null;
13
17
  /**
14
- * Hash an API key for secure storage
15
- * Store this hash, not the raw key
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 });
16
25
  */
17
26
  export declare function hashApiKey(apiKey: string): string;
18
27
  /**
19
- * Validate API key against stored hash
20
- *
21
- * @returns Result indicating success or failure with error message
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();
22
35
  */
23
36
  export declare function validateApiKey(apiKey: string, storedHash: string): Result<void>;
24
37
  /**
25
- * Generate a new API key (random 32-byte hex string)
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
26
43
  */
27
44
  export declare function generateApiKey(): string;
@@ -10,22 +10,35 @@ const types_1 = require("../types");
10
10
  * API Key utilities for machine-to-machine authentication
11
11
  */
12
12
  /**
13
- * Extract API key from X-API-Key header
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);
14
18
  */
15
19
  function extractApiKey(request) {
16
20
  return request.headers.get('X-API-Key');
17
21
  }
18
22
  /**
19
- * Hash an API key for secure storage
20
- * Store this hash, not the raw key
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 });
21
30
  */
22
31
  function hashApiKey(apiKey) {
23
32
  return (0, crypto_1.createHash)('sha256').update(apiKey).digest('hex');
24
33
  }
25
34
  /**
26
- * Validate API key against stored hash
27
- *
28
- * @returns Result indicating success or failure with error message
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();
29
42
  */
30
43
  function validateApiKey(apiKey, storedHash) {
31
44
  const keyHash = hashApiKey(apiKey);
@@ -35,7 +48,11 @@ function validateApiKey(apiKey, storedHash) {
35
48
  return (0, types_1.err)('Invalid API key');
36
49
  }
37
50
  /**
38
- * Generate a new API key (random 32-byte hex string)
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
39
56
  */
40
57
  function generateApiKey() {
41
58
  return (0, crypto_1.randomBytes)(32).toString('hex');
@@ -1,29 +1,56 @@
1
1
  import { Result } from '../types';
2
2
  /**
3
- * Initialize JWT secret - call once at startup
4
- * Fails fast if secret is missing or too short
3
+ * Initializes the JWT authentication system. Call once at application startup.
4
+ * @param secret - The JWT secret key (from environment variable)
5
+ * @param minLength - Minimum required secret length (default: 32)
6
+ * @throws Error if secret is missing or too short
7
+ * @example
8
+ * initAuth(process.env.JWT_SECRET);
5
9
  */
6
10
  export declare function initAuth(secret: string | undefined, minLength?: number): void;
7
11
  /**
8
- * Get the JWT secret (throws if not initialized)
12
+ * Gets the configured JWT secret.
13
+ * @returns The JWT secret string
14
+ * @throws Error if initAuth() has not been called
9
15
  */
10
16
  export declare function getJwtSecret(): string;
11
17
  /**
12
- * Extract Bearer token from Authorization header
18
+ * Extracts the Bearer token from an Authorization header.
19
+ * @param authHeader - The Authorization header value
20
+ * @returns The token string, or null if not a valid Bearer token
21
+ * @example
22
+ * const token = extractToken(request.headers.get('Authorization'));
13
23
  */
14
24
  export declare function extractToken(authHeader: string | null): string | null;
15
25
  /**
16
- * Validate and decode a JWT token
17
- * Generic type allows project-specific payload shapes
18
- *
19
- * @returns Result with decoded payload or error message
26
+ * Validates and decodes a JWT token.
27
+ * @typeParam T - The expected payload type (extends object)
28
+ * @param token - The JWT token string to validate
29
+ * @returns Result with decoded payload on success, or error message on failure
30
+ * @example
31
+ * const result = validateToken<UserPayload>(token);
32
+ * if (result.ok) {
33
+ * console.log(result.data.username);
34
+ * }
20
35
  */
21
36
  export declare function validateToken<T extends object>(token: string): Result<T>;
22
37
  /**
23
- * Generate a JWT token with custom payload
38
+ * Generates a signed JWT token with the given payload.
39
+ * @typeParam T - The payload type (extends object)
40
+ * @param payload - The data to encode in the token
41
+ * @param expiresIn - Token expiration time (default: '7d')
42
+ * @returns The signed JWT token string
43
+ * @example
44
+ * const token = generateToken({ userId: '123', role: 'admin' }, '1h');
24
45
  */
25
46
  export declare function generateToken<T extends object>(payload: T, expiresIn?: string): string;
26
47
  /**
27
- * Generate a long-lived token (e.g., for machine/API access)
48
+ * Generates a long-lived JWT token for machine/API access.
49
+ * @typeParam T - The payload type (extends object)
50
+ * @param payload - The data to encode in the token
51
+ * @param expiresInDays - Token expiration in days (default: 3650 ≈ 10 years)
52
+ * @returns The signed JWT token string
53
+ * @example
54
+ * const apiToken = generateLongLivedToken({ machineId: 'server-1' });
28
55
  */
29
56
  export declare function generateLongLivedToken<T extends object>(payload: T, expiresInDays?: number): string;
@@ -17,8 +17,12 @@ const types_1 = require("../types");
17
17
  */
18
18
  let jwtSecret = null;
19
19
  /**
20
- * Initialize JWT secret - call once at startup
21
- * Fails fast if secret is missing or too short
20
+ * Initializes the JWT authentication system. Call once at application startup.
21
+ * @param secret - The JWT secret key (from environment variable)
22
+ * @param minLength - Minimum required secret length (default: 32)
23
+ * @throws Error if secret is missing or too short
24
+ * @example
25
+ * initAuth(process.env.JWT_SECRET);
22
26
  */
23
27
  function initAuth(secret, minLength = 32) {
24
28
  if (!secret || secret.length < minLength) {
@@ -27,7 +31,9 @@ function initAuth(secret, minLength = 32) {
27
31
  jwtSecret = secret;
28
32
  }
29
33
  /**
30
- * Get the JWT secret (throws if not initialized)
34
+ * Gets the configured JWT secret.
35
+ * @returns The JWT secret string
36
+ * @throws Error if initAuth() has not been called
31
37
  */
32
38
  function getJwtSecret() {
33
39
  if (!jwtSecret) {
@@ -36,7 +42,11 @@ function getJwtSecret() {
36
42
  return jwtSecret;
37
43
  }
38
44
  /**
39
- * Extract Bearer token from Authorization header
45
+ * Extracts the Bearer token from an Authorization header.
46
+ * @param authHeader - The Authorization header value
47
+ * @returns The token string, or null if not a valid Bearer token
48
+ * @example
49
+ * const token = extractToken(request.headers.get('Authorization'));
40
50
  */
41
51
  function extractToken(authHeader) {
42
52
  if (!authHeader?.startsWith('Bearer ')) {
@@ -45,10 +55,15 @@ function extractToken(authHeader) {
45
55
  return authHeader.slice(7);
46
56
  }
47
57
  /**
48
- * Validate and decode a JWT token
49
- * Generic type allows project-specific payload shapes
50
- *
51
- * @returns Result with decoded payload or error message
58
+ * Validates and decodes a JWT token.
59
+ * @typeParam T - The expected payload type (extends object)
60
+ * @param token - The JWT token string to validate
61
+ * @returns Result with decoded payload on success, or error message on failure
62
+ * @example
63
+ * const result = validateToken<UserPayload>(token);
64
+ * if (result.ok) {
65
+ * console.log(result.data.username);
66
+ * }
52
67
  */
53
68
  function validateToken(token) {
54
69
  try {
@@ -64,13 +79,25 @@ function validateToken(token) {
64
79
  }
65
80
  }
66
81
  /**
67
- * Generate a JWT token with custom payload
82
+ * Generates a signed JWT token with the given payload.
83
+ * @typeParam T - The payload type (extends object)
84
+ * @param payload - The data to encode in the token
85
+ * @param expiresIn - Token expiration time (default: '7d')
86
+ * @returns The signed JWT token string
87
+ * @example
88
+ * const token = generateToken({ userId: '123', role: 'admin' }, '1h');
68
89
  */
69
90
  function generateToken(payload, expiresIn = '7d') {
70
91
  return jsonwebtoken_1.default.sign(payload, getJwtSecret(), { expiresIn });
71
92
  }
72
93
  /**
73
- * Generate a long-lived token (e.g., for machine/API access)
94
+ * Generates a long-lived JWT token for machine/API access.
95
+ * @typeParam T - The payload type (extends object)
96
+ * @param payload - The data to encode in the token
97
+ * @param expiresInDays - Token expiration in days (default: 3650 ≈ 10 years)
98
+ * @returns The signed JWT token string
99
+ * @example
100
+ * const apiToken = generateLongLivedToken({ machineId: 'server-1' });
74
101
  */
75
102
  function generateLongLivedToken(payload, expiresInDays = 3650) {
76
103
  return jsonwebtoken_1.default.sign(payload, getJwtSecret(), { expiresIn: `${expiresInDays}d` });
@@ -32,14 +32,32 @@ export interface RoleTokenPayload extends BaseJwtPayload {
32
32
  viewerTokenId?: string;
33
33
  }
34
34
  /**
35
- * Type guard to check if payload has a username
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
+ * }
36
42
  */
37
43
  export declare function hasUsername(payload: BaseJwtPayload): payload is UsernameTokenPayload;
38
44
  /**
39
- * Type guard to check if payload has a role
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
+ * }
40
52
  */
41
53
  export declare function hasRole(payload: BaseJwtPayload): payload is RoleTokenPayload;
42
54
  /**
43
- * Type guard to check if payload is admin
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
+ * }
44
62
  */
45
63
  export declare function isAdmin(payload: BaseJwtPayload): boolean;
@@ -4,19 +4,37 @@ exports.hasUsername = hasUsername;
4
4
  exports.hasRole = hasRole;
5
5
  exports.isAdmin = isAdmin;
6
6
  /**
7
- * Type guard to check if payload has a username
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
+ * }
8
14
  */
9
15
  function hasUsername(payload) {
10
16
  return 'username' in payload && typeof payload.username === 'string';
11
17
  }
12
18
  /**
13
- * Type guard to check if payload has a role
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
+ * }
14
26
  */
15
27
  function hasRole(payload) {
16
28
  return 'role' in payload;
17
29
  }
18
30
  /**
19
- * Type guard to check if payload is admin
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
+ * }
20
38
  */
21
39
  function isAdmin(payload) {
22
40
  return hasRole(payload) && payload.role === 'admin';