@payloadcms/figma 0.0.1-alpha.41 → 0.0.1-alpha.42

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 (100) hide show
  1. package/dist/auth/oauth-flow.d.ts.map +1 -1
  2. package/dist/auth/oauth-flow.js +6 -3
  3. package/dist/auth/oauth-flow.js.map +1 -1
  4. package/dist/auth/refresh.d.ts +2 -1
  5. package/dist/auth/refresh.d.ts.map +1 -1
  6. package/dist/auth/refresh.js +11 -3
  7. package/dist/auth/refresh.js.map +1 -1
  8. package/dist/auth/token-store.d.ts +6 -4
  9. package/dist/auth/token-store.d.ts.map +1 -1
  10. package/dist/auth/token-store.js +17 -14
  11. package/dist/auth/token-store.js.map +1 -1
  12. package/dist/auth/types.d.ts +7 -4
  13. package/dist/auth/types.d.ts.map +1 -1
  14. package/dist/auth/types.js.map +1 -1
  15. package/dist/cli.js +8 -6
  16. package/dist/cli.js.map +1 -1
  17. package/dist/commands/deploy.d.ts.map +1 -1
  18. package/dist/commands/deploy.js +2 -1
  19. package/dist/commands/deploy.js.map +1 -1
  20. package/dist/commands/init.d.ts.map +1 -1
  21. package/dist/commands/init.js +12 -1
  22. package/dist/commands/init.js.map +1 -1
  23. package/dist/commands/list-tokens.d.ts +3 -3
  24. package/dist/commands/list-tokens.d.ts.map +1 -1
  25. package/dist/commands/list-tokens.js +20 -8
  26. package/dist/commands/list-tokens.js.map +1 -1
  27. package/dist/commands/login.d.ts.map +1 -1
  28. package/dist/commands/login.js +12 -4
  29. package/dist/commands/login.js.map +1 -1
  30. package/dist/commands/logout.d.ts +5 -4
  31. package/dist/commands/logout.d.ts.map +1 -1
  32. package/dist/commands/logout.js +55 -12
  33. package/dist/commands/logout.js.map +1 -1
  34. package/dist/db-content-api/generated/content-api-types.d.ts +163 -103
  35. package/dist/db-content-api/generated/content-api-types.d.ts.map +1 -1
  36. package/dist/db-content-api/generated/content-api-types.js.map +1 -1
  37. package/dist/db-content-api/index.d.ts.map +1 -1
  38. package/dist/db-content-api/index.js +73 -65
  39. package/dist/db-content-api/index.js.map +1 -1
  40. package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts +31 -1
  41. package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts.map +1 -1
  42. package/dist/db-content-api/temp-utilities/unwrapDocument.js +24 -24
  43. package/dist/db-content-api/temp-utilities/unwrapDocument.js.map +1 -1
  44. package/dist/db-content-api/utilities/buildPathTypes.d.ts +12 -0
  45. package/dist/db-content-api/utilities/buildPathTypes.d.ts.map +1 -0
  46. package/dist/db-content-api/utilities/buildPathTypes.js +157 -0
  47. package/dist/db-content-api/utilities/buildPathTypes.js.map +1 -0
  48. package/dist/db-content-api/utilities/data/index.d.ts +2 -0
  49. package/dist/db-content-api/utilities/data/index.d.ts.map +1 -1
  50. package/dist/db-content-api/utilities/data/index.js +8 -0
  51. package/dist/db-content-api/utilities/data/index.js.map +1 -1
  52. package/dist/db-content-api/utilities/where.d.ts.map +1 -1
  53. package/dist/db-content-api/utilities/where.js +17 -3
  54. package/dist/db-content-api/utilities/where.js.map +1 -1
  55. package/dist/oauth/components/LogoutButton/index.d.ts.map +1 -1
  56. package/dist/oauth/components/LogoutButton/index.js +5 -3
  57. package/dist/oauth/components/LogoutButton/index.js.map +1 -1
  58. package/dist/oauth/endpoints/getLoginEndpoint.d.ts.map +1 -1
  59. package/dist/oauth/endpoints/getLoginEndpoint.js +17 -7
  60. package/dist/oauth/endpoints/getLoginEndpoint.js.map +1 -1
  61. package/dist/oauth/endpoints/getLogoutEndpoint.d.ts.map +1 -1
  62. package/dist/oauth/endpoints/getLogoutEndpoint.js +7 -4
  63. package/dist/oauth/endpoints/getLogoutEndpoint.js.map +1 -1
  64. package/dist/plugin/build-config.js +3 -3
  65. package/dist/plugin/build-config.js.map +1 -1
  66. package/dist/storage-content-api/api-types.d.ts +8 -1
  67. package/dist/storage-content-api/api-types.d.ts.map +1 -1
  68. package/dist/storage-content-api/api-types.js.map +1 -1
  69. package/dist/storage-content-api/client-uploads/generateSignedURL.js +1 -0
  70. package/dist/storage-content-api/client-uploads/generateSignedURL.js.map +1 -1
  71. package/dist/storage-content-api/generateURL.d.ts.map +1 -1
  72. package/dist/storage-content-api/generateURL.js +3 -2
  73. package/dist/storage-content-api/generateURL.js.map +1 -1
  74. package/dist/storage-content-api/handleDelete.d.ts.map +1 -1
  75. package/dist/storage-content-api/handleDelete.js +4 -2
  76. package/dist/storage-content-api/handleDelete.js.map +1 -1
  77. package/dist/storage-content-api/handleUpload.d.ts +1 -1
  78. package/dist/storage-content-api/handleUpload.d.ts.map +1 -1
  79. package/dist/storage-content-api/handleUpload.js +11 -3
  80. package/dist/storage-content-api/handleUpload.js.map +1 -1
  81. package/dist/storage-content-api/staticHandler.js +1 -0
  82. package/dist/storage-content-api/staticHandler.js.map +1 -1
  83. package/dist/utils/format-env-suffix.d.ts +12 -0
  84. package/dist/utils/format-env-suffix.d.ts.map +1 -0
  85. package/dist/utils/format-env-suffix.js +13 -0
  86. package/dist/utils/format-env-suffix.js.map +1 -0
  87. package/dist/utils/lambda-config.js +26 -12
  88. package/dist/utils/lambda-config.js.map +1 -1
  89. package/dist/utils/messages.d.ts.map +1 -1
  90. package/dist/utils/messages.js +1 -2
  91. package/dist/utils/messages.js.map +1 -1
  92. package/package.json +1 -1
  93. package/dist/db-content-api/temp-utilities/pagination.d.ts +0 -13
  94. package/dist/db-content-api/temp-utilities/pagination.d.ts.map +0 -1
  95. package/dist/db-content-api/temp-utilities/pagination.js +0 -45
  96. package/dist/db-content-api/temp-utilities/pagination.js.map +0 -1
  97. package/dist/oauth/utilities/getAdminPath.d.ts +0 -8
  98. package/dist/oauth/utilities/getAdminPath.d.ts.map +0 -1
  99. package/dist/oauth/utilities/getAdminPath.js +0 -9
  100. package/dist/oauth/utilities/getAdminPath.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-flow.d.ts","sourceRoot":"","sources":["../../src/auth/oauth-flow.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAS7C;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IAG9B,KAAK,CAAC,EAAE,KAAK;gBADpB,OAAO,EAAE,MAAM,EACR,KAAK,CAAC,EAAE,KAAK,YAAA;CAKvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oBAAoB;IACpB,MAAM,EAAE,WAAW,CAAA;IACnB,oBAAoB;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAwI1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAyBxF"}
1
+ {"version":3,"file":"oauth-flow.d.ts","sourceRoot":"","sources":["../../src/auth/oauth-flow.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAS7C;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IAG9B,KAAK,CAAC,EAAE,KAAK;gBADpB,OAAO,EAAE,MAAM,EACR,KAAK,CAAC,EAAE,KAAK,YAAA;CAKvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oBAAoB;IACpB,MAAM,EAAE,WAAW,CAAA;IACnB,oBAAoB;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAwI1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAkCxF"}
@@ -5,7 +5,7 @@ import * as log from '../utils/log.js';
5
5
  import { buildAuthorizationUrl, openBrowser } from './browser.js';
6
6
  import { CallbackServer } from './callback-server.js';
7
7
  import { generatePKCEPair } from './pkce.js';
8
- import { exchangeCodeForTokens, refreshAccessToken } from './refresh.js';
8
+ import { exchangeCodeForTokens, refreshAccessToken, TokenRefreshError } from './refresh.js';
9
9
  /**
10
10
  * Error during OAuth flow
11
11
  */ export class OAuthFlowError extends Error {
@@ -161,8 +161,11 @@ import { exchangeCodeForTokens, refreshAccessToken } from './refresh.js';
161
161
  tokenStore.setTokens(tokens);
162
162
  return tokens.accessToken;
163
163
  } catch (error) {
164
- // Refresh failed - clear invalid tokens
165
- tokenStore.clearTokens();
164
+ // Only clear tokens for actual auth failures, not transient errors
165
+ const isAuthFailure = error instanceof TokenRefreshError && (error.statusCode === 401 || error.statusCode === 403 || error.statusCode === 400 && error.errorCode === 'invalid_grant');
166
+ if (isAuthFailure) {
167
+ tokenStore.clearTokens();
168
+ }
166
169
  throw new OAuthFlowError('Failed to refresh access token', error instanceof Error ? error : undefined);
167
170
  }
168
171
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/auth/oauth-flow.ts"],"sourcesContent":["import * as p from '@clack/prompts'\nimport crypto from 'crypto'\n\nimport type { TokenStore } from './token-store.js'\nimport type { FigmaTokens } from './types.js'\n\nimport { DEFAULT_CALLBACK_PORT, getOAuthConfig } from '../config/oauth.js'\nimport * as log from '../utils/log.js'\nimport { buildAuthorizationUrl, openBrowser } from './browser.js'\nimport { CallbackServer } from './callback-server.js'\nimport { generatePKCEPair } from './pkce.js'\nimport { exchangeCodeForTokens, refreshAccessToken } from './refresh.js'\n\n/**\n * Error during OAuth flow\n */\nexport class OAuthFlowError extends Error {\n constructor(\n message: string,\n public cause?: Error,\n ) {\n super(message)\n this.name = 'OAuthFlowError'\n }\n}\n\n/**\n * Options for OAuth flow\n */\nexport interface OAuthFlowOptions {\n /** OAuth2 client ID (defaults to config) */\n clientId?: string\n /** Callback server port (defaults to DEFAULT_CALLBACK_PORT) */\n port?: number\n /** Redirect URI (defaults to config) */\n redirectUri?: string\n /** OAuth scopes (defaults to config) */\n scopes?: string[]\n /** Callback timeout in milliseconds (defaults to 120000) */\n timeoutMs?: number\n}\n\n/**\n * Result of OAuth flow\n */\nexport interface OAuthFlowResult {\n /** OAuth2 tokens */\n tokens: FigmaTokens\n /** Figma user ID */\n userId?: string\n}\n\n/**\n * Execute complete OAuth2 authorization flow\n *\n * This orchestrates the full OAuth flow:\n * 1. Generate PKCE pair for security\n * 2. Start local callback server\n * 3. Open browser to authorization URL\n * 4. Wait for user to authorize\n * 5. Exchange authorization code for tokens\n * 6. Store tokens securely\n *\n * @param tokenStore - Token store for persisting tokens\n * @param options - Flow configuration options\n * @returns Promise resolving to OAuth flow result\n * @throws OAuthFlowError if any step fails\n */\nexport async function executeOAuthFlow(\n tokenStore: TokenStore,\n options: OAuthFlowOptions = {},\n): Promise<OAuthFlowResult> {\n // Check if OAuth flow should be mocked\n // Default to false for OAuth\n const shouldMock = process.env.FIGMA_MOCK_OAUTH === 'true'\n\n const oauthConfig = getOAuthConfig()\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Return mock tokens for testing\n log.debug('Using mock OAuth flow (FIGMA_MOCK_OAUTH = true)')\n const mockTokens: FigmaTokens = {\n accessToken: 'figt_mock_access_token_' + crypto.randomBytes(16).toString('hex'),\n expiresAt: Date.now() + 2 * 60 * 60 * 1000, // 2 hours\n refreshToken: 'figt_mock_refresh_token_' + crypto.randomBytes(16).toString('hex'),\n scopes: (options.scopes || oauthConfig.scopes) as FigmaTokens['scopes'],\n tokenType: 'Bearer',\n }\n\n // Store mock tokens\n tokenStore.setTokens(mockTokens)\n\n return {\n tokens: mockTokens,\n userId: 'mock_user_123',\n }\n }\n\n // REAL OAUTH IMPLEMENTATION\n const {\n clientId = oauthConfig.clientId,\n port = DEFAULT_CALLBACK_PORT,\n redirectUri = oauthConfig.redirectUri,\n scopes = oauthConfig.scopes,\n timeoutMs = 120000,\n } = options\n\n // Generate PKCE pair for enhanced security\n const { codeChallenge, codeChallengeMethod, codeVerifier } = generatePKCEPair()\n\n // Generate random state for CSRF protection\n const state = crypto.randomBytes(32).toString('base64url')\n\n // Create callback server\n const callbackServer = new CallbackServer({ port, timeoutMs })\n\n try {\n // Start waiting for callback (this starts the server and captures actual port)\n const callbackPromise = callbackServer.waitForCallback(state)\n\n // Wait a bit for server to start and determine actual port\n await new Promise((resolve) => setTimeout(resolve, 100))\n\n // Get the actual port (may differ from preferred if that was in use)\n const actualPort = callbackServer.getActualPort()\n\n // Build redirect URI with actual port\n const actualRedirectUri = redirectUri.replace(/:\\d+/, `:${actualPort}`)\n\n // Build authorization URL\n const authUrl = buildAuthorizationUrl({\n authorizationUrl: oauthConfig.authorizationUrl,\n clientId,\n codeChallenge,\n codeChallengeMethod,\n redirectUri: actualRedirectUri,\n scopes,\n state,\n })\n\n // Prompt user to open browser\n const shouldOpenBrowser = await p.confirm({\n initialValue: true,\n message: 'Authenticate with Figma in your browser?',\n })\n\n if (p.isCancel(shouldOpenBrowser)) {\n throw new OAuthFlowError('Authentication cancelled by user')\n }\n\n if (shouldOpenBrowser) {\n // Try to open browser, but don't fail if it doesn't work\n try {\n await openBrowser(authUrl)\n } catch (_error) {\n // Browser opening failed, show URL for manual opening\n log.warning('Could not open browser automatically.')\n log.info(`Please open this URL in your browser:`)\n log.info(` ${authUrl}`)\n }\n } else {\n // User declined automatic opening, show URL\n log.info(`Please open this URL manually in your browser:`)\n log.info(` ${authUrl}`)\n }\n\n // Wait for callback\n const { code } = await callbackPromise\n\n // Exchange authorization code for tokens\n let tokens: FigmaTokens\n try {\n tokens = await exchangeCodeForTokens(code, codeVerifier, actualRedirectUri, clientId)\n } catch (error) {\n throw new OAuthFlowError(\n 'Failed to exchange authorization code for tokens',\n error instanceof Error ? error : undefined,\n )\n }\n\n // Store tokens\n try {\n tokenStore.setTokens(tokens)\n } catch (error) {\n throw new OAuthFlowError('Failed to store tokens', error instanceof Error ? error : undefined)\n }\n\n return {\n tokens,\n userId: tokens.userId,\n }\n } catch (error) {\n // Clean up server on error\n callbackServer.stop()\n\n if (error instanceof OAuthFlowError) {\n throw error\n }\n\n throw new OAuthFlowError(\n `OAuth flow failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n error instanceof Error ? error : undefined,\n )\n } finally {\n // Ensure server is always stopped\n callbackServer.stop()\n }\n}\n\n/**\n * Get valid access token, refreshing if necessary\n *\n * Checks if stored tokens exist and are valid. If expired, automatically\n * refreshes using the refresh token. If no tokens exist, returns null.\n *\n * @param tokenStore - Token store to use\n * @returns Promise resolving to access token or null if no valid tokens\n * @throws OAuthFlowError if refresh fails\n */\nexport async function getValidAccessToken(tokenStore: TokenStore): Promise<null | string> {\n // Check if we have valid tokens\n if (tokenStore.hasValidTokens()) {\n return tokenStore.getAccessToken()\n }\n\n // Check if we have a refresh token\n const refreshToken = tokenStore.getRefreshToken()\n if (!refreshToken) {\n return null\n }\n\n // Try to refresh\n try {\n const tokens = await refreshAccessToken(refreshToken)\n tokenStore.setTokens(tokens)\n return tokens.accessToken\n } catch (error) {\n // Refresh failed - clear invalid tokens\n tokenStore.clearTokens()\n throw new OAuthFlowError(\n 'Failed to refresh access token',\n error instanceof Error ? error : undefined,\n )\n }\n}\n"],"names":["p","crypto","DEFAULT_CALLBACK_PORT","getOAuthConfig","log","buildAuthorizationUrl","openBrowser","CallbackServer","generatePKCEPair","exchangeCodeForTokens","refreshAccessToken","OAuthFlowError","Error","message","cause","name","executeOAuthFlow","tokenStore","options","shouldMock","process","env","FIGMA_MOCK_OAUTH","oauthConfig","debug","mockTokens","accessToken","randomBytes","toString","expiresAt","Date","now","refreshToken","scopes","tokenType","setTokens","tokens","userId","clientId","port","redirectUri","timeoutMs","codeChallenge","codeChallengeMethod","codeVerifier","state","callbackServer","callbackPromise","waitForCallback","Promise","resolve","setTimeout","actualPort","getActualPort","actualRedirectUri","replace","authUrl","authorizationUrl","shouldOpenBrowser","confirm","initialValue","isCancel","_error","warning","info","code","error","undefined","stop","getValidAccessToken","hasValidTokens","getAccessToken","getRefreshToken","clearTokens"],"mappings":"AAAA,YAAYA,OAAO,iBAAgB;AACnC,OAAOC,YAAY,SAAQ;AAK3B,SAASC,qBAAqB,EAAEC,cAAc,QAAQ,qBAAoB;AAC1E,YAAYC,SAAS,kBAAiB;AACtC,SAASC,qBAAqB,EAAEC,WAAW,QAAQ,eAAc;AACjE,SAASC,cAAc,QAAQ,uBAAsB;AACrD,SAASC,gBAAgB,QAAQ,YAAW;AAC5C,SAASC,qBAAqB,EAAEC,kBAAkB,QAAQ,eAAc;AAExE;;CAEC,GACD,OAAO,MAAMC,uBAAuBC;;IAClC,YACEC,OAAe,EACf,AAAOC,KAAa,CACpB;QACA,KAAK,CAACD,eAFCC,QAAAA;QAGP,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AA4BA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,eAAeC,iBACpBC,UAAsB,EACtBC,UAA4B,CAAC,CAAC;IAE9B,uCAAuC;IACvC,6BAA6B;IAC7B,MAAMC,aAAaC,QAAQC,GAAG,CAACC,gBAAgB,KAAK;IAEpD,MAAMC,cAAcpB;IAEpB,IAAIgB,YAAY;QACd,sDAAsD;QACtDf,IAAIoB,KAAK,CAAC;QACV,MAAMC,aAA0B;YAC9BC,aAAa,4BAA4BzB,OAAO0B,WAAW,CAAC,IAAIC,QAAQ,CAAC;YACzEC,WAAWC,KAAKC,GAAG,KAAK,IAAI,KAAK,KAAK;YACtCC,cAAc,6BAA6B/B,OAAO0B,WAAW,CAAC,IAAIC,QAAQ,CAAC;YAC3EK,QAASf,QAAQe,MAAM,IAAIV,YAAYU,MAAM;YAC7CC,WAAW;QACb;QAEA,oBAAoB;QACpBjB,WAAWkB,SAAS,CAACV;QAErB,OAAO;YACLW,QAAQX;YACRY,QAAQ;QACV;IACF;IAEA,4BAA4B;IAC5B,MAAM,EACJC,WAAWf,YAAYe,QAAQ,EAC/BC,OAAOrC,qBAAqB,EAC5BsC,cAAcjB,YAAYiB,WAAW,EACrCP,SAASV,YAAYU,MAAM,EAC3BQ,YAAY,MAAM,EACnB,GAAGvB;IAEJ,2CAA2C;IAC3C,MAAM,EAAEwB,aAAa,EAAEC,mBAAmB,EAAEC,YAAY,EAAE,GAAGpC;IAE7D,4CAA4C;IAC5C,MAAMqC,QAAQ5C,OAAO0B,WAAW,CAAC,IAAIC,QAAQ,CAAC;IAE9C,yBAAyB;IACzB,MAAMkB,iBAAiB,IAAIvC,eAAe;QAAEgC;QAAME;IAAU;IAE5D,IAAI;QACF,+EAA+E;QAC/E,MAAMM,kBAAkBD,eAAeE,eAAe,CAACH;QAEvD,2DAA2D;QAC3D,MAAM,IAAII,QAAQ,CAACC,UAAYC,WAAWD,SAAS;QAEnD,qEAAqE;QACrE,MAAME,aAAaN,eAAeO,aAAa;QAE/C,sCAAsC;QACtC,MAAMC,oBAAoBd,YAAYe,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAEH,YAAY;QAEtE,0BAA0B;QAC1B,MAAMI,UAAUnD,sBAAsB;YACpCoD,kBAAkBlC,YAAYkC,gBAAgB;YAC9CnB;YACAI;YACAC;YACAH,aAAac;YACbrB;YACAY;QACF;QAEA,8BAA8B;QAC9B,MAAMa,oBAAoB,MAAM1D,EAAE2D,OAAO,CAAC;YACxCC,cAAc;YACd/C,SAAS;QACX;QAEA,IAAIb,EAAE6D,QAAQ,CAACH,oBAAoB;YACjC,MAAM,IAAI/C,eAAe;QAC3B;QAEA,IAAI+C,mBAAmB;YACrB,yDAAyD;YACzD,IAAI;gBACF,MAAMpD,YAAYkD;YACpB,EAAE,OAAOM,QAAQ;gBACf,sDAAsD;gBACtD1D,IAAI2D,OAAO,CAAC;gBACZ3D,IAAI4D,IAAI,CAAC,CAAC,qCAAqC,CAAC;gBAChD5D,IAAI4D,IAAI,CAAC,CAAC,EAAE,EAAER,SAAS;YACzB;QACF,OAAO;YACL,4CAA4C;YAC5CpD,IAAI4D,IAAI,CAAC,CAAC,8CAA8C,CAAC;YACzD5D,IAAI4D,IAAI,CAAC,CAAC,EAAE,EAAER,SAAS;QACzB;QAEA,oBAAoB;QACpB,MAAM,EAAES,IAAI,EAAE,GAAG,MAAMlB;QAEvB,yCAAyC;QACzC,IAAIX;QACJ,IAAI;YACFA,SAAS,MAAM3B,sBAAsBwD,MAAMrB,cAAcU,mBAAmBhB;QAC9E,EAAE,OAAO4B,OAAO;YACd,MAAM,IAAIvD,eACR,oDACAuD,iBAAiBtD,QAAQsD,QAAQC;QAErC;QAEA,eAAe;QACf,IAAI;YACFlD,WAAWkB,SAAS,CAACC;QACvB,EAAE,OAAO8B,OAAO;YACd,MAAM,IAAIvD,eAAe,0BAA0BuD,iBAAiBtD,QAAQsD,QAAQC;QACtF;QAEA,OAAO;YACL/B;YACAC,QAAQD,OAAOC,MAAM;QACvB;IACF,EAAE,OAAO6B,OAAO;QACd,2BAA2B;QAC3BpB,eAAesB,IAAI;QAEnB,IAAIF,iBAAiBvD,gBAAgB;YACnC,MAAMuD;QACR;QAEA,MAAM,IAAIvD,eACR,CAAC,mBAAmB,EAAEuD,iBAAiBtD,QAAQsD,MAAMrD,OAAO,GAAG,iBAAiB,EAChFqD,iBAAiBtD,QAAQsD,QAAQC;IAErC,SAAU;QACR,kCAAkC;QAClCrB,eAAesB,IAAI;IACrB;AACF;AAEA;;;;;;;;;CASC,GACD,OAAO,eAAeC,oBAAoBpD,UAAsB;IAC9D,gCAAgC;IAChC,IAAIA,WAAWqD,cAAc,IAAI;QAC/B,OAAOrD,WAAWsD,cAAc;IAClC;IAEA,mCAAmC;IACnC,MAAMvC,eAAef,WAAWuD,eAAe;IAC/C,IAAI,CAACxC,cAAc;QACjB,OAAO;IACT;IAEA,iBAAiB;IACjB,IAAI;QACF,MAAMI,SAAS,MAAM1B,mBAAmBsB;QACxCf,WAAWkB,SAAS,CAACC;QACrB,OAAOA,OAAOV,WAAW;IAC3B,EAAE,OAAOwC,OAAO;QACd,wCAAwC;QACxCjD,WAAWwD,WAAW;QACtB,MAAM,IAAI9D,eACR,kCACAuD,iBAAiBtD,QAAQsD,QAAQC;IAErC;AACF"}
1
+ {"version":3,"sources":["../../src/auth/oauth-flow.ts"],"sourcesContent":["import * as p from '@clack/prompts'\nimport crypto from 'crypto'\n\nimport type { TokenStore } from './token-store.js'\nimport type { FigmaTokens } from './types.js'\n\nimport { DEFAULT_CALLBACK_PORT, getOAuthConfig } from '../config/oauth.js'\nimport * as log from '../utils/log.js'\nimport { buildAuthorizationUrl, openBrowser } from './browser.js'\nimport { CallbackServer } from './callback-server.js'\nimport { generatePKCEPair } from './pkce.js'\nimport { exchangeCodeForTokens, refreshAccessToken, TokenRefreshError } from './refresh.js'\n\n/**\n * Error during OAuth flow\n */\nexport class OAuthFlowError extends Error {\n constructor(\n message: string,\n public cause?: Error,\n ) {\n super(message)\n this.name = 'OAuthFlowError'\n }\n}\n\n/**\n * Options for OAuth flow\n */\nexport interface OAuthFlowOptions {\n /** OAuth2 client ID (defaults to config) */\n clientId?: string\n /** Callback server port (defaults to DEFAULT_CALLBACK_PORT) */\n port?: number\n /** Redirect URI (defaults to config) */\n redirectUri?: string\n /** OAuth scopes (defaults to config) */\n scopes?: string[]\n /** Callback timeout in milliseconds (defaults to 120000) */\n timeoutMs?: number\n}\n\n/**\n * Result of OAuth flow\n */\nexport interface OAuthFlowResult {\n /** OAuth2 tokens */\n tokens: FigmaTokens\n /** Figma user ID */\n userId?: string\n}\n\n/**\n * Execute complete OAuth2 authorization flow\n *\n * This orchestrates the full OAuth flow:\n * 1. Generate PKCE pair for security\n * 2. Start local callback server\n * 3. Open browser to authorization URL\n * 4. Wait for user to authorize\n * 5. Exchange authorization code for tokens\n * 6. Store tokens securely\n *\n * @param tokenStore - Token store for persisting tokens\n * @param options - Flow configuration options\n * @returns Promise resolving to OAuth flow result\n * @throws OAuthFlowError if any step fails\n */\nexport async function executeOAuthFlow(\n tokenStore: TokenStore,\n options: OAuthFlowOptions = {},\n): Promise<OAuthFlowResult> {\n // Check if OAuth flow should be mocked\n // Default to false for OAuth\n const shouldMock = process.env.FIGMA_MOCK_OAUTH === 'true'\n\n const oauthConfig = getOAuthConfig()\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Return mock tokens for testing\n log.debug('Using mock OAuth flow (FIGMA_MOCK_OAUTH = true)')\n const mockTokens: FigmaTokens = {\n accessToken: 'figt_mock_access_token_' + crypto.randomBytes(16).toString('hex'),\n expiresAt: Date.now() + 2 * 60 * 60 * 1000, // 2 hours\n refreshToken: 'figt_mock_refresh_token_' + crypto.randomBytes(16).toString('hex'),\n scopes: (options.scopes || oauthConfig.scopes) as FigmaTokens['scopes'],\n tokenType: 'Bearer',\n }\n\n // Store mock tokens\n tokenStore.setTokens(mockTokens)\n\n return {\n tokens: mockTokens,\n userId: 'mock_user_123',\n }\n }\n\n // REAL OAUTH IMPLEMENTATION\n const {\n clientId = oauthConfig.clientId,\n port = DEFAULT_CALLBACK_PORT,\n redirectUri = oauthConfig.redirectUri,\n scopes = oauthConfig.scopes,\n timeoutMs = 120000,\n } = options\n\n // Generate PKCE pair for enhanced security\n const { codeChallenge, codeChallengeMethod, codeVerifier } = generatePKCEPair()\n\n // Generate random state for CSRF protection\n const state = crypto.randomBytes(32).toString('base64url')\n\n // Create callback server\n const callbackServer = new CallbackServer({ port, timeoutMs })\n\n try {\n // Start waiting for callback (this starts the server and captures actual port)\n const callbackPromise = callbackServer.waitForCallback(state)\n\n // Wait a bit for server to start and determine actual port\n await new Promise((resolve) => setTimeout(resolve, 100))\n\n // Get the actual port (may differ from preferred if that was in use)\n const actualPort = callbackServer.getActualPort()\n\n // Build redirect URI with actual port\n const actualRedirectUri = redirectUri.replace(/:\\d+/, `:${actualPort}`)\n\n // Build authorization URL\n const authUrl = buildAuthorizationUrl({\n authorizationUrl: oauthConfig.authorizationUrl,\n clientId,\n codeChallenge,\n codeChallengeMethod,\n redirectUri: actualRedirectUri,\n scopes,\n state,\n })\n\n // Prompt user to open browser\n const shouldOpenBrowser = await p.confirm({\n initialValue: true,\n message: 'Authenticate with Figma in your browser?',\n })\n\n if (p.isCancel(shouldOpenBrowser)) {\n throw new OAuthFlowError('Authentication cancelled by user')\n }\n\n if (shouldOpenBrowser) {\n // Try to open browser, but don't fail if it doesn't work\n try {\n await openBrowser(authUrl)\n } catch (_error) {\n // Browser opening failed, show URL for manual opening\n log.warning('Could not open browser automatically.')\n log.info(`Please open this URL in your browser:`)\n log.info(` ${authUrl}`)\n }\n } else {\n // User declined automatic opening, show URL\n log.info(`Please open this URL manually in your browser:`)\n log.info(` ${authUrl}`)\n }\n\n // Wait for callback\n const { code } = await callbackPromise\n\n // Exchange authorization code for tokens\n let tokens: FigmaTokens\n try {\n tokens = await exchangeCodeForTokens(code, codeVerifier, actualRedirectUri, clientId)\n } catch (error) {\n throw new OAuthFlowError(\n 'Failed to exchange authorization code for tokens',\n error instanceof Error ? error : undefined,\n )\n }\n\n // Store tokens\n try {\n tokenStore.setTokens(tokens)\n } catch (error) {\n throw new OAuthFlowError('Failed to store tokens', error instanceof Error ? error : undefined)\n }\n\n return {\n tokens,\n userId: tokens.userId,\n }\n } catch (error) {\n // Clean up server on error\n callbackServer.stop()\n\n if (error instanceof OAuthFlowError) {\n throw error\n }\n\n throw new OAuthFlowError(\n `OAuth flow failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n error instanceof Error ? error : undefined,\n )\n } finally {\n // Ensure server is always stopped\n callbackServer.stop()\n }\n}\n\n/**\n * Get valid access token, refreshing if necessary\n *\n * Checks if stored tokens exist and are valid. If expired, automatically\n * refreshes using the refresh token. If no tokens exist, returns null.\n *\n * @param tokenStore - Token store to use\n * @returns Promise resolving to access token or null if no valid tokens\n * @throws OAuthFlowError if refresh fails\n */\nexport async function getValidAccessToken(tokenStore: TokenStore): Promise<null | string> {\n // Check if we have valid tokens\n if (tokenStore.hasValidTokens()) {\n return tokenStore.getAccessToken()\n }\n\n // Check if we have a refresh token\n const refreshToken = tokenStore.getRefreshToken()\n if (!refreshToken) {\n return null\n }\n\n // Try to refresh\n try {\n const tokens = await refreshAccessToken(refreshToken)\n tokenStore.setTokens(tokens)\n return tokens.accessToken\n } catch (error) {\n // Only clear tokens for actual auth failures, not transient errors\n const isAuthFailure =\n error instanceof TokenRefreshError &&\n (error.statusCode === 401 ||\n error.statusCode === 403 ||\n (error.statusCode === 400 && error.errorCode === 'invalid_grant'))\n\n if (isAuthFailure) {\n tokenStore.clearTokens()\n }\n\n throw new OAuthFlowError(\n 'Failed to refresh access token',\n error instanceof Error ? error : undefined,\n )\n }\n}\n"],"names":["p","crypto","DEFAULT_CALLBACK_PORT","getOAuthConfig","log","buildAuthorizationUrl","openBrowser","CallbackServer","generatePKCEPair","exchangeCodeForTokens","refreshAccessToken","TokenRefreshError","OAuthFlowError","Error","message","cause","name","executeOAuthFlow","tokenStore","options","shouldMock","process","env","FIGMA_MOCK_OAUTH","oauthConfig","debug","mockTokens","accessToken","randomBytes","toString","expiresAt","Date","now","refreshToken","scopes","tokenType","setTokens","tokens","userId","clientId","port","redirectUri","timeoutMs","codeChallenge","codeChallengeMethod","codeVerifier","state","callbackServer","callbackPromise","waitForCallback","Promise","resolve","setTimeout","actualPort","getActualPort","actualRedirectUri","replace","authUrl","authorizationUrl","shouldOpenBrowser","confirm","initialValue","isCancel","_error","warning","info","code","error","undefined","stop","getValidAccessToken","hasValidTokens","getAccessToken","getRefreshToken","isAuthFailure","statusCode","errorCode","clearTokens"],"mappings":"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"}
@@ -4,7 +4,8 @@ import type { FigmaTokens } from './types.js';
4
4
  */
5
5
  export declare class TokenRefreshError extends Error {
6
6
  statusCode?: number | undefined;
7
- constructor(message: string, statusCode?: number | undefined);
7
+ errorCode?: string | undefined;
8
+ constructor(message: string, statusCode?: number | undefined, errorCode?: string | undefined);
8
9
  }
9
10
  /**
10
11
  * Refresh an expired OAuth2 access token using a refresh token
@@ -1 +1 @@
1
- {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../src/auth/refresh.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,YAAY,CAAA;AAKjE;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAGjC,UAAU,CAAC,EAAE,MAAM;gBAD1B,OAAO,EAAE,MAAM,EACR,UAAU,CAAC,EAAE,MAAM,YAAA;CAK7B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAsDtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CA2FtB"}
1
+ {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../src/auth/refresh.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,YAAY,CAAA;AAKjE;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAGjC,UAAU,CAAC,EAAE,MAAM;IACnB,SAAS,CAAC,EAAE,MAAM;gBAFzB,OAAO,EAAE,MAAM,EACR,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,SAAS,CAAC,EAAE,MAAM,YAAA;CAK5B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CA8DtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CA2FtB"}
@@ -4,8 +4,9 @@ import * as log from '../utils/log.js';
4
4
  * Error thrown when token refresh fails
5
5
  */ export class TokenRefreshError extends Error {
6
6
  statusCode;
7
- constructor(message, statusCode){
8
- super(message), this.statusCode = statusCode;
7
+ errorCode;
8
+ constructor(message, statusCode, errorCode){
9
+ super(message), this.statusCode = statusCode, this.errorCode = errorCode;
9
10
  this.name = 'TokenRefreshError';
10
11
  }
11
12
  }
@@ -38,7 +39,14 @@ import * as log from '../utils/log.js';
38
39
  });
39
40
  if (!response.ok) {
40
41
  const errorText = await response.text();
41
- throw new TokenRefreshError(`Token refresh failed: ${response.statusText}. ${errorText}`, response.status);
42
+ let errorCode;
43
+ try {
44
+ const parsed = JSON.parse(errorText);
45
+ errorCode = parsed.error;
46
+ } catch {
47
+ // Not JSON, leave errorCode undefined
48
+ }
49
+ throw new TokenRefreshError(`Token refresh failed: ${response.statusText}. ${errorText}`, response.status, errorCode);
42
50
  }
43
51
  const data = await response.json();
44
52
  // Validate response contains required fields
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/auth/refresh.ts"],"sourcesContent":["/* eslint-disable perfectionist/sort-objects */\nimport type { FigmaTokenResponse, FigmaTokens } from './types.js'\n\nimport { getOAuthConfig } from '../config/oauth.js'\nimport * as log from '../utils/log.js'\n\n/**\n * Error thrown when token refresh fails\n */\nexport class TokenRefreshError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n ) {\n super(message)\n this.name = 'TokenRefreshError'\n }\n}\n\n/**\n * Refresh an expired OAuth2 access token using a refresh token\n *\n * Makes a POST request to Figma's token endpoint with the refresh token\n * to obtain a new access token. Uses public client pattern (no client secret).\n *\n * @param refreshToken - The refresh token to use\n * @param clientId - OAuth2 client ID (defaults to config)\n * @returns Promise resolving to new tokens\n * @throws TokenRefreshError if refresh fails\n */\nexport async function refreshAccessToken(\n refreshToken: string,\n clientId?: string,\n): Promise<FigmaTokens> {\n const oauthConfig = getOAuthConfig()\n const resolvedClientId = clientId ?? oauthConfig.clientId\n\n // Prepare form data for token refresh request (public client)\n const params = new URLSearchParams({\n client_id: resolvedClientId,\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n })\n\n try {\n const response = await fetch(oauthConfig.refreshUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new TokenRefreshError(\n `Token refresh failed: ${response.statusText}. ${errorText}`,\n response.status,\n )\n }\n\n const data = (await response.json()) as FigmaTokenResponse\n\n // Validate response contains required fields\n if (!data.access_token || !data.refresh_token || !data.expires_in) {\n throw new TokenRefreshError('Invalid token response: missing required fields')\n }\n\n // Convert response to our internal token format\n const expiresAt = Date.now() + data.expires_in * 1000\n\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt,\n userId: data.user_id_string,\n }\n } catch (error) {\n if (error instanceof TokenRefreshError) {\n throw error\n }\n\n // Handle network errors and other exceptions\n throw new TokenRefreshError(\n `Failed to refresh token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n}\n\n/**\n * Exchange an authorization code for OAuth2 tokens\n *\n * This is used during the initial OAuth flow after the user authorizes the app.\n * Uses public client pattern with PKCE (code verifier is required).\n *\n * @param code - Authorization code from OAuth callback\n * @param codeVerifier - PKCE code verifier (required for public clients)\n * @param redirectUri - Redirect URI used in the authorization request\n * @param clientId - OAuth2 client ID (defaults to config)\n * @returns Promise resolving to tokens\n * @throws TokenRefreshError if exchange fails\n */\nexport async function exchangeCodeForTokens(\n code: string,\n codeVerifier: string,\n redirectUri?: string,\n clientId?: string,\n): Promise<FigmaTokens> {\n const oauthConfig = getOAuthConfig()\n const resolvedRedirectUri = redirectUri ?? oauthConfig.redirectUri\n const resolvedClientId = clientId ?? oauthConfig.clientId\n\n // Prepare form data for token exchange (public client with PKCE)\n const params = new URLSearchParams({\n client_id: resolvedClientId,\n code,\n code_verifier: codeVerifier,\n grant_type: 'authorization_code',\n redirect_uri: resolvedRedirectUri,\n })\n\n try {\n // Public client - no client secret or Basic Auth header needed\n // Security is provided by PKCE (code_verifier in the request body)\n const headers: Record<string, string> = {\n 'Content-Type': 'application/x-www-form-urlencoded',\n }\n\n // Debug logging\n log.debug(\n `Token exchange request: ${JSON.stringify({\n url: oauthConfig.tokenUrl,\n headers,\n body: params.toString(),\n })}`,\n )\n\n const response = await fetch(oauthConfig.tokenUrl, {\n method: 'POST',\n headers,\n body: params.toString(),\n })\n\n log.debug(\n `Token exchange response: ${JSON.stringify({\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries()),\n })}`,\n )\n\n if (!response.ok) {\n const errorText = await response.text()\n log.debug(`Error response body: ${errorText}`)\n\n throw new TokenRefreshError(\n `Token exchange failed: ${response.statusText}. ${errorText}`,\n response.status,\n )\n }\n\n const data = (await response.json()) as FigmaTokenResponse\n\n log.debug(\n `Token response: ${JSON.stringify({\n access_token: data.access_token,\n token_type: data.token_type,\n expires_in: data.expires_in,\n scope: data.scope,\n user_id_string: data.user_id_string,\n refresh_token: data.refresh_token?.substring(0, 10) + '...',\n })}`,\n )\n\n // Validate response contains required fields\n if (!data.access_token || !data.refresh_token || !data.expires_in) {\n throw new TokenRefreshError('Invalid token response: missing required fields')\n }\n\n // Convert response to our internal token format\n const expiresAt = Date.now() + data.expires_in * 1000\n\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt,\n userId: data.user_id_string,\n }\n } catch (error) {\n if (error instanceof TokenRefreshError) {\n throw error\n }\n\n // Handle network errors and other exceptions\n throw new TokenRefreshError(\n `Failed to exchange code for tokens: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n}\n"],"names":["getOAuthConfig","log","TokenRefreshError","Error","message","statusCode","name","refreshAccessToken","refreshToken","clientId","oauthConfig","resolvedClientId","params","URLSearchParams","client_id","grant_type","refresh_token","response","fetch","refreshUrl","method","headers","body","toString","ok","errorText","text","statusText","status","data","json","access_token","expires_in","expiresAt","Date","now","accessToken","userId","user_id_string","error","exchangeCodeForTokens","code","codeVerifier","redirectUri","resolvedRedirectUri","code_verifier","redirect_uri","debug","JSON","stringify","url","tokenUrl","Object","fromEntries","entries","token_type","scope","substring"],"mappings":"AAAA,6CAA6C,GAG7C,SAASA,cAAc,QAAQ,qBAAoB;AACnD,YAAYC,SAAS,kBAAiB;AAEtC;;CAEC,GACD,OAAO,MAAMC,0BAA0BC;;IACrC,YACEC,OAAe,EACf,AAAOC,UAAmB,CAC1B;QACA,KAAK,CAACD,eAFCC,aAAAA;QAGP,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA;;;;;;;;;;CAUC,GACD,OAAO,eAAeC,mBACpBC,YAAoB,EACpBC,QAAiB;IAEjB,MAAMC,cAAcV;IACpB,MAAMW,mBAAmBF,YAAYC,YAAYD,QAAQ;IAEzD,8DAA8D;IAC9D,MAAMG,SAAS,IAAIC,gBAAgB;QACjCC,WAAWH;QACXI,YAAY;QACZC,eAAeR;IACjB;IAEA,IAAI;QACF,MAAMS,WAAW,MAAMC,MAAMR,YAAYS,UAAU,EAAE;YACnDC,QAAQ;YACRC,SAAS;gBACP,gBAAgB;YAClB;YACAC,MAAMV,OAAOW,QAAQ;QACvB;QAEA,IAAI,CAACN,SAASO,EAAE,EAAE;YAChB,MAAMC,YAAY,MAAMR,SAASS,IAAI;YACrC,MAAM,IAAIxB,kBACR,CAAC,sBAAsB,EAAEe,SAASU,UAAU,CAAC,EAAE,EAAEF,WAAW,EAC5DR,SAASW,MAAM;QAEnB;QAEA,MAAMC,OAAQ,MAAMZ,SAASa,IAAI;QAEjC,6CAA6C;QAC7C,IAAI,CAACD,KAAKE,YAAY,IAAI,CAACF,KAAKb,aAAa,IAAI,CAACa,KAAKG,UAAU,EAAE;YACjE,MAAM,IAAI9B,kBAAkB;QAC9B;QAEA,gDAAgD;QAChD,MAAM+B,YAAYC,KAAKC,GAAG,KAAKN,KAAKG,UAAU,GAAG;QAEjD,OAAO;YACLI,aAAaP,KAAKE,YAAY;YAC9BvB,cAAcqB,KAAKb,aAAa;YAChCiB;YACAI,QAAQR,KAAKS,cAAc;QAC7B;IACF,EAAE,OAAOC,OAAO;QACd,IAAIA,iBAAiBrC,mBAAmB;YACtC,MAAMqC;QACR;QAEA,6CAA6C;QAC7C,MAAM,IAAIrC,kBACR,CAAC,yBAAyB,EAAEqC,iBAAiBpC,QAAQoC,MAAMnC,OAAO,GAAG,iBAAiB;IAE1F;AACF;AAEA;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeoC,sBACpBC,IAAY,EACZC,YAAoB,EACpBC,WAAoB,EACpBlC,QAAiB;IAEjB,MAAMC,cAAcV;IACpB,MAAM4C,sBAAsBD,eAAejC,YAAYiC,WAAW;IAClE,MAAMhC,mBAAmBF,YAAYC,YAAYD,QAAQ;IAEzD,iEAAiE;IACjE,MAAMG,SAAS,IAAIC,gBAAgB;QACjCC,WAAWH;QACX8B;QACAI,eAAeH;QACf3B,YAAY;QACZ+B,cAAcF;IAChB;IAEA,IAAI;QACF,+DAA+D;QAC/D,mEAAmE;QACnE,MAAMvB,UAAkC;YACtC,gBAAgB;QAClB;QAEA,gBAAgB;QAChBpB,IAAI8C,KAAK,CACP,CAAC,wBAAwB,EAAEC,KAAKC,SAAS,CAAC;YACxCC,KAAKxC,YAAYyC,QAAQ;YACzB9B;YACAC,MAAMV,OAAOW,QAAQ;QACvB,IAAI;QAGN,MAAMN,WAAW,MAAMC,MAAMR,YAAYyC,QAAQ,EAAE;YACjD/B,QAAQ;YACRC;YACAC,MAAMV,OAAOW,QAAQ;QACvB;QAEAtB,IAAI8C,KAAK,CACP,CAAC,yBAAyB,EAAEC,KAAKC,SAAS,CAAC;YACzCrB,QAAQX,SAASW,MAAM;YACvBD,YAAYV,SAASU,UAAU;YAC/BN,SAAS+B,OAAOC,WAAW,CAACpC,SAASI,OAAO,CAACiC,OAAO;QACtD,IAAI;QAGN,IAAI,CAACrC,SAASO,EAAE,EAAE;YAChB,MAAMC,YAAY,MAAMR,SAASS,IAAI;YACrCzB,IAAI8C,KAAK,CAAC,CAAC,qBAAqB,EAAEtB,WAAW;YAE7C,MAAM,IAAIvB,kBACR,CAAC,uBAAuB,EAAEe,SAASU,UAAU,CAAC,EAAE,EAAEF,WAAW,EAC7DR,SAASW,MAAM;QAEnB;QAEA,MAAMC,OAAQ,MAAMZ,SAASa,IAAI;QAEjC7B,IAAI8C,KAAK,CACP,CAAC,gBAAgB,EAAEC,KAAKC,SAAS,CAAC;YAChClB,cAAcF,KAAKE,YAAY;YAC/BwB,YAAY1B,KAAK0B,UAAU;YAC3BvB,YAAYH,KAAKG,UAAU;YAC3BwB,OAAO3B,KAAK2B,KAAK;YACjBlB,gBAAgBT,KAAKS,cAAc;YACnCtB,eAAea,KAAKb,aAAa,EAAEyC,UAAU,GAAG,MAAM;QACxD,IAAI;QAGN,6CAA6C;QAC7C,IAAI,CAAC5B,KAAKE,YAAY,IAAI,CAACF,KAAKb,aAAa,IAAI,CAACa,KAAKG,UAAU,EAAE;YACjE,MAAM,IAAI9B,kBAAkB;QAC9B;QAEA,gDAAgD;QAChD,MAAM+B,YAAYC,KAAKC,GAAG,KAAKN,KAAKG,UAAU,GAAG;QAEjD,OAAO;YACLI,aAAaP,KAAKE,YAAY;YAC9BvB,cAAcqB,KAAKb,aAAa;YAChCiB;YACAI,QAAQR,KAAKS,cAAc;QAC7B;IACF,EAAE,OAAOC,OAAO;QACd,IAAIA,iBAAiBrC,mBAAmB;YACtC,MAAMqC;QACR;QAEA,6CAA6C;QAC7C,MAAM,IAAIrC,kBACR,CAAC,oCAAoC,EAAEqC,iBAAiBpC,QAAQoC,MAAMnC,OAAO,GAAG,iBAAiB;IAErG;AACF"}
1
+ {"version":3,"sources":["../../src/auth/refresh.ts"],"sourcesContent":["/* eslint-disable perfectionist/sort-objects */\nimport type { FigmaTokenResponse, FigmaTokens } from './types.js'\n\nimport { getOAuthConfig } from '../config/oauth.js'\nimport * as log from '../utils/log.js'\n\n/**\n * Error thrown when token refresh fails\n */\nexport class TokenRefreshError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public errorCode?: string,\n ) {\n super(message)\n this.name = 'TokenRefreshError'\n }\n}\n\n/**\n * Refresh an expired OAuth2 access token using a refresh token\n *\n * Makes a POST request to Figma's token endpoint with the refresh token\n * to obtain a new access token. Uses public client pattern (no client secret).\n *\n * @param refreshToken - The refresh token to use\n * @param clientId - OAuth2 client ID (defaults to config)\n * @returns Promise resolving to new tokens\n * @throws TokenRefreshError if refresh fails\n */\nexport async function refreshAccessToken(\n refreshToken: string,\n clientId?: string,\n): Promise<FigmaTokens> {\n const oauthConfig = getOAuthConfig()\n const resolvedClientId = clientId ?? oauthConfig.clientId\n\n // Prepare form data for token refresh request (public client)\n const params = new URLSearchParams({\n client_id: resolvedClientId,\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n })\n\n try {\n const response = await fetch(oauthConfig.refreshUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n let errorCode: string | undefined\n try {\n const parsed = JSON.parse(errorText)\n errorCode = parsed.error\n } catch {\n // Not JSON, leave errorCode undefined\n }\n throw new TokenRefreshError(\n `Token refresh failed: ${response.statusText}. ${errorText}`,\n response.status,\n errorCode,\n )\n }\n\n const data = (await response.json()) as FigmaTokenResponse\n\n // Validate response contains required fields\n if (!data.access_token || !data.refresh_token || !data.expires_in) {\n throw new TokenRefreshError('Invalid token response: missing required fields')\n }\n\n // Convert response to our internal token format\n const expiresAt = Date.now() + data.expires_in * 1000\n\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt,\n userId: data.user_id_string,\n }\n } catch (error) {\n if (error instanceof TokenRefreshError) {\n throw error\n }\n\n // Handle network errors and other exceptions\n throw new TokenRefreshError(\n `Failed to refresh token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n}\n\n/**\n * Exchange an authorization code for OAuth2 tokens\n *\n * This is used during the initial OAuth flow after the user authorizes the app.\n * Uses public client pattern with PKCE (code verifier is required).\n *\n * @param code - Authorization code from OAuth callback\n * @param codeVerifier - PKCE code verifier (required for public clients)\n * @param redirectUri - Redirect URI used in the authorization request\n * @param clientId - OAuth2 client ID (defaults to config)\n * @returns Promise resolving to tokens\n * @throws TokenRefreshError if exchange fails\n */\nexport async function exchangeCodeForTokens(\n code: string,\n codeVerifier: string,\n redirectUri?: string,\n clientId?: string,\n): Promise<FigmaTokens> {\n const oauthConfig = getOAuthConfig()\n const resolvedRedirectUri = redirectUri ?? oauthConfig.redirectUri\n const resolvedClientId = clientId ?? oauthConfig.clientId\n\n // Prepare form data for token exchange (public client with PKCE)\n const params = new URLSearchParams({\n client_id: resolvedClientId,\n code,\n code_verifier: codeVerifier,\n grant_type: 'authorization_code',\n redirect_uri: resolvedRedirectUri,\n })\n\n try {\n // Public client - no client secret or Basic Auth header needed\n // Security is provided by PKCE (code_verifier in the request body)\n const headers: Record<string, string> = {\n 'Content-Type': 'application/x-www-form-urlencoded',\n }\n\n // Debug logging\n log.debug(\n `Token exchange request: ${JSON.stringify({\n url: oauthConfig.tokenUrl,\n headers,\n body: params.toString(),\n })}`,\n )\n\n const response = await fetch(oauthConfig.tokenUrl, {\n method: 'POST',\n headers,\n body: params.toString(),\n })\n\n log.debug(\n `Token exchange response: ${JSON.stringify({\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries()),\n })}`,\n )\n\n if (!response.ok) {\n const errorText = await response.text()\n log.debug(`Error response body: ${errorText}`)\n\n throw new TokenRefreshError(\n `Token exchange failed: ${response.statusText}. ${errorText}`,\n response.status,\n )\n }\n\n const data = (await response.json()) as FigmaTokenResponse\n\n log.debug(\n `Token response: ${JSON.stringify({\n access_token: data.access_token,\n token_type: data.token_type,\n expires_in: data.expires_in,\n scope: data.scope,\n user_id_string: data.user_id_string,\n refresh_token: data.refresh_token?.substring(0, 10) + '...',\n })}`,\n )\n\n // Validate response contains required fields\n if (!data.access_token || !data.refresh_token || !data.expires_in) {\n throw new TokenRefreshError('Invalid token response: missing required fields')\n }\n\n // Convert response to our internal token format\n const expiresAt = Date.now() + data.expires_in * 1000\n\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt,\n userId: data.user_id_string,\n }\n } catch (error) {\n if (error instanceof TokenRefreshError) {\n throw error\n }\n\n // Handle network errors and other exceptions\n throw new TokenRefreshError(\n `Failed to exchange code for tokens: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n}\n"],"names":["getOAuthConfig","log","TokenRefreshError","Error","message","statusCode","errorCode","name","refreshAccessToken","refreshToken","clientId","oauthConfig","resolvedClientId","params","URLSearchParams","client_id","grant_type","refresh_token","response","fetch","refreshUrl","method","headers","body","toString","ok","errorText","text","parsed","JSON","parse","error","statusText","status","data","json","access_token","expires_in","expiresAt","Date","now","accessToken","userId","user_id_string","exchangeCodeForTokens","code","codeVerifier","redirectUri","resolvedRedirectUri","code_verifier","redirect_uri","debug","stringify","url","tokenUrl","Object","fromEntries","entries","token_type","scope","substring"],"mappings":"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"}
@@ -1,11 +1,13 @@
1
+ import type { Environment } from '../constants.js';
1
2
  import type { FigmaTokens, JWTPayload, ProjectToken, TokenStoreConfig } from './types.js';
2
3
  /**
3
- * Get the singleton TokenStore instance
4
- * Use this for all production code to ensure a single shared instance
4
+ * Get the singleton TokenStore instance for an environment
5
+ * Use this for all production code to ensure a single shared instance per environment
5
6
  *
6
- * @returns The singleton TokenStore instance
7
+ * @param environment - The environment to get the token store for
8
+ * @returns The singleton TokenStore instance for that environment
7
9
  */
8
- export declare function getTokenStore(): TokenStore;
10
+ export declare function getTokenStore(environment: Environment): TokenStore;
9
11
  /**
10
12
  * Secure storage manager for Figma OAuth2 tokens
11
13
  *
@@ -1 +1 @@
1
- {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAUzF;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAK1C;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAA4E;gBAE9E,OAAO,CAAC,EAAE,gBAAgB;IActC;;;OAGG;IACH,SAAS,IAAI,WAAW,GAAG,IAAI;IAK/B;;;OAGG;IACH,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAIpC;;OAEG;IACH,WAAW,IAAI,IAAI;IAInB;;;OAGG;IACH,cAAc,IAAI,OAAO;IAQzB;;;;OAIG;IACH,SAAS,IAAI,OAAO;IAcpB;;;OAGG;IACH,cAAc,IAAI,IAAI,GAAG,MAAM;IAQ/B;;;OAGG;IACH,eAAe,IAAI,IAAI,GAAG,MAAM;IAKhC;;;;;OAKG;IACH,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAe/D;;;OAGG;IACH,cAAc,IAAI,MAAM;IAIxB;;;;;OAKG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY;IAiBtD;;;;OAIG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI;IAMnE;;;OAGG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMzC;;OAEG;IACH,qBAAqB,IAAI,IAAI;IAI7B;;;;;OAKG;IACH,qBAAqB,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO;IAQ1D;;;;OAIG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAQ/C;;;;OAIG;IACH,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAK1D;;;OAGG;IACH,2BAA2B,IAAI,MAAM,EAAE;CAIxC"}
1
+ {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAUzF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAKlE;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAA4E;gBAE9E,OAAO,CAAC,EAAE,gBAAgB;IAiBtC;;;OAGG;IACH,SAAS,IAAI,WAAW,GAAG,IAAI;IAK/B;;;OAGG;IACH,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAIpC;;OAEG;IACH,WAAW,IAAI,IAAI;IAInB;;;OAGG;IACH,cAAc,IAAI,OAAO;IAQzB;;;;OAIG;IACH,SAAS,IAAI,OAAO;IAcpB;;;OAGG;IACH,cAAc,IAAI,IAAI,GAAG,MAAM;IAQ/B;;;OAGG;IACH,eAAe,IAAI,IAAI,GAAG,MAAM;IAKhC;;;;;OAKG;IACH,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAe/D;;;OAGG;IACH,cAAc,IAAI,MAAM;IAIxB;;;;;OAKG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY;IAiBtD;;;;OAIG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI;IAMnE;;;OAGG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMzC;;OAEG;IACH,qBAAqB,IAAI,IAAI;IAI7B;;;;;OAKG;IACH,qBAAqB,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO;IAQ1D;;;;OAIG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAQ/C;;;;OAIG;IACH,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAK1D;;;OAGG;IACH,2BAA2B,IAAI,MAAM,EAAE;CAIxC"}
@@ -2,18 +2,21 @@
2
2
  import { TOKEN_EXPIRY_BUFFER_SECONDS } from '../config/oauth.js';
3
3
  import { deriveEncryptionKey } from './crypto-utils.js';
4
4
  /**
5
- * Module-level singleton instance for production use
6
- */ let defaultInstance = null;
5
+ * Environment-keyed instances for singleton pattern
6
+ */ const instances = {};
7
7
  /**
8
- * Get the singleton TokenStore instance
9
- * Use this for all production code to ensure a single shared instance
8
+ * Get the singleton TokenStore instance for an environment
9
+ * Use this for all production code to ensure a single shared instance per environment
10
10
  *
11
- * @returns The singleton TokenStore instance
12
- */ export function getTokenStore() {
13
- if (!defaultInstance) {
14
- defaultInstance = new TokenStore();
11
+ * @param environment - The environment to get the token store for
12
+ * @returns The singleton TokenStore instance for that environment
13
+ */ export function getTokenStore(environment) {
14
+ if (!instances[environment]) {
15
+ instances[environment] = new TokenStore({
16
+ environment
17
+ });
15
18
  }
16
- return defaultInstance;
19
+ return instances[environment];
17
20
  }
18
21
  /**
19
22
  * Secure storage manager for Figma OAuth2 tokens
@@ -34,13 +37,13 @@ import { deriveEncryptionKey } from './crypto-utils.js';
34
37
  if (process.env.AWS_EXECUTION_ENV) {
35
38
  throw new Error('TokenStore cannot be used in AWS Lambda environments');
36
39
  }
40
+ const environment = options?.environment ?? 'production';
41
+ const projectName = environment === 'production' ? 'payloadcms-figma' : `payloadcms-figma-${environment}`;
37
42
  this.config = new Conf({
38
- configName: options?.configName || 'payloadcms-figma',
39
- projectName: 'payloadcms-figma',
40
- // Enable encryption with machine-specific key for better security
43
+ clearInvalidConfig: true,
44
+ configName: options?.configName || projectName,
41
45
  encryptionKey: options?.encryptionKey || deriveEncryptionKey(),
42
- // Clear schema to avoid validation issues
43
- clearInvalidConfig: true
46
+ projectName
44
47
  });
45
48
  }
46
49
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/auth/token-store.ts"],"sourcesContent":["/* eslint-disable perfectionist/sort-classes */\nimport Conf from 'conf'\n\nimport type { FigmaTokens, JWTPayload, ProjectToken, TokenStoreConfig } from './types.js'\n\nimport { TOKEN_EXPIRY_BUFFER_SECONDS } from '../config/oauth.js'\nimport { deriveEncryptionKey } from './crypto-utils.js'\n\n/**\n * Module-level singleton instance for production use\n */\nlet defaultInstance: null | TokenStore = null\n\n/**\n * Get the singleton TokenStore instance\n * Use this for all production code to ensure a single shared instance\n *\n * @returns The singleton TokenStore instance\n */\nexport function getTokenStore(): TokenStore {\n if (!defaultInstance) {\n defaultInstance = new TokenStore()\n }\n return defaultInstance\n}\n\n/**\n * Secure storage manager for Figma OAuth2 tokens\n *\n * Uses the `conf` library to store tokens in an OS-specific secure location:\n * - macOS: ~/Library/Preferences/payloadcms-figma\n * - Linux: ~/.config/payloadcms-figma or $XDG_CONFIG_HOME/payloadcms-figma\n * - Windows: %APPDATA%/payloadcms-figma/Config\n *\n * Tokens are encrypted at rest with a machine-specific key and file permissions\n * are set to 0600 (owner read/write only)\n *\n * For production code, use getTokenStore() to get the singleton instance.\n * The constructor is still exported for test isolation.\n */\nexport class TokenStore {\n private config: Conf<{ projectTokens: Record<string, ProjectToken>; tokens: FigmaTokens }>\n\n constructor(options?: TokenStoreConfig) {\n if (process.env.AWS_EXECUTION_ENV) {\n throw new Error('TokenStore cannot be used in AWS Lambda environments')\n }\n this.config = new Conf<{ projectTokens: Record<string, ProjectToken>; tokens: FigmaTokens }>({\n configName: options?.configName || 'payloadcms-figma',\n projectName: 'payloadcms-figma',\n // Enable encryption with machine-specific key for better security\n encryptionKey: options?.encryptionKey || deriveEncryptionKey(),\n // Clear schema to avoid validation issues\n clearInvalidConfig: true,\n })\n }\n\n /**\n * Retrieve stored tokens\n * @returns FigmaTokens if stored, null otherwise\n */\n getTokens(): FigmaTokens | null {\n const tokens = this.config.get('tokens')\n return tokens || null\n }\n\n /**\n * Store OAuth2 tokens securely\n * @param tokens - Figma OAuth2 tokens to store\n */\n setTokens(tokens: FigmaTokens): void {\n this.config.set('tokens', tokens)\n }\n\n /**\n * Clear all stored tokens (used for logout)\n */\n clearTokens(): void {\n this.config.delete('tokens')\n }\n\n /**\n * Check if valid (non-expired) tokens exist\n * @returns true if tokens exist and are not expired\n */\n hasValidTokens(): boolean {\n const tokens = this.getTokens()\n if (!tokens) {\n return false\n }\n return !this.isExpired()\n }\n\n /**\n * Check if the current access token is expired\n * Includes a buffer time to avoid using tokens that are about to expire\n * @returns true if token is expired or will expire within buffer time\n */\n isExpired(): boolean {\n // Access config directly to avoid circular dependency with getTokens()\n const tokens = this.config.get('tokens')\n if (!tokens) {\n return true\n }\n\n const now = Date.now()\n const bufferMs = TOKEN_EXPIRY_BUFFER_SECONDS * 1000\n const expiryWithBuffer = tokens.expiresAt - bufferMs\n\n return now >= expiryWithBuffer\n }\n\n /**\n * Get the access token if it exists and is valid\n * @returns Access token string or null if expired/missing\n */\n getAccessToken(): null | string {\n if (!this.hasValidTokens()) {\n return null\n }\n const tokens = this.getTokens()\n return tokens?.accessToken || null\n }\n\n /**\n * Get the refresh token if it exists\n * @returns Refresh token string or null if missing\n */\n getRefreshToken(): null | string {\n const tokens = this.getTokens()\n return tokens?.refreshToken || null\n }\n\n /**\n * Update just the access token after a refresh\n * Preserves the existing refresh token and other metadata\n * @param accessToken - New access token\n * @param expiresIn - Expiration time in seconds\n */\n updateAccessToken(accessToken: string, expiresIn: number): void {\n const existingTokens = this.getTokens()\n if (!existingTokens) {\n throw new Error('Cannot update access token: no existing tokens found')\n }\n\n const expiresAt = Date.now() + expiresIn * 1000\n\n this.setTokens({\n ...existingTokens,\n accessToken,\n expiresAt,\n })\n }\n\n /**\n * Get the path to the config file (useful for debugging)\n * @returns Absolute path to the token storage file\n */\n getStoragePath(): string {\n return this.config.path\n }\n\n /**\n * Retrieve a project token for a specific tenant\n * Automatically removes expired tokens from storage\n * @param tenantId - The tenant/CMS ID\n * @returns ProjectToken if stored and valid, null otherwise\n */\n getProjectToken(tenantId: string): null | ProjectToken {\n const projectTokens = this.config.get('projectTokens') || {}\n const projectToken = projectTokens[tenantId]\n\n if (!projectToken) {\n return null\n }\n\n // Auto-cleanup expired project token\n if (this.isProjectTokenExpired(projectToken)) {\n this.clearProjectToken(tenantId)\n return null\n }\n\n return projectToken\n }\n\n /**\n * Store a project token for a specific tenant\n * @param tenantId - The tenant/CMS ID\n * @param projectToken - The project token to store\n */\n setProjectToken(tenantId: string, projectToken: ProjectToken): void {\n const projectTokens = this.config.get('projectTokens') || {}\n projectTokens[tenantId] = projectToken\n this.config.set('projectTokens', projectTokens)\n }\n\n /**\n * Clear a project token for a specific tenant\n * @param tenantId - The tenant/CMS ID\n */\n clearProjectToken(tenantId: string): void {\n const projectTokens = this.config.get('projectTokens') || {}\n delete projectTokens[tenantId]\n this.config.set('projectTokens', projectTokens)\n }\n\n /**\n * Clear all project tokens\n */\n clearAllProjectTokens(): void {\n this.config.delete('projectTokens')\n }\n\n /**\n * Check if a project token is expired\n * Includes a buffer time to avoid using tokens that are about to expire\n * @param projectToken - The project token to check\n * @returns true if token is expired or will expire within buffer time\n */\n isProjectTokenExpired(projectToken: ProjectToken): boolean {\n const now = Date.now()\n const bufferMs = TOKEN_EXPIRY_BUFFER_SECONDS * 1000\n const expiryWithBuffer = projectToken.expiresAt - bufferMs\n\n return now >= expiryWithBuffer\n }\n\n /**\n * Check if a valid (non-expired) project token exists for a tenant\n * @param tenantId - The tenant/CMS ID\n * @returns true if token exists and is not expired\n */\n hasValidProjectToken(tenantId: string): boolean {\n const projectToken = this.getProjectToken(tenantId)\n if (!projectToken) {\n return false\n }\n return !this.isProjectTokenExpired(projectToken)\n }\n\n /**\n * Get validated JWT claims from a project token\n * @param tenantId - The tenant/CMS ID\n * @returns JWT payload claims or null if token not found or has no claims\n */\n getProjectTokenClaims(tenantId: string): JWTPayload | null {\n const projectToken = this.getProjectToken(tenantId)\n return projectToken?.claims || null\n }\n\n /**\n * Get all tenant IDs that have stored project tokens\n * @returns Array of tenant IDs\n */\n getAllProjectTokenTenantIds(): string[] {\n const projectTokens = this.config.get('projectTokens') || {}\n return Object.keys(projectTokens)\n }\n}\n"],"names":["Conf","TOKEN_EXPIRY_BUFFER_SECONDS","deriveEncryptionKey","defaultInstance","getTokenStore","TokenStore","config","options","process","env","AWS_EXECUTION_ENV","Error","configName","projectName","encryptionKey","clearInvalidConfig","getTokens","tokens","get","setTokens","set","clearTokens","delete","hasValidTokens","isExpired","now","Date","bufferMs","expiryWithBuffer","expiresAt","getAccessToken","accessToken","getRefreshToken","refreshToken","updateAccessToken","expiresIn","existingTokens","getStoragePath","path","getProjectToken","tenantId","projectTokens","projectToken","isProjectTokenExpired","clearProjectToken","setProjectToken","clearAllProjectTokens","hasValidProjectToken","getProjectTokenClaims","claims","getAllProjectTokenTenantIds","Object","keys"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/auth/token-store.ts"],"sourcesContent":["/* eslint-disable perfectionist/sort-classes */\nimport Conf from 'conf'\n\nimport type { Environment } from '../constants.js'\nimport type { FigmaTokens, JWTPayload, ProjectToken, TokenStoreConfig } from './types.js'\n\nimport { TOKEN_EXPIRY_BUFFER_SECONDS } from '../config/oauth.js'\nimport { deriveEncryptionKey } from './crypto-utils.js'\n\n/**\n * Environment-keyed instances for singleton pattern\n */\nconst instances: Partial<Record<Environment, TokenStore>> = {}\n\n/**\n * Get the singleton TokenStore instance for an environment\n * Use this for all production code to ensure a single shared instance per environment\n *\n * @param environment - The environment to get the token store for\n * @returns The singleton TokenStore instance for that environment\n */\nexport function getTokenStore(environment: Environment): TokenStore {\n if (!instances[environment]) {\n instances[environment] = new TokenStore({ environment })\n }\n return instances[environment]\n}\n\n/**\n * Secure storage manager for Figma OAuth2 tokens\n *\n * Uses the `conf` library to store tokens in an OS-specific secure location:\n * - macOS: ~/Library/Preferences/payloadcms-figma\n * - Linux: ~/.config/payloadcms-figma or $XDG_CONFIG_HOME/payloadcms-figma\n * - Windows: %APPDATA%/payloadcms-figma/Config\n *\n * Tokens are encrypted at rest with a machine-specific key and file permissions\n * are set to 0600 (owner read/write only)\n *\n * For production code, use getTokenStore() to get the singleton instance.\n * The constructor is still exported for test isolation.\n */\nexport class TokenStore {\n private config: Conf<{ projectTokens: Record<string, ProjectToken>; tokens: FigmaTokens }>\n\n constructor(options?: TokenStoreConfig) {\n if (process.env.AWS_EXECUTION_ENV) {\n throw new Error('TokenStore cannot be used in AWS Lambda environments')\n }\n\n const environment = options?.environment ?? 'production'\n const projectName =\n environment === 'production' ? 'payloadcms-figma' : `payloadcms-figma-${environment}`\n\n this.config = new Conf<{ projectTokens: Record<string, ProjectToken>; tokens: FigmaTokens }>({\n clearInvalidConfig: true,\n configName: options?.configName || projectName,\n encryptionKey: options?.encryptionKey || deriveEncryptionKey(),\n projectName,\n })\n }\n\n /**\n * Retrieve stored tokens\n * @returns FigmaTokens if stored, null otherwise\n */\n getTokens(): FigmaTokens | null {\n const tokens = this.config.get('tokens')\n return tokens || null\n }\n\n /**\n * Store OAuth2 tokens securely\n * @param tokens - Figma OAuth2 tokens to store\n */\n setTokens(tokens: FigmaTokens): void {\n this.config.set('tokens', tokens)\n }\n\n /**\n * Clear all stored tokens (used for logout)\n */\n clearTokens(): void {\n this.config.delete('tokens')\n }\n\n /**\n * Check if valid (non-expired) tokens exist\n * @returns true if tokens exist and are not expired\n */\n hasValidTokens(): boolean {\n const tokens = this.getTokens()\n if (!tokens) {\n return false\n }\n return !this.isExpired()\n }\n\n /**\n * Check if the current access token is expired\n * Includes a buffer time to avoid using tokens that are about to expire\n * @returns true if token is expired or will expire within buffer time\n */\n isExpired(): boolean {\n // Access config directly to avoid circular dependency with getTokens()\n const tokens = this.config.get('tokens')\n if (!tokens) {\n return true\n }\n\n const now = Date.now()\n const bufferMs = TOKEN_EXPIRY_BUFFER_SECONDS * 1000\n const expiryWithBuffer = tokens.expiresAt - bufferMs\n\n return now >= expiryWithBuffer\n }\n\n /**\n * Get the access token if it exists and is valid\n * @returns Access token string or null if expired/missing\n */\n getAccessToken(): null | string {\n if (!this.hasValidTokens()) {\n return null\n }\n const tokens = this.getTokens()\n return tokens?.accessToken || null\n }\n\n /**\n * Get the refresh token if it exists\n * @returns Refresh token string or null if missing\n */\n getRefreshToken(): null | string {\n const tokens = this.getTokens()\n return tokens?.refreshToken || null\n }\n\n /**\n * Update just the access token after a refresh\n * Preserves the existing refresh token and other metadata\n * @param accessToken - New access token\n * @param expiresIn - Expiration time in seconds\n */\n updateAccessToken(accessToken: string, expiresIn: number): void {\n const existingTokens = this.getTokens()\n if (!existingTokens) {\n throw new Error('Cannot update access token: no existing tokens found')\n }\n\n const expiresAt = Date.now() + expiresIn * 1000\n\n this.setTokens({\n ...existingTokens,\n accessToken,\n expiresAt,\n })\n }\n\n /**\n * Get the path to the config file (useful for debugging)\n * @returns Absolute path to the token storage file\n */\n getStoragePath(): string {\n return this.config.path\n }\n\n /**\n * Retrieve a project token for a specific tenant\n * Automatically removes expired tokens from storage\n * @param tenantId - The tenant/CMS ID\n * @returns ProjectToken if stored and valid, null otherwise\n */\n getProjectToken(tenantId: string): null | ProjectToken {\n const projectTokens = this.config.get('projectTokens') || {}\n const projectToken = projectTokens[tenantId]\n\n if (!projectToken) {\n return null\n }\n\n // Auto-cleanup expired project token\n if (this.isProjectTokenExpired(projectToken)) {\n this.clearProjectToken(tenantId)\n return null\n }\n\n return projectToken\n }\n\n /**\n * Store a project token for a specific tenant\n * @param tenantId - The tenant/CMS ID\n * @param projectToken - The project token to store\n */\n setProjectToken(tenantId: string, projectToken: ProjectToken): void {\n const projectTokens = this.config.get('projectTokens') || {}\n projectTokens[tenantId] = projectToken\n this.config.set('projectTokens', projectTokens)\n }\n\n /**\n * Clear a project token for a specific tenant\n * @param tenantId - The tenant/CMS ID\n */\n clearProjectToken(tenantId: string): void {\n const projectTokens = this.config.get('projectTokens') || {}\n delete projectTokens[tenantId]\n this.config.set('projectTokens', projectTokens)\n }\n\n /**\n * Clear all project tokens\n */\n clearAllProjectTokens(): void {\n this.config.delete('projectTokens')\n }\n\n /**\n * Check if a project token is expired\n * Includes a buffer time to avoid using tokens that are about to expire\n * @param projectToken - The project token to check\n * @returns true if token is expired or will expire within buffer time\n */\n isProjectTokenExpired(projectToken: ProjectToken): boolean {\n const now = Date.now()\n const bufferMs = TOKEN_EXPIRY_BUFFER_SECONDS * 1000\n const expiryWithBuffer = projectToken.expiresAt - bufferMs\n\n return now >= expiryWithBuffer\n }\n\n /**\n * Check if a valid (non-expired) project token exists for a tenant\n * @param tenantId - The tenant/CMS ID\n * @returns true if token exists and is not expired\n */\n hasValidProjectToken(tenantId: string): boolean {\n const projectToken = this.getProjectToken(tenantId)\n if (!projectToken) {\n return false\n }\n return !this.isProjectTokenExpired(projectToken)\n }\n\n /**\n * Get validated JWT claims from a project token\n * @param tenantId - The tenant/CMS ID\n * @returns JWT payload claims or null if token not found or has no claims\n */\n getProjectTokenClaims(tenantId: string): JWTPayload | null {\n const projectToken = this.getProjectToken(tenantId)\n return projectToken?.claims || null\n }\n\n /**\n * Get all tenant IDs that have stored project tokens\n * @returns Array of tenant IDs\n */\n getAllProjectTokenTenantIds(): string[] {\n const projectTokens = this.config.get('projectTokens') || {}\n return Object.keys(projectTokens)\n }\n}\n"],"names":["Conf","TOKEN_EXPIRY_BUFFER_SECONDS","deriveEncryptionKey","instances","getTokenStore","environment","TokenStore","config","options","process","env","AWS_EXECUTION_ENV","Error","projectName","clearInvalidConfig","configName","encryptionKey","getTokens","tokens","get","setTokens","set","clearTokens","delete","hasValidTokens","isExpired","now","Date","bufferMs","expiryWithBuffer","expiresAt","getAccessToken","accessToken","getRefreshToken","refreshToken","updateAccessToken","expiresIn","existingTokens","getStoragePath","path","getProjectToken","tenantId","projectTokens","projectToken","isProjectTokenExpired","clearProjectToken","setProjectToken","clearAllProjectTokens","hasValidProjectToken","getProjectTokenClaims","claims","getAllProjectTokenTenantIds","Object","keys"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import type { ErrorResponsePayloadWithErrMessage, ErrorResponsePayloadWithErrorBoolean, User } from '@figma/rest-api-spec';
2
2
  import type { JWTPayload as JoseJWTPayload } from 'jose';
3
+ import type { Environment } from '../constants.js';
3
4
  /**
4
5
  * Standard OAuth2 scopes for user-level permissions
5
6
  * @see https://www.figma.com/developers/api#oauth2
@@ -105,12 +106,14 @@ export interface OAuthTokenRefreshParams {
105
106
  /**
106
107
  * Configuration for token storage
107
108
  */
108
- export interface TokenStoreConfig {
109
- /** Custom config name (defaults to 'payloadcms-figma') */
109
+ export type TokenStoreConfig = {
110
+ /** Config file name (for testing) */
110
111
  configName?: string;
111
- /** Enable encryption (defaults to true) */
112
+ /** Encryption key (for testing) */
112
113
  encryptionKey?: string;
113
- }
114
+ /** Environment for config file naming */
115
+ environment?: Environment;
116
+ };
114
117
  /**
115
118
  * PKCE (Proof Key for Code Exchange) pair for OAuth2 public clients
116
119
  * @see https://datatracker.ietf.org/doc/html/rfc7636
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kCAAkC,EAClC,oCAAoC,EACpC,IAAI,EACL,MAAM,sBAAsB,CAAA;AAC7B,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,MAAM,CAAA;AAExD;;;GAGG;AACH,MAAM,MAAM,eAAe,GACvB,mBAAmB,GACnB,oBAAoB,GACpB,qBAAqB,GACrB,mBAAmB,GACnB,yBAAyB,GACzB,0BAA0B,GAC1B,oBAAoB,GACpB,qBAAqB,GACrB,sBAAsB,GACtB,oBAAoB,GACpB,YAAY,GACZ,wBAAwB,GACxB,qBAAqB,GACrB,sBAAsB,GACtB,eAAe,GACf,2BAA2B,GAC3B,eAAe,GACf,gBAAgB,CAAA;AAEpB;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,uBAAuB,CAAA;AAExD;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,kBAAkB,CAAA;AAE7D;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAA;IACnB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAA;IACjB,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAA;IACpB,yCAAyC;IACzC,MAAM,CAAC,EAAE,eAAe,EAAE,CAAA;IAC1B,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,YAAY,EAAE,MAAM,CAAA;IACpB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,2BAA2B;IAC3B,aAAa,EAAE,MAAM,CAAA;IACrB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2BAA2B;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,iBAAiB;IACjB,KAAK,EACD,gBAAgB,GAChB,eAAe,GACf,iBAAiB,GACjB,eAAe,GACf,qBAAqB,GACrB,wBAAwB,CAAA;IAC5B,uCAAuC;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,8DAA8D;IAC9D,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAA;IACrB,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,+CAA+C;IAC/C,UAAU,EAAE,oBAAoB,CAAA;IAChC,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,UAAU,EAAE,eAAe,CAAA;IAC3B,oBAAoB;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAA;IACrB,0CAA0C;IAC1C,mBAAmB,EAAE,MAAM,CAAA;IAC3B,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,gBAAgB,EAAE,MAAM,CAAA;IACxB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAA;IAChB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAA;IACnB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,+BAA+B;IAC/B,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;GAEG;AACH,YAAY,EAAE,kCAAkC,EAAE,oCAAoC,EAAE,IAAI,EAAE,CAAA;AAE9F;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAC1B,kCAAkC,GAClC,oCAAoC,CAAA;AAExC;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,IAAI,CAAA;AAE5B;;;GAGG;AACH,MAAM,WAAW,UAAW,SAAQ,cAAc;IAChD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IACtB,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAA;IACjB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAA;IAChB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,IAAI,EAAE;QACJ,uBAAuB;QACvB,KAAK,EAAE,MAAM,CAAA;KACd,CAAA;CACF"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/auth/types.ts"],"sourcesContent":["import type {\n ErrorResponsePayloadWithErrMessage,\n ErrorResponsePayloadWithErrorBoolean,\n User,\n} from '@figma/rest-api-spec'\nimport type { JWTPayload as JoseJWTPayload } from 'jose'\n\n/**\n * Standard OAuth2 scopes for user-level permissions\n * @see https://www.figma.com/developers/api#oauth2\n */\nexport type FigmaOAuthScope =\n | 'current_user:read' // Read user name, email, profile image\n | 'file_comments:read' // Read file comments\n | 'file_comments:write' // Post/delete comments and reactions\n | 'file_content:read' // Read file contents (nodes, editor type)\n | 'file_dev_resources:read' // Read dev resources\n | 'file_dev_resources:write' // Write dev resources\n | 'file_metadata:read' // Read file metadata\n | 'file_variables:read' // Read variables (Enterprise only)\n | 'file_variables:write' // Write variables (Enterprise only)\n | 'file_versions:read' // Read version history\n | 'files:read' // @deprecated Read files, projects, users\n | 'library_analytics:read' // Read library analytics\n | 'library_assets:read' // Read published components/styles data\n | 'library_content:read' // Read published components/styles\n | 'projects:read' // List projects and files\n | 'team_library_content:read' // Read team published content\n | 'webhooks:read' // Read webhook metadata\n | 'webhooks:write' // Create/manage webhooks\n\n/**\n * Organization-level OAuth2 scopes\n * Requires organization admin permissions\n */\nexport type FigmaOrgOAuthScope = 'org:activity_log_read' // Read organization activity logs\n\n/**\n * All valid Figma OAuth scopes\n */\nexport type FigmaScope = FigmaOAuthScope | FigmaOrgOAuthScope\n\n/**\n * OAuth tokens returned from Figma API (internal format)\n */\nexport interface FigmaTokens {\n /** OAuth2 access token for making authenticated API requests */\n accessToken: string\n /** Unix timestamp (milliseconds) when the access token expires */\n expiresAt: number\n /** OAuth2 refresh token for obtaining new access tokens */\n refreshToken: string\n /** OAuth scopes granted to this token */\n scopes?: FigmaOAuthScope[]\n /** Token type (typically \"Bearer\") */\n tokenType?: string\n /** Figma user ID string */\n userId?: string\n}\n\n/**\n * Raw token response from Figma OAuth2 token endpoint\n * Response from POST https://api.figma.com/v1/oauth/token\n */\nexport interface FigmaTokenResponse {\n /** OAuth2 access token */\n access_token: string\n /** Token expiration time in seconds */\n expires_in: number\n /** OAuth2 refresh token */\n refresh_token: string\n /** Space-separated list of granted scopes */\n scope?: string\n /** Token type (typically \"Bearer\") */\n token_type?: string\n /** Figma user ID string */\n user_id_string?: string\n}\n\n/**\n * OAuth2 error response\n * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\n */\nexport interface OAuthErrorResponse {\n /** Error code */\n error:\n | 'invalid_client'\n | 'invalid_grant'\n | 'invalid_request'\n | 'invalid_scope'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n /** Human-readable error description */\n error_description?: string\n /** URI with error information */\n error_uri?: string\n}\n\n/**\n * Parameters for OAuth2 authorization URL\n */\nexport interface OAuthAuthorizationParams {\n /** OAuth2 client ID */\n client_id: string\n /** PKCE code challenge (optional but recommended) */\n code_challenge?: string\n /** PKCE code challenge method (must be S256 if using PKCE) */\n code_challenge_method?: 'S256'\n /** Redirect URI for callback */\n redirect_uri: string\n /** Response type (always \"code\" for authorization code flow) */\n response_type: 'code'\n /** Requested scopes (space-separated) */\n scope: string\n /** State parameter for CSRF protection */\n state: string\n}\n\n/**\n * Parameters for OAuth2 token exchange\n */\nexport interface OAuthTokenExchangeParams {\n /** Authorization code from callback */\n code: string\n /** PKCE code verifier (if PKCE was used) */\n code_verifier?: string\n /** Grant type (always \"authorization_code\") */\n grant_type: 'authorization_code'\n /** Redirect URI (must match authorization request) */\n redirect_uri: string\n}\n\n/**\n * Parameters for OAuth2 token refresh\n */\nexport interface OAuthTokenRefreshParams {\n /** Grant type (always \"refresh_token\") */\n grant_type: 'refresh_token'\n /** Refresh token */\n refresh_token: string\n}\n\n/**\n * Configuration for token storage\n */\nexport interface TokenStoreConfig {\n /** Custom config name (defaults to 'payloadcms-figma') */\n configName?: string\n /** Enable encryption (defaults to true) */\n encryptionKey?: string\n}\n\n/**\n * PKCE (Proof Key for Code Exchange) pair for OAuth2 public clients\n * @see https://datatracker.ietf.org/doc/html/rfc7636\n */\nexport interface PKCEPair {\n /** Code challenge derived from code verifier */\n codeChallenge: string\n /** Code challenge method (always S256) */\n codeChallengeMethod: 'S256'\n /** Code verifier - random string used to generate challenge */\n codeVerifier: string\n}\n\n/**\n * OAuth configuration for Figma API (public client with PKCE)\n *\n * This configuration is for a PUBLIC OAuth client that uses PKCE\n * (Proof Key for Code Exchange) instead of a client secret.\n * Public clients (like CLIs) cannot securely store client secrets.\n */\nexport interface OAuthConfig {\n /** Figma OAuth authorization URL */\n authorizationUrl: string\n /** OAuth2 client ID (public client) */\n clientId: string\n /** OAuth2 redirect URI for callback */\n redirectUri: string\n /** Figma OAuth token refresh URL */\n refreshUrl: string\n /** OAuth2 scopes to request */\n scopes: FigmaOAuthScope[]\n /** Figma OAuth token exchange URL */\n tokenUrl: string\n}\n\n/**\n * Re-export Figma's official types for convenience\n */\nexport type { ErrorResponsePayloadWithErrMessage, ErrorResponsePayloadWithErrorBoolean, User }\n\n/**\n * Type alias for Figma API error responses\n * Combines both error response formats from Figma's REST API\n */\nexport type FigmaErrorResponse =\n | ErrorResponsePayloadWithErrMessage\n | ErrorResponsePayloadWithErrorBoolean\n\n/**\n * Figma user information\n * Re-exported from @figma/rest-api-spec\n */\nexport type FigmaUser = User\n\n/**\n * JWT payload structure from Figma project tokens\n * Extends jose's standard JWTPayload with Figma-specific claims\n */\nexport interface JWTPayload extends JoseJWTPayload {\n [key: string]: unknown\n /** User email address */\n email?: string\n /** Tenant/CMS ID this token is scoped to */\n tenant_id?: string\n}\n\n/**\n * Project token (JWT) for authenticating to Content API\n * Used by local Payload instances to make authenticated requests\n */\nexport interface ProjectToken {\n /** Validated JWT claims */\n claims?: JWTPayload\n /** Unix timestamp (milliseconds) when the token expires (from validated exp claim) */\n expiresAt: number\n /** Tenant/CMS ID this token is scoped to */\n tenantId: string\n /** JWT token string signed by Figma API */\n token: string\n}\n\n/**\n * Raw project token response from Figma API\n * Response from GET https://api.figma.com/v1/cms/:id/token/\n */\nexport interface ProjectTokenResponse {\n /** Response metadata containing the JWT token */\n meta: {\n /** JWT token string */\n token: string\n }\n}\n"],"names":[],"mappings":"AAyOA;;;CAGC,GACD,WAMC"}
1
+ {"version":3,"sources":["../../src/auth/types.ts"],"sourcesContent":["import type {\n ErrorResponsePayloadWithErrMessage,\n ErrorResponsePayloadWithErrorBoolean,\n User,\n} from '@figma/rest-api-spec'\nimport type { JWTPayload as JoseJWTPayload } from 'jose'\n\nimport type { Environment } from '../constants.js'\n\n/**\n * Standard OAuth2 scopes for user-level permissions\n * @see https://www.figma.com/developers/api#oauth2\n */\nexport type FigmaOAuthScope =\n | 'current_user:read' // Read user name, email, profile image\n | 'file_comments:read' // Read file comments\n | 'file_comments:write' // Post/delete comments and reactions\n | 'file_content:read' // Read file contents (nodes, editor type)\n | 'file_dev_resources:read' // Read dev resources\n | 'file_dev_resources:write' // Write dev resources\n | 'file_metadata:read' // Read file metadata\n | 'file_variables:read' // Read variables (Enterprise only)\n | 'file_variables:write' // Write variables (Enterprise only)\n | 'file_versions:read' // Read version history\n | 'files:read' // @deprecated Read files, projects, users\n | 'library_analytics:read' // Read library analytics\n | 'library_assets:read' // Read published components/styles data\n | 'library_content:read' // Read published components/styles\n | 'projects:read' // List projects and files\n | 'team_library_content:read' // Read team published content\n | 'webhooks:read' // Read webhook metadata\n | 'webhooks:write' // Create/manage webhooks\n\n/**\n * Organization-level OAuth2 scopes\n * Requires organization admin permissions\n */\nexport type FigmaOrgOAuthScope = 'org:activity_log_read' // Read organization activity logs\n\n/**\n * All valid Figma OAuth scopes\n */\nexport type FigmaScope = FigmaOAuthScope | FigmaOrgOAuthScope\n\n/**\n * OAuth tokens returned from Figma API (internal format)\n */\nexport interface FigmaTokens {\n /** OAuth2 access token for making authenticated API requests */\n accessToken: string\n /** Unix timestamp (milliseconds) when the access token expires */\n expiresAt: number\n /** OAuth2 refresh token for obtaining new access tokens */\n refreshToken: string\n /** OAuth scopes granted to this token */\n scopes?: FigmaOAuthScope[]\n /** Token type (typically \"Bearer\") */\n tokenType?: string\n /** Figma user ID string */\n userId?: string\n}\n\n/**\n * Raw token response from Figma OAuth2 token endpoint\n * Response from POST https://api.figma.com/v1/oauth/token\n */\nexport interface FigmaTokenResponse {\n /** OAuth2 access token */\n access_token: string\n /** Token expiration time in seconds */\n expires_in: number\n /** OAuth2 refresh token */\n refresh_token: string\n /** Space-separated list of granted scopes */\n scope?: string\n /** Token type (typically \"Bearer\") */\n token_type?: string\n /** Figma user ID string */\n user_id_string?: string\n}\n\n/**\n * OAuth2 error response\n * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\n */\nexport interface OAuthErrorResponse {\n /** Error code */\n error:\n | 'invalid_client'\n | 'invalid_grant'\n | 'invalid_request'\n | 'invalid_scope'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n /** Human-readable error description */\n error_description?: string\n /** URI with error information */\n error_uri?: string\n}\n\n/**\n * Parameters for OAuth2 authorization URL\n */\nexport interface OAuthAuthorizationParams {\n /** OAuth2 client ID */\n client_id: string\n /** PKCE code challenge (optional but recommended) */\n code_challenge?: string\n /** PKCE code challenge method (must be S256 if using PKCE) */\n code_challenge_method?: 'S256'\n /** Redirect URI for callback */\n redirect_uri: string\n /** Response type (always \"code\" for authorization code flow) */\n response_type: 'code'\n /** Requested scopes (space-separated) */\n scope: string\n /** State parameter for CSRF protection */\n state: string\n}\n\n/**\n * Parameters for OAuth2 token exchange\n */\nexport interface OAuthTokenExchangeParams {\n /** Authorization code from callback */\n code: string\n /** PKCE code verifier (if PKCE was used) */\n code_verifier?: string\n /** Grant type (always \"authorization_code\") */\n grant_type: 'authorization_code'\n /** Redirect URI (must match authorization request) */\n redirect_uri: string\n}\n\n/**\n * Parameters for OAuth2 token refresh\n */\nexport interface OAuthTokenRefreshParams {\n /** Grant type (always \"refresh_token\") */\n grant_type: 'refresh_token'\n /** Refresh token */\n refresh_token: string\n}\n\n/**\n * Configuration for token storage\n */\nexport type TokenStoreConfig = {\n /** Config file name (for testing) */\n configName?: string\n /** Encryption key (for testing) */\n encryptionKey?: string\n /** Environment for config file naming */\n environment?: Environment\n}\n\n/**\n * PKCE (Proof Key for Code Exchange) pair for OAuth2 public clients\n * @see https://datatracker.ietf.org/doc/html/rfc7636\n */\nexport interface PKCEPair {\n /** Code challenge derived from code verifier */\n codeChallenge: string\n /** Code challenge method (always S256) */\n codeChallengeMethod: 'S256'\n /** Code verifier - random string used to generate challenge */\n codeVerifier: string\n}\n\n/**\n * OAuth configuration for Figma API (public client with PKCE)\n *\n * This configuration is for a PUBLIC OAuth client that uses PKCE\n * (Proof Key for Code Exchange) instead of a client secret.\n * Public clients (like CLIs) cannot securely store client secrets.\n */\nexport interface OAuthConfig {\n /** Figma OAuth authorization URL */\n authorizationUrl: string\n /** OAuth2 client ID (public client) */\n clientId: string\n /** OAuth2 redirect URI for callback */\n redirectUri: string\n /** Figma OAuth token refresh URL */\n refreshUrl: string\n /** OAuth2 scopes to request */\n scopes: FigmaOAuthScope[]\n /** Figma OAuth token exchange URL */\n tokenUrl: string\n}\n\n/**\n * Re-export Figma's official types for convenience\n */\nexport type { ErrorResponsePayloadWithErrMessage, ErrorResponsePayloadWithErrorBoolean, User }\n\n/**\n * Type alias for Figma API error responses\n * Combines both error response formats from Figma's REST API\n */\nexport type FigmaErrorResponse =\n | ErrorResponsePayloadWithErrMessage\n | ErrorResponsePayloadWithErrorBoolean\n\n/**\n * Figma user information\n * Re-exported from @figma/rest-api-spec\n */\nexport type FigmaUser = User\n\n/**\n * JWT payload structure from Figma project tokens\n * Extends jose's standard JWTPayload with Figma-specific claims\n */\nexport interface JWTPayload extends JoseJWTPayload {\n [key: string]: unknown\n /** User email address */\n email?: string\n /** Tenant/CMS ID this token is scoped to */\n tenant_id?: string\n}\n\n/**\n * Project token (JWT) for authenticating to Content API\n * Used by local Payload instances to make authenticated requests\n */\nexport interface ProjectToken {\n /** Validated JWT claims */\n claims?: JWTPayload\n /** Unix timestamp (milliseconds) when the token expires (from validated exp claim) */\n expiresAt: number\n /** Tenant/CMS ID this token is scoped to */\n tenantId: string\n /** JWT token string signed by Figma API */\n token: string\n}\n\n/**\n * Raw project token response from Figma API\n * Response from GET https://api.figma.com/v1/cms/:id/token/\n */\nexport interface ProjectTokenResponse {\n /** Response metadata containing the JWT token */\n meta: {\n /** JWT token string */\n token: string\n }\n}\n"],"names":[],"mappings":"AA6OA;;;CAGC,GACD,WAMC"}
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import { listTokensCommand } from './commands/list-tokens.js';
8
8
  import { loginCommand } from './commands/login.js';
9
9
  import { logoutCommand } from './commands/logout.js';
10
10
  import { setEnvironment } from './constants.js';
11
- import { feedbackOutro, helpMessage } from './utils/messages.js';
11
+ import { helpMessage } from './utils/messages.js';
12
12
  /**
13
13
  * Entrypoint for bin/cli.js
14
14
  */ export async function main() {
@@ -26,6 +26,7 @@ class Main {
26
26
  constructor(){
27
27
  // @ts-expect-error bad typings
28
28
  this.args = arg({
29
+ '--all': Boolean,
29
30
  '--debug': Boolean,
30
31
  '--env': String,
31
32
  '--force': Boolean,
@@ -77,6 +78,10 @@ class Main {
77
78
  }
78
79
  // Get the subcommand (first non-flag argument)
79
80
  const subcommand = this.args._[0];
81
+ if (!subcommand) {
82
+ helpMessage();
83
+ process.exit(0);
84
+ }
80
85
  // eslint-disable-next-line no-console
81
86
  console.log('\n');
82
87
  p.intro(pc.bgCyan(pc.black(' @payloadcms/figma ')));
@@ -104,6 +109,7 @@ class Main {
104
109
  break;
105
110
  case 'list-tokens':
106
111
  listTokensCommand({
112
+ all: this.args['--all'],
107
113
  debug: this.args['--debug']
108
114
  });
109
115
  break;
@@ -112,14 +118,10 @@ class Main {
112
118
  break;
113
119
  case 'logout':
114
120
  await logoutCommand({
121
+ all: this.args['--all'],
115
122
  debug: this.args['--debug']
116
123
  });
117
124
  break;
118
- case undefined:
119
- // No subcommand - show default message
120
- p.note("Let's create a new CMS!");
121
- p.outro(feedbackOutro());
122
- break;
123
125
  default:
124
126
  p.log.error(pc.red(`Unknown command: ${subcommand}`));
125
127
  p.note('Use --help to see available commands', 'Tip');
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import * as p from '@clack/prompts'\nimport arg from 'arg'\nimport pc from 'picocolors'\n\nimport type { CliArgs } from './types.js'\n\nimport { buildLambdaZipCommand } from './commands/build-lambda-zip.js'\nimport { deployCommand } from './commands/deploy.js'\nimport { initCommand } from './commands/init.js'\nimport { listTokensCommand } from './commands/list-tokens.js'\nimport { loginCommand } from './commands/login.js'\nimport { logoutCommand } from './commands/logout.js'\nimport { setEnvironment } from './constants.js'\nimport { feedbackOutro, helpMessage } from './utils/messages.js'\n\n/**\n * Entrypoint for bin/cli.js\n */\nexport async function main(): Promise<void> {\n try {\n await new Main().init()\n } catch (e) {\n if (e instanceof Error) {\n // eslint-disable-next-line no-console\n console.error(e.message)\n }\n }\n}\n\nclass Main {\n args: CliArgs\n\n constructor() {\n // @ts-expect-error bad typings\n this.args = arg(\n {\n '--debug': Boolean,\n '--env': String,\n '--force': Boolean,\n '--help': Boolean,\n '--id': String,\n '--name': String,\n '--skip-auth': Boolean,\n '--skip-build': Boolean,\n '--version': Boolean,\n '--yes': Boolean,\n\n // Aliases\n '-f': '--force',\n '-h': '--help',\n '-v': '--version',\n '-y': '--yes',\n },\n { permissive: true },\n )\n\n // Set debug flag globally\n if (this.args['--debug']) {\n process.env.DEBUG = 'true'\n }\n\n // Handle --env flag for environment switching\n if (this.args['--env']) {\n const envArg = this.args['--env'].toLowerCase()\n if (envArg === 'prod' || envArg === 'production') {\n setEnvironment('production')\n } else if (envArg === 'staging') {\n setEnvironment('staging')\n } else {\n // eslint-disable-next-line no-console\n console.error(`Invalid --env value: ${this.args['--env']}. Use 'prod' or 'staging'.`)\n process.exit(1)\n }\n }\n }\n\n async init(): Promise<void> {\n try {\n if (this.args['--help']) {\n helpMessage()\n process.exit(0)\n }\n\n if (this.args['--version']) {\n const { getOwnVersion } = await import('./utils/version-check.js')\n const version = await getOwnVersion()\n // eslint-disable-next-line no-console\n console.log(version)\n process.exit(0)\n }\n\n // Get the subcommand (first non-flag argument)\n const subcommand = this.args._[0]\n\n // eslint-disable-next-line no-console\n console.log('\\n')\n p.intro(pc.bgCyan(pc.black(' @payloadcms/figma ')))\n\n // Route to appropriate command handler\n switch (subcommand) {\n case 'build-lambda-zip':\n await buildLambdaZipCommand()\n break\n\n case 'deploy':\n await deployCommand({\n id: this.args['--id'],\n debug: this.args['--debug'],\n skipBuild: this.args['--skip-build'],\n yes: this.args['--yes'],\n })\n break\n\n case 'init':\n await initCommand({\n id: this.args['--id'],\n name: this.args['--name'],\n debug: this.args['--debug'],\n force: this.args['--force'],\n skipAuth: this.args['--skip-auth'],\n })\n break\n\n case 'list-tokens':\n listTokensCommand({\n debug: this.args['--debug'],\n })\n break\n\n case 'login':\n await loginCommand()\n break\n\n case 'logout':\n await logoutCommand({\n debug: this.args['--debug'],\n })\n break\n\n case undefined:\n // No subcommand - show default message\n p.note(\"Let's create a new CMS!\")\n p.outro(feedbackOutro())\n break\n\n default:\n p.log.error(pc.red(`Unknown command: ${subcommand}`))\n p.note('Use --help to see available commands', 'Tip')\n process.exit(1)\n }\n\n process.exit(0)\n } catch (err: unknown) {\n // eslint-disable-next-line no-console\n console.error(err instanceof Error ? err.message : 'An error occurred')\n process.exit(1)\n }\n }\n}\n"],"names":["p","arg","pc","buildLambdaZipCommand","deployCommand","initCommand","listTokensCommand","loginCommand","logoutCommand","setEnvironment","feedbackOutro","helpMessage","main","Main","init","e","Error","console","error","message","args","Boolean","String","permissive","process","env","DEBUG","envArg","toLowerCase","exit","getOwnVersion","version","log","subcommand","_","intro","bgCyan","black","id","debug","skipBuild","yes","name","force","skipAuth","undefined","note","outro","red","err"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import * as p from '@clack/prompts'\nimport arg from 'arg'\nimport pc from 'picocolors'\n\nimport type { CliArgs } from './types.js'\n\nimport { buildLambdaZipCommand } from './commands/build-lambda-zip.js'\nimport { deployCommand } from './commands/deploy.js'\nimport { initCommand } from './commands/init.js'\nimport { listTokensCommand } from './commands/list-tokens.js'\nimport { loginCommand } from './commands/login.js'\nimport { logoutCommand } from './commands/logout.js'\nimport { setEnvironment } from './constants.js'\nimport { feedbackOutro, helpMessage } from './utils/messages.js'\n\n/**\n * Entrypoint for bin/cli.js\n */\nexport async function main(): Promise<void> {\n try {\n await new Main().init()\n } catch (e) {\n if (e instanceof Error) {\n // eslint-disable-next-line no-console\n console.error(e.message)\n }\n }\n}\n\nclass Main {\n args: CliArgs\n\n constructor() {\n // @ts-expect-error bad typings\n this.args = arg(\n {\n '--all': Boolean,\n '--debug': Boolean,\n '--env': String,\n '--force': Boolean,\n '--help': Boolean,\n '--id': String,\n '--name': String,\n '--skip-auth': Boolean,\n '--skip-build': Boolean,\n '--version': Boolean,\n '--yes': Boolean,\n\n // Aliases\n '-f': '--force',\n '-h': '--help',\n '-v': '--version',\n '-y': '--yes',\n },\n { permissive: true },\n )\n\n // Set debug flag globally\n if (this.args['--debug']) {\n process.env.DEBUG = 'true'\n }\n\n // Handle --env flag for environment switching\n if (this.args['--env']) {\n const envArg = this.args['--env'].toLowerCase()\n if (envArg === 'prod' || envArg === 'production') {\n setEnvironment('production')\n } else if (envArg === 'staging') {\n setEnvironment('staging')\n } else {\n // eslint-disable-next-line no-console\n console.error(`Invalid --env value: ${this.args['--env']}. Use 'prod' or 'staging'.`)\n process.exit(1)\n }\n }\n }\n\n async init(): Promise<void> {\n try {\n if (this.args['--help']) {\n helpMessage()\n process.exit(0)\n }\n\n if (this.args['--version']) {\n const { getOwnVersion } = await import('./utils/version-check.js')\n const version = await getOwnVersion()\n // eslint-disable-next-line no-console\n console.log(version)\n process.exit(0)\n }\n\n // Get the subcommand (first non-flag argument)\n const subcommand = this.args._[0]\n\n if (!subcommand) {\n helpMessage()\n process.exit(0)\n }\n\n // eslint-disable-next-line no-console\n console.log('\\n')\n p.intro(pc.bgCyan(pc.black(' @payloadcms/figma ')))\n\n // Route to appropriate command handler\n switch (subcommand) {\n case 'build-lambda-zip':\n await buildLambdaZipCommand()\n break\n\n case 'deploy':\n await deployCommand({\n id: this.args['--id'],\n debug: this.args['--debug'],\n skipBuild: this.args['--skip-build'],\n yes: this.args['--yes'],\n })\n break\n\n case 'init':\n await initCommand({\n id: this.args['--id'],\n name: this.args['--name'],\n debug: this.args['--debug'],\n force: this.args['--force'],\n skipAuth: this.args['--skip-auth'],\n })\n break\n\n case 'list-tokens':\n listTokensCommand({\n all: this.args['--all'],\n debug: this.args['--debug'],\n })\n break\n\n case 'login':\n await loginCommand()\n break\n\n case 'logout':\n await logoutCommand({\n all: this.args['--all'],\n debug: this.args['--debug'],\n })\n break\n\n default:\n p.log.error(pc.red(`Unknown command: ${subcommand}`))\n p.note('Use --help to see available commands', 'Tip')\n process.exit(1)\n }\n\n process.exit(0)\n } catch (err: unknown) {\n // eslint-disable-next-line no-console\n console.error(err instanceof Error ? err.message : 'An error occurred')\n process.exit(1)\n }\n }\n}\n"],"names":["p","arg","pc","buildLambdaZipCommand","deployCommand","initCommand","listTokensCommand","loginCommand","logoutCommand","setEnvironment","helpMessage","main","Main","init","e","Error","console","error","message","args","Boolean","String","permissive","process","env","DEBUG","envArg","toLowerCase","exit","getOwnVersion","version","log","subcommand","_","intro","bgCyan","black","id","debug","skipBuild","yes","name","force","skipAuth","all","red","note","err"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAiBA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,wBAAwB;IACxB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,gBAAgB;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,+CAA+C;IAC/C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gCAAgC;IAChC,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AA6BD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyPhF"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAkBA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,wBAAwB;IACxB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,gBAAgB;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,+CAA+C;IAC/C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gCAAgC;IAChC,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AA6BD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyPhF"}
@@ -5,6 +5,7 @@ import pc from 'picocolors';
5
5
  import { ControlPlaneError, createDeployment, performDeployment } from '../api/control-plane.js';
6
6
  import { getValidAccessToken } from '../auth/oauth-flow.js';
7
7
  import { getTokenStore } from '../auth/token-store.js';
8
+ import { getEnvironment } from '../constants.js';
8
9
  import { collectStaticAssets, createLambdaZip, getFileSize } from '../utils/asset-collection.js';
9
10
  import { detectBuild, getBuildCommand } from '../utils/build-detection.js';
10
11
  import { getEnvVar } from '../utils/env-management.js';
@@ -65,7 +66,7 @@ import { loginCommand } from './login.js';
65
66
  process.exit(1);
66
67
  }
67
68
  // Check authentication
68
- const tokenStore = getTokenStore();
69
+ const tokenStore = getTokenStore(getEnvironment());
69
70
  let accessToken = await getValidAccessToken(tokenStore);
70
71
  if (!accessToken) {
71
72
  p.log.message('Please log in to continue');