@payloadcms/figma 0.0.1-alpha.35 → 0.0.1-alpha.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/control-plane.d.ts.map +1 -1
- package/dist/api/control-plane.js +2 -8
- package/dist/api/control-plane.js.map +1 -1
- package/dist/api/figma-api.d.ts.map +1 -1
- package/dist/api/figma-api.js +4 -15
- package/dist/api/figma-api.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/config/oauth.js +2 -2
- package/dist/config/oauth.js.map +1 -1
- package/dist/constants.d.ts +18 -8
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +21 -8
- package/dist/constants.js.map +1 -1
- package/dist/db-content-api/index.d.ts.map +1 -1
- package/dist/db-content-api/index.js +4 -3
- package/dist/db-content-api/index.js.map +1 -1
- package/dist/db-content-api/temp-utilities/sorting.d.ts +10 -0
- package/dist/db-content-api/temp-utilities/sorting.d.ts.map +1 -1
- package/dist/db-content-api/temp-utilities/sorting.js +30 -0
- package/dist/db-content-api/temp-utilities/sorting.js.map +1 -1
- package/dist/oauth/endpoints/getLoginEndpoint.js +3 -3
- package/dist/oauth/endpoints/getLoginEndpoint.js.map +1 -1
- package/dist/oauth/index.d.ts.map +1 -1
- package/dist/oauth/index.js +0 -1
- package/dist/oauth/index.js.map +1 -1
- package/dist/oauth/strategy/index.d.ts +0 -2
- package/dist/oauth/strategy/index.d.ts.map +1 -1
- package/dist/oauth/strategy/index.js +0 -2
- package/dist/oauth/strategy/index.js.map +1 -1
- package/dist/plugin/build-config.d.ts.map +1 -1
- package/dist/plugin/build-config.js +5 -4
- package/dist/plugin/build-config.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/oauth/strategy/index.ts"],"sourcesContent":["import type jwt from 'jsonwebtoken'\n/* eslint-disable @typescript-eslint/restrict-template-expressions */\nimport type {\n AuthStrategyFunctionArgs,\n AuthStrategyResult,\n CollectionConfig,\n Payload,\n} from 'payload'\n\nimport * as jose from 'jose'\nimport { APIError, parseCookies } from 'payload'\n\nimport type { CollectionOptions, PluginOptions, VerifyFunction } from '../types.js'\n\nimport { JWTValidationError, validateProjectToken } from '../../auth/jwt-validator.js'\nimport { getEnvironment } from '../../constants.js'\nimport { createDebugLogger } from '../utilities/createDebugLogger.js'\nimport { getTokenExp } from '../utilities/getTokenExp.js'\nimport { mergeHeaders } from '../utilities/mergeHeaders.js'\nimport { refreshTokens } from '../utilities/refreshTokens.js'\n\nexport interface Options {\n clientID: string\n clientSecret?: string\n collection: CollectionConfig\n collectionOptions: CollectionOptions\n cookieName: string\n debug?: boolean\n identityMetadata: string\n pluginOptions: PluginOptions\n strategyName: string\n verify: VerifyFunction\n}\n\nexport class Strategy {\n clientID: string\n clientSecret?: string\n collection: CollectionConfig\n collectionOptions: CollectionOptions\n cookieName: string\n\n debug: boolean\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n debugLog: (...args: any[]) => void\n\n identityMetadataURL: string\n jwks?: {\n keys: jose.JWK[]\n }\n meta?: {\n authorization_endpoint: string\n end_session_endpoint: string\n jwks_uri: string\n token_endpoint: string\n userinfo_endpoint: string\n }\n metaLastFetchedTime?: Date\n\n metaPromise?: Promise<void>\n metaPromiseResolve?: () => void\n name: string\n pluginOptions: PluginOptions\n verify: VerifyFunction\n\n constructor(options: Options) {\n this.verify = options.verify\n this.cookieName = options.cookieName\n this.clientID = options.clientID\n this.clientSecret = options.clientSecret\n this.identityMetadataURL = options.identityMetadata\n this.name = options.strategyName\n this.collection = options.collection\n this.collectionOptions = options.collectionOptions\n this.pluginOptions = options.pluginOptions\n this.debug = options.debug || false\n // eslint-disable-next-line no-console, @typescript-eslint/no-explicit-any\n this.debugLog = options.debug ? (...args: any[]) => console.log(...args) : () => null\n // Prevent wokiness with `this`\n this.ensureMeta = this.ensureMeta.bind(this)\n }\n\n async authenticate({\n canSetHeaders,\n headers,\n payload,\n }: AuthStrategyFunctionArgs): Promise<AuthStrategyResult> {\n const debugLogger = createDebugLogger(payload, this.debug)\n await this.ensureMeta()\n\n const cookies = parseCookies(headers)\n\n let oauthToken = cookies.get(this.cookieName)\n const refreshToken = cookies.get(`${this.cookieName}-refresh`)\n\n debugLogger.info({\n msg: 'Retrieved tokens from cookies',\n oauthToken,\n refreshToken,\n })\n\n if (!this.collectionOptions.disableJWTFromHeader) {\n // Parse Authorization header if exists\n const jwtFromHeader = headers.get('authorization')\n if (typeof jwtFromHeader === 'string' && jwtFromHeader.startsWith('Bearer ')) {\n debugLogger.info({\n jwtFromHeader,\n msg: 'Found bearer token in authorization header',\n })\n oauthToken = jwtFromHeader.replace('Bearer ', '')\n }\n }\n\n if (!oauthToken) {\n // Attempt refresh even when canSetHeaders=false (e.g., during RSC navigation).\n // This allows authentication to succeed for the current request even when\n // cookies cannot be updated. The cookie will be refreshed on the next\n // full page load when canSetHeaders=true.\n if (refreshToken) {\n debugLogger.info({\n msg: 'No access token found, but refresh token exists. Attempting to refresh...',\n refreshToken,\n })\n const refreshed = await refreshTokens({ payload, refreshToken, strategy: this })\n if (refreshed) {\n const verifyResult = await this.verify({\n headers,\n payload,\n token: refreshed.decodedOauthToken,\n })\n\n debugLogger.info({\n msg: 'Token refreshed. Verifying...',\n verifyResult,\n })\n\n // Only return response headers (to set cookies) when canSetHeaders is true.\n // During RSC navigation, canSetHeaders=false because response streaming\n // has already started and cookies cannot be modified.\n if (canSetHeaders) {\n if (verifyResult.responseHeaders) {\n mergeHeaders(verifyResult.responseHeaders, refreshed.headers)\n }\n return {\n responseHeaders: refreshed.headers,\n user: verifyResult.user,\n }\n }\n\n // Authenticate this request without updating cookies\n return {\n user: verifyResult.user,\n }\n } else {\n debugLogger.info({\n msg: 'Token refresh failed',\n refreshed,\n refreshToken,\n })\n }\n }\n\n return {\n user: null,\n }\n }\n\n let oauthTokenVerifyResult: Error | jwt.JwtPayload | null = null\n\n // First, try to verify token\n if (oauthToken) {\n oauthTokenVerifyResult = await this.jwtVerify({ payload, token: oauthToken })\n\n // If verify comes back null, there is no user\n if (oauthTokenVerifyResult === null) {\n return {\n user: null,\n }\n }\n\n // If verify comes back truthy, use it\n // but first set exp from refresh token\n // because access exp is likely very short lived\n if (\n typeof oauthTokenVerifyResult === 'object' &&\n !(oauthTokenVerifyResult instanceof Error)\n ) {\n if (refreshToken) {\n const refreshTokenExp = getTokenExp({ debugLogger, token: refreshToken })\n\n if (refreshTokenExp) {\n oauthTokenVerifyResult.exp = refreshTokenExp\n }\n }\n\n return this.verify({\n headers,\n payload,\n token: oauthTokenVerifyResult,\n })\n }\n }\n\n // Handle expired access token with valid refresh token.\n // Same pattern as above: refresh regardless of canSetHeaders to support RSC navigation,\n // but only update cookies when canSetHeaders=true.\n if (\n oauthTokenVerifyResult instanceof JWTValidationError &&\n oauthTokenVerifyResult.code === 'EXPIRED' &&\n refreshToken &&\n !this.collectionOptions.usePayloadJWT\n ) {\n const refreshed = await refreshTokens({ payload, refreshToken, strategy: this })\n\n debugLogger.info({\n msg: 'Token expired. Refreshing...',\n refreshed,\n refreshToken,\n })\n\n if (refreshed) {\n const verifyResult = await this.verify({\n headers,\n payload,\n token: refreshed.decodedOauthToken,\n })\n\n debugLogger.info({\n msg: 'Token refreshed. Verifying...',\n verifyResult,\n })\n\n if (canSetHeaders) {\n if (verifyResult.responseHeaders) {\n mergeHeaders(verifyResult.responseHeaders, refreshed.headers)\n }\n return {\n responseHeaders: refreshed.headers,\n user: verifyResult.user,\n }\n }\n\n // Authenticate this request without updating cookies\n return {\n user: verifyResult.user,\n }\n } else {\n debugLogger.info({\n msg: 'Token refresh failed',\n refreshed,\n refreshToken,\n })\n }\n }\n\n return {\n user: null,\n }\n }\n\n async ensureMeta(): Promise<void> {\n if (this.metaPromise) {\n this.debugLog('strategy - metaPromise exists. Waiting for it to resolve...')\n await this.metaPromise\n }\n\n // If no meta or this.metaLastFetchedTime is more than 24 hours old, fetch meta\n if (!this.metaLastFetchedTime || Date.now() - this.metaLastFetchedTime.getTime() > 86_400_000) {\n this.debugLog(\n `Meta is stale. Fetching... Last fetched: ${this.metaLastFetchedTime || 'never'}`,\n )\n await this.fetchMeta()\n }\n }\n\n async fetchMeta(): Promise<void> {\n // If already in-flight, return the existing promise\n if (this.metaPromise) {\n this.debugLog('fetchMeta - returning existing metaPromise')\n return this.metaPromise\n }\n\n this.debugLog('fetchMeta - creating new metaPromise...')\n\n // Build the shared promise\n this.metaPromise = (async () => {\n let lastError: unknown\n\n for (let attempt = 1; attempt <= 5; attempt++) {\n try {\n this.meta = await fetch(this.identityMetadataURL).then((res) => res.json())\n\n if (this.meta) {\n // TODO: This is a hack to get the DefaultLoginButton to work with dynamic OAuth client registration\n this.meta.authorization_endpoint = this.meta?.authorization_endpoint + '/cms'\n }\n\n this.debugLog(`fetchMeta - success on attempt ${attempt}`)\n\n if (this.meta?.jwks_uri) {\n this.jwks = await fetch(this.meta.jwks_uri).then((res) => res.json())\n }\n\n this.metaLastFetchedTime = new Date()\n this.debugLog(`fetchMeta - finished at ${this.metaLastFetchedTime.toISOString()}`)\n return\n } catch (error: unknown) {\n lastError = error\n this.debugLog(`fetchMeta - FAILED attempt ${attempt}/5: ${(error as Error)?.message}`)\n\n if (attempt < 5) {\n // Exponential backoff (up to 1s)\n await new Promise((resolve) => setTimeout(resolve, Math.min(attempt * 200, 1000)))\n }\n }\n }\n\n throw new APIError(\n `Failed to fetch identity metadata from \"${this.identityMetadataURL}\". Last error: ${\n (lastError as Error)?.message ?? 'unknown'\n }`,\n )\n })()\n\n try {\n await this.metaPromise\n } finally {\n this.metaPromise = undefined\n }\n }\n\n async generateOidcPEM(kid: string, alg: string): Promise<string> {\n const keys = this && this.jwks && Array.isArray(this.jwks.keys) ? this.jwks.keys : null\n\n let pubKey: null | string = null\n let foundKey = false\n\n if (!kid) {\n throw new Error('kid is missing')\n }\n\n if (!alg) {\n throw new Error('alg is missing')\n }\n\n if (!keys) {\n throw new Error('keys is missing')\n }\n\n for (const key of keys) {\n if (!('kid' in key) || key.kid !== kid) {\n continue\n }\n\n const cryptoKey = await jose.importJWK(key, alg)\n if (!(cryptoKey instanceof CryptoKey)) {\n throw new Error('importJWK did not return a CryptoKey')\n }\n\n pubKey = await jose.exportSPKI(cryptoKey)\n foundKey = true\n\n break\n }\n\n if (!foundKey) {\n throw new Error('a key with the specific kid cannot be found')\n }\n\n if (!pubKey) {\n throw new Error('generating public key pem failed')\n }\n\n return pubKey\n }\n\n async jwtVerify({ payload, token }: JwtVerifyArgs): Promise<Error | jwt.JwtPayload | null> {\n try {\n const verified = await validateProjectToken(token, getEnvironment())\n if (typeof verified !== 'string') {\n return verified\n }\n } catch (err: unknown) {\n payload.logger.error({\n err,\n msg: 'Error verifying jwt token',\n })\n\n if (err instanceof Error) {\n return err\n }\n return null\n }\n return null\n }\n}\n\ntype JwtVerifyArgs = {\n payload: Payload\n token: string\n}\n"],"names":["jose","APIError","parseCookies","JWTValidationError","validateProjectToken","getEnvironment","createDebugLogger","getTokenExp","mergeHeaders","refreshTokens","Strategy","clientID","clientSecret","collection","collectionOptions","cookieName","debug","debugLog","identityMetadataURL","jwks","meta","metaLastFetchedTime","metaPromise","metaPromiseResolve","name","pluginOptions","verify","options","identityMetadata","strategyName","args","console","log","ensureMeta","bind","authenticate","canSetHeaders","headers","payload","debugLogger","cookies","oauthToken","get","refreshToken","info","msg","disableJWTFromHeader","jwtFromHeader","startsWith","replace","refreshed","strategy","verifyResult","token","decodedOauthToken","responseHeaders","user","oauthTokenVerifyResult","jwtVerify","Error","refreshTokenExp","exp","code","usePayloadJWT","Date","now","getTime","fetchMeta","lastError","attempt","fetch","then","res","json","authorization_endpoint","jwks_uri","toISOString","error","message","Promise","resolve","setTimeout","Math","min","undefined","generateOidcPEM","kid","alg","keys","Array","isArray","pubKey","foundKey","key","cryptoKey","importJWK","CryptoKey","exportSPKI","verified","err","logger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/oauth/strategy/index.ts"],"sourcesContent":["import type jwt from 'jsonwebtoken'\n/* eslint-disable @typescript-eslint/restrict-template-expressions */\nimport type {\n AuthStrategyFunctionArgs,\n AuthStrategyResult,\n CollectionConfig,\n Payload,\n} from 'payload'\n\nimport * as jose from 'jose'\nimport { APIError, parseCookies } from 'payload'\n\nimport type { CollectionOptions, PluginOptions, VerifyFunction } from '../types.js'\n\nimport { JWTValidationError, validateProjectToken } from '../../auth/jwt-validator.js'\nimport { getEnvironment } from '../../constants.js'\nimport { createDebugLogger } from '../utilities/createDebugLogger.js'\nimport { getTokenExp } from '../utilities/getTokenExp.js'\nimport { mergeHeaders } from '../utilities/mergeHeaders.js'\nimport { refreshTokens } from '../utilities/refreshTokens.js'\n\nexport interface Options {\n clientID: string\n collection: CollectionConfig\n collectionOptions: CollectionOptions\n cookieName: string\n debug?: boolean\n identityMetadata: string\n pluginOptions: PluginOptions\n strategyName: string\n verify: VerifyFunction\n}\n\nexport class Strategy {\n clientID: string\n collection: CollectionConfig\n collectionOptions: CollectionOptions\n cookieName: string\n\n debug: boolean\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n debugLog: (...args: any[]) => void\n\n identityMetadataURL: string\n jwks?: {\n keys: jose.JWK[]\n }\n meta?: {\n authorization_endpoint: string\n end_session_endpoint: string\n jwks_uri: string\n token_endpoint: string\n userinfo_endpoint: string\n }\n metaLastFetchedTime?: Date\n\n metaPromise?: Promise<void>\n metaPromiseResolve?: () => void\n name: string\n pluginOptions: PluginOptions\n verify: VerifyFunction\n\n constructor(options: Options) {\n this.verify = options.verify\n this.cookieName = options.cookieName\n this.clientID = options.clientID\n this.identityMetadataURL = options.identityMetadata\n this.name = options.strategyName\n this.collection = options.collection\n this.collectionOptions = options.collectionOptions\n this.pluginOptions = options.pluginOptions\n this.debug = options.debug || false\n // eslint-disable-next-line no-console, @typescript-eslint/no-explicit-any\n this.debugLog = options.debug ? (...args: any[]) => console.log(...args) : () => null\n // Prevent wokiness with `this`\n this.ensureMeta = this.ensureMeta.bind(this)\n }\n\n async authenticate({\n canSetHeaders,\n headers,\n payload,\n }: AuthStrategyFunctionArgs): Promise<AuthStrategyResult> {\n const debugLogger = createDebugLogger(payload, this.debug)\n await this.ensureMeta()\n\n const cookies = parseCookies(headers)\n\n let oauthToken = cookies.get(this.cookieName)\n const refreshToken = cookies.get(`${this.cookieName}-refresh`)\n\n debugLogger.info({\n msg: 'Retrieved tokens from cookies',\n oauthToken,\n refreshToken,\n })\n\n if (!this.collectionOptions.disableJWTFromHeader) {\n // Parse Authorization header if exists\n const jwtFromHeader = headers.get('authorization')\n if (typeof jwtFromHeader === 'string' && jwtFromHeader.startsWith('Bearer ')) {\n debugLogger.info({\n jwtFromHeader,\n msg: 'Found bearer token in authorization header',\n })\n oauthToken = jwtFromHeader.replace('Bearer ', '')\n }\n }\n\n if (!oauthToken) {\n // Attempt refresh even when canSetHeaders=false (e.g., during RSC navigation).\n // This allows authentication to succeed for the current request even when\n // cookies cannot be updated. The cookie will be refreshed on the next\n // full page load when canSetHeaders=true.\n if (refreshToken) {\n debugLogger.info({\n msg: 'No access token found, but refresh token exists. Attempting to refresh...',\n refreshToken,\n })\n const refreshed = await refreshTokens({ payload, refreshToken, strategy: this })\n if (refreshed) {\n const verifyResult = await this.verify({\n headers,\n payload,\n token: refreshed.decodedOauthToken,\n })\n\n debugLogger.info({\n msg: 'Token refreshed. Verifying...',\n verifyResult,\n })\n\n // Only return response headers (to set cookies) when canSetHeaders is true.\n // During RSC navigation, canSetHeaders=false because response streaming\n // has already started and cookies cannot be modified.\n if (canSetHeaders) {\n if (verifyResult.responseHeaders) {\n mergeHeaders(verifyResult.responseHeaders, refreshed.headers)\n }\n return {\n responseHeaders: refreshed.headers,\n user: verifyResult.user,\n }\n }\n\n // Authenticate this request without updating cookies\n return {\n user: verifyResult.user,\n }\n } else {\n debugLogger.info({\n msg: 'Token refresh failed',\n refreshed,\n refreshToken,\n })\n }\n }\n\n return {\n user: null,\n }\n }\n\n let oauthTokenVerifyResult: Error | jwt.JwtPayload | null = null\n\n // First, try to verify token\n if (oauthToken) {\n oauthTokenVerifyResult = await this.jwtVerify({ payload, token: oauthToken })\n\n // If verify comes back null, there is no user\n if (oauthTokenVerifyResult === null) {\n return {\n user: null,\n }\n }\n\n // If verify comes back truthy, use it\n // but first set exp from refresh token\n // because access exp is likely very short lived\n if (\n typeof oauthTokenVerifyResult === 'object' &&\n !(oauthTokenVerifyResult instanceof Error)\n ) {\n if (refreshToken) {\n const refreshTokenExp = getTokenExp({ debugLogger, token: refreshToken })\n\n if (refreshTokenExp) {\n oauthTokenVerifyResult.exp = refreshTokenExp\n }\n }\n\n return this.verify({\n headers,\n payload,\n token: oauthTokenVerifyResult,\n })\n }\n }\n\n // Handle expired access token with valid refresh token.\n // Same pattern as above: refresh regardless of canSetHeaders to support RSC navigation,\n // but only update cookies when canSetHeaders=true.\n if (\n oauthTokenVerifyResult instanceof JWTValidationError &&\n oauthTokenVerifyResult.code === 'EXPIRED' &&\n refreshToken &&\n !this.collectionOptions.usePayloadJWT\n ) {\n const refreshed = await refreshTokens({ payload, refreshToken, strategy: this })\n\n debugLogger.info({\n msg: 'Token expired. Refreshing...',\n refreshed,\n refreshToken,\n })\n\n if (refreshed) {\n const verifyResult = await this.verify({\n headers,\n payload,\n token: refreshed.decodedOauthToken,\n })\n\n debugLogger.info({\n msg: 'Token refreshed. Verifying...',\n verifyResult,\n })\n\n if (canSetHeaders) {\n if (verifyResult.responseHeaders) {\n mergeHeaders(verifyResult.responseHeaders, refreshed.headers)\n }\n return {\n responseHeaders: refreshed.headers,\n user: verifyResult.user,\n }\n }\n\n // Authenticate this request without updating cookies\n return {\n user: verifyResult.user,\n }\n } else {\n debugLogger.info({\n msg: 'Token refresh failed',\n refreshed,\n refreshToken,\n })\n }\n }\n\n return {\n user: null,\n }\n }\n\n async ensureMeta(): Promise<void> {\n if (this.metaPromise) {\n this.debugLog('strategy - metaPromise exists. Waiting for it to resolve...')\n await this.metaPromise\n }\n\n // If no meta or this.metaLastFetchedTime is more than 24 hours old, fetch meta\n if (!this.metaLastFetchedTime || Date.now() - this.metaLastFetchedTime.getTime() > 86_400_000) {\n this.debugLog(\n `Meta is stale. Fetching... Last fetched: ${this.metaLastFetchedTime || 'never'}`,\n )\n await this.fetchMeta()\n }\n }\n\n async fetchMeta(): Promise<void> {\n // If already in-flight, return the existing promise\n if (this.metaPromise) {\n this.debugLog('fetchMeta - returning existing metaPromise')\n return this.metaPromise\n }\n\n this.debugLog('fetchMeta - creating new metaPromise...')\n\n // Build the shared promise\n this.metaPromise = (async () => {\n let lastError: unknown\n\n for (let attempt = 1; attempt <= 5; attempt++) {\n try {\n this.meta = await fetch(this.identityMetadataURL).then((res) => res.json())\n\n if (this.meta) {\n // TODO: This is a hack to get the DefaultLoginButton to work with dynamic OAuth client registration\n this.meta.authorization_endpoint = this.meta?.authorization_endpoint + '/cms'\n }\n\n this.debugLog(`fetchMeta - success on attempt ${attempt}`)\n\n if (this.meta?.jwks_uri) {\n this.jwks = await fetch(this.meta.jwks_uri).then((res) => res.json())\n }\n\n this.metaLastFetchedTime = new Date()\n this.debugLog(`fetchMeta - finished at ${this.metaLastFetchedTime.toISOString()}`)\n return\n } catch (error: unknown) {\n lastError = error\n this.debugLog(`fetchMeta - FAILED attempt ${attempt}/5: ${(error as Error)?.message}`)\n\n if (attempt < 5) {\n // Exponential backoff (up to 1s)\n await new Promise((resolve) => setTimeout(resolve, Math.min(attempt * 200, 1000)))\n }\n }\n }\n\n throw new APIError(\n `Failed to fetch identity metadata from \"${this.identityMetadataURL}\". Last error: ${\n (lastError as Error)?.message ?? 'unknown'\n }`,\n )\n })()\n\n try {\n await this.metaPromise\n } finally {\n this.metaPromise = undefined\n }\n }\n\n async generateOidcPEM(kid: string, alg: string): Promise<string> {\n const keys = this && this.jwks && Array.isArray(this.jwks.keys) ? this.jwks.keys : null\n\n let pubKey: null | string = null\n let foundKey = false\n\n if (!kid) {\n throw new Error('kid is missing')\n }\n\n if (!alg) {\n throw new Error('alg is missing')\n }\n\n if (!keys) {\n throw new Error('keys is missing')\n }\n\n for (const key of keys) {\n if (!('kid' in key) || key.kid !== kid) {\n continue\n }\n\n const cryptoKey = await jose.importJWK(key, alg)\n if (!(cryptoKey instanceof CryptoKey)) {\n throw new Error('importJWK did not return a CryptoKey')\n }\n\n pubKey = await jose.exportSPKI(cryptoKey)\n foundKey = true\n\n break\n }\n\n if (!foundKey) {\n throw new Error('a key with the specific kid cannot be found')\n }\n\n if (!pubKey) {\n throw new Error('generating public key pem failed')\n }\n\n return pubKey\n }\n\n async jwtVerify({ payload, token }: JwtVerifyArgs): Promise<Error | jwt.JwtPayload | null> {\n try {\n const verified = await validateProjectToken(token, getEnvironment())\n if (typeof verified !== 'string') {\n return verified\n }\n } catch (err: unknown) {\n payload.logger.error({\n err,\n msg: 'Error verifying jwt token',\n })\n\n if (err instanceof Error) {\n return err\n }\n return null\n }\n return null\n }\n}\n\ntype JwtVerifyArgs = {\n payload: Payload\n token: string\n}\n"],"names":["jose","APIError","parseCookies","JWTValidationError","validateProjectToken","getEnvironment","createDebugLogger","getTokenExp","mergeHeaders","refreshTokens","Strategy","clientID","collection","collectionOptions","cookieName","debug","debugLog","identityMetadataURL","jwks","meta","metaLastFetchedTime","metaPromise","metaPromiseResolve","name","pluginOptions","verify","options","identityMetadata","strategyName","args","console","log","ensureMeta","bind","authenticate","canSetHeaders","headers","payload","debugLogger","cookies","oauthToken","get","refreshToken","info","msg","disableJWTFromHeader","jwtFromHeader","startsWith","replace","refreshed","strategy","verifyResult","token","decodedOauthToken","responseHeaders","user","oauthTokenVerifyResult","jwtVerify","Error","refreshTokenExp","exp","code","usePayloadJWT","Date","now","getTime","fetchMeta","lastError","attempt","fetch","then","res","json","authorization_endpoint","jwks_uri","toISOString","error","message","Promise","resolve","setTimeout","Math","min","undefined","generateOidcPEM","kid","alg","keys","Array","isArray","pubKey","foundKey","key","cryptoKey","importJWK","CryptoKey","exportSPKI","verified","err","logger"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-config.d.ts","sourceRoot":"","sources":["../../src/plugin/build-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"build-config.d.ts","sourceRoot":"","sources":["../../src/plugin/build-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAkBtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;CAC1B,GAAG;IACF,KAAK,EAAE;QACL,eAAe,EAAE,MAAM,CAAA;QACvB,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAC3B,CAAA;CACF,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,QAAQ,GAAG,QAAQ,CAAC,GAC1C,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;AAE7B;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CA8HpF"}
|
|
@@ -3,6 +3,7 @@ import { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
|
3
3
|
import { lexicalEditor } from '@payloadcms/richtext-lexical';
|
|
4
4
|
import { buildConfig as payloadBuildConfig } from 'payload';
|
|
5
5
|
import { getTokenStore } from '../auth/token-store.js';
|
|
6
|
+
import { ENV_CONFIG, getEnvironment } from '../constants.js';
|
|
6
7
|
import { contentAPIAdapter } from '../db-content-api/index.js';
|
|
7
8
|
import { health } from '../endpoints/health.js';
|
|
8
9
|
import { oAuth2Plugin } from '../oauth/index.js';
|
|
@@ -23,8 +24,8 @@ import { contentApiStorageAdapter } from '../storage-content-api/index.js';
|
|
|
23
24
|
if (!config.figma.contentSystemId) {
|
|
24
25
|
throw new Error('FIGMA_CONTENT_SYSTEM_ID is required. ' + 'Ensure your .env file contains: FIGMA_CONTENT_SYSTEM_ID=your-content-system-id');
|
|
25
26
|
}
|
|
26
|
-
const
|
|
27
|
-
const url = process.env.FIGMA_CONTENT_API_URL ||
|
|
27
|
+
const env = getEnvironment();
|
|
28
|
+
const url = process.env.FIGMA_CONTENT_API_URL || ENV_CONFIG[env].contentApiUrl;
|
|
28
29
|
const { contentSystemId } = config.figma;
|
|
29
30
|
// Determine database adapter based on environment
|
|
30
31
|
let db;
|
|
@@ -136,9 +137,9 @@ import { contentApiStorageAdapter } from '../storage-content-api/index.js';
|
|
|
136
137
|
response_mode: 'query',
|
|
137
138
|
response_type: 'code'
|
|
138
139
|
},
|
|
139
|
-
clientID: process.env.FIGMA_OAUTH_CLIENT_ID,
|
|
140
|
+
clientID: process.env.FIGMA_OAUTH_CLIENT_ID || ENV_CONFIG[env].clientId,
|
|
140
141
|
clientSecret: process.env.FIGMA_OAUTH_CLIENT_SECRET,
|
|
141
|
-
identityMetadata:
|
|
142
|
+
identityMetadata: ENV_CONFIG[env].identityMetadata,
|
|
142
143
|
scope: [
|
|
143
144
|
'current_user:read'
|
|
144
145
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugin/build-config.ts"],"sourcesContent":["import type { Config, SanitizedConfig } from 'payload'\n\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { lexicalEditor } from '@payloadcms/richtext-lexical'\nimport { buildConfig as payloadBuildConfig } from 'payload'\n\nimport type { StorageClientConfig } from '../storage-content-api/index.js'\n\nimport { getTokenStore } from '../auth/token-store.js'\nimport { contentAPIAdapter } from '../db-content-api/index.js'\nimport { health } from '../endpoints/health.js'\nimport { oAuth2Plugin } from '../oauth/index.js'\nimport { createStorageClient } from '../storage-content-api/client.js'\nimport { getGenerateSignedURLHandler } from '../storage-content-api/client-uploads/generateSignedURL.js'\nimport { contentApiStorageAdapter } from '../storage-content-api/index.js'\n\n/**\n * Configuration type for Payload projects on the Figma platform.\n *\n * Differences from standard Payload Config:\n * - `db`: Omitted (auto-injected by Figma platform)\n * - `secret`: Omitted (auto-injected by Figma platform)\n * - `editor`: Optional (defaults to lexicalEditor() if not provided)\n *\n * @example\n * // Minimal config\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!\n * },\n * collections: [...]\n * }\n *\n * @example\n * // With custom editor\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!\n * },\n * collections: [...],\n * editor: lexicalEditor({ features: [...] })\n * }\n *\n * @example\n * // Disable Content System (use custom db)\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!,\n * useContentSystem: false\n * },\n * db: mongooseAdapter({ url: process.env.DATABASE_URI }),\n * collections: [...]\n * }\n */\nexport type FigmaConfig = {\n editor?: Config['editor']\n} & {\n figma: {\n contentSystemId: string\n storage?: boolean\n useContentSystem?: boolean\n }\n} & Omit<Config, 'db' | 'editor' | 'secret'> &\n Partial<Pick<Config, 'db'>>\n\n/**\n * Figma platform wrapper for Payload's buildConfig.\n * Automatically injects:\n * - Figma database adapter\n * - Secret (platform-provided)\n * - Default lexicalEditor (if no custom editor specified)\n *\n * @param config - Payload configuration (db and secret omitted, editor optional)\n * @returns Promise<SanitizedConfig> - Built Payload configuration\n */\nexport async function buildFigmaConfig(config: FigmaConfig): Promise<SanitizedConfig> {\n // Validate required environment variable\n if (!config.figma.contentSystemId) {\n throw new Error(\n 'FIGMA_CONTENT_SYSTEM_ID is required. ' +\n 'Ensure your .env file contains: FIGMA_CONTENT_SYSTEM_ID=your-content-system-id',\n )\n }\n\n const DEFAULT_CONTENT_API_URL =\n 'https://us-east-1.cms-tenants-001-staging.figmacontentstaging.com'\n\n const url = process.env.FIGMA_CONTENT_API_URL || DEFAULT_CONTENT_API_URL\n const { contentSystemId } = config.figma\n\n // Determine database adapter based on environment\n let db: Config['db']\n\n if (config.figma.useContentSystem === false) {\n db = config.db!\n } else if (process.env.FIGMA_CONTENT_API_ACCESS_KEY) {\n db = contentAPIAdapter({\n auth: { apiKey: process.env.FIGMA_CONTENT_API_ACCESS_KEY, mode: 'apiKey' },\n contentSystemId,\n url,\n })\n } else if (process.env.FIGMA_DEV_JWT === 'true') {\n db = contentAPIAdapter({\n auth: { mode: 'devJwt' },\n contentSystemId,\n url,\n })\n } else {\n db = contentAPIAdapter({\n auth: { mode: 'tokenStore', tokenStore: getTokenStore() },\n contentSystemId,\n url,\n })\n }\n\n // Build storage plugin if there are upload collections and storage is not disabled\n const uploadCollections = (config.collections || []).filter((c) => c.upload)\n let storagePlugin: ReturnType<typeof cloudStoragePlugin> | undefined\n\n if (uploadCollections.length > 0 && config.figma.storage !== false) {\n const storageConfig: StorageClientConfig = process.env.FIGMA_CONTENT_API_ACCESS_KEY\n ? { baseUrl: url, contentApiKey: process.env.FIGMA_CONTENT_API_ACCESS_KEY }\n : process.env.FIGMA_DEV_JWT === 'true'\n ? {\n auth: { mode: 'devJwt' },\n baseUrl: url,\n contentSystemId,\n }\n : {\n auth: { mode: 'tokenStore', tokenStore: getTokenStore() },\n baseUrl: url,\n contentSystemId,\n }\n\n const adapter = contentApiStorageAdapter(storageConfig)\n const storageClient = createStorageClient(storageConfig)\n\n const collectionsMap = uploadCollections.reduce(\n (acc, c) => {\n acc[c.slug] = { adapter, disableLocalStorage: true }\n return acc\n },\n {} as Record<string, { adapter: typeof adapter; disableLocalStorage: boolean }>,\n )\n\n initClientUploads({\n clientHandler: '@payloadcms/figma/client#ContentApiClientUploadHandler',\n collections: collectionsMap,\n config: config as unknown as Config,\n enabled: true,\n serverHandler: getGenerateSignedURLHandler({ client: storageClient }),\n serverHandlerPath: '/content-api-storage-signed-url',\n })\n\n storagePlugin = cloudStoragePlugin({ collections: collectionsMap })\n }\n\n // Build complete config with Figma platform defaults\n const configWithFigmaDefaults: Config = {\n ...config,\n admin: {\n // Auto-refresh tokens before expiry to prevent unexpected logouts.\n // The project token (access token) expires in ~15 minutes, so the client\n // will automatically refresh at ~14 minutes to keep the session alive.\n autoRefresh: true,\n ...config.admin,\n },\n custom: {\n figma: config.figma || {},\n },\n db,\n editor: config.editor ?? lexicalEditor(),\n endpoints: [...(config.endpoints ?? []), health],\n secret: 'MY_SECRET',\n // Add oauth to plugins if not already present\n plugins: [\n ...(config.plugins ?? []),\n ...(storagePlugin ? [storagePlugin] : []),\n oAuth2Plugin({\n collections: [\n {\n slug: 'users',\n // Use adapter because Figma app doesn't support OIDC scopes\n authorizationURLParams: {\n response_mode: 'query',\n response_type: 'code',\n },\n clientID: process.env.FIGMA_OAUTH_CLIENT_ID!,\n clientSecret: process.env.FIGMA_OAUTH_CLIENT_SECRET,\n identityMetadata: 'https://staging.figma.com/.well-known/openid-configuration',\n scope: ['current_user:read'],\n token: 'id_token', // Adapter creates JWT id_token from Figma user info\n usernameField: 'email',\n },\n ],\n debug: !!process.env.DEBUG,\n disabled: false,\n }),\n ],\n }\n\n // Delegate to Payload's buildConfig\n return await payloadBuildConfig(configWithFigmaDefaults)\n}\n"],"names":["cloudStoragePlugin","initClientUploads","lexicalEditor","buildConfig","payloadBuildConfig","getTokenStore","contentAPIAdapter","health","oAuth2Plugin","createStorageClient","getGenerateSignedURLHandler","contentApiStorageAdapter","buildFigmaConfig","config","figma","contentSystemId","Error","DEFAULT_CONTENT_API_URL","url","process","env","FIGMA_CONTENT_API_URL","db","useContentSystem","FIGMA_CONTENT_API_ACCESS_KEY","auth","apiKey","mode","FIGMA_DEV_JWT","tokenStore","uploadCollections","collections","filter","c","upload","storagePlugin","length","storage","storageConfig","baseUrl","contentApiKey","adapter","storageClient","collectionsMap","reduce","acc","slug","disableLocalStorage","clientHandler","enabled","serverHandler","client","serverHandlerPath","configWithFigmaDefaults","admin","autoRefresh","custom","editor","endpoints","secret","plugins","authorizationURLParams","response_mode","response_type","clientID","FIGMA_OAUTH_CLIENT_ID","clientSecret","FIGMA_OAUTH_CLIENT_SECRET","identityMetadata","scope","token","usernameField","debug","DEBUG","disabled"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/plugin/build-config.ts"],"sourcesContent":["import type { Config, SanitizedConfig } from 'payload'\n\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { lexicalEditor } from '@payloadcms/richtext-lexical'\nimport { buildConfig as payloadBuildConfig } from 'payload'\n\nimport type { StorageClientConfig } from '../storage-content-api/index.js'\n\nimport { getTokenStore } from '../auth/token-store.js'\nimport { ENV_CONFIG, getEnvironment } from '../constants.js'\nimport { contentAPIAdapter } from '../db-content-api/index.js'\nimport { health } from '../endpoints/health.js'\nimport { oAuth2Plugin } from '../oauth/index.js'\nimport { createStorageClient } from '../storage-content-api/client.js'\nimport { getGenerateSignedURLHandler } from '../storage-content-api/client-uploads/generateSignedURL.js'\nimport { contentApiStorageAdapter } from '../storage-content-api/index.js'\n\n/**\n * Configuration type for Payload projects on the Figma platform.\n *\n * Differences from standard Payload Config:\n * - `db`: Omitted (auto-injected by Figma platform)\n * - `secret`: Omitted (auto-injected by Figma platform)\n * - `editor`: Optional (defaults to lexicalEditor() if not provided)\n *\n * @example\n * // Minimal config\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!\n * },\n * collections: [...]\n * }\n *\n * @example\n * // With custom editor\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!\n * },\n * collections: [...],\n * editor: lexicalEditor({ features: [...] })\n * }\n *\n * @example\n * // Disable Content System (use custom db)\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!,\n * useContentSystem: false\n * },\n * db: mongooseAdapter({ url: process.env.DATABASE_URI }),\n * collections: [...]\n * }\n */\nexport type FigmaConfig = {\n editor?: Config['editor']\n} & {\n figma: {\n contentSystemId: string\n storage?: boolean\n useContentSystem?: boolean\n }\n} & Omit<Config, 'db' | 'editor' | 'secret'> &\n Partial<Pick<Config, 'db'>>\n\n/**\n * Figma platform wrapper for Payload's buildConfig.\n * Automatically injects:\n * - Figma database adapter\n * - Secret (platform-provided)\n * - Default lexicalEditor (if no custom editor specified)\n *\n * @param config - Payload configuration (db and secret omitted, editor optional)\n * @returns Promise<SanitizedConfig> - Built Payload configuration\n */\nexport async function buildFigmaConfig(config: FigmaConfig): Promise<SanitizedConfig> {\n // Validate required environment variable\n if (!config.figma.contentSystemId) {\n throw new Error(\n 'FIGMA_CONTENT_SYSTEM_ID is required. ' +\n 'Ensure your .env file contains: FIGMA_CONTENT_SYSTEM_ID=your-content-system-id',\n )\n }\n\n const env = getEnvironment()\n const url = process.env.FIGMA_CONTENT_API_URL || ENV_CONFIG[env].contentApiUrl\n const { contentSystemId } = config.figma\n\n // Determine database adapter based on environment\n let db: Config['db']\n\n if (config.figma.useContentSystem === false) {\n db = config.db!\n } else if (process.env.FIGMA_CONTENT_API_ACCESS_KEY) {\n db = contentAPIAdapter({\n auth: { apiKey: process.env.FIGMA_CONTENT_API_ACCESS_KEY, mode: 'apiKey' },\n contentSystemId,\n url,\n })\n } else if (process.env.FIGMA_DEV_JWT === 'true') {\n db = contentAPIAdapter({\n auth: { mode: 'devJwt' },\n contentSystemId,\n url,\n })\n } else {\n db = contentAPIAdapter({\n auth: { mode: 'tokenStore', tokenStore: getTokenStore() },\n contentSystemId,\n url,\n })\n }\n\n // Build storage plugin if there are upload collections and storage is not disabled\n const uploadCollections = (config.collections || []).filter((c) => c.upload)\n let storagePlugin: ReturnType<typeof cloudStoragePlugin> | undefined\n\n if (uploadCollections.length > 0 && config.figma.storage !== false) {\n const storageConfig: StorageClientConfig = process.env.FIGMA_CONTENT_API_ACCESS_KEY\n ? { baseUrl: url, contentApiKey: process.env.FIGMA_CONTENT_API_ACCESS_KEY }\n : process.env.FIGMA_DEV_JWT === 'true'\n ? {\n auth: { mode: 'devJwt' },\n baseUrl: url,\n contentSystemId,\n }\n : {\n auth: { mode: 'tokenStore', tokenStore: getTokenStore() },\n baseUrl: url,\n contentSystemId,\n }\n\n const adapter = contentApiStorageAdapter(storageConfig)\n const storageClient = createStorageClient(storageConfig)\n\n const collectionsMap = uploadCollections.reduce(\n (acc, c) => {\n acc[c.slug] = { adapter, disableLocalStorage: true }\n return acc\n },\n {} as Record<string, { adapter: typeof adapter; disableLocalStorage: boolean }>,\n )\n\n initClientUploads({\n clientHandler: '@payloadcms/figma/client#ContentApiClientUploadHandler',\n collections: collectionsMap,\n config: config as unknown as Config,\n enabled: true,\n serverHandler: getGenerateSignedURLHandler({ client: storageClient }),\n serverHandlerPath: '/content-api-storage-signed-url',\n })\n\n storagePlugin = cloudStoragePlugin({ collections: collectionsMap })\n }\n\n // Build complete config with Figma platform defaults\n const configWithFigmaDefaults: Config = {\n ...config,\n admin: {\n // Auto-refresh tokens before expiry to prevent unexpected logouts.\n // The project token (access token) expires in ~15 minutes, so the client\n // will automatically refresh at ~14 minutes to keep the session alive.\n autoRefresh: true,\n ...config.admin,\n },\n custom: {\n figma: config.figma || {},\n },\n db,\n editor: config.editor ?? lexicalEditor(),\n endpoints: [...(config.endpoints ?? []), health],\n secret: 'MY_SECRET',\n // Add oauth to plugins if not already present\n plugins: [\n ...(config.plugins ?? []),\n ...(storagePlugin ? [storagePlugin] : []),\n oAuth2Plugin({\n collections: [\n {\n slug: 'users',\n // Use adapter because Figma app doesn't support OIDC scopes\n authorizationURLParams: {\n response_mode: 'query',\n response_type: 'code',\n },\n clientID: process.env.FIGMA_OAUTH_CLIENT_ID || ENV_CONFIG[env].clientId,\n clientSecret: process.env.FIGMA_OAUTH_CLIENT_SECRET,\n identityMetadata: ENV_CONFIG[env].identityMetadata,\n scope: ['current_user:read'],\n token: 'id_token', // Adapter creates JWT id_token from Figma user info\n usernameField: 'email',\n },\n ],\n debug: !!process.env.DEBUG,\n disabled: false,\n }),\n ],\n }\n\n // Delegate to Payload's buildConfig\n return await payloadBuildConfig(configWithFigmaDefaults)\n}\n"],"names":["cloudStoragePlugin","initClientUploads","lexicalEditor","buildConfig","payloadBuildConfig","getTokenStore","ENV_CONFIG","getEnvironment","contentAPIAdapter","health","oAuth2Plugin","createStorageClient","getGenerateSignedURLHandler","contentApiStorageAdapter","buildFigmaConfig","config","figma","contentSystemId","Error","env","url","process","FIGMA_CONTENT_API_URL","contentApiUrl","db","useContentSystem","FIGMA_CONTENT_API_ACCESS_KEY","auth","apiKey","mode","FIGMA_DEV_JWT","tokenStore","uploadCollections","collections","filter","c","upload","storagePlugin","length","storage","storageConfig","baseUrl","contentApiKey","adapter","storageClient","collectionsMap","reduce","acc","slug","disableLocalStorage","clientHandler","enabled","serverHandler","client","serverHandlerPath","configWithFigmaDefaults","admin","autoRefresh","custom","editor","endpoints","secret","plugins","authorizationURLParams","response_mode","response_type","clientID","FIGMA_OAUTH_CLIENT_ID","clientId","clientSecret","FIGMA_OAUTH_CLIENT_SECRET","identityMetadata","scope","token","usernameField","debug","DEBUG","disabled"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payloadcms/figma",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.37",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"esprima-next": "^6.0.2",
|
|
37
37
|
"figures": "^6.1.0",
|
|
38
38
|
"jose": "6.0.12",
|
|
39
|
-
"jsonwebtoken": "9.0.
|
|
39
|
+
"jsonwebtoken": "9.0.3",
|
|
40
40
|
"open": "^10.1.0",
|
|
41
41
|
"openapi-fetch": "0.15.0",
|
|
42
42
|
"picocolors": "1.1.1",
|