@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.
Files changed (305) hide show
  1. package/LICENSE.md +11 -0
  2. package/bin/cli.js +4 -0
  3. package/dist/api/control-plane.d.ts +124 -0
  4. package/dist/api/control-plane.d.ts.map +1 -0
  5. package/dist/api/control-plane.js +245 -0
  6. package/dist/api/control-plane.js.map +1 -0
  7. package/dist/api/control-plane.spec.d.ts +2 -0
  8. package/dist/api/control-plane.spec.d.ts.map +1 -0
  9. package/dist/api/control-plane.spec.js +296 -0
  10. package/dist/api/control-plane.spec.js.map +1 -0
  11. package/dist/api/figma-api.d.ts +31 -0
  12. package/dist/api/figma-api.d.ts.map +1 -0
  13. package/dist/api/figma-api.js +169 -0
  14. package/dist/api/figma-api.js.map +1 -0
  15. package/dist/api/figma-api.spec.d.ts +14 -0
  16. package/dist/api/figma-api.spec.d.ts.map +1 -0
  17. package/dist/api/figma-api.spec.js +201 -0
  18. package/dist/api/figma-api.spec.js.map +1 -0
  19. package/dist/auth/__tests__/fixtures.d.ts +26 -0
  20. package/dist/auth/__tests__/fixtures.d.ts.map +1 -0
  21. package/dist/auth/__tests__/fixtures.js +67 -0
  22. package/dist/auth/__tests__/fixtures.js.map +1 -0
  23. package/dist/auth/__tests__/mocks.d.ts +33 -0
  24. package/dist/auth/__tests__/mocks.d.ts.map +1 -0
  25. package/dist/auth/__tests__/mocks.js +63 -0
  26. package/dist/auth/__tests__/mocks.js.map +1 -0
  27. package/dist/auth/browser.d.ts +31 -0
  28. package/dist/auth/browser.d.ts.map +1 -0
  29. package/dist/auth/browser.js +41 -0
  30. package/dist/auth/browser.js.map +1 -0
  31. package/dist/auth/callback-server.d.ts +61 -0
  32. package/dist/auth/callback-server.d.ts.map +1 -0
  33. package/dist/auth/callback-server.js +168 -0
  34. package/dist/auth/callback-server.js.map +1 -0
  35. package/dist/auth/callback-server.spec.d.ts +2 -0
  36. package/dist/auth/callback-server.spec.d.ts.map +1 -0
  37. package/dist/auth/callback-server.spec.js +85 -0
  38. package/dist/auth/callback-server.spec.js.map +1 -0
  39. package/dist/auth/crypto-utils.d.ts +33 -0
  40. package/dist/auth/crypto-utils.d.ts.map +1 -0
  41. package/dist/auth/crypto-utils.js +96 -0
  42. package/dist/auth/crypto-utils.js.map +1 -0
  43. package/dist/auth/crypto-utils.spec.d.ts +2 -0
  44. package/dist/auth/crypto-utils.spec.d.ts.map +1 -0
  45. package/dist/auth/crypto-utils.spec.js +36 -0
  46. package/dist/auth/crypto-utils.spec.js.map +1 -0
  47. package/dist/auth/jwt-validator.d.ts +41 -0
  48. package/dist/auth/jwt-validator.d.ts.map +1 -0
  49. package/dist/auth/jwt-validator.js +113 -0
  50. package/dist/auth/jwt-validator.js.map +1 -0
  51. package/dist/auth/jwt-validator.spec.d.ts +10 -0
  52. package/dist/auth/jwt-validator.spec.d.ts.map +1 -0
  53. package/dist/auth/jwt-validator.spec.js +236 -0
  54. package/dist/auth/jwt-validator.spec.js.map +1 -0
  55. package/dist/auth/oauth-flow.d.ts +62 -0
  56. package/dist/auth/oauth-flow.d.ts.map +1 -0
  57. package/dist/auth/oauth-flow.js +172 -0
  58. package/dist/auth/oauth-flow.js.map +1 -0
  59. package/dist/auth/oauth-flow.spec.d.ts +2 -0
  60. package/dist/auth/oauth-flow.spec.d.ts.map +1 -0
  61. package/dist/auth/oauth-flow.spec.js +134 -0
  62. package/dist/auth/oauth-flow.spec.js.map +1 -0
  63. package/dist/auth/pages/error.html +85 -0
  64. package/dist/auth/pages/success.html +74 -0
  65. package/dist/auth/pkce.d.ts +51 -0
  66. package/dist/auth/pkce.d.ts.map +1 -0
  67. package/dist/auth/pkce.js +71 -0
  68. package/dist/auth/pkce.js.map +1 -0
  69. package/dist/auth/pkce.spec.d.ts +2 -0
  70. package/dist/auth/pkce.spec.d.ts.map +1 -0
  71. package/dist/auth/pkce.spec.js +32 -0
  72. package/dist/auth/pkce.spec.js.map +1 -0
  73. package/dist/auth/project-token.d.ts +47 -0
  74. package/dist/auth/project-token.d.ts.map +1 -0
  75. package/dist/auth/project-token.js +134 -0
  76. package/dist/auth/project-token.js.map +1 -0
  77. package/dist/auth/project-token.spec.d.ts +2 -0
  78. package/dist/auth/project-token.spec.d.ts.map +1 -0
  79. package/dist/auth/project-token.spec.js +332 -0
  80. package/dist/auth/project-token.spec.js.map +1 -0
  81. package/dist/auth/refresh.d.ts +35 -0
  82. package/dist/auth/refresh.d.ts.map +1 -0
  83. package/dist/auth/refresh.js +149 -0
  84. package/dist/auth/refresh.js.map +1 -0
  85. package/dist/auth/refresh.spec.d.ts +2 -0
  86. package/dist/auth/refresh.spec.d.ts.map +1 -0
  87. package/dist/auth/refresh.spec.js +105 -0
  88. package/dist/auth/refresh.spec.js.map +1 -0
  89. package/dist/auth/token-store.d.ts +110 -0
  90. package/dist/auth/token-store.d.ts.map +1 -0
  91. package/dist/auth/token-store.js +188 -0
  92. package/dist/auth/token-store.js.map +1 -0
  93. package/dist/auth/token-store.spec.d.ts +2 -0
  94. package/dist/auth/token-store.spec.d.ts.map +1 -0
  95. package/dist/auth/token-store.spec.js +276 -0
  96. package/dist/auth/token-store.spec.js.map +1 -0
  97. package/dist/auth/types.d.ts +197 -0
  98. package/dist/auth/types.d.ts.map +1 -0
  99. package/dist/auth/types.js +6 -0
  100. package/dist/auth/types.js.map +1 -0
  101. package/dist/cli.d.ts +5 -0
  102. package/dist/cli.d.ts.map +1 -0
  103. package/dist/cli.js +108 -0
  104. package/dist/cli.js.map +1 -0
  105. package/dist/commands/__tests__/test-utils.d.ts +28 -0
  106. package/dist/commands/__tests__/test-utils.d.ts.map +1 -0
  107. package/dist/commands/__tests__/test-utils.js +34 -0
  108. package/dist/commands/__tests__/test-utils.js.map +1 -0
  109. package/dist/commands/deploy.d.ts +16 -0
  110. package/dist/commands/deploy.d.ts.map +1 -0
  111. package/dist/commands/deploy.js +297 -0
  112. package/dist/commands/deploy.js.map +1 -0
  113. package/dist/commands/deploy.spec.d.ts +2 -0
  114. package/dist/commands/deploy.spec.d.ts.map +1 -0
  115. package/dist/commands/deploy.spec.js +18 -0
  116. package/dist/commands/deploy.spec.js.map +1 -0
  117. package/dist/commands/init.d.ts +30 -0
  118. package/dist/commands/init.d.ts.map +1 -0
  119. package/dist/commands/init.js +410 -0
  120. package/dist/commands/init.js.map +1 -0
  121. package/dist/commands/init.spec.d.ts +2 -0
  122. package/dist/commands/init.spec.d.ts.map +1 -0
  123. package/dist/commands/init.spec.js +283 -0
  124. package/dist/commands/init.spec.js.map +1 -0
  125. package/dist/commands/list-tokens.d.ts +16 -0
  126. package/dist/commands/list-tokens.d.ts.map +1 -0
  127. package/dist/commands/list-tokens.js +44 -0
  128. package/dist/commands/list-tokens.js.map +1 -0
  129. package/dist/commands/login.d.ts +17 -0
  130. package/dist/commands/login.d.ts.map +1 -0
  131. package/dist/commands/login.js +69 -0
  132. package/dist/commands/login.js.map +1 -0
  133. package/dist/commands/logout.d.ts +16 -0
  134. package/dist/commands/logout.d.ts.map +1 -0
  135. package/dist/commands/logout.js +43 -0
  136. package/dist/commands/logout.js.map +1 -0
  137. package/dist/config/oauth.d.ts +19 -0
  138. package/dist/config/oauth.d.ts.map +1 -0
  139. package/dist/config/oauth.js +43 -0
  140. package/dist/config/oauth.js.map +1 -0
  141. package/dist/constants.d.ts +27 -0
  142. package/dist/constants.d.ts.map +1 -0
  143. package/dist/constants.js +27 -0
  144. package/dist/constants.js.map +1 -0
  145. package/dist/index.d.ts +5 -0
  146. package/dist/index.d.ts.map +1 -0
  147. package/dist/index.js +6 -0
  148. package/dist/index.js.map +1 -0
  149. package/dist/plugin/adapter.d.ts +5 -0
  150. package/dist/plugin/adapter.d.ts.map +1 -0
  151. package/dist/plugin/adapter.js +139 -0
  152. package/dist/plugin/adapter.js.map +1 -0
  153. package/dist/plugin/build-config.d.ts +37 -0
  154. package/dist/plugin/build-config.d.ts.map +1 -0
  155. package/dist/plugin/build-config.js +25 -0
  156. package/dist/plugin/build-config.js.map +1 -0
  157. package/dist/plugin/build-config.spec.d.ts +2 -0
  158. package/dist/plugin/build-config.spec.d.ts.map +1 -0
  159. package/dist/plugin/build-config.spec.js +72 -0
  160. package/dist/plugin/build-config.spec.js.map +1 -0
  161. package/dist/types/config.d.ts +45 -0
  162. package/dist/types/config.d.ts.map +1 -0
  163. package/dist/types/config.js +9 -0
  164. package/dist/types/config.js.map +1 -0
  165. package/dist/types.d.ts +60 -0
  166. package/dist/types.d.ts.map +1 -0
  167. package/dist/types.js +3 -0
  168. package/dist/types.js.map +1 -0
  169. package/dist/utils/asset-collection.d.ts +30 -0
  170. package/dist/utils/asset-collection.d.ts.map +1 -0
  171. package/dist/utils/asset-collection.js +117 -0
  172. package/dist/utils/asset-collection.js.map +1 -0
  173. package/dist/utils/asset-collection.spec.d.ts +2 -0
  174. package/dist/utils/asset-collection.spec.d.ts.map +1 -0
  175. package/dist/utils/asset-collection.spec.js +155 -0
  176. package/dist/utils/asset-collection.spec.js.map +1 -0
  177. package/dist/utils/build-detection.d.ts +32 -0
  178. package/dist/utils/build-detection.d.ts.map +1 -0
  179. package/dist/utils/build-detection.js +55 -0
  180. package/dist/utils/build-detection.js.map +1 -0
  181. package/dist/utils/build-detection.spec.d.ts +2 -0
  182. package/dist/utils/build-detection.spec.d.ts.map +1 -0
  183. package/dist/utils/build-detection.spec.js +110 -0
  184. package/dist/utils/build-detection.spec.js.map +1 -0
  185. package/dist/utils/config.d.ts +33 -0
  186. package/dist/utils/config.d.ts.map +1 -0
  187. package/dist/utils/config.js +78 -0
  188. package/dist/utils/config.js.map +1 -0
  189. package/dist/utils/config.spec.d.ts +2 -0
  190. package/dist/utils/config.spec.d.ts.map +1 -0
  191. package/dist/utils/config.spec.js +167 -0
  192. package/dist/utils/config.spec.js.map +1 -0
  193. package/dist/utils/download-template.d.ts +15 -0
  194. package/dist/utils/download-template.d.ts.map +1 -0
  195. package/dist/utils/download-template.js +74 -0
  196. package/dist/utils/download-template.js.map +1 -0
  197. package/dist/utils/download-template.spec.d.ts +2 -0
  198. package/dist/utils/download-template.spec.d.ts.map +1 -0
  199. package/dist/utils/download-template.spec.js +76 -0
  200. package/dist/utils/download-template.spec.js.map +1 -0
  201. package/dist/utils/env-management.d.ts +16 -0
  202. package/dist/utils/env-management.d.ts.map +1 -0
  203. package/dist/utils/env-management.js +98 -0
  204. package/dist/utils/env-management.js.map +1 -0
  205. package/dist/utils/env-management.spec.d.ts +2 -0
  206. package/dist/utils/env-management.spec.d.ts.map +1 -0
  207. package/dist/utils/env-management.spec.js +123 -0
  208. package/dist/utils/env-management.spec.js.map +1 -0
  209. package/dist/utils/format.d.ts +23 -0
  210. package/dist/utils/format.d.ts.map +1 -0
  211. package/dist/utils/format.js +49 -0
  212. package/dist/utils/format.js.map +1 -0
  213. package/dist/utils/formatter.d.ts +11 -0
  214. package/dist/utils/formatter.d.ts.map +1 -0
  215. package/dist/utils/formatter.js +47 -0
  216. package/dist/utils/formatter.js.map +1 -0
  217. package/dist/utils/git.d.ts +15 -0
  218. package/dist/utils/git.d.ts.map +1 -0
  219. package/dist/utils/git.js +92 -0
  220. package/dist/utils/git.js.map +1 -0
  221. package/dist/utils/git.spec.d.ts +2 -0
  222. package/dist/utils/git.spec.d.ts.map +1 -0
  223. package/dist/utils/git.spec.js +99 -0
  224. package/dist/utils/git.spec.js.map +1 -0
  225. package/dist/utils/is-debug.d.ts +6 -0
  226. package/dist/utils/is-debug.d.ts.map +1 -0
  227. package/dist/utils/is-debug.js +8 -0
  228. package/dist/utils/is-debug.js.map +1 -0
  229. package/dist/utils/lambda-config.d.ts +7 -0
  230. package/dist/utils/lambda-config.d.ts.map +1 -0
  231. package/dist/utils/lambda-config.js +144 -0
  232. package/dist/utils/lambda-config.js.map +1 -0
  233. package/dist/utils/lambda-config.spec.d.ts +2 -0
  234. package/dist/utils/lambda-config.spec.d.ts.map +1 -0
  235. package/dist/utils/lambda-config.spec.js +141 -0
  236. package/dist/utils/lambda-config.spec.js.map +1 -0
  237. package/dist/utils/log.d.ts +6 -0
  238. package/dist/utils/log.d.ts.map +1 -0
  239. package/dist/utils/log.js +22 -0
  240. package/dist/utils/log.js.map +1 -0
  241. package/dist/utils/messages.d.ts +10 -0
  242. package/dist/utils/messages.d.ts.map +1 -0
  243. package/dist/utils/messages.js +96 -0
  244. package/dist/utils/messages.js.map +1 -0
  245. package/dist/utils/package-manager.d.ts +12 -0
  246. package/dist/utils/package-manager.d.ts.map +1 -0
  247. package/dist/utils/package-manager.js +37 -0
  248. package/dist/utils/package-manager.js.map +1 -0
  249. package/dist/utils/payload-config-ast.d.ts +30 -0
  250. package/dist/utils/payload-config-ast.d.ts.map +1 -0
  251. package/dist/utils/payload-config-ast.js +259 -0
  252. package/dist/utils/payload-config-ast.js.map +1 -0
  253. package/dist/utils/payload-config-ast.spec.d.ts +2 -0
  254. package/dist/utils/payload-config-ast.spec.d.ts.map +1 -0
  255. package/dist/utils/payload-config-ast.spec.js +303 -0
  256. package/dist/utils/payload-config-ast.spec.js.map +1 -0
  257. package/dist/utils/payload-config-finder.d.ts +9 -0
  258. package/dist/utils/payload-config-finder.d.ts.map +1 -0
  259. package/dist/utils/payload-config-finder.js +26 -0
  260. package/dist/utils/payload-config-finder.js.map +1 -0
  261. package/dist/utils/payload-config-modifier.d.ts +18 -0
  262. package/dist/utils/payload-config-modifier.d.ts.map +1 -0
  263. package/dist/utils/payload-config-modifier.int.spec.d.ts +2 -0
  264. package/dist/utils/payload-config-modifier.int.spec.d.ts.map +1 -0
  265. package/dist/utils/payload-config-modifier.int.spec.js +116 -0
  266. package/dist/utils/payload-config-modifier.int.spec.js.map +1 -0
  267. package/dist/utils/payload-config-modifier.js +232 -0
  268. package/dist/utils/payload-config-modifier.js.map +1 -0
  269. package/dist/utils/payload-config-modifier.spec.d.ts +2 -0
  270. package/dist/utils/payload-config-modifier.spec.d.ts.map +1 -0
  271. package/dist/utils/payload-config-modifier.spec.js +162 -0
  272. package/dist/utils/payload-config-modifier.spec.js.map +1 -0
  273. package/dist/utils/payload-package-check.d.ts +15 -0
  274. package/dist/utils/payload-package-check.d.ts.map +1 -0
  275. package/dist/utils/payload-package-check.js +116 -0
  276. package/dist/utils/payload-package-check.js.map +1 -0
  277. package/dist/utils/payload-package-check.spec.d.ts +2 -0
  278. package/dist/utils/payload-package-check.spec.d.ts.map +1 -0
  279. package/dist/utils/payload-package-check.spec.js +148 -0
  280. package/dist/utils/payload-package-check.spec.js.map +1 -0
  281. package/dist/utils/project.d.ts +58 -0
  282. package/dist/utils/project.d.ts.map +1 -0
  283. package/dist/utils/project.js +252 -0
  284. package/dist/utils/project.js.map +1 -0
  285. package/dist/utils/project.spec.d.ts +2 -0
  286. package/dist/utils/project.spec.d.ts.map +1 -0
  287. package/dist/utils/project.spec.js +222 -0
  288. package/dist/utils/project.spec.js.map +1 -0
  289. package/dist/utils/s3-upload.d.ts +43 -0
  290. package/dist/utils/s3-upload.d.ts.map +1 -0
  291. package/dist/utils/s3-upload.js +128 -0
  292. package/dist/utils/s3-upload.js.map +1 -0
  293. package/dist/utils/s3-upload.spec.d.ts +2 -0
  294. package/dist/utils/s3-upload.spec.d.ts.map +1 -0
  295. package/dist/utils/s3-upload.spec.js +140 -0
  296. package/dist/utils/s3-upload.spec.js.map +1 -0
  297. package/dist/utils/token-display.d.ts +14 -0
  298. package/dist/utils/token-display.d.ts.map +1 -0
  299. package/dist/utils/token-display.js +62 -0
  300. package/dist/utils/token-display.js.map +1 -0
  301. package/dist/utils/typescript-validator.d.ts +11 -0
  302. package/dist/utils/typescript-validator.d.ts.map +1 -0
  303. package/dist/utils/typescript-validator.js +58 -0
  304. package/dist/utils/typescript-validator.js.map +1 -0
  305. 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"}