@payloadcms/figma 0.0.1-alpha.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/LICENSE.md +11 -0
- package/bin/cli.js +4 -0
- package/dist/api/control-plane.d.ts +124 -0
- package/dist/api/control-plane.d.ts.map +1 -0
- package/dist/api/control-plane.js +245 -0
- package/dist/api/control-plane.js.map +1 -0
- package/dist/api/control-plane.spec.d.ts +2 -0
- package/dist/api/control-plane.spec.d.ts.map +1 -0
- package/dist/api/control-plane.spec.js +296 -0
- package/dist/api/control-plane.spec.js.map +1 -0
- package/dist/api/figma-api.d.ts +31 -0
- package/dist/api/figma-api.d.ts.map +1 -0
- package/dist/api/figma-api.js +169 -0
- package/dist/api/figma-api.js.map +1 -0
- package/dist/api/figma-api.spec.d.ts +14 -0
- package/dist/api/figma-api.spec.d.ts.map +1 -0
- package/dist/api/figma-api.spec.js +201 -0
- package/dist/api/figma-api.spec.js.map +1 -0
- package/dist/auth/__tests__/fixtures.d.ts +26 -0
- package/dist/auth/__tests__/fixtures.d.ts.map +1 -0
- package/dist/auth/__tests__/fixtures.js +67 -0
- package/dist/auth/__tests__/fixtures.js.map +1 -0
- package/dist/auth/__tests__/mocks.d.ts +33 -0
- package/dist/auth/__tests__/mocks.d.ts.map +1 -0
- package/dist/auth/__tests__/mocks.js +63 -0
- package/dist/auth/__tests__/mocks.js.map +1 -0
- package/dist/auth/browser.d.ts +31 -0
- package/dist/auth/browser.d.ts.map +1 -0
- package/dist/auth/browser.js +41 -0
- package/dist/auth/browser.js.map +1 -0
- package/dist/auth/callback-server.d.ts +61 -0
- package/dist/auth/callback-server.d.ts.map +1 -0
- package/dist/auth/callback-server.js +168 -0
- package/dist/auth/callback-server.js.map +1 -0
- package/dist/auth/callback-server.spec.d.ts +2 -0
- package/dist/auth/callback-server.spec.d.ts.map +1 -0
- package/dist/auth/callback-server.spec.js +85 -0
- package/dist/auth/callback-server.spec.js.map +1 -0
- package/dist/auth/crypto-utils.d.ts +33 -0
- package/dist/auth/crypto-utils.d.ts.map +1 -0
- package/dist/auth/crypto-utils.js +96 -0
- package/dist/auth/crypto-utils.js.map +1 -0
- package/dist/auth/crypto-utils.spec.d.ts +2 -0
- package/dist/auth/crypto-utils.spec.d.ts.map +1 -0
- package/dist/auth/crypto-utils.spec.js +36 -0
- package/dist/auth/crypto-utils.spec.js.map +1 -0
- package/dist/auth/jwt-validator.d.ts +41 -0
- package/dist/auth/jwt-validator.d.ts.map +1 -0
- package/dist/auth/jwt-validator.js +113 -0
- package/dist/auth/jwt-validator.js.map +1 -0
- package/dist/auth/jwt-validator.spec.d.ts +10 -0
- package/dist/auth/jwt-validator.spec.d.ts.map +1 -0
- package/dist/auth/jwt-validator.spec.js +236 -0
- package/dist/auth/jwt-validator.spec.js.map +1 -0
- package/dist/auth/oauth-flow.d.ts +62 -0
- package/dist/auth/oauth-flow.d.ts.map +1 -0
- package/dist/auth/oauth-flow.js +172 -0
- package/dist/auth/oauth-flow.js.map +1 -0
- package/dist/auth/oauth-flow.spec.d.ts +2 -0
- package/dist/auth/oauth-flow.spec.d.ts.map +1 -0
- package/dist/auth/oauth-flow.spec.js +134 -0
- package/dist/auth/oauth-flow.spec.js.map +1 -0
- package/dist/auth/pages/error.html +85 -0
- package/dist/auth/pages/success.html +74 -0
- package/dist/auth/pkce.d.ts +51 -0
- package/dist/auth/pkce.d.ts.map +1 -0
- package/dist/auth/pkce.js +71 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/auth/pkce.spec.d.ts +2 -0
- package/dist/auth/pkce.spec.d.ts.map +1 -0
- package/dist/auth/pkce.spec.js +32 -0
- package/dist/auth/pkce.spec.js.map +1 -0
- package/dist/auth/project-token.d.ts +47 -0
- package/dist/auth/project-token.d.ts.map +1 -0
- package/dist/auth/project-token.js +134 -0
- package/dist/auth/project-token.js.map +1 -0
- package/dist/auth/project-token.spec.d.ts +2 -0
- package/dist/auth/project-token.spec.d.ts.map +1 -0
- package/dist/auth/project-token.spec.js +332 -0
- package/dist/auth/project-token.spec.js.map +1 -0
- package/dist/auth/refresh.d.ts +35 -0
- package/dist/auth/refresh.d.ts.map +1 -0
- package/dist/auth/refresh.js +149 -0
- package/dist/auth/refresh.js.map +1 -0
- package/dist/auth/refresh.spec.d.ts +2 -0
- package/dist/auth/refresh.spec.d.ts.map +1 -0
- package/dist/auth/refresh.spec.js +105 -0
- package/dist/auth/refresh.spec.js.map +1 -0
- package/dist/auth/token-store.d.ts +110 -0
- package/dist/auth/token-store.d.ts.map +1 -0
- package/dist/auth/token-store.js +188 -0
- package/dist/auth/token-store.js.map +1 -0
- package/dist/auth/token-store.spec.d.ts +2 -0
- package/dist/auth/token-store.spec.d.ts.map +1 -0
- package/dist/auth/token-store.spec.js +276 -0
- package/dist/auth/token-store.spec.js.map +1 -0
- package/dist/auth/types.d.ts +197 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +6 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +108 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/__tests__/test-utils.d.ts +28 -0
- package/dist/commands/__tests__/test-utils.d.ts.map +1 -0
- package/dist/commands/__tests__/test-utils.js +34 -0
- package/dist/commands/__tests__/test-utils.js.map +1 -0
- package/dist/commands/deploy.d.ts +16 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +297 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/deploy.spec.d.ts +2 -0
- package/dist/commands/deploy.spec.d.ts.map +1 -0
- package/dist/commands/deploy.spec.js +18 -0
- package/dist/commands/deploy.spec.js.map +1 -0
- package/dist/commands/init.d.ts +30 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +410 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/init.spec.d.ts +2 -0
- package/dist/commands/init.spec.d.ts.map +1 -0
- package/dist/commands/init.spec.js +283 -0
- package/dist/commands/init.spec.js.map +1 -0
- package/dist/commands/list-tokens.d.ts +16 -0
- package/dist/commands/list-tokens.d.ts.map +1 -0
- package/dist/commands/list-tokens.js +44 -0
- package/dist/commands/list-tokens.js.map +1 -0
- package/dist/commands/login.d.ts +17 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +69 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +16 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +43 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/config/oauth.d.ts +19 -0
- package/dist/config/oauth.d.ts.map +1 -0
- package/dist/config/oauth.js +43 -0
- package/dist/config/oauth.js.map +1 -0
- package/dist/constants.d.ts +27 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +27 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin/adapter.d.ts +5 -0
- package/dist/plugin/adapter.d.ts.map +1 -0
- package/dist/plugin/adapter.js +139 -0
- package/dist/plugin/adapter.js.map +1 -0
- package/dist/plugin/build-config.d.ts +37 -0
- package/dist/plugin/build-config.d.ts.map +1 -0
- package/dist/plugin/build-config.js +25 -0
- package/dist/plugin/build-config.js.map +1 -0
- package/dist/plugin/build-config.spec.d.ts +2 -0
- package/dist/plugin/build-config.spec.d.ts.map +1 -0
- package/dist/plugin/build-config.spec.js +72 -0
- package/dist/plugin/build-config.spec.js.map +1 -0
- package/dist/types/config.d.ts +45 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +9 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/asset-collection.d.ts +30 -0
- package/dist/utils/asset-collection.d.ts.map +1 -0
- package/dist/utils/asset-collection.js +117 -0
- package/dist/utils/asset-collection.js.map +1 -0
- package/dist/utils/asset-collection.spec.d.ts +2 -0
- package/dist/utils/asset-collection.spec.d.ts.map +1 -0
- package/dist/utils/asset-collection.spec.js +155 -0
- package/dist/utils/asset-collection.spec.js.map +1 -0
- package/dist/utils/build-detection.d.ts +32 -0
- package/dist/utils/build-detection.d.ts.map +1 -0
- package/dist/utils/build-detection.js +55 -0
- package/dist/utils/build-detection.js.map +1 -0
- package/dist/utils/build-detection.spec.d.ts +2 -0
- package/dist/utils/build-detection.spec.d.ts.map +1 -0
- package/dist/utils/build-detection.spec.js +110 -0
- package/dist/utils/build-detection.spec.js.map +1 -0
- package/dist/utils/config.d.ts +33 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +78 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/config.spec.d.ts +2 -0
- package/dist/utils/config.spec.d.ts.map +1 -0
- package/dist/utils/config.spec.js +167 -0
- package/dist/utils/config.spec.js.map +1 -0
- package/dist/utils/download-template.d.ts +15 -0
- package/dist/utils/download-template.d.ts.map +1 -0
- package/dist/utils/download-template.js +74 -0
- package/dist/utils/download-template.js.map +1 -0
- package/dist/utils/download-template.spec.d.ts +2 -0
- package/dist/utils/download-template.spec.d.ts.map +1 -0
- package/dist/utils/download-template.spec.js +76 -0
- package/dist/utils/download-template.spec.js.map +1 -0
- package/dist/utils/env-management.d.ts +16 -0
- package/dist/utils/env-management.d.ts.map +1 -0
- package/dist/utils/env-management.js +98 -0
- package/dist/utils/env-management.js.map +1 -0
- package/dist/utils/env-management.spec.d.ts +2 -0
- package/dist/utils/env-management.spec.d.ts.map +1 -0
- package/dist/utils/env-management.spec.js +123 -0
- package/dist/utils/env-management.spec.js.map +1 -0
- package/dist/utils/format.d.ts +23 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +49 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/formatter.d.ts +11 -0
- package/dist/utils/formatter.d.ts.map +1 -0
- package/dist/utils/formatter.js +47 -0
- package/dist/utils/formatter.js.map +1 -0
- package/dist/utils/git.d.ts +15 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +92 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/git.spec.d.ts +2 -0
- package/dist/utils/git.spec.d.ts.map +1 -0
- package/dist/utils/git.spec.js +99 -0
- package/dist/utils/git.spec.js.map +1 -0
- package/dist/utils/is-debug.d.ts +6 -0
- package/dist/utils/is-debug.d.ts.map +1 -0
- package/dist/utils/is-debug.js +8 -0
- package/dist/utils/is-debug.js.map +1 -0
- package/dist/utils/lambda-config.d.ts +7 -0
- package/dist/utils/lambda-config.d.ts.map +1 -0
- package/dist/utils/lambda-config.js +144 -0
- package/dist/utils/lambda-config.js.map +1 -0
- package/dist/utils/lambda-config.spec.d.ts +2 -0
- package/dist/utils/lambda-config.spec.d.ts.map +1 -0
- package/dist/utils/lambda-config.spec.js +141 -0
- package/dist/utils/lambda-config.spec.js.map +1 -0
- package/dist/utils/log.d.ts +6 -0
- package/dist/utils/log.d.ts.map +1 -0
- package/dist/utils/log.js +22 -0
- package/dist/utils/log.js.map +1 -0
- package/dist/utils/messages.d.ts +10 -0
- package/dist/utils/messages.d.ts.map +1 -0
- package/dist/utils/messages.js +96 -0
- package/dist/utils/messages.js.map +1 -0
- package/dist/utils/package-manager.d.ts +12 -0
- package/dist/utils/package-manager.d.ts.map +1 -0
- package/dist/utils/package-manager.js +37 -0
- package/dist/utils/package-manager.js.map +1 -0
- package/dist/utils/payload-config-ast.d.ts +30 -0
- package/dist/utils/payload-config-ast.d.ts.map +1 -0
- package/dist/utils/payload-config-ast.js +259 -0
- package/dist/utils/payload-config-ast.js.map +1 -0
- package/dist/utils/payload-config-ast.spec.d.ts +2 -0
- package/dist/utils/payload-config-ast.spec.d.ts.map +1 -0
- package/dist/utils/payload-config-ast.spec.js +303 -0
- package/dist/utils/payload-config-ast.spec.js.map +1 -0
- package/dist/utils/payload-config-finder.d.ts +9 -0
- package/dist/utils/payload-config-finder.d.ts.map +1 -0
- package/dist/utils/payload-config-finder.js +26 -0
- package/dist/utils/payload-config-finder.js.map +1 -0
- package/dist/utils/payload-config-modifier.d.ts +18 -0
- package/dist/utils/payload-config-modifier.d.ts.map +1 -0
- package/dist/utils/payload-config-modifier.int.spec.d.ts +2 -0
- package/dist/utils/payload-config-modifier.int.spec.d.ts.map +1 -0
- package/dist/utils/payload-config-modifier.int.spec.js +116 -0
- package/dist/utils/payload-config-modifier.int.spec.js.map +1 -0
- package/dist/utils/payload-config-modifier.js +232 -0
- package/dist/utils/payload-config-modifier.js.map +1 -0
- package/dist/utils/payload-config-modifier.spec.d.ts +2 -0
- package/dist/utils/payload-config-modifier.spec.d.ts.map +1 -0
- package/dist/utils/payload-config-modifier.spec.js +162 -0
- package/dist/utils/payload-config-modifier.spec.js.map +1 -0
- package/dist/utils/payload-package-check.d.ts +15 -0
- package/dist/utils/payload-package-check.d.ts.map +1 -0
- package/dist/utils/payload-package-check.js +116 -0
- package/dist/utils/payload-package-check.js.map +1 -0
- package/dist/utils/payload-package-check.spec.d.ts +2 -0
- package/dist/utils/payload-package-check.spec.d.ts.map +1 -0
- package/dist/utils/payload-package-check.spec.js +148 -0
- package/dist/utils/payload-package-check.spec.js.map +1 -0
- package/dist/utils/project.d.ts +58 -0
- package/dist/utils/project.d.ts.map +1 -0
- package/dist/utils/project.js +252 -0
- package/dist/utils/project.js.map +1 -0
- package/dist/utils/project.spec.d.ts +2 -0
- package/dist/utils/project.spec.d.ts.map +1 -0
- package/dist/utils/project.spec.js +222 -0
- package/dist/utils/project.spec.js.map +1 -0
- package/dist/utils/s3-upload.d.ts +43 -0
- package/dist/utils/s3-upload.d.ts.map +1 -0
- package/dist/utils/s3-upload.js +128 -0
- package/dist/utils/s3-upload.js.map +1 -0
- package/dist/utils/s3-upload.spec.d.ts +2 -0
- package/dist/utils/s3-upload.spec.d.ts.map +1 -0
- package/dist/utils/s3-upload.spec.js +140 -0
- package/dist/utils/s3-upload.spec.js.map +1 -0
- package/dist/utils/token-display.d.ts +14 -0
- package/dist/utils/token-display.d.ts.map +1 -0
- package/dist/utils/token-display.js +62 -0
- package/dist/utils/token-display.js.map +1 -0
- package/dist/utils/typescript-validator.d.ts +11 -0
- package/dist/utils/typescript-validator.d.ts.map +1 -0
- package/dist/utils/typescript-validator.js +58 -0
- package/dist/utils/typescript-validator.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests JWT parsing helpers and validates mock API contract.
|
|
3
|
+
*
|
|
4
|
+
* NOTE: The Figma API is currently mocked for development.
|
|
5
|
+
*
|
|
6
|
+
* These tests verify:
|
|
7
|
+
* 1. JWT parsing helper functions (our code)
|
|
8
|
+
* 2. JWT structure matches expected Figma API contract
|
|
9
|
+
* 3. Mock responses are consistent for development
|
|
10
|
+
*
|
|
11
|
+
* TODO: Replace with integration tests when real API is available
|
|
12
|
+
*/ import { describe, expect, it } from 'vitest';
|
|
13
|
+
import { getProjectToken } from './figma-api.js';
|
|
14
|
+
/**
|
|
15
|
+
* Helper function to decode base64url JWT part
|
|
16
|
+
*/ function decodeJwtPart(part) {
|
|
17
|
+
const base64 = part.replace(/-/g, '+').replace(/_/g, '/');
|
|
18
|
+
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, '=');
|
|
19
|
+
return Buffer.from(padded, 'base64').toString('utf-8');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Helper function to parse JWT header
|
|
23
|
+
*/ function parseJwtHeader(token) {
|
|
24
|
+
const headerPart = token.split('.')[0];
|
|
25
|
+
return JSON.parse(decodeJwtPart(headerPart));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Helper function to parse JWT payload
|
|
29
|
+
*/ function parseJwtPayload(token) {
|
|
30
|
+
const payloadPart = token.split('.')[1];
|
|
31
|
+
return JSON.parse(decodeJwtPart(payloadPart));
|
|
32
|
+
}
|
|
33
|
+
describe('Figma API Client', ()=>{
|
|
34
|
+
const mockAccessToken = 'figt_mock_access_token';
|
|
35
|
+
const mockTenantId = 'tenant-123';
|
|
36
|
+
describe('getProjectToken', ()=>{
|
|
37
|
+
// TODO: Remove/modify this when real API is available
|
|
38
|
+
describe('mock implementation', ()=>{
|
|
39
|
+
it('should generate valid project token', async ()=>{
|
|
40
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
41
|
+
expect(result).toHaveProperty('token');
|
|
42
|
+
expect(result).toHaveProperty('expiresAt');
|
|
43
|
+
expect(result).toHaveProperty('tenantId');
|
|
44
|
+
expect(result.tenantId).toBe(mockTenantId);
|
|
45
|
+
});
|
|
46
|
+
it('should generate JWT in correct format', async ()=>{
|
|
47
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
48
|
+
// JWT should be three base64url parts separated by dots
|
|
49
|
+
const parts = result.token.split('.');
|
|
50
|
+
expect(parts).toHaveLength(3);
|
|
51
|
+
});
|
|
52
|
+
it('should include valid expiry time', async ()=>{
|
|
53
|
+
const before = Date.now();
|
|
54
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
55
|
+
const after = Date.now();
|
|
56
|
+
// Token should expire in ~25 minutes (mock generates 25 min expiry)
|
|
57
|
+
const expectedMin = before + 24 * 60 * 1000 // 24 minutes
|
|
58
|
+
;
|
|
59
|
+
const expectedMax = after + 26 * 60 * 1000 // 26 minutes
|
|
60
|
+
;
|
|
61
|
+
expect(result.expiresAt).toBeGreaterThan(expectedMin);
|
|
62
|
+
expect(result.expiresAt).toBeLessThan(expectedMax);
|
|
63
|
+
});
|
|
64
|
+
it('should generate unique tokens on multiple calls', async ()=>{
|
|
65
|
+
const token1 = await getProjectToken(mockAccessToken, mockTenantId);
|
|
66
|
+
const token2 = await getProjectToken(mockAccessToken, mockTenantId);
|
|
67
|
+
// Tokens should be unique (signatures will differ)
|
|
68
|
+
expect(token1.token).not.toBe(token2.token);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe('JWT structure and claims', ()=>{
|
|
72
|
+
it('should generate JWT with RS256 algorithm in header', async ()=>{
|
|
73
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
74
|
+
const header = parseJwtHeader(result.token);
|
|
75
|
+
expect(header.alg).toBe('RS256');
|
|
76
|
+
expect(header.typ).toBe('JWT');
|
|
77
|
+
});
|
|
78
|
+
it('should include required claims in payload', async ()=>{
|
|
79
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
80
|
+
const payload = parseJwtPayload(result.token);
|
|
81
|
+
expect(payload).toHaveProperty('exp');
|
|
82
|
+
expect(payload).toHaveProperty('iat');
|
|
83
|
+
expect(payload).toHaveProperty('aud');
|
|
84
|
+
expect(payload.aud).toBe(mockTenantId);
|
|
85
|
+
});
|
|
86
|
+
it('should have exp claim as numeric timestamp', async ()=>{
|
|
87
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
88
|
+
const payload = parseJwtPayload(result.token);
|
|
89
|
+
expect(typeof payload.exp).toBe('number');
|
|
90
|
+
expect(payload.exp).toBeGreaterThan(Date.now() / 1000); // Should be in future
|
|
91
|
+
});
|
|
92
|
+
it('should have iat claim as current timestamp', async ()=>{
|
|
93
|
+
const before = Math.floor(Date.now() / 1000);
|
|
94
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
95
|
+
const after = Math.floor(Date.now() / 1000);
|
|
96
|
+
const payload = parseJwtPayload(result.token);
|
|
97
|
+
expect(payload.iat).toBeGreaterThanOrEqual(before);
|
|
98
|
+
expect(payload.iat).toBeLessThanOrEqual(after);
|
|
99
|
+
});
|
|
100
|
+
it('should convert exp from seconds to milliseconds in expiresAt', async ()=>{
|
|
101
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
102
|
+
const payload = parseJwtPayload(result.token);
|
|
103
|
+
// expiresAt should be exp * 1000 (converted to milliseconds)
|
|
104
|
+
expect(result.expiresAt).toBe(payload.exp * 1000);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
describe('JWT expiry parsing', ()=>{
|
|
108
|
+
it('should correctly parse expiry from generated JWT', async ()=>{
|
|
109
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
110
|
+
// expiresAt should be a valid timestamp in milliseconds
|
|
111
|
+
expect(typeof result.expiresAt).toBe('number');
|
|
112
|
+
expect(result.expiresAt).toBeGreaterThan(Date.now());
|
|
113
|
+
// Should be ~25 minutes in the future
|
|
114
|
+
const expectedExpiry = Date.now() + 25 * 60 * 1000;
|
|
115
|
+
const tolerance = 2 * 60 * 1000 // 2 minutes tolerance
|
|
116
|
+
;
|
|
117
|
+
expect(Math.abs(result.expiresAt - expectedExpiry)).toBeLessThan(tolerance);
|
|
118
|
+
});
|
|
119
|
+
it('should handle JWT with past expiry date', async ()=>{
|
|
120
|
+
// This tests that parsing works even for expired tokens
|
|
121
|
+
// (validation happens later in jwt-validator)
|
|
122
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
123
|
+
// Should parse without throwing, even if theoretically expired
|
|
124
|
+
expect(result.expiresAt).toBeDefined();
|
|
125
|
+
expect(typeof result.expiresAt).toBe('number');
|
|
126
|
+
});
|
|
127
|
+
it('should handle JWT with far future expiry', async ()=>{
|
|
128
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
129
|
+
// expiresAt should be reasonable (within next 30 days)
|
|
130
|
+
const maxFuture = Date.now() + 30 * 24 * 60 * 60 * 1000;
|
|
131
|
+
expect(result.expiresAt).toBeLessThan(maxFuture);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe('error handling', ()=>{
|
|
135
|
+
it('should handle empty access token', async ()=>{
|
|
136
|
+
// Mock implementation doesn't validate access token, but real impl will
|
|
137
|
+
// For now, this should still work
|
|
138
|
+
const result = await getProjectToken('', mockTenantId);
|
|
139
|
+
expect(result).toHaveProperty('token');
|
|
140
|
+
});
|
|
141
|
+
it('should handle empty tenant ID', async ()=>{
|
|
142
|
+
// Mock implementation should handle this
|
|
143
|
+
const result = await getProjectToken(mockAccessToken, '');
|
|
144
|
+
expect(result.tenantId).toBe('');
|
|
145
|
+
});
|
|
146
|
+
it('should handle special characters in tenant ID', async ()=>{
|
|
147
|
+
const specialTenantId = 'tenant-with-special_chars.123';
|
|
148
|
+
const result = await getProjectToken(mockAccessToken, specialTenantId);
|
|
149
|
+
expect(result.tenantId).toBe(specialTenantId);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
describe('JWT format edge cases', ()=>{
|
|
154
|
+
it('should generate base64url encoded parts (no + / =)', async ()=>{
|
|
155
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
156
|
+
// Base64url should not contain +, /, or =
|
|
157
|
+
expect(result.token).not.toMatch(/[+/=]/);
|
|
158
|
+
});
|
|
159
|
+
it('should generate decodable header and payload', async ()=>{
|
|
160
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
161
|
+
// Should be able to decode without errors
|
|
162
|
+
expect(()=>parseJwtHeader(result.token)).not.toThrow();
|
|
163
|
+
expect(()=>parseJwtPayload(result.token)).not.toThrow();
|
|
164
|
+
});
|
|
165
|
+
it('should generate valid JSON in payload', async ()=>{
|
|
166
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
167
|
+
const payload = parseJwtPayload(result.token);
|
|
168
|
+
// Should be a valid object with expected structure
|
|
169
|
+
expect(payload).toBeTypeOf('object');
|
|
170
|
+
expect(payload).not.toBeNull();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
describe('return value structure', ()=>{
|
|
174
|
+
it('should return ProjectToken with all required fields', async ()=>{
|
|
175
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
176
|
+
expect(result).toHaveProperty('token');
|
|
177
|
+
expect(result).toHaveProperty('expiresAt');
|
|
178
|
+
expect(result).toHaveProperty('tenantId');
|
|
179
|
+
expect(typeof result.token).toBe('string');
|
|
180
|
+
expect(typeof result.expiresAt).toBe('number');
|
|
181
|
+
expect(typeof result.tenantId).toBe('string');
|
|
182
|
+
});
|
|
183
|
+
it('should not include claims in initial response', async ()=>{
|
|
184
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
185
|
+
// Claims are added after JWT validation, not in API response
|
|
186
|
+
expect(result).not.toHaveProperty('claims');
|
|
187
|
+
});
|
|
188
|
+
it('should match ProjectToken interface', async ()=>{
|
|
189
|
+
const result = await getProjectToken(mockAccessToken, mockTenantId);
|
|
190
|
+
// Verify structure matches ProjectToken interface
|
|
191
|
+
const keys = Object.keys(result).sort();
|
|
192
|
+
expect(keys).toEqual([
|
|
193
|
+
'expiresAt',
|
|
194
|
+
'tenantId',
|
|
195
|
+
'token'
|
|
196
|
+
]);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
//# sourceMappingURL=figma-api.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/api/figma-api.spec.ts"],"sourcesContent":["/**\n * Tests JWT parsing helpers and validates mock API contract.\n *\n * NOTE: The Figma API is currently mocked for development.\n *\n * These tests verify:\n * 1. JWT parsing helper functions (our code)\n * 2. JWT structure matches expected Figma API contract\n * 3. Mock responses are consistent for development\n *\n * TODO: Replace with integration tests when real API is available\n */\n\nimport { describe, expect, it } from 'vitest'\n\nimport { getProjectToken } from './figma-api.js'\n\n/**\n * Helper function to decode base64url JWT part\n */\nfunction decodeJwtPart(part: string): string {\n const base64 = part.replace(/-/g, '+').replace(/_/g, '/')\n const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=')\n return Buffer.from(padded, 'base64').toString('utf-8')\n}\n\n/**\n * Helper function to parse JWT header\n */\nfunction parseJwtHeader(token: string): { alg: string; typ: string } {\n const headerPart = token.split('.')[0]\n return JSON.parse(decodeJwtPart(headerPart))\n}\n\n/**\n * Helper function to parse JWT payload\n */\nfunction parseJwtPayload(token: string): {\n aud?: string\n email?: string\n exp?: number\n iat?: number\n sub?: string\n} {\n const payloadPart = token.split('.')[1]\n return JSON.parse(decodeJwtPart(payloadPart))\n}\n\ndescribe('Figma API Client', () => {\n const mockAccessToken = 'figt_mock_access_token'\n const mockTenantId = 'tenant-123'\n\n describe('getProjectToken', () => {\n // TODO: Remove/modify this when real API is available\n describe('mock implementation', () => {\n it('should generate valid project token', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n\n expect(result).toHaveProperty('token')\n expect(result).toHaveProperty('expiresAt')\n expect(result).toHaveProperty('tenantId')\n expect(result.tenantId).toBe(mockTenantId)\n })\n\n it('should generate JWT in correct format', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n\n // JWT should be three base64url parts separated by dots\n const parts = result.token.split('.')\n expect(parts).toHaveLength(3)\n })\n\n it('should include valid expiry time', async () => {\n const before = Date.now()\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n const after = Date.now()\n\n // Token should expire in ~25 minutes (mock generates 25 min expiry)\n const expectedMin = before + 24 * 60 * 1000 // 24 minutes\n const expectedMax = after + 26 * 60 * 1000 // 26 minutes\n\n expect(result.expiresAt).toBeGreaterThan(expectedMin)\n expect(result.expiresAt).toBeLessThan(expectedMax)\n })\n\n it('should generate unique tokens on multiple calls', async () => {\n const token1 = await getProjectToken(mockAccessToken, mockTenantId)\n const token2 = await getProjectToken(mockAccessToken, mockTenantId)\n\n // Tokens should be unique (signatures will differ)\n expect(token1.token).not.toBe(token2.token)\n })\n })\n\n describe('JWT structure and claims', () => {\n it('should generate JWT with RS256 algorithm in header', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n const header = parseJwtHeader(result.token)\n\n expect(header.alg).toBe('RS256')\n expect(header.typ).toBe('JWT')\n })\n\n it('should include required claims in payload', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n const payload = parseJwtPayload(result.token)\n\n expect(payload).toHaveProperty('exp')\n expect(payload).toHaveProperty('iat')\n expect(payload).toHaveProperty('aud')\n expect(payload.aud).toBe(mockTenantId)\n })\n\n it('should have exp claim as numeric timestamp', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n const payload = parseJwtPayload(result.token)\n\n expect(typeof payload.exp).toBe('number')\n expect(payload.exp).toBeGreaterThan(Date.now() / 1000) // Should be in future\n })\n\n it('should have iat claim as current timestamp', async () => {\n const before = Math.floor(Date.now() / 1000)\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n const after = Math.floor(Date.now() / 1000)\n\n const payload = parseJwtPayload(result.token)\n\n expect(payload.iat).toBeGreaterThanOrEqual(before)\n expect(payload.iat).toBeLessThanOrEqual(after)\n })\n\n it('should convert exp from seconds to milliseconds in expiresAt', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n const payload = parseJwtPayload(result.token)\n\n // expiresAt should be exp * 1000 (converted to milliseconds)\n expect(result.expiresAt).toBe(payload.exp! * 1000)\n })\n })\n\n describe('JWT expiry parsing', () => {\n it('should correctly parse expiry from generated JWT', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n\n // expiresAt should be a valid timestamp in milliseconds\n expect(typeof result.expiresAt).toBe('number')\n expect(result.expiresAt).toBeGreaterThan(Date.now())\n\n // Should be ~25 minutes in the future\n const expectedExpiry = Date.now() + 25 * 60 * 1000\n const tolerance = 2 * 60 * 1000 // 2 minutes tolerance\n expect(Math.abs(result.expiresAt - expectedExpiry)).toBeLessThan(tolerance)\n })\n\n it('should handle JWT with past expiry date', async () => {\n // This tests that parsing works even for expired tokens\n // (validation happens later in jwt-validator)\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n\n // Should parse without throwing, even if theoretically expired\n expect(result.expiresAt).toBeDefined()\n expect(typeof result.expiresAt).toBe('number')\n })\n\n it('should handle JWT with far future expiry', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n\n // expiresAt should be reasonable (within next 30 days)\n const maxFuture = Date.now() + 30 * 24 * 60 * 60 * 1000\n expect(result.expiresAt).toBeLessThan(maxFuture)\n })\n })\n\n describe('error handling', () => {\n it('should handle empty access token', async () => {\n // Mock implementation doesn't validate access token, but real impl will\n // For now, this should still work\n const result = await getProjectToken('', mockTenantId)\n expect(result).toHaveProperty('token')\n })\n\n it('should handle empty tenant ID', async () => {\n // Mock implementation should handle this\n const result = await getProjectToken(mockAccessToken, '')\n expect(result.tenantId).toBe('')\n })\n\n it('should handle special characters in tenant ID', async () => {\n const specialTenantId = 'tenant-with-special_chars.123'\n const result = await getProjectToken(mockAccessToken, specialTenantId)\n\n expect(result.tenantId).toBe(specialTenantId)\n })\n })\n })\n\n describe('JWT format edge cases', () => {\n it('should generate base64url encoded parts (no + / =)', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n\n // Base64url should not contain +, /, or =\n expect(result.token).not.toMatch(/[+/=]/)\n })\n\n it('should generate decodable header and payload', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n\n // Should be able to decode without errors\n expect(() => parseJwtHeader(result.token)).not.toThrow()\n expect(() => parseJwtPayload(result.token)).not.toThrow()\n })\n\n it('should generate valid JSON in payload', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n const payload = parseJwtPayload(result.token)\n\n // Should be a valid object with expected structure\n expect(payload).toBeTypeOf('object')\n expect(payload).not.toBeNull()\n })\n })\n\n describe('return value structure', () => {\n it('should return ProjectToken with all required fields', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n\n expect(result).toHaveProperty('token')\n expect(result).toHaveProperty('expiresAt')\n expect(result).toHaveProperty('tenantId')\n expect(typeof result.token).toBe('string')\n expect(typeof result.expiresAt).toBe('number')\n expect(typeof result.tenantId).toBe('string')\n })\n\n it('should not include claims in initial response', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n\n // Claims are added after JWT validation, not in API response\n expect(result).not.toHaveProperty('claims')\n })\n\n it('should match ProjectToken interface', async () => {\n const result = await getProjectToken(mockAccessToken, mockTenantId)\n\n // Verify structure matches ProjectToken interface\n const keys = Object.keys(result).sort()\n expect(keys).toEqual(['expiresAt', 'tenantId', 'token'])\n })\n })\n})\n"],"names":["describe","expect","it","getProjectToken","decodeJwtPart","part","base64","replace","padded","padEnd","length","Buffer","from","toString","parseJwtHeader","token","headerPart","split","JSON","parse","parseJwtPayload","payloadPart","mockAccessToken","mockTenantId","result","toHaveProperty","tenantId","toBe","parts","toHaveLength","before","Date","now","after","expectedMin","expectedMax","expiresAt","toBeGreaterThan","toBeLessThan","token1","token2","not","header","alg","typ","payload","aud","exp","Math","floor","iat","toBeGreaterThanOrEqual","toBeLessThanOrEqual","expectedExpiry","tolerance","abs","toBeDefined","maxFuture","specialTenantId","toMatch","toThrow","toBeTypeOf","toBeNull","keys","Object","sort","toEqual"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FigmaTokenResponse, FigmaTokens } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate mock FigmaTokens for testing
|
|
4
|
+
*/
|
|
5
|
+
export declare function mockFigmaTokens(overrides?: Partial<FigmaTokens>): FigmaTokens;
|
|
6
|
+
/**
|
|
7
|
+
* Generate mock expired tokens for testing
|
|
8
|
+
*/
|
|
9
|
+
export declare function mockExpiredTokens(overrides?: Partial<FigmaTokens>): FigmaTokens;
|
|
10
|
+
/**
|
|
11
|
+
* Generate mock tokens expiring soon (within buffer window) for testing
|
|
12
|
+
*/
|
|
13
|
+
export declare function mockTokensExpiringSoon(overrides?: Partial<FigmaTokens>): FigmaTokens;
|
|
14
|
+
/**
|
|
15
|
+
* Generate mock tokens with plenty of time remaining
|
|
16
|
+
*/
|
|
17
|
+
export declare function mockFreshTokens(overrides?: Partial<FigmaTokens>): FigmaTokens;
|
|
18
|
+
/**
|
|
19
|
+
* Generate mock Figma API token response
|
|
20
|
+
*/
|
|
21
|
+
export declare function mockFigmaTokenResponse(overrides?: Partial<FigmaTokenResponse>): FigmaTokenResponse;
|
|
22
|
+
/**
|
|
23
|
+
* Generate mock malformed token response (missing required fields)
|
|
24
|
+
*/
|
|
25
|
+
export declare function mockMalformedTokenResponse(): Partial<FigmaTokenResponse>;
|
|
26
|
+
//# sourceMappingURL=fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../../src/auth/__tests__/fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAElE;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAY7E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAO/E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAQpF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAQ7E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GACtC,kBAAkB,CAQpB;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAKxE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate mock FigmaTokens for testing
|
|
3
|
+
*/ export function mockFigmaTokens(overrides) {
|
|
4
|
+
const now = Date.now();
|
|
5
|
+
const oneHourFromNow = now + 60 * 60 * 1000 // 1 hour
|
|
6
|
+
;
|
|
7
|
+
return {
|
|
8
|
+
accessToken: 'figt_test_access_token_123456789',
|
|
9
|
+
expiresAt: oneHourFromNow,
|
|
10
|
+
refreshToken: 'figt_test_refresh_token_987654321',
|
|
11
|
+
scopes: [
|
|
12
|
+
'current_user:read'
|
|
13
|
+
],
|
|
14
|
+
userId: 'user_123',
|
|
15
|
+
...overrides
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generate mock expired tokens for testing
|
|
20
|
+
*/ export function mockExpiredTokens(overrides) {
|
|
21
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000 // 1 hour ago
|
|
22
|
+
;
|
|
23
|
+
return mockFigmaTokens({
|
|
24
|
+
expiresAt: oneHourAgo,
|
|
25
|
+
...overrides
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Generate mock tokens expiring soon (within buffer window) for testing
|
|
30
|
+
*/ export function mockTokensExpiringSoon(overrides) {
|
|
31
|
+
// 4 minutes from now (within 5 minute buffer)
|
|
32
|
+
const fourMinutesFromNow = Date.now() + 4 * 60 * 1000;
|
|
33
|
+
return mockFigmaTokens({
|
|
34
|
+
expiresAt: fourMinutesFromNow,
|
|
35
|
+
...overrides
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Generate mock tokens with plenty of time remaining
|
|
40
|
+
*/ export function mockFreshTokens(overrides) {
|
|
41
|
+
// 2 hours from now
|
|
42
|
+
const twoHoursFromNow = Date.now() + 2 * 60 * 60 * 1000;
|
|
43
|
+
return mockFigmaTokens({
|
|
44
|
+
expiresAt: twoHoursFromNow,
|
|
45
|
+
...overrides
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Generate mock Figma API token response
|
|
50
|
+
*/ export function mockFigmaTokenResponse(overrides) {
|
|
51
|
+
return {
|
|
52
|
+
access_token: 'figt_test_access_token_123456789',
|
|
53
|
+
expires_in: 3600,
|
|
54
|
+
refresh_token: 'figt_test_refresh_token_987654321',
|
|
55
|
+
user_id_string: 'user_123',
|
|
56
|
+
...overrides
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Generate mock malformed token response (missing required fields)
|
|
61
|
+
*/ export function mockMalformedTokenResponse() {
|
|
62
|
+
return {
|
|
63
|
+
access_token: 'figt_test_access_token_123456789'
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/auth/__tests__/fixtures.ts"],"sourcesContent":["import type { FigmaTokenResponse, FigmaTokens } from '../types.js'\n\n/**\n * Generate mock FigmaTokens for testing\n */\nexport function mockFigmaTokens(overrides?: Partial<FigmaTokens>): FigmaTokens {\n const now = Date.now()\n const oneHourFromNow = now + 60 * 60 * 1000 // 1 hour\n\n return {\n accessToken: 'figt_test_access_token_123456789',\n expiresAt: oneHourFromNow,\n refreshToken: 'figt_test_refresh_token_987654321',\n scopes: ['current_user:read'],\n userId: 'user_123',\n ...overrides,\n }\n}\n\n/**\n * Generate mock expired tokens for testing\n */\nexport function mockExpiredTokens(overrides?: Partial<FigmaTokens>): FigmaTokens {\n const oneHourAgo = Date.now() - 60 * 60 * 1000 // 1 hour ago\n\n return mockFigmaTokens({\n expiresAt: oneHourAgo,\n ...overrides,\n })\n}\n\n/**\n * Generate mock tokens expiring soon (within buffer window) for testing\n */\nexport function mockTokensExpiringSoon(overrides?: Partial<FigmaTokens>): FigmaTokens {\n // 4 minutes from now (within 5 minute buffer)\n const fourMinutesFromNow = Date.now() + 4 * 60 * 1000\n\n return mockFigmaTokens({\n expiresAt: fourMinutesFromNow,\n ...overrides,\n })\n}\n\n/**\n * Generate mock tokens with plenty of time remaining\n */\nexport function mockFreshTokens(overrides?: Partial<FigmaTokens>): FigmaTokens {\n // 2 hours from now\n const twoHoursFromNow = Date.now() + 2 * 60 * 60 * 1000\n\n return mockFigmaTokens({\n expiresAt: twoHoursFromNow,\n ...overrides,\n })\n}\n\n/**\n * Generate mock Figma API token response\n */\nexport function mockFigmaTokenResponse(\n overrides?: Partial<FigmaTokenResponse>,\n): FigmaTokenResponse {\n return {\n access_token: 'figt_test_access_token_123456789',\n expires_in: 3600, // 1 hour in seconds\n refresh_token: 'figt_test_refresh_token_987654321',\n user_id_string: 'user_123',\n ...overrides,\n }\n}\n\n/**\n * Generate mock malformed token response (missing required fields)\n */\nexport function mockMalformedTokenResponse(): Partial<FigmaTokenResponse> {\n return {\n access_token: 'figt_test_access_token_123456789',\n // Missing refresh_token and expires_in\n }\n}\n"],"names":["mockFigmaTokens","overrides","now","Date","oneHourFromNow","accessToken","expiresAt","refreshToken","scopes","userId","mockExpiredTokens","oneHourAgo","mockTokensExpiringSoon","fourMinutesFromNow","mockFreshTokens","twoHoursFromNow","mockFigmaTokenResponse","access_token","expires_in","refresh_token","user_id_string","mockMalformedTokenResponse"],"mappings":"AAEA;;CAEC,GACD,OAAO,SAASA,gBAAgBC,SAAgC;IAC9D,MAAMC,MAAMC,KAAKD,GAAG;IACpB,MAAME,iBAAiBF,MAAM,KAAK,KAAK,KAAK,SAAS;;IAErD,OAAO;QACLG,aAAa;QACbC,WAAWF;QACXG,cAAc;QACdC,QAAQ;YAAC;SAAoB;QAC7BC,QAAQ;QACR,GAAGR,SAAS;IACd;AACF;AAEA;;CAEC,GACD,OAAO,SAASS,kBAAkBT,SAAgC;IAChE,MAAMU,aAAaR,KAAKD,GAAG,KAAK,KAAK,KAAK,KAAK,aAAa;;IAE5D,OAAOF,gBAAgB;QACrBM,WAAWK;QACX,GAAGV,SAAS;IACd;AACF;AAEA;;CAEC,GACD,OAAO,SAASW,uBAAuBX,SAAgC;IACrE,8CAA8C;IAC9C,MAAMY,qBAAqBV,KAAKD,GAAG,KAAK,IAAI,KAAK;IAEjD,OAAOF,gBAAgB;QACrBM,WAAWO;QACX,GAAGZ,SAAS;IACd;AACF;AAEA;;CAEC,GACD,OAAO,SAASa,gBAAgBb,SAAgC;IAC9D,mBAAmB;IACnB,MAAMc,kBAAkBZ,KAAKD,GAAG,KAAK,IAAI,KAAK,KAAK;IAEnD,OAAOF,gBAAgB;QACrBM,WAAWS;QACX,GAAGd,SAAS;IACd;AACF;AAEA;;CAEC,GACD,OAAO,SAASe,uBACdf,SAAuC;IAEvC,OAAO;QACLgB,cAAc;QACdC,YAAY;QACZC,eAAe;QACfC,gBAAgB;QAChB,GAAGnB,SAAS;IACd;AACF;AAEA;;CAEC,GACD,OAAO,SAASoB;IACd,OAAO;QACLJ,cAAc;IAEhB;AACF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { FigmaTokenResponse } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a mock fetch response
|
|
4
|
+
*/
|
|
5
|
+
export declare function mockFetchResponse(data: unknown, options?: {
|
|
6
|
+
ok?: boolean;
|
|
7
|
+
status?: number;
|
|
8
|
+
}): Response;
|
|
9
|
+
/**
|
|
10
|
+
* Create a successful mock fetch response with token data
|
|
11
|
+
*/
|
|
12
|
+
export declare function mockSuccessfulTokenResponse(data: FigmaTokenResponse): Response;
|
|
13
|
+
/**
|
|
14
|
+
* Create a failed mock fetch response
|
|
15
|
+
*/
|
|
16
|
+
export declare function mockFailedFetchResponse(status: number, errorMessage: string): Response;
|
|
17
|
+
/**
|
|
18
|
+
* Mock global fetch with a successful response
|
|
19
|
+
*/
|
|
20
|
+
export declare function mockFetchSuccess(responseData: FigmaTokenResponse): import("vitest").Mock<(...args: any[]) => any>;
|
|
21
|
+
/**
|
|
22
|
+
* Mock global fetch with a failed response
|
|
23
|
+
*/
|
|
24
|
+
export declare function mockFetchFailure(status: number, errorMessage: string): import("vitest").Mock<(...args: any[]) => any>;
|
|
25
|
+
/**
|
|
26
|
+
* Mock global fetch with a network error
|
|
27
|
+
*/
|
|
28
|
+
export declare function mockFetchNetworkError(errorMessage?: string): import("vitest").Mock<(...args: any[]) => any>;
|
|
29
|
+
/**
|
|
30
|
+
* Restore original fetch (cleanup after tests)
|
|
31
|
+
*/
|
|
32
|
+
export declare function restoreFetch(): void;
|
|
33
|
+
//# sourceMappingURL=mocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mocks.d.ts","sourceRoot":"","sources":["../../../src/auth/__tests__/mocks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAErD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAQzF,QAAQ,CACd;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,kBAAkB,YAEnE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAQrE,QAAQ,CACd;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,kBAAkB,kDAIhE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,kDAIpE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,SAAkB,kDAInE;AAED;;GAEG;AACH,wBAAgB,YAAY,SAE3B"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/* eslint-disable perfectionist/sort-objects */ /* eslint-disable @typescript-eslint/require-await */ import { vi } from 'vitest';
|
|
2
|
+
/**
|
|
3
|
+
* Create a mock fetch response
|
|
4
|
+
*/ export function mockFetchResponse(data, options = {}) {
|
|
5
|
+
return {
|
|
6
|
+
ok: options.ok ?? true,
|
|
7
|
+
status: options.status ?? 200,
|
|
8
|
+
statusText: options.status === 200 ? 'OK' : 'Error',
|
|
9
|
+
headers: new Headers(),
|
|
10
|
+
json: async ()=>data,
|
|
11
|
+
text: async ()=>JSON.stringify(data)
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create a successful mock fetch response with token data
|
|
16
|
+
*/ export function mockSuccessfulTokenResponse(data) {
|
|
17
|
+
return mockFetchResponse(data, {
|
|
18
|
+
ok: true,
|
|
19
|
+
status: 200
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a failed mock fetch response
|
|
24
|
+
*/ export function mockFailedFetchResponse(status, errorMessage) {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
status,
|
|
28
|
+
statusText: 'Error',
|
|
29
|
+
headers: new Headers(),
|
|
30
|
+
json: async ()=>({
|
|
31
|
+
error: errorMessage
|
|
32
|
+
}),
|
|
33
|
+
text: async ()=>errorMessage
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Mock global fetch with a successful response
|
|
38
|
+
*/ export function mockFetchSuccess(responseData) {
|
|
39
|
+
const mockFetch = vi.fn().mockResolvedValue(mockSuccessfulTokenResponse(responseData));
|
|
40
|
+
global.fetch = mockFetch;
|
|
41
|
+
return mockFetch;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Mock global fetch with a failed response
|
|
45
|
+
*/ export function mockFetchFailure(status, errorMessage) {
|
|
46
|
+
const mockFetch = vi.fn().mockResolvedValue(mockFailedFetchResponse(status, errorMessage));
|
|
47
|
+
global.fetch = mockFetch;
|
|
48
|
+
return mockFetch;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Mock global fetch with a network error
|
|
52
|
+
*/ export function mockFetchNetworkError(errorMessage = 'Network error') {
|
|
53
|
+
const mockFetch = vi.fn().mockRejectedValue(new Error(errorMessage));
|
|
54
|
+
global.fetch = mockFetch;
|
|
55
|
+
return mockFetch;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Restore original fetch (cleanup after tests)
|
|
59
|
+
*/ export function restoreFetch() {
|
|
60
|
+
vi.restoreAllMocks();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=mocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/auth/__tests__/mocks.ts"],"sourcesContent":["/* eslint-disable perfectionist/sort-objects */\n/* eslint-disable @typescript-eslint/require-await */\nimport { vi } from 'vitest'\n\nimport type { FigmaTokenResponse } from '../types.js'\n\n/**\n * Create a mock fetch response\n */\nexport function mockFetchResponse(data: unknown, options: { ok?: boolean; status?: number } = {}) {\n return {\n ok: options.ok ?? true,\n status: options.status ?? 200,\n statusText: options.status === 200 ? 'OK' : 'Error',\n headers: new Headers(),\n json: async () => data,\n text: async () => JSON.stringify(data),\n } as Response\n}\n\n/**\n * Create a successful mock fetch response with token data\n */\nexport function mockSuccessfulTokenResponse(data: FigmaTokenResponse) {\n return mockFetchResponse(data, { ok: true, status: 200 })\n}\n\n/**\n * Create a failed mock fetch response\n */\nexport function mockFailedFetchResponse(status: number, errorMessage: string) {\n return {\n ok: false,\n status,\n statusText: 'Error',\n headers: new Headers(),\n json: async () => ({ error: errorMessage }),\n text: async () => errorMessage,\n } as Response\n}\n\n/**\n * Mock global fetch with a successful response\n */\nexport function mockFetchSuccess(responseData: FigmaTokenResponse) {\n const mockFetch = vi.fn().mockResolvedValue(mockSuccessfulTokenResponse(responseData))\n global.fetch = mockFetch\n return mockFetch\n}\n\n/**\n * Mock global fetch with a failed response\n */\nexport function mockFetchFailure(status: number, errorMessage: string) {\n const mockFetch = vi.fn().mockResolvedValue(mockFailedFetchResponse(status, errorMessage))\n global.fetch = mockFetch\n return mockFetch\n}\n\n/**\n * Mock global fetch with a network error\n */\nexport function mockFetchNetworkError(errorMessage = 'Network error') {\n const mockFetch = vi.fn().mockRejectedValue(new Error(errorMessage))\n global.fetch = mockFetch\n return mockFetch\n}\n\n/**\n * Restore original fetch (cleanup after tests)\n */\nexport function restoreFetch() {\n vi.restoreAllMocks()\n}\n"],"names":["vi","mockFetchResponse","data","options","ok","status","statusText","headers","Headers","json","text","JSON","stringify","mockSuccessfulTokenResponse","mockFailedFetchResponse","errorMessage","error","mockFetchSuccess","responseData","mockFetch","fn","mockResolvedValue","global","fetch","mockFetchFailure","mockFetchNetworkError","mockRejectedValue","Error","restoreFetch","restoreAllMocks"],"mappings":"AAAA,6CAA6C,GAC7C,mDAAmD,GACnD,SAASA,EAAE,QAAQ,SAAQ;AAI3B;;CAEC,GACD,OAAO,SAASC,kBAAkBC,IAAa,EAAEC,UAA6C,CAAC,CAAC;IAC9F,OAAO;QACLC,IAAID,QAAQC,EAAE,IAAI;QAClBC,QAAQF,QAAQE,MAAM,IAAI;QAC1BC,YAAYH,QAAQE,MAAM,KAAK,MAAM,OAAO;QAC5CE,SAAS,IAAIC;QACbC,MAAM,UAAYP;QAClBQ,MAAM,UAAYC,KAAKC,SAAS,CAACV;IACnC;AACF;AAEA;;CAEC,GACD,OAAO,SAASW,4BAA4BX,IAAwB;IAClE,OAAOD,kBAAkBC,MAAM;QAAEE,IAAI;QAAMC,QAAQ;IAAI;AACzD;AAEA;;CAEC,GACD,OAAO,SAASS,wBAAwBT,MAAc,EAAEU,YAAoB;IAC1E,OAAO;QACLX,IAAI;QACJC;QACAC,YAAY;QACZC,SAAS,IAAIC;QACbC,MAAM,UAAa,CAAA;gBAAEO,OAAOD;YAAa,CAAA;QACzCL,MAAM,UAAYK;IACpB;AACF;AAEA;;CAEC,GACD,OAAO,SAASE,iBAAiBC,YAAgC;IAC/D,MAAMC,YAAYnB,GAAGoB,EAAE,GAAGC,iBAAiB,CAACR,4BAA4BK;IACxEI,OAAOC,KAAK,GAAGJ;IACf,OAAOA;AACT;AAEA;;CAEC,GACD,OAAO,SAASK,iBAAiBnB,MAAc,EAAEU,YAAoB;IACnE,MAAMI,YAAYnB,GAAGoB,EAAE,GAAGC,iBAAiB,CAACP,wBAAwBT,QAAQU;IAC5EO,OAAOC,KAAK,GAAGJ;IACf,OAAOA;AACT;AAEA;;CAEC,GACD,OAAO,SAASM,sBAAsBV,eAAe,eAAe;IAClE,MAAMI,YAAYnB,GAAGoB,EAAE,GAAGM,iBAAiB,CAAC,IAAIC,MAAMZ;IACtDO,OAAOC,KAAK,GAAGJ;IACf,OAAOA;AACT;AAEA;;CAEC,GACD,OAAO,SAASS;IACd5B,GAAG6B,eAAe;AACpB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown when browser launch fails
|
|
3
|
+
*/
|
|
4
|
+
export declare class BrowserError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Open a URL in the user's default browser
|
|
9
|
+
*
|
|
10
|
+
* Uses the `open` package for cross-platform browser launching
|
|
11
|
+
*
|
|
12
|
+
* @param url - URL to open in browser
|
|
13
|
+
* @throws BrowserError if browser launch fails
|
|
14
|
+
*/
|
|
15
|
+
export declare function openBrowser(url: string): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Build OAuth authorization URL with all required parameters
|
|
18
|
+
*
|
|
19
|
+
* @param params - Authorization URL parameters
|
|
20
|
+
* @returns Complete authorization URL
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildAuthorizationUrl(params: {
|
|
23
|
+
authorizationUrl: string;
|
|
24
|
+
clientId: string;
|
|
25
|
+
codeChallenge: string;
|
|
26
|
+
codeChallengeMethod: 'S256';
|
|
27
|
+
redirectUri: string;
|
|
28
|
+
scopes: string[];
|
|
29
|
+
state: string;
|
|
30
|
+
}): string;
|
|
31
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/auth/browser.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5D;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,gBAAgB,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CACd,GAAG,MAAM,CAYT"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import open from 'open';
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when browser launch fails
|
|
4
|
+
*/ export class BrowserError extends Error {
|
|
5
|
+
constructor(message){
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'BrowserError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Open a URL in the user's default browser
|
|
12
|
+
*
|
|
13
|
+
* Uses the `open` package for cross-platform browser launching
|
|
14
|
+
*
|
|
15
|
+
* @param url - URL to open in browser
|
|
16
|
+
* @throws BrowserError if browser launch fails
|
|
17
|
+
*/ export async function openBrowser(url) {
|
|
18
|
+
try {
|
|
19
|
+
await open(url);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
throw new BrowserError(`Failed to open browser: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build OAuth authorization URL with all required parameters
|
|
26
|
+
*
|
|
27
|
+
* @param params - Authorization URL parameters
|
|
28
|
+
* @returns Complete authorization URL
|
|
29
|
+
*/ export function buildAuthorizationUrl(params) {
|
|
30
|
+
const url = new URL(params.authorizationUrl);
|
|
31
|
+
url.searchParams.set('client_id', params.clientId);
|
|
32
|
+
url.searchParams.set('redirect_uri', params.redirectUri);
|
|
33
|
+
url.searchParams.set('response_type', 'code');
|
|
34
|
+
url.searchParams.set('scope', params.scopes.join(' '));
|
|
35
|
+
url.searchParams.set('state', params.state);
|
|
36
|
+
url.searchParams.set('code_challenge', params.codeChallenge);
|
|
37
|
+
url.searchParams.set('code_challenge_method', params.codeChallengeMethod);
|
|
38
|
+
return url.toString();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/auth/browser.ts"],"sourcesContent":["import open from 'open'\n\n/**\n * Error thrown when browser launch fails\n */\nexport class BrowserError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'BrowserError'\n }\n}\n\n/**\n * Open a URL in the user's default browser\n *\n * Uses the `open` package for cross-platform browser launching\n *\n * @param url - URL to open in browser\n * @throws BrowserError if browser launch fails\n */\nexport async function openBrowser(url: string): Promise<void> {\n try {\n await open(url)\n } catch (error) {\n throw new BrowserError(\n `Failed to open browser: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n}\n\n/**\n * Build OAuth authorization URL with all required parameters\n *\n * @param params - Authorization URL parameters\n * @returns Complete authorization URL\n */\nexport function buildAuthorizationUrl(params: {\n authorizationUrl: string\n clientId: string\n codeChallenge: string\n codeChallengeMethod: 'S256'\n redirectUri: string\n scopes: string[]\n state: string\n}): string {\n const url = new URL(params.authorizationUrl)\n\n url.searchParams.set('client_id', params.clientId)\n url.searchParams.set('redirect_uri', params.redirectUri)\n url.searchParams.set('response_type', 'code')\n url.searchParams.set('scope', params.scopes.join(' '))\n url.searchParams.set('state', params.state)\n url.searchParams.set('code_challenge', params.codeChallenge)\n url.searchParams.set('code_challenge_method', params.codeChallengeMethod)\n\n return url.toString()\n}\n"],"names":["open","BrowserError","Error","message","name","openBrowser","url","error","buildAuthorizationUrl","params","URL","authorizationUrl","searchParams","set","clientId","redirectUri","scopes","join","state","codeChallenge","codeChallengeMethod","toString"],"mappings":"AAAA,OAAOA,UAAU,OAAM;AAEvB;;CAEC,GACD,OAAO,MAAMC,qBAAqBC;IAChC,YAAYC,OAAe,CAAE;QAC3B,KAAK,CAACA;QACN,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA;;;;;;;CAOC,GACD,OAAO,eAAeC,YAAYC,GAAW;IAC3C,IAAI;QACF,MAAMN,KAAKM;IACb,EAAE,OAAOC,OAAO;QACd,MAAM,IAAIN,aACR,CAAC,wBAAwB,EAAEM,iBAAiBL,QAAQK,MAAMJ,OAAO,GAAG,iBAAiB;IAEzF;AACF;AAEA;;;;;CAKC,GACD,OAAO,SAASK,sBAAsBC,MAQrC;IACC,MAAMH,MAAM,IAAII,IAAID,OAAOE,gBAAgB;IAE3CL,IAAIM,YAAY,CAACC,GAAG,CAAC,aAAaJ,OAAOK,QAAQ;IACjDR,IAAIM,YAAY,CAACC,GAAG,CAAC,gBAAgBJ,OAAOM,WAAW;IACvDT,IAAIM,YAAY,CAACC,GAAG,CAAC,iBAAiB;IACtCP,IAAIM,YAAY,CAACC,GAAG,CAAC,SAASJ,OAAOO,MAAM,CAACC,IAAI,CAAC;IACjDX,IAAIM,YAAY,CAACC,GAAG,CAAC,SAASJ,OAAOS,KAAK;IAC1CZ,IAAIM,YAAY,CAACC,GAAG,CAAC,kBAAkBJ,OAAOU,aAAa;IAC3Db,IAAIM,YAAY,CAACC,GAAG,CAAC,yBAAyBJ,OAAOW,mBAAmB;IAExE,OAAOd,IAAIe,QAAQ;AACrB"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result from OAuth callback server
|
|
3
|
+
*/
|
|
4
|
+
export interface CallbackResult {
|
|
5
|
+
/** Authorization code from OAuth provider */
|
|
6
|
+
code: string;
|
|
7
|
+
/** State parameter for CSRF verification */
|
|
8
|
+
state: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Error from OAuth callback
|
|
12
|
+
*/
|
|
13
|
+
export declare class CallbackError extends Error {
|
|
14
|
+
code?: string | undefined;
|
|
15
|
+
constructor(message: string, code?: string | undefined);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Local HTTP server for handling OAuth callbacks
|
|
19
|
+
*
|
|
20
|
+
* Starts a temporary server on localhost to receive the OAuth authorization code.
|
|
21
|
+
* The server automatically shuts down after receiving the callback or on timeout.
|
|
22
|
+
*
|
|
23
|
+
* Implements dynamic port allocation: tries preferred port first, falls back to
|
|
24
|
+
* OS-assigned port if unavailable.
|
|
25
|
+
*/
|
|
26
|
+
export declare class CallbackServer {
|
|
27
|
+
private actualPort?;
|
|
28
|
+
private preferredPort;
|
|
29
|
+
private server;
|
|
30
|
+
private timeoutMs;
|
|
31
|
+
constructor(options?: {
|
|
32
|
+
port?: number;
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* Render error page shown in browser when auth fails
|
|
37
|
+
*/
|
|
38
|
+
private renderErrorPage;
|
|
39
|
+
/**
|
|
40
|
+
* Render success page shown in browser after successful auth
|
|
41
|
+
*/
|
|
42
|
+
private renderSuccessPage;
|
|
43
|
+
/**
|
|
44
|
+
* Get the actual port the server is listening on
|
|
45
|
+
* @returns Port number (may differ from preferred if that was in use)
|
|
46
|
+
*/
|
|
47
|
+
getActualPort(): number;
|
|
48
|
+
/**
|
|
49
|
+
* Stop the server if running
|
|
50
|
+
*/
|
|
51
|
+
stop(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Start server and wait for OAuth callback
|
|
54
|
+
*
|
|
55
|
+
* @param expectedState - State parameter to verify CSRF protection
|
|
56
|
+
* @returns Promise resolving to callback result with code and state
|
|
57
|
+
* @throws CallbackError if callback fails or times out
|
|
58
|
+
*/
|
|
59
|
+
waitForCallback(expectedState: string): Promise<CallbackResult>;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=callback-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback-server.d.ts","sourceRoot":"","sources":["../../src/auth/callback-server.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAA;IACZ,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAG7B,IAAI,CAAC,EAAE,MAAM;gBADpB,OAAO,EAAE,MAAM,EACR,IAAI,CAAC,EAAE,MAAM,YAAA;CAKvB;AAED;;;;;;;;GAQG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,SAAS,CAAQ;gBAEb,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO;IAM/D;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAIzB;;;OAGG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;;;;;OAMG;IACG,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CA8GtE"}
|