@designfever/web-review-kit 0.7.0 → 0.7.2

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/vite.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/vite.ts","../src/vite/figma-image-store.ts","../src/vite/figma-asset.ts","../src/figma/parse.ts","../src/figma/token.ts","../src/figma/render.ts","../src/figma/image.store.ts","../src/vite/figma-image-store.server.ts","../src/figma/image.snapshot.ts","../src/vite/figma-image-store.image.ts"],"sourcesContent":["import { type Plugin, type ResolvedConfig } from 'vite';\n\nexport * from './vite/figma-image-store';\n\ntype SourceLocatorPattern = string | RegExp;\n\nexport interface ReviewSourceLocatorOptions {\n enabled?: boolean;\n root?: string;\n include?: readonly SourceLocatorPattern[];\n exclude?: readonly SourceLocatorPattern[];\n filePath?: 'relative' | 'absolute';\n line?: boolean;\n column?: boolean;\n attributePrefix?: string;\n}\n\ntype RuntimeMatcher =\n | { type: 'path'; value: string }\n | { type: 'regex'; value: string; flags: string };\n\ntype RuntimeOptions = {\n enabled: boolean;\n root: string;\n include: RuntimeMatcher[];\n exclude: RuntimeMatcher[];\n filePath: 'relative' | 'absolute';\n line: boolean;\n column: boolean;\n fileAttribute: string;\n lineAttribute: string;\n columnAttribute: string;\n};\n\nconst VIRTUAL_JSX_DEV_RUNTIME_ID =\n '\\0@designfever/web-review-kit/source-locator/jsx-dev-runtime';\n\nconst REVIEW_SOURCE_ENV_DEFINE_KEYS = [\n ['__DF_WRK_REVIEW_SOURCE_ROOT__', 'VITE_REVIEW_SOURCE_ROOT'],\n ['__DF_WRK_REVIEW_SOURCE_EDITOR__', 'VITE_REVIEW_SOURCE_EDITOR'],\n [\n '__DF_WRK_REVIEW_SOURCE_URL_TEMPLATE__',\n 'VITE_REVIEW_SOURCE_URL_TEMPLATE',\n ],\n] as const;\n\ntype ReviewSourceEnvReplacements = Record<string, string>;\n\nconst createReviewSourceEnvReplacements = (\n env: ResolvedConfig['env'] = {}\n): ReviewSourceEnvReplacements => {\n return Object.fromEntries(\n REVIEW_SOURCE_ENV_DEFINE_KEYS.map(([defineKey, envKey]) => [\n defineKey,\n JSON.stringify(env[envKey] ?? ''),\n ])\n );\n};\n\nconst injectReviewSourceEnv = (\n code: string,\n replacements: ReviewSourceEnvReplacements\n) => {\n let nextCode = code;\n for (const [defineKey, value] of Object.entries(replacements)) {\n nextCode = nextCode\n .split(`typeof ${defineKey}`)\n .join(`typeof ${value}`)\n .split(`: ${defineKey}`)\n .join(`: ${value}`);\n }\n\n return nextCode === code ? null : nextCode;\n};\n\nexport const reviewSourceLocator = (\n options: ReviewSourceLocatorOptions = {}\n): Plugin => {\n let runtimeOptions = createRuntimeOptions(options);\n let sourceEnvReplacements = createReviewSourceEnvReplacements();\n\n return {\n name: 'df-web-review-kit-source-locator',\n enforce: 'pre',\n configResolved(config) {\n runtimeOptions = createRuntimeOptions(options, config);\n sourceEnvReplacements = createReviewSourceEnvReplacements(config.env);\n },\n resolveId(id, importer) {\n if (!runtimeOptions.enabled) return null;\n if (id !== 'react/jsx-dev-runtime') return null;\n if (importer === VIRTUAL_JSX_DEV_RUNTIME_ID) return null;\n\n return VIRTUAL_JSX_DEV_RUNTIME_ID;\n },\n load(id) {\n if (id !== VIRTUAL_JSX_DEV_RUNTIME_ID) return null;\n return createJsxDevRuntime(runtimeOptions);\n },\n transform(code) {\n const injectedCode = injectReviewSourceEnv(code, sourceEnvReplacements);\n return injectedCode ? { code: injectedCode, map: null } : null;\n },\n };\n};\n\nexport interface ReviewDataLocatorOptions {\n enabled?: boolean;\n root?: string;\n include?: readonly SourceLocatorPattern[];\n exclude?: readonly SourceLocatorPattern[];\n filePath?: 'relative' | 'absolute';\n /** 매칭할 component 이름 패턴. 기본은 `Section`으로 시작하는 이름. */\n componentPattern?: RegExp;\n fileAttribute?: string;\n lineAttribute?: string;\n}\n\n/**\n * page data 파일의 section 객체(`component: 'SectionXxx'`)에 출처 파일/라인을\n * `__wrkDataFile`/`__wrkDataLine` prop 으로 주입한다. 라인 보존을 위해 같은 줄에만 삽입한다.\n */\nexport const reviewDataLocator = (\n options: ReviewDataLocatorOptions = {}\n): Plugin => {\n let root = normalizePath(options.root ?? '');\n let enabled = options.enabled ?? false;\n let sourceEnvReplacements = createReviewSourceEnvReplacements();\n const include = (options.include ?? []).map(createRuntimeMatcher);\n const exclude = (options.exclude ?? ['node_modules', 'dist']).map(\n createRuntimeMatcher\n );\n const componentPattern = options.componentPattern ?? /Section[A-Za-z0-9_]*/;\n const fileKey = options.fileAttribute ?? '__wrkDataFile';\n const lineKey = options.lineAttribute ?? '__wrkDataLine';\n\n const componentSource = `(^|[\\\\n,{(\\\\[]\\\\s*)(component:\\\\s*)(['\"\\`])(${componentPattern.source})\\\\3`;\n\n return {\n name: 'df-web-review-kit-data-locator',\n enforce: 'pre',\n configResolved(config) {\n root = normalizePath(options.root ?? config.root ?? '');\n enabled = options.enabled ?? config.command === 'serve';\n sourceEnvReplacements = createReviewSourceEnvReplacements(config.env);\n },\n transform(code, id) {\n const envInjectedCode = injectReviewSourceEnv(\n code,\n sourceEnvReplacements\n );\n const inputCode = envInjectedCode ?? code;\n if (!enabled) return null;\n const file = normalizePath(id.split('?')[0]);\n const relativeFile =\n root && file.startsWith(root + '/') ? file.slice(root.length + 1) : file;\n if (\n include.length > 0 &&\n !include.some((m) => matchesPath(m, file, relativeFile))\n )\n return envInjectedCode ? { code: envInjectedCode, map: null } : null;\n if (exclude.some((m) => matchesPath(m, file, relativeFile))) {\n return envInjectedCode ? { code: envInjectedCode, map: null } : null;\n }\n\n const sourceFile =\n (options.filePath ?? 'relative') === 'absolute' ? file : relativeFile;\n const regex = new RegExp(componentSource, 'g');\n let changed = false;\n const out = inputCode.replace(\n regex,\n (\n _match,\n pre: string,\n comp: string,\n quote: string,\n name: string,\n offset: number\n ) => {\n const line = inputCode\n .slice(0, offset + pre.length)\n .split('\\n').length;\n changed = true;\n return `${pre}${JSON.stringify(fileKey)}: ${JSON.stringify(sourceFile)}, ${JSON.stringify(lineKey)}: ${line}, ${comp}${quote}${name}${quote}`;\n }\n );\n\n return changed || envInjectedCode ? { code: out, map: null } : null;\n },\n };\n};\n\nfunction matchesPath(\n matcher: RuntimeMatcher,\n absoluteFile: string,\n relativeFile: string\n) {\n if (matcher.type === 'regex') {\n const regex = new RegExp(matcher.value, matcher.flags);\n return regex.test(absoluteFile) || regex.test(relativeFile);\n }\n const target = matcher.value.startsWith('/') ? absoluteFile : relativeFile;\n return target === matcher.value || target.startsWith(matcher.value + '/') || target.includes('/' + matcher.value);\n}\n\nfunction createRuntimeOptions(\n options: ReviewSourceLocatorOptions,\n config?: ResolvedConfig\n): RuntimeOptions {\n const attributePrefix = (options.attributePrefix ?? 'data-wrk-source').replace(\n /-+$/,\n ''\n );\n const root = normalizePath(options.root ?? config?.root ?? '');\n const enabled = options.enabled ?? (config?.command === 'serve');\n\n return {\n enabled,\n root,\n include: (options.include ?? []).map(createRuntimeMatcher),\n exclude: (options.exclude ?? ['node_modules', 'dist']).map(\n createRuntimeMatcher\n ),\n filePath: options.filePath ?? 'relative',\n line: options.line ?? true,\n column: options.column ?? true,\n fileAttribute: `${attributePrefix}-file`,\n lineAttribute: `${attributePrefix}-line`,\n columnAttribute: `${attributePrefix}-column`,\n };\n}\n\nfunction createRuntimeMatcher(pattern: SourceLocatorPattern): RuntimeMatcher {\n if (pattern instanceof RegExp) {\n return { type: 'regex', value: pattern.source, flags: pattern.flags };\n }\n\n return { type: 'path', value: normalizePath(pattern).replace(/^\\.\\//, '') };\n}\n\nfunction normalizePath(value: string) {\n return value.replace(/\\\\/g, '/').replace(/\\/+$/, '');\n}\n\nfunction createJsxDevRuntime(options: RuntimeOptions) {\n return `\nimport { Fragment, jsxDEV as baseJsxDEV } from 'react/jsx-dev-runtime';\n\nconst OPTIONS = ${JSON.stringify(options)};\n\nexport { Fragment };\n\nexport function jsxDEV(type, props, key, isStaticChildren, source, self) {\n return baseJsxDEV(\n type,\n injectSourceProps(type, props, source),\n key,\n isStaticChildren,\n source,\n self\n );\n}\n\nfunction injectSourceProps(type, props, source) {\n if (typeof type !== 'string') return props;\n if (!source || typeof source.fileName !== 'string') return props;\n\n const sourceFile = getSourceFile(source.fileName);\n if (!sourceFile) return props;\n\n const nextProps = props ? { ...props } : {};\n if (nextProps[OPTIONS.fileAttribute] == null) {\n nextProps[OPTIONS.fileAttribute] = sourceFile;\n }\n if (OPTIONS.line && source.lineNumber != null && nextProps[OPTIONS.lineAttribute] == null) {\n nextProps[OPTIONS.lineAttribute] = String(source.lineNumber);\n }\n if (OPTIONS.column && source.columnNumber != null && nextProps[OPTIONS.columnAttribute] == null) {\n nextProps[OPTIONS.columnAttribute] = String(source.columnNumber);\n }\n\n return nextProps;\n}\n\nfunction getSourceFile(fileName) {\n const absoluteFile = normalizePath(fileName);\n const relativeFile = getRelativeFile(absoluteFile);\n\n if (OPTIONS.include.length > 0 && !matchesAny(OPTIONS.include, absoluteFile, relativeFile)) {\n return null;\n }\n if (matchesAny(OPTIONS.exclude, absoluteFile, relativeFile)) return null;\n\n return OPTIONS.filePath === 'absolute' ? absoluteFile : relativeFile;\n}\n\nfunction getRelativeFile(absoluteFile) {\n if (!OPTIONS.root) return absoluteFile;\n if (absoluteFile === OPTIONS.root) return '';\n if (absoluteFile.startsWith(OPTIONS.root + '/')) {\n return absoluteFile.slice(OPTIONS.root.length + 1);\n }\n\n return absoluteFile;\n}\n\nfunction matchesAny(patterns, absoluteFile, relativeFile) {\n return patterns.some((pattern) =>\n matchesPattern(pattern, absoluteFile, relativeFile)\n );\n}\n\nfunction matchesPattern(pattern, absoluteFile, relativeFile) {\n if (pattern.type === 'regex') {\n const regex = new RegExp(pattern.value, pattern.flags);\n return regex.test(absoluteFile) || regex.test(relativeFile);\n }\n\n const value = pattern.value;\n const target = isAbsolutePattern(value) ? absoluteFile : relativeFile;\n if (!value.includes('*')) {\n return target === value || target.startsWith(value + '/');\n }\n\n return globToRegExp(value).test(target);\n}\n\nfunction isAbsolutePattern(value) {\n return value.startsWith('/') || /^[a-zA-Z]:\\\\//.test(value);\n}\n\nfunction globToRegExp(value) {\n const source = escapeRegExp(value)\n .replace(/\\\\\\\\\\\\*\\\\\\\\\\\\*/g, '.*')\n .replace(/\\\\\\\\\\\\*/g, '[^/]*');\n\n return new RegExp('^' + source + '$');\n}\n\nfunction escapeRegExp(value) {\n return value.replace(/[|\\\\\\\\{}()[\\\\]^$+*?.]/g, '\\\\\\\\$&');\n}\n\nfunction normalizePath(value) {\n return value.replace(/\\\\\\\\/g, '/').replace(/\\\\/+$/, '');\n}\n`;\n}\n","import path from 'node:path';\nimport { loadEnv, type Plugin } from 'vite';\nimport type { ReviewFigmaImageFormat } from '../figma/image.types';\nimport {\n DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT,\n} from '../figma/image.store';\nimport {\n DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,\n readReviewFigmaToken,\n requireReviewFigmaToken,\n type ReviewFigmaTokenEnv,\n} from '../figma/token';\nimport {\n renderReviewFigmaImage,\n type ReviewFigmaImageRenderOptions,\n type ReviewFigmaRenderFormat,\n} from '../figma/render';\nimport {\n handleReviewFigmaImageStoreRequest,\n normalizeEndpoint,\n readJsonRequestBody,\n sendJson,\n sendReviewFigmaAsset,\n} from './figma-image-store.server';\n\nexport interface ReviewFigmaImageStorePluginOptions\n extends ReviewFigmaServerTokenOptions {\n enabled?: boolean;\n projectId?: string;\n endpoint?: string;\n dataFile?: string;\n assetDir?: string;\n assetEndpoint?: string;\n cacheAssets?: boolean;\n imageFormat?: ReviewFigmaImageFormat;\n renderFormat?: ReviewFigmaRenderFormat;\n renderScale?: number;\n useAbsoluteBounds?: boolean;\n apiBaseUrl?: string;\n fetch?: typeof fetch;\n transformAsset?: ReviewFigmaImageAssetTransformer;\n}\n\nexport interface ReviewFigmaServerTokenOptions {\n token?: string | null;\n env?: ReviewFigmaTokenEnv;\n envKey?: string;\n enabled?: boolean;\n}\n\nexport type ReviewFigmaServerImageRenderOptions =\n Omit<ReviewFigmaImageRenderOptions, 'token'> &\n ReviewFigmaServerTokenOptions;\n\nexport type ReviewFigmaImageAssetTransformInput = {\n data: Uint8Array;\n imageFormat: ReviewFigmaImageFormat;\n mimeType: string;\n targetFormat: ReviewFigmaImageFormat;\n};\n\nexport type ReviewFigmaImageAssetTransformResult = {\n data: Uint8Array | ArrayBuffer;\n imageFormat: ReviewFigmaImageFormat;\n mimeType?: string;\n};\n\nexport type ReviewFigmaImageAssetTransformer = (\n input: ReviewFigmaImageAssetTransformInput\n) =>\n | ReviewFigmaImageAssetTransformResult\n | null\n | undefined\n | Promise<ReviewFigmaImageAssetTransformResult | null | undefined>;\n\nexport {\n createReviewFigmaImageApiUrl,\n renderReviewFigmaImage,\n} from '../figma/render';\nexport {\n collectReviewFigmaReleaseSnapshot,\n createReviewFigmaImagesSnapshot,\n createReviewFigmaReleaseSnapshot,\n} from '../figma/image.snapshot';\nexport type {\n ReviewFigmaImageRenderOptions,\n ReviewFigmaRenderFormat,\n ReviewFigmaRenderedImage,\n} from '../figma/render';\nexport type {\n CollectReviewFigmaReleaseSnapshotOptions,\n CreateReviewFigmaImagesSnapshotOptions,\n CreateReviewFigmaReleaseSnapshotOptions,\n ReviewFigmaImagesSnapshot,\n ReviewFigmaReleaseSnapshot,\n} from '../figma/image.snapshot';\n\nexport const readReviewFigmaServerToken = (\n options: ReviewFigmaServerTokenOptions = {}\n) =>\n readReviewFigmaToken({\n token: options.token,\n env: options.env ?? getServerEnv(),\n envKey: options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,\n enabled: options.enabled,\n });\n\nexport const requireReviewFigmaServerToken = (\n options: ReviewFigmaServerTokenOptions = {}\n) =>\n requireReviewFigmaToken({\n token: options.token,\n env: options.env ?? getServerEnv(),\n envKey: options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,\n enabled: options.enabled,\n });\n\nexport const renderReviewFigmaServerImage = (\n options: ReviewFigmaServerImageRenderOptions\n) => {\n const { token, env, envKey, enabled, ...renderOptions } = options;\n const explicitToken = typeof token === 'string' ? token.trim() : token;\n\n return renderReviewFigmaImage({\n ...renderOptions,\n token:\n explicitToken ||\n requireReviewFigmaServerToken({ env, envKey, enabled }),\n });\n};\n\nexport const reviewFigmaImageStore = (\n options: ReviewFigmaImageStorePluginOptions = {}\n): Plugin => {\n let root = '';\n let dataFile = '';\n let assetDir = '';\n let env: ReviewFigmaTokenEnv = {};\n const enabled = options.enabled ?? true;\n const endpoint = normalizeEndpoint(\n options.endpoint ?? DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT\n );\n const assetEndpoint = normalizeEndpoint(\n options.assetEndpoint ?? `${endpoint}/assets`\n );\n\n return {\n name: 'df-web-review-kit-figma-image-store',\n apply: 'serve',\n configResolved(config) {\n root = config.root;\n dataFile = path.resolve(\n root,\n options.dataFile ?? '.df-review/figma-images.json'\n );\n assetDir = options.assetDir\n ? path.resolve(root, options.assetDir)\n : path.join(path.dirname(dataFile), 'figma-assets');\n env = {\n ...loadEnv(config.mode, config.envDir, ''),\n ...getServerEnv(),\n ...(options.env ?? {}),\n };\n },\n configureServer(server) {\n if (!enabled) return;\n\n server.middlewares.use(async (req, res, next) => {\n const requestUrl = new URL(req.url ?? '/', 'http://localhost');\n const pathname = requestUrl.pathname;\n if (pathname.startsWith(`${assetEndpoint}/`)) {\n await sendReviewFigmaAsset(res, assetDir, assetEndpoint, pathname);\n return;\n }\n if (pathname !== endpoint && !pathname.startsWith(`${endpoint}/`)) {\n next();\n return;\n }\n\n try {\n const response = await handleReviewFigmaImageStoreRequest({\n dataFile,\n assetDir,\n assetEndpoint,\n endpoint,\n options,\n env,\n pathname,\n requestUrl,\n method: req.method ?? 'GET',\n body: await readJsonRequestBody(req),\n });\n\n sendJson(res, response.status, response.body);\n } catch (error) {\n sendJson(res, 500, {\n error:\n error instanceof Error\n ? error.message\n : 'Figma image store request failed.',\n });\n }\n });\n },\n };\n};\n\nfunction getServerEnv(): ReviewFigmaTokenEnv {\n const runtime = globalThis as typeof globalThis & {\n process?: {\n env?: ReviewFigmaTokenEnv;\n };\n };\n\n return runtime.process?.env ?? {};\n}\n","// Pure helpers for Figma image asset format conversion and storage-key handling.\n// Extracted from src/vite.ts to keep the plugin entry focused on request flow.\nimport type { ReviewFigmaImageFormat } from '../figma/image.types';\nimport type { ReviewFigmaRenderFormat } from '../figma/render';\n\nexport function parseReviewFigmaImageFormat(value: unknown) {\n return value === 'webp' || value === 'png' || value === 'jpg'\n ? value\n : undefined;\n}\n\nexport function getStoreRenderFormat(\n renderFormat: ReviewFigmaRenderFormat | undefined,\n imageFormat: ReviewFigmaImageFormat | undefined\n): Extract<ReviewFigmaRenderFormat, 'png' | 'jpg'> {\n if (renderFormat === 'jpg' || renderFormat === 'png') return renderFormat;\n if (imageFormat === 'jpg') return 'jpg';\n return 'png';\n}\n\nexport function getReviewFigmaImageFormatFromMimeType(\n mimeType: string\n): ReviewFigmaImageFormat | null {\n if (mimeType === 'image/webp') return 'webp';\n if (mimeType === 'image/png') return 'png';\n if (mimeType === 'image/jpeg') return 'jpg';\n return null;\n}\n\nexport function normalizeImageMimeType(value: string | null | undefined) {\n const mimeType = value?.split(';')[0]?.trim().toLowerCase();\n if (mimeType === 'image/jpg') return 'image/jpeg';\n if (\n mimeType === 'image/jpeg' ||\n mimeType === 'image/png' ||\n mimeType === 'image/webp'\n ) {\n return mimeType;\n }\n return null;\n}\n\nexport function createReviewFigmaAssetStorageKey(\n id: string,\n imageFormat: ReviewFigmaImageFormat\n) {\n return `${id}.${getReviewFigmaAssetExtension(imageFormat)}`;\n}\n\nexport function createReviewFigmaAssetUrl(\n assetEndpoint: string,\n storageKey: string\n) {\n return `${assetEndpoint}/${encodeURIComponent(storageKey)}`;\n}\n\nexport function getReviewFigmaAssetStorageKeyFromPathname(\n pathname: string,\n assetEndpoint: string\n) {\n try {\n const storageKey = decodeURIComponent(\n pathname.slice(assetEndpoint.length + 1)\n );\n return isSafeReviewFigmaAssetStorageKey(storageKey) ? storageKey : null;\n } catch {\n return null;\n }\n}\n\nexport function isSafeReviewFigmaAssetStorageKey(value: string) {\n return /^figma_[a-z0-9_]+\\.(webp|png|jpg)$/.test(value);\n}\n\nfunction getReviewFigmaAssetExtension(format: ReviewFigmaImageFormat) {\n return format === 'jpg' ? 'jpg' : format;\n}\n\nexport function getReviewFigmaAssetMimeType(storageKey: string) {\n if (storageKey.endsWith('.jpg')) return 'image/jpeg';\n if (storageKey.endsWith('.webp')) return 'image/webp';\n return 'image/png';\n}\n","export type ReviewFigmaNodeRef = {\n fileKey: string;\n nodeId: string;\n sourceUrl?: string;\n};\n\nexport const FIGMA_NODE_REF_SEPARATOR = '->';\n\nexport function parseReviewFigmaNodeRef(\n value: string | ReviewFigmaNodeRef\n): ReviewFigmaNodeRef | null {\n if (typeof value !== 'string') return normalizeReviewFigmaNodeRef(value);\n\n const input = value.trim();\n if (!input) return null;\n\n return parseReviewFigmaNodeRefValue(input) ?? parseReviewFigmaUrl(input);\n}\n\nexport function requireReviewFigmaNodeRef(\n value: string | ReviewFigmaNodeRef\n): ReviewFigmaNodeRef {\n const ref = parseReviewFigmaNodeRef(value);\n if (!ref) {\n throw new Error('A Figma node link or fileKey->nodeId value is required.');\n }\n return ref;\n}\n\nexport function createReviewFigmaNodeValue(ref: ReviewFigmaNodeRef) {\n return `${ref.fileKey}${FIGMA_NODE_REF_SEPARATOR}${ref.nodeId}`;\n}\n\nexport function createReviewFigmaFrameUrl(\n value: string | ReviewFigmaNodeRef\n) {\n const ref = parseReviewFigmaNodeRef(value);\n if (!ref) return null;\n\n return `https://www.figma.com/design/${encodeURIComponent(\n ref.fileKey\n )}?node-id=${encodeURIComponent(ref.nodeId.replace(/:/g, '-'))}`;\n}\n\nfunction parseReviewFigmaNodeRefValue(value: string) {\n const [fileKey, nodeId, extra] = value\n .split(FIGMA_NODE_REF_SEPARATOR)\n .map((part) => part.trim());\n\n if (!fileKey || !nodeId || extra !== undefined) return null;\n\n return normalizeReviewFigmaNodeRef({ fileKey, nodeId });\n}\n\nfunction parseReviewFigmaUrl(value: string) {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return null;\n }\n\n const fileKey = getFigmaFileKey(url);\n const nodeId = normalizeFigmaNodeId(url.searchParams.get('node-id'));\n if (!fileKey || !nodeId) return null;\n\n return { fileKey, nodeId, sourceUrl: value };\n}\n\nfunction getFigmaFileKey(url: URL) {\n if (!/(^|\\.)figma\\.com$/i.test(url.hostname)) return null;\n\n const parts = url.pathname.split('/').filter(Boolean);\n const fileKeyIndex = parts.findIndex((part) =>\n ['design', 'file', 'proto', 'board'].includes(part)\n );\n const fileKey = fileKeyIndex >= 0 ? parts[fileKeyIndex + 1] : null;\n\n return normalizeFigmaFileKey(fileKey);\n}\n\nfunction normalizeReviewFigmaNodeRef(\n value: ReviewFigmaNodeRef | null | undefined\n) {\n const fileKey = normalizeFigmaFileKey(value?.fileKey);\n const nodeId = normalizeFigmaNodeId(value?.nodeId);\n if (!fileKey || !nodeId) return null;\n const sourceUrl = normalizeOptionalString(value?.sourceUrl);\n\n return sourceUrl ? { fileKey, nodeId, sourceUrl } : { fileKey, nodeId };\n}\n\nfunction normalizeFigmaFileKey(value: string | null | undefined) {\n return normalizeOptionalString(value)?.replace(/[?#].*$/, '') ?? null;\n}\n\nfunction normalizeFigmaNodeId(value: string | null | undefined) {\n const nodeId = normalizeOptionalString(value);\n if (!nodeId) return null;\n if (nodeId.includes(':')) return nodeId;\n return nodeId.replace(/-/g, ':');\n}\n\nfunction normalizeOptionalString(value: string | null | undefined) {\n if (typeof value !== 'string') return null;\n return value.trim() || null;\n}\n","export const DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY = 'FIGMA_TOKEN';\nexport const REVIEW_FIGMA_TOKEN_MISSING_CODE = 'DFWR_FIGMA_TOKEN_MISSING';\n\nexport type ReviewFigmaTokenEnv = Record<\n string,\n string | null | undefined\n>;\n\nexport type ReviewFigmaTokenOptions = {\n token?: string | null;\n env?: ReviewFigmaTokenEnv;\n envKey?: string;\n enabled?: boolean;\n};\n\nexport class ReviewFigmaTokenError extends Error {\n readonly code = REVIEW_FIGMA_TOKEN_MISSING_CODE;\n readonly envKey: string;\n\n constructor(envKey = DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY) {\n super(\n `Figma image rendering requires server env ${envKey}. Set ${envKey} in the dev/server environment; do not expose it as VITE_${envKey}.`\n );\n this.name = 'ReviewFigmaTokenError';\n this.envKey = envKey;\n }\n}\n\nexport function readReviewFigmaToken(\n options: ReviewFigmaTokenOptions = {}\n): string | null {\n if (options.enabled === false) return null;\n\n const envKey = options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;\n return normalizeReviewFigmaToken(options.token ?? options.env?.[envKey]);\n}\n\nexport function requireReviewFigmaToken(\n options: ReviewFigmaTokenOptions = {}\n): string {\n const envKey = options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;\n const token = readReviewFigmaToken(options);\n\n if (!token) throw new ReviewFigmaTokenError(envKey);\n return token;\n}\n\nexport function isReviewFigmaTokenError(\n error: unknown\n): error is ReviewFigmaTokenError {\n if (!error || typeof error !== 'object') return false;\n\n return (\n error instanceof ReviewFigmaTokenError ||\n ('code' in error && error.code === REVIEW_FIGMA_TOKEN_MISSING_CODE)\n );\n}\n\nfunction normalizeReviewFigmaToken(value: string | null | undefined) {\n if (typeof value !== 'string') return null;\n return value.trim() || null;\n}\n","import {\n requireReviewFigmaNodeRef,\n type ReviewFigmaNodeRef,\n} from './parse';\nimport { requireReviewFigmaToken } from './token';\n\nexport type ReviewFigmaRenderFormat = 'png' | 'jpg' | 'svg' | 'pdf';\n\nexport type ReviewFigmaImageRenderOptions = {\n figmaUrl: string | ReviewFigmaNodeRef;\n token?: string | null;\n format?: ReviewFigmaRenderFormat;\n scale?: number;\n useAbsoluteBounds?: boolean;\n apiBaseUrl?: string;\n fetch?: typeof fetch;\n signal?: AbortSignal;\n};\n\nexport type ReviewFigmaRenderedImage = {\n fileKey: string;\n nodeId: string;\n figmaUrl?: string;\n imageUrl: string;\n renderFormat: ReviewFigmaRenderFormat;\n};\n\ntype FigmaImageResponse = {\n err?: string | null;\n images?: Record<string, string | null | undefined>;\n};\n\nconst DEFAULT_FIGMA_API_BASE_URL = 'https://api.figma.com';\n\nexport async function renderReviewFigmaImage(\n options: ReviewFigmaImageRenderOptions\n): Promise<ReviewFigmaRenderedImage> {\n const token = requireReviewFigmaToken({ token: options.token });\n const ref = requireReviewFigmaNodeRef(options.figmaUrl);\n const renderFormat = options.format ?? 'png';\n const requestUrl = createReviewFigmaImageApiUrl({\n apiBaseUrl: options.apiBaseUrl,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n format: renderFormat,\n scale: options.scale,\n useAbsoluteBounds: options.useAbsoluteBounds,\n });\n const fetchImage = options.fetch ?? globalThis.fetch;\n if (!fetchImage) throw new Error('Figma image rendering requires fetch.');\n\n const response = await fetchImage(requestUrl, {\n headers: {\n 'X-Figma-Token': token,\n },\n signal: options.signal,\n });\n const body = (await response.json().catch(() => null)) as\n | FigmaImageResponse\n | null;\n\n if (!response.ok) {\n throw new Error(\n body?.err || `Figma image render failed with ${response.status}`\n );\n }\n\n const imageUrl = body?.images?.[ref.nodeId];\n if (!imageUrl) {\n throw new Error(`Figma image render returned no URL for ${ref.nodeId}.`);\n }\n\n return {\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n figmaUrl: typeof options.figmaUrl === 'string'\n ? options.figmaUrl\n : options.figmaUrl.sourceUrl,\n imageUrl,\n renderFormat,\n };\n}\n\nexport function createReviewFigmaImageApiUrl({\n apiBaseUrl = DEFAULT_FIGMA_API_BASE_URL,\n fileKey,\n nodeId,\n format = 'png',\n scale,\n useAbsoluteBounds,\n}: {\n apiBaseUrl?: string;\n fileKey: string;\n nodeId: string;\n format?: ReviewFigmaRenderFormat;\n scale?: number;\n useAbsoluteBounds?: boolean;\n}) {\n const url = new URL(`/v1/images/${encodeURIComponent(fileKey)}`, apiBaseUrl);\n url.searchParams.set('ids', nodeId);\n url.searchParams.set('format', format);\n\n if (typeof scale === 'number' && Number.isFinite(scale) && scale > 0) {\n url.searchParams.set('scale', String(scale));\n }\n if (useAbsoluteBounds !== undefined) {\n url.searchParams.set('use_absolute_bounds', String(useAbsoluteBounds));\n }\n\n return url.toString();\n}\n","import type {\n AddReviewFigmaImageInput,\n ReorderReviewFigmaImagesInput,\n ReviewFigmaImage,\n ReviewFigmaImageAssetInput,\n ReviewFigmaImageFormat,\n ReviewFigmaImageStore,\n ReviewFigmaImageTarget,\n} from './image.types';\nimport { getReviewFigmaImageFormatFromMimeType } from '../vite/figma-asset';\nimport { parseReviewFigmaNodeRef } from './parse';\nimport {\n createReviewFigmaImageApiUrl,\n type ReviewFigmaRenderFormat,\n} from './render';\n\nexport const DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT =\n '/__dfwr/figma-images';\n\nexport type ReviewFigmaImageStoreClientOptions = {\n endpoint?: string;\n fetch?: typeof fetch;\n clientRender?: boolean | ReviewFigmaImageClientRenderOptions;\n};\n\nexport type ReviewFigmaImageClientRenderOptions = {\n token?: string | null | (() => string | null | undefined);\n apiBaseUrl?: string;\n renderFormat?: Extract<ReviewFigmaRenderFormat, 'png' | 'jpg'>;\n renderScale?: number;\n useAbsoluteBounds?: boolean;\n convertToWebp?: boolean;\n webpQuality?: number;\n timeoutMs?: number;\n};\n\nexport function createReviewFigmaImageStoreClient(\n options: ReviewFigmaImageStoreClientOptions = {}\n): ReviewFigmaImageStore {\n const endpoint =\n options.endpoint ?? DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT;\n const request = createReviewFigmaImageStoreRequest(endpoint, options.fetch);\n\n return {\n listImages(target) {\n const url = `${endpoint}?target=${encodeURIComponent(\n JSON.stringify(target)\n )}`;\n return request<ReviewFigmaImage[]>(url);\n },\n async addImage(input) {\n const nextInput = await createClientRenderedAddImageInput(\n input,\n options.clientRender,\n options.fetch\n );\n return request<ReviewFigmaImage>(endpoint, {\n method: 'POST',\n body: JSON.stringify(nextInput),\n });\n },\n updateImage(id, patch) {\n return request<ReviewFigmaImage>(`${endpoint}/${encodeURIComponent(id)}`, {\n method: 'PATCH',\n body: JSON.stringify(patch),\n });\n },\n reorderImages(input) {\n return request<ReviewFigmaImage[]>(`${endpoint}/reorder`, {\n method: 'PATCH',\n body: JSON.stringify(input),\n });\n },\n deleteImage(id) {\n return request<void>(`${endpoint}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n });\n },\n };\n}\n\nasync function createClientRenderedAddImageInput(\n input: AddReviewFigmaImageInput,\n clientRender: ReviewFigmaImageStoreClientOptions['clientRender'],\n fetchOption: typeof fetch | undefined\n): Promise<AddReviewFigmaImageInput> {\n const options = normalizeClientRenderOptions(clientRender);\n if (!options) return input;\n\n const token = readClientRenderToken(options);\n if (!token) return input;\n\n try {\n const asset = await withTimeout(\n createClientRenderedFigmaAsset(input.figmaUrl, token, options, fetchOption),\n options.timeoutMs ?? 10000\n );\n return {\n ...input,\n imageFormat: asset.imageFormat,\n asset,\n };\n } catch {\n return input;\n }\n}\n\nfunction normalizeClientRenderOptions(\n clientRender: ReviewFigmaImageStoreClientOptions['clientRender']\n): ReviewFigmaImageClientRenderOptions | null {\n if (!clientRender) return null;\n if (clientRender === true) return {};\n return clientRender;\n}\n\nfunction readClientRenderToken(options: ReviewFigmaImageClientRenderOptions) {\n const token =\n typeof options.token === 'function' ? options.token() : options.token;\n return typeof token === 'string' ? token.trim() : '';\n}\n\nasync function createClientRenderedFigmaAsset(\n figmaUrl: string,\n token: string,\n options: ReviewFigmaImageClientRenderOptions,\n fetchOption: typeof fetch | undefined\n): Promise<ReviewFigmaImageAssetInput> {\n const ref = parseReviewFigmaNodeRef(figmaUrl);\n if (!ref) throw new Error('A Figma node link is required.');\n\n const requestFetch = fetchOption ?? globalThis.fetch;\n if (!requestFetch) throw new Error('Figma client rendering requires fetch.');\n\n const renderFormat = options.renderFormat ?? 'png';\n const response = await requestFetch(\n createReviewFigmaImageApiUrl({\n apiBaseUrl: options.apiBaseUrl,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n format: renderFormat,\n scale: options.renderScale,\n useAbsoluteBounds: options.useAbsoluteBounds,\n }),\n {\n headers: {\n 'X-Figma-Token': token,\n },\n }\n );\n const body = (await response.json().catch(() => null)) as\n | { err?: string | null; images?: Record<string, string | null | undefined> }\n | null;\n if (!response.ok) {\n throw new Error(body?.err || `Figma image render failed with ${response.status}`);\n }\n\n const imageUrl = body?.images?.[ref.nodeId];\n if (!imageUrl) throw new Error(`Figma image render returned no URL for ${ref.nodeId}.`);\n\n const imageResponse = await requestFetch(imageUrl);\n if (!imageResponse.ok) {\n throw new Error(`Figma image download failed with ${imageResponse.status}`);\n }\n\n const originalBlob = await imageResponse.blob();\n const originalMimeType =\n normalizeClientImageMimeType(originalBlob.type) ??\n getReviewFigmaImageMimeType(renderFormat === 'jpg' ? 'jpg' : 'png');\n const originalFormat =\n getReviewFigmaImageFormatFromMimeType(originalMimeType) ??\n (renderFormat === 'jpg' ? 'jpg' : 'png');\n const dimensions = await readImageBlobDimensions(originalBlob);\n const shouldConvertToWebp = options.convertToWebp ?? true;\n const convertedBlob = shouldConvertToWebp\n ? await convertImageBlobToWebp(\n originalBlob,\n options.webpQuality ?? 0.9,\n dimensions\n ).catch(() => null)\n : null;\n const finalBlob =\n convertedBlob?.type === 'image/webp' ? convertedBlob : originalBlob;\n const finalMimeType =\n normalizeClientImageMimeType(finalBlob.type) ?? originalMimeType;\n const finalFormat =\n getReviewFigmaImageFormatFromMimeType(finalMimeType) ?? originalFormat;\n\n return {\n dataUrl: await blobToDataUrl(finalBlob),\n imageFormat: finalFormat,\n mimeType: finalMimeType,\n byteSize: finalBlob.size,\n width: dimensions.width,\n height: dimensions.height,\n };\n}\n\nasync function readImageBlobDimensions(blob: Blob) {\n const image = await loadImageBlob(blob);\n return {\n width: image.naturalWidth || image.width,\n height: image.naturalHeight || image.height,\n };\n}\n\nasync function convertImageBlobToWebp(\n blob: Blob,\n quality: number,\n dimensions: { width: number; height: number }\n) {\n if (typeof document === 'undefined') return null;\n if (!dimensions.width || !dimensions.height) return null;\n\n const image = await loadImageBlob(blob);\n const canvas = document.createElement('canvas');\n canvas.width = dimensions.width;\n canvas.height = dimensions.height;\n const context = canvas.getContext('2d');\n if (!context) return null;\n\n context.drawImage(image, 0, 0);\n\n return new Promise<Blob | null>((resolve) => {\n canvas.toBlob(resolve, 'image/webp', quality);\n });\n}\n\nfunction loadImageBlob(blob: Blob) {\n return new Promise<HTMLImageElement>((resolve, reject) => {\n if (typeof Image === 'undefined' || typeof URL === 'undefined') {\n reject(new Error('Image decoding is unavailable.'));\n return;\n }\n\n const image = new Image();\n const objectUrl = URL.createObjectURL(blob);\n image.onload = () => {\n URL.revokeObjectURL(objectUrl);\n resolve(image);\n };\n image.onerror = () => {\n URL.revokeObjectURL(objectUrl);\n reject(new Error('Image decoding failed.'));\n };\n image.src = objectUrl;\n });\n}\n\nfunction blobToDataUrl(blob: Blob) {\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (typeof reader.result === 'string') {\n resolve(reader.result);\n return;\n }\n reject(new Error('Blob encoding failed.'));\n };\n reader.onerror = () => reject(reader.error ?? new Error('Blob encoding failed.'));\n reader.readAsDataURL(blob);\n });\n}\n\nfunction normalizeClientImageMimeType(value: string | null | undefined) {\n const mimeType = value?.split(';')[0]?.trim().toLowerCase();\n if (mimeType === 'image/jpg') return 'image/jpeg';\n if (\n mimeType === 'image/jpeg' ||\n mimeType === 'image/png' ||\n mimeType === 'image/webp'\n ) {\n return mimeType;\n }\n return null;\n}\n\nasync function withTimeout<T>(promise: Promise<T>, timeoutMs: number) {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<T>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new Error('Figma client rendering timed out.')),\n timeoutMs\n );\n }),\n ]);\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n}\n\nexport function getReviewFigmaImageTargetKey(target: ReviewFigmaImageTarget) {\n return JSON.stringify(normalizeReviewFigmaImageTarget(target));\n}\n\nexport function getReviewFigmaImageMimeType(\n format: ReviewFigmaImageFormat\n) {\n if (format === 'jpg') return 'image/jpeg';\n if (format === 'png') return 'image/png';\n return 'image/webp';\n}\n\nfunction createReviewFigmaImageStoreRequest(\n endpoint: string,\n fetchOption: typeof fetch | undefined\n) {\n return async <T>(input: string, init: RequestInit = {}) => {\n const requestFetch = fetchOption ?? globalThis.fetch;\n if (!requestFetch) throw new Error('Figma image store requires fetch.');\n\n const response = await requestFetch(input, {\n ...init,\n headers: {\n 'Content-Type': 'application/json',\n ...(init.headers ?? {}),\n },\n });\n const text = await response.text();\n const body = text ? JSON.parse(text) : null;\n\n if (!response.ok) {\n const message =\n typeof body?.error === 'string'\n ? body.error\n : `Figma image store request failed: ${response.status}`;\n throw new Error(message);\n }\n\n return body as T;\n };\n}\n\nfunction normalizeReviewFigmaImageTarget(target: ReviewFigmaImageTarget) {\n if (target.type === 'figma-node') {\n return {\n type: target.type,\n projectId: target.projectId,\n fileKey: target.fileKey,\n nodeId: target.nodeId,\n };\n }\n\n return {\n type: target.type,\n projectId: target.projectId,\n pageUrl: target.pageUrl,\n slot: target.slot ?? '',\n viewport: target.viewport\n ? {\n label: target.viewport.label ?? '',\n width: target.viewport.width ?? null,\n height: target.viewport.height ?? null,\n scope: target.viewport.scope ?? '',\n }\n : null,\n };\n}\n\nexport type { ReorderReviewFigmaImagesInput };\n","import { readFile } from 'node:fs/promises';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport path from 'node:path';\nimport type {\n ReviewFigmaImage,\n ReviewFigmaImageTarget,\n AddReviewFigmaImageInput,\n} from '../figma/image.types';\nimport {\n getReviewFigmaImageTargetKey,\n type ReorderReviewFigmaImagesInput,\n} from '../figma/image.store';\nimport type { ReviewFigmaTokenEnv } from '../figma/token';\nimport { createReviewFigmaReleaseSnapshot } from '../figma/image.snapshot';\nimport {\n getReviewFigmaAssetMimeType,\n getReviewFigmaAssetStorageKeyFromPathname,\n parseReviewFigmaImageFormat,\n} from './figma-asset';\nimport type { ReviewFigmaImageStorePluginOptions } from './figma-image-store';\nimport {\n createReviewFigmaImage,\n deleteReviewFigmaImageAsset,\n isNodeError,\n listImagesForTarget,\n normalizeOptionalText,\n readReviewFigmaImageStoreFile,\n reorderReviewFigmaImages,\n updateReviewFigmaImage,\n writeReviewFigmaImageStoreFile,\n} from './figma-image-store.image';\n\ntype ReviewFigmaImageStoreResponse = {\n status: number;\n body: unknown;\n};\n\nexport async function handleReviewFigmaImageStoreRequest({\n dataFile,\n assetDir,\n assetEndpoint,\n endpoint,\n method,\n options,\n pathname,\n requestUrl,\n body,\n env,\n}: {\n dataFile: string;\n assetDir: string;\n assetEndpoint: string;\n endpoint: string;\n method: string;\n options: ReviewFigmaImageStorePluginOptions;\n env: ReviewFigmaTokenEnv;\n pathname: string;\n requestUrl: URL;\n body: unknown;\n}): Promise<ReviewFigmaImageStoreResponse> {\n if (method === 'OPTIONS') return { status: 204, body: null };\n\n if (\n (method === 'GET' || method === 'POST') &&\n pathname === `${endpoint}/snapshot`\n ) {\n const input = parseReleaseSnapshotInput(body, requestUrl, options.projectId);\n if (!input) return jsonError(400, 'valid snapshot input is required.');\n if (options.projectId && input.projectId !== options.projectId) {\n return jsonError(403, 'snapshot project is not allowed.');\n }\n if (input.targets.some((target) => !isAllowedProjectTarget(target, options.projectId))) {\n return jsonError(403, 'snapshot target project is not allowed.');\n }\n\n const data = await readReviewFigmaImageStoreFile(dataFile);\n return {\n status: 200,\n body: createReviewFigmaReleaseSnapshot({\n images: data.images,\n projectId: input.projectId,\n releaseId: input.releaseId,\n label: input.label,\n targets: input.targets.length > 0 ? input.targets : undefined,\n }),\n };\n }\n\n if (method === 'GET' && pathname === endpoint) {\n const target = parseTargetParam(requestUrl.searchParams.get('target'));\n if (!target) return jsonError(400, 'target query is required.');\n if (!isAllowedProjectTarget(target, options.projectId)) {\n return { status: 200, body: [] };\n }\n\n const data = await readReviewFigmaImageStoreFile(dataFile);\n return { status: 200, body: listImagesForTarget(data.images, target) };\n }\n\n if (method === 'POST' && pathname === endpoint) {\n const input = parseAddImageInput(body);\n if (!input) return jsonError(400, 'valid add image input is required.');\n if (!isAllowedProjectTarget(input.target, options.projectId)) {\n return jsonError(403, 'target project is not allowed.');\n }\n\n const data = await readReviewFigmaImageStoreFile(dataFile);\n const image = await createReviewFigmaImage({\n assetDir,\n assetEndpoint,\n currentImages: data.images,\n env,\n input,\n options,\n });\n data.images = [image, ...data.images];\n await writeReviewFigmaImageStoreFile(dataFile, data);\n\n return { status: 201, body: image };\n }\n\n if (method === 'PATCH' && pathname === `${endpoint}/reorder`) {\n const input = parseReorderImagesInput(body);\n if (!input) return jsonError(400, 'valid reorder input is required.');\n if (!isAllowedProjectTarget(input.target, options.projectId)) {\n return jsonError(403, 'target project is not allowed.');\n }\n\n const data = await readReviewFigmaImageStoreFile(dataFile);\n const images = reorderReviewFigmaImages(data.images, input);\n data.images = images.allImages;\n await writeReviewFigmaImageStoreFile(dataFile, data);\n\n return { status: 200, body: images.targetImages };\n }\n\n const id = getEndpointItemId(pathname, endpoint);\n if (id && method === 'PATCH') {\n const patch = parseUpdateImageInput(body);\n if (!patch) return jsonError(400, 'valid update patch is required.');\n\n const data = await readReviewFigmaImageStoreFile(dataFile);\n const result = updateReviewFigmaImage(data.images, id, patch);\n if (!result) return jsonError(404, `Figma image not found: ${id}`);\n if (!isAllowedProjectTarget(result.image.target, options.projectId)) {\n return jsonError(403, 'target project is not allowed.');\n }\n\n data.images = result.images;\n await writeReviewFigmaImageStoreFile(dataFile, data);\n\n return { status: 200, body: result.image };\n }\n\n if (id && method === 'DELETE') {\n const data = await readReviewFigmaImageStoreFile(dataFile);\n const image = data.images.find((item) => item.id === id);\n if (!image) return jsonError(404, `Figma image not found: ${id}`);\n if (!isAllowedProjectTarget(image.target, options.projectId)) {\n return jsonError(403, 'target project is not allowed.');\n }\n\n data.images = data.images.filter((item) => item.id !== id);\n await writeReviewFigmaImageStoreFile(dataFile, data);\n await deleteReviewFigmaImageAsset(assetDir, image.storageKey);\n\n return { status: 200, body: { ok: true } };\n }\n\n return jsonError(405, 'method not allowed.');\n}\n\nexport async function readJsonRequestBody(req: IncomingMessage) {\n if (req.method === 'GET' || req.method === 'DELETE') return null;\n\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);\n }\n const raw = Buffer.concat(chunks).toString('utf8').trim();\n if (!raw) return null;\n\n return JSON.parse(raw);\n}\n\nexport function sendJson(res: ServerResponse, status: number, body: unknown) {\n res.statusCode = status;\n res.setHeader('Content-Type', 'application/json; charset=utf-8');\n if (status === 204) {\n res.end();\n return;\n }\n res.end(JSON.stringify(body ?? null));\n}\n\nexport async function sendReviewFigmaAsset(\n res: ServerResponse,\n assetDir: string,\n assetEndpoint: string,\n pathname: string\n) {\n const storageKey = getReviewFigmaAssetStorageKeyFromPathname(pathname, assetEndpoint);\n if (!storageKey) {\n sendPlainText(res, 400, 'Invalid Figma image asset path.');\n return;\n }\n\n try {\n const data = await readFile(path.join(assetDir, storageKey));\n res.statusCode = 200;\n res.setHeader('Content-Type', getReviewFigmaAssetMimeType(storageKey));\n res.setHeader('Cache-Control', 'private, max-age=31536000, immutable');\n res.end(data);\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') {\n sendPlainText(res, 404, 'Figma image asset not found.');\n return;\n }\n sendPlainText(\n res,\n 500,\n error instanceof Error ? error.message : 'Figma image asset request failed.'\n );\n }\n}\n\nfunction sendPlainText(res: ServerResponse, status: number, body: string) {\n res.statusCode = status;\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\n res.end(body);\n}\n\nfunction parseTargetParam(value: string | null) {\n if (!value) return null;\n try {\n return parseReviewFigmaImageTarget(JSON.parse(value));\n } catch {\n return null;\n }\n}\n\nfunction parseAddImageInput(value: unknown): AddReviewFigmaImageInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Partial<AddReviewFigmaImageInput>;\n const target = parseReviewFigmaImageTarget(input.target);\n if (!target || typeof input.figmaUrl !== 'string') return null;\n\n return {\n target,\n figmaUrl: input.figmaUrl,\n label: typeof input.label === 'string' ? input.label : undefined,\n order: typeof input.order === 'number' ? input.order : undefined,\n imageFormat: parseReviewFigmaImageFormat(input.imageFormat),\n asset: parseAddImageAssetInput(input.asset),\n };\n}\n\nfunction parseAddImageAssetInput(\n value: unknown\n): AddReviewFigmaImageInput['asset'] {\n if (!value || typeof value !== 'object') return undefined;\n const input = value as Partial<NonNullable<AddReviewFigmaImageInput['asset']>>;\n const imageFormat = parseReviewFigmaImageFormat(input.imageFormat);\n if (\n !imageFormat ||\n typeof input.dataUrl !== 'string' ||\n typeof input.mimeType !== 'string'\n ) {\n return undefined;\n }\n\n return {\n dataUrl: input.dataUrl,\n imageFormat,\n mimeType: input.mimeType,\n byteSize: typeof input.byteSize === 'number' ? input.byteSize : undefined,\n width: typeof input.width === 'number' ? input.width : undefined,\n height: typeof input.height === 'number' ? input.height : undefined,\n };\n}\n\nfunction parseUpdateImageInput(value: unknown) {\n if (!value || typeof value !== 'object') return null;\n const input = value as { label?: unknown; order?: unknown };\n\n return {\n label: typeof input.label === 'string' ? input.label : undefined,\n order: typeof input.order === 'number' ? input.order : undefined,\n };\n}\n\nfunction parseReorderImagesInput(\n value: unknown\n): ReorderReviewFigmaImagesInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as ReorderReviewFigmaImagesInput;\n const target = parseReviewFigmaImageTarget(input.target);\n if (!target || !Array.isArray(input.imageIds)) return null;\n\n return {\n target,\n imageIds: input.imageIds.filter((id) => typeof id === 'string'),\n };\n}\n\nfunction parseReleaseSnapshotInput(\n value: unknown,\n requestUrl: URL,\n fallbackProjectId: string | undefined\n): {\n projectId: string;\n releaseId?: string;\n label?: string;\n targets: ReviewFigmaImageTarget[];\n} | null {\n const input = value && typeof value === 'object'\n ? (value as Partial<{ projectId: unknown; releaseId: unknown; label: unknown; targets: unknown }>)\n : null;\n const projectId = normalizeOptionalText(\n typeof input?.projectId === 'string'\n ? input.projectId\n : requestUrl.searchParams.get('projectId') ?? fallbackProjectId\n );\n if (!projectId) return null;\n\n return {\n projectId,\n releaseId: normalizeOptionalText(\n typeof input?.releaseId === 'string'\n ? input.releaseId\n : requestUrl.searchParams.get('releaseId')\n ),\n label: normalizeOptionalText(\n typeof input?.label === 'string'\n ? input.label\n : requestUrl.searchParams.get('label')\n ),\n targets: parseReleaseSnapshotTargets(input?.targets, requestUrl),\n };\n}\n\nfunction parseReleaseSnapshotTargets(value: unknown, requestUrl: URL) {\n const bodyTargets = Array.isArray(value)\n ? value.flatMap((target) => {\n const parsed = parseReviewFigmaImageTarget(target);\n return parsed ? [parsed] : [];\n })\n : [];\n const queryTargets = requestUrl.searchParams\n .getAll('target')\n .flatMap((target) => {\n const parsed = parseTargetParam(target);\n return parsed ? [parsed] : [];\n });\n const targetByKey = new Map(\n [...bodyTargets, ...queryTargets].map((target) => [\n getReviewFigmaImageTargetKey(target),\n target,\n ])\n );\n\n return Array.from(targetByKey.values());\n}\n\nfunction parseReviewFigmaImageTarget(\n value: unknown\n): ReviewFigmaImageTarget | null {\n if (!value || typeof value !== 'object') return null;\n const target = value as ReviewFigmaImageTarget;\n if (target.type === 'route') {\n if (\n typeof target.projectId !== 'string' ||\n typeof target.pageUrl !== 'string'\n ) {\n return null;\n }\n\n return {\n type: 'route',\n projectId: target.projectId,\n pageUrl: target.pageUrl,\n viewport:\n target.viewport && typeof target.viewport === 'object'\n ? target.viewport\n : undefined,\n slot: typeof target.slot === 'string' ? target.slot : undefined,\n };\n }\n\n if (target.type === 'figma-node') {\n if (\n typeof target.projectId !== 'string' ||\n typeof target.fileKey !== 'string' ||\n typeof target.nodeId !== 'string'\n ) {\n return null;\n }\n\n return {\n type: 'figma-node',\n projectId: target.projectId,\n fileKey: target.fileKey,\n nodeId: target.nodeId,\n };\n }\n\n return null;\n}\n\nexport function normalizeEndpoint(endpoint: string) {\n const normalized = endpoint.trim().replace(/\\/+$/, '');\n return normalized.startsWith('/') ? normalized : `/${normalized}`;\n}\n\nfunction getEndpointItemId(pathname: string, endpoint: string) {\n if (!pathname.startsWith(`${endpoint}/`)) return null;\n const value = pathname.slice(endpoint.length + 1);\n if (!value || value.includes('/')) return null;\n return decodeURIComponent(value);\n}\n\nfunction isAllowedProjectTarget(\n target: ReviewFigmaImageTarget,\n projectId: string | undefined\n) {\n return !projectId || target.projectId === projectId;\n}\n\nfunction jsonError(status: number, error: string): ReviewFigmaImageStoreResponse {\n return { status, body: { error } };\n}\n","import type {\n ReviewFigmaImage,\n ReviewFigmaImageStore,\n ReviewFigmaImageTarget,\n} from './image.types';\nimport { getReviewFigmaImageTargetKey } from './image.store';\n\nexport type ReviewFigmaImagesSnapshot = ReviewFigmaImage[];\n\nexport type ReviewFigmaReleaseSnapshot = {\n version: 1;\n projectId: string;\n releaseId?: string;\n label?: string;\n createdAt: string;\n figmaImagesSnapshot: ReviewFigmaImagesSnapshot;\n};\n\nexport type CreateReviewFigmaImagesSnapshotOptions = {\n projectId?: string;\n targets?: readonly ReviewFigmaImageTarget[];\n};\n\nexport type CreateReviewFigmaReleaseSnapshotOptions =\n CreateReviewFigmaImagesSnapshotOptions & {\n images: readonly ReviewFigmaImage[];\n projectId: string;\n releaseId?: string;\n label?: string;\n createdAt?: string;\n };\n\nexport type CollectReviewFigmaReleaseSnapshotOptions = Omit<\n CreateReviewFigmaReleaseSnapshotOptions,\n 'images'\n> & {\n store: ReviewFigmaImageStore;\n targets: readonly ReviewFigmaImageTarget[];\n};\n\nexport function createReviewFigmaImagesSnapshot(\n images: readonly ReviewFigmaImage[],\n options: CreateReviewFigmaImagesSnapshotOptions = {}\n): ReviewFigmaImagesSnapshot {\n const targetKeys = options.targets?.length\n ? new Set(options.targets.map(getReviewFigmaImageTargetKey))\n : null;\n\n return images\n .filter((image) => {\n if (options.projectId && image.projectId !== options.projectId) {\n return false;\n }\n if (\n targetKeys &&\n !targetKeys.has(getReviewFigmaImageTargetKey(image.target))\n ) {\n return false;\n }\n return true;\n })\n .map(cloneReviewFigmaImage)\n .sort(compareReviewFigmaSnapshotImages);\n}\n\nexport function createReviewFigmaReleaseSnapshot({\n images,\n projectId,\n releaseId,\n label,\n createdAt,\n targets,\n}: CreateReviewFigmaReleaseSnapshotOptions): ReviewFigmaReleaseSnapshot {\n return {\n version: 1,\n projectId,\n ...(releaseId ? { releaseId } : null),\n ...(label ? { label } : null),\n createdAt: createdAt ?? new Date().toISOString(),\n figmaImagesSnapshot: createReviewFigmaImagesSnapshot(images, {\n projectId,\n targets,\n }),\n };\n}\n\nexport async function collectReviewFigmaReleaseSnapshot({\n store,\n targets,\n ...snapshotOptions\n}: CollectReviewFigmaReleaseSnapshotOptions): Promise<ReviewFigmaReleaseSnapshot> {\n const imagesByTarget = await Promise.all(\n targets.map((target) => store.listImages(target))\n );\n\n return createReviewFigmaReleaseSnapshot({\n ...snapshotOptions,\n targets,\n images: dedupeReviewFigmaImages(imagesByTarget.flat()),\n });\n}\n\nfunction dedupeReviewFigmaImages(images: readonly ReviewFigmaImage[]) {\n return Array.from(new Map(images.map((image) => [image.id, image])).values());\n}\n\nfunction cloneReviewFigmaImage(image: ReviewFigmaImage): ReviewFigmaImage {\n return {\n ...image,\n target: cloneReviewFigmaImageTarget(image.target),\n };\n}\n\nfunction cloneReviewFigmaImageTarget(\n target: ReviewFigmaImageTarget\n): ReviewFigmaImageTarget {\n if (target.type === 'figma-node') {\n return {\n type: target.type,\n projectId: target.projectId,\n fileKey: target.fileKey,\n nodeId: target.nodeId,\n };\n }\n\n return {\n type: target.type,\n projectId: target.projectId,\n pageUrl: target.pageUrl,\n slot: target.slot,\n viewport: target.viewport\n ? {\n label: target.viewport.label,\n width: target.viewport.width,\n height: target.viewport.height,\n scope: target.viewport.scope,\n }\n : undefined,\n };\n}\n\nfunction compareReviewFigmaSnapshotImages(\n a: ReviewFigmaImage,\n b: ReviewFigmaImage\n) {\n return (\n a.projectId.localeCompare(b.projectId) ||\n getReviewFigmaImageTargetKey(a.target).localeCompare(\n getReviewFigmaImageTargetKey(b.target)\n ) ||\n a.order - b.order ||\n a.createdAt.localeCompare(b.createdAt) ||\n a.id.localeCompare(b.id)\n );\n}\n","import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type {\n ReviewFigmaImage,\n ReviewFigmaImageAssetInput,\n ReviewFigmaImageFormat,\n ReviewFigmaImageTarget,\n AddReviewFigmaImageInput,\n UpdateReviewFigmaImageInput,\n} from '../figma/image.types';\nimport {\n getReviewFigmaImageMimeType,\n getReviewFigmaImageTargetKey,\n type ReorderReviewFigmaImagesInput,\n} from '../figma/image.store';\nimport { parseReviewFigmaNodeRef } from '../figma/parse';\nimport {\n requireReviewFigmaToken,\n DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,\n type ReviewFigmaTokenEnv,\n} from '../figma/token';\nimport {\n renderReviewFigmaImage,\n type ReviewFigmaRenderFormat,\n} from '../figma/render';\nimport {\n createReviewFigmaAssetStorageKey,\n createReviewFigmaAssetUrl,\n getReviewFigmaImageFormatFromMimeType,\n getStoreRenderFormat,\n isSafeReviewFigmaAssetStorageKey,\n normalizeImageMimeType,\n} from './figma-asset';\nimport type { ReviewFigmaImageStorePluginOptions, ReviewFigmaImageAssetTransformer } from './figma-image-store';\n\nexport type ReviewFigmaImageStoreFile = {\n version: 1;\n images: ReviewFigmaImage[];\n};\n\nasync function readReviewFigmaNodeName({\n apiBaseUrl,\n enabled,\n env,\n envKey,\n fetchOption,\n fileKey,\n nodeId,\n token,\n}: {\n apiBaseUrl?: string;\n enabled?: boolean;\n env: ReviewFigmaTokenEnv;\n envKey?: string;\n fetchOption?: typeof fetch;\n fileKey: string;\n nodeId: string;\n token?: string | null;\n}) {\n const explicitToken = typeof token === 'string' ? token.trim() : token;\n const figmaToken =\n explicitToken ||\n requireReviewFigmaToken({\n token: null,\n env,\n envKey: envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,\n enabled,\n });\n const fetchNode = fetchOption ?? globalThis.fetch;\n if (!fetchNode) throw new Error('Figma node name lookup requires fetch.');\n\n type FigmaNodeResponse = {\n err?: string | null;\n nodes?: Record<string, { document?: { name?: string | null } | null } | null | undefined>;\n };\n\n const response = await fetchNode(\n createReviewFigmaNodeApiUrl({ apiBaseUrl, fileKey, nodeId }),\n {\n headers: {\n 'X-Figma-Token': figmaToken,\n },\n }\n );\n const body = (await response.json().catch(() => null)) as FigmaNodeResponse | null;\n\n if (!response.ok) {\n throw new Error(body?.err || `Figma node lookup failed with ${response.status}`);\n }\n\n const nodes = body?.nodes;\n const node = nodes?.[nodeId] ?? Object.values(nodes ?? {})[0];\n return normalizeOptionalText(node?.document?.name);\n}\n\nfunction createReviewFigmaNodeApiUrl({\n apiBaseUrl = 'https://api.figma.com',\n fileKey,\n nodeId,\n}: {\n apiBaseUrl?: string;\n fileKey: string;\n nodeId: string;\n}) {\n const url = new URL(\n `/v1/files/${encodeURIComponent(fileKey)}/nodes`,\n apiBaseUrl\n );\n url.searchParams.set('ids', nodeId);\n return url.toString();\n}\n\nexport async function createReviewFigmaImage({\n assetDir,\n assetEndpoint,\n currentImages,\n env,\n input,\n options,\n}: {\n assetDir: string;\n assetEndpoint: string;\n currentImages: ReviewFigmaImage[];\n env: ReviewFigmaTokenEnv;\n input: AddReviewFigmaImageInput;\n options: ReviewFigmaImageStorePluginOptions;\n}): Promise<ReviewFigmaImage> {\n const ref = parseReviewFigmaNodeRef(input.figmaUrl);\n if (!ref) {\n throw new Error('A Figma node copy link or fileKey->nodeId value is required.');\n }\n\n const id = createReviewFigmaImageId();\n const explicitLabel = normalizeOptionalText(input.label);\n if (input.asset) {\n const cachedAsset = await cacheReviewFigmaProvidedImageAsset({\n assetDir,\n assetEndpoint,\n id,\n asset: input.asset,\n options,\n });\n const now = new Date().toISOString();\n const order =\n typeof input.order === 'number' && Number.isFinite(input.order)\n ? input.order\n : getNextImageOrder(currentImages, input.target);\n\n return {\n id,\n projectId: input.target.projectId,\n target: input.target,\n figmaUrl: input.figmaUrl,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n imageUrl: cachedAsset.imageUrl,\n imageFormat: cachedAsset.imageFormat,\n mimeType: cachedAsset.mimeType,\n label: explicitLabel,\n order,\n storageKey: cachedAsset.storageKey,\n width: input.asset.width,\n height: input.asset.height,\n byteSize: cachedAsset.byteSize,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n const nodeLabelPromise = explicitLabel\n ? Promise.resolve(undefined)\n : readReviewFigmaNodeName({\n apiBaseUrl: options.apiBaseUrl,\n enabled: options.enabled,\n env,\n envKey: options.envKey,\n fetchOption: options.fetch,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n token: options.token,\n }).catch(() => undefined);\n const targetImageFormat = input.imageFormat ?? options.imageFormat ?? 'webp';\n const renderFormat = getStoreRenderFormat(options.renderFormat, targetImageFormat);\n const explicitToken = typeof options.token === 'string' ? options.token.trim() : options.token;\n const rendered = await renderReviewFigmaImage({\n figmaUrl: input.figmaUrl,\n format: renderFormat,\n scale: options.renderScale,\n useAbsoluteBounds: options.useAbsoluteBounds,\n apiBaseUrl: options.apiBaseUrl,\n fetch: options.fetch,\n token:\n explicitToken ||\n requireReviewFigmaToken({\n token: null,\n env,\n envKey: options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,\n enabled: options.enabled,\n }),\n });\n const cachedAsset = await cacheReviewFigmaImageAsset({\n assetDir,\n assetEndpoint,\n id,\n imageUrl: rendered.imageUrl,\n options,\n renderFormat,\n targetImageFormat,\n });\n const imageFormat =\n cachedAsset?.imageFormat ?? (renderFormat === 'jpg' ? 'jpg' : 'png');\n const now = new Date().toISOString();\n const order =\n typeof input.order === 'number' && Number.isFinite(input.order)\n ? input.order\n : getNextImageOrder(currentImages, input.target);\n const nodeLabel = await nodeLabelPromise;\n\n return {\n id,\n projectId: input.target.projectId,\n target: input.target,\n figmaUrl: input.figmaUrl,\n fileKey: rendered.fileKey,\n nodeId: rendered.nodeId,\n imageUrl: cachedAsset?.imageUrl ?? rendered.imageUrl,\n imageFormat,\n mimeType: cachedAsset?.mimeType ?? getReviewFigmaImageMimeType(imageFormat),\n label: explicitLabel ?? nodeLabel,\n order,\n storageKey: cachedAsset?.storageKey,\n byteSize: cachedAsset?.byteSize,\n createdAt: now,\n updatedAt: now,\n };\n}\n\ntype CachedReviewFigmaImageAsset = {\n imageUrl: string;\n imageFormat: ReviewFigmaImageFormat;\n mimeType: string;\n storageKey: string;\n byteSize: number;\n};\n\nasync function cacheReviewFigmaProvidedImageAsset({\n assetDir,\n assetEndpoint,\n id,\n asset,\n options,\n}: {\n assetDir: string;\n assetEndpoint: string;\n id: string;\n asset: ReviewFigmaImageAssetInput;\n options: ReviewFigmaImageStorePluginOptions;\n}): Promise<CachedReviewFigmaImageAsset> {\n const decodedAsset = decodeReviewFigmaImageAsset(asset);\n const storageKey = createReviewFigmaAssetStorageKey(\n id,\n decodedAsset.imageFormat\n );\n\n await mkdir(assetDir, { recursive: true });\n await writeFile(path.join(assetDir, storageKey), decodedAsset.data);\n\n return {\n imageUrl: createReviewFigmaAssetUrl(assetEndpoint, storageKey),\n imageFormat: decodedAsset.imageFormat,\n mimeType: decodedAsset.mimeType,\n storageKey,\n byteSize: decodedAsset.data.byteLength,\n };\n}\n\nfunction decodeReviewFigmaImageAsset(asset: ReviewFigmaImageAssetInput) {\n const mimeType = normalizeImageMimeType(asset.mimeType);\n if (!mimeType) throw new Error('Unsupported Figma image asset MIME type.');\n\n const imageFormat =\n getReviewFigmaImageFormatFromMimeType(mimeType) ?? asset.imageFormat;\n const match = /^data:([^;,]+);base64,([a-zA-Z0-9+/=\\s]+)$/.exec(\n asset.dataUrl\n );\n if (!match) throw new Error('Valid Figma image asset data URL is required.');\n\n const dataUrlMimeType = normalizeImageMimeType(match[1]);\n if (dataUrlMimeType && dataUrlMimeType !== mimeType) {\n throw new Error('Figma image asset MIME type mismatch.');\n }\n\n return {\n data: Buffer.from(match[2].replace(/\\s/g, ''), 'base64'),\n imageFormat,\n mimeType,\n };\n}\n\nasync function cacheReviewFigmaImageAsset({\n assetDir,\n assetEndpoint,\n id,\n imageUrl,\n options,\n renderFormat,\n targetImageFormat,\n}: {\n assetDir: string;\n assetEndpoint: string;\n id: string;\n imageUrl: string;\n options: ReviewFigmaImageStorePluginOptions;\n renderFormat: Extract<ReviewFigmaRenderFormat, 'png' | 'jpg'>;\n targetImageFormat: ReviewFigmaImageFormat;\n}): Promise<CachedReviewFigmaImageAsset | null> {\n if (options.cacheAssets === false) return null;\n\n const asset = await downloadReviewFigmaImageAsset({\n fetchOption: options.fetch,\n imageUrl,\n renderFormat,\n targetImageFormat,\n transformAsset: options.transformAsset,\n });\n const storageKey = createReviewFigmaAssetStorageKey(id, asset.imageFormat);\n\n await mkdir(assetDir, { recursive: true });\n await writeFile(path.join(assetDir, storageKey), asset.data);\n\n return {\n imageUrl: createReviewFigmaAssetUrl(assetEndpoint, storageKey),\n imageFormat: asset.imageFormat,\n mimeType: asset.mimeType,\n storageKey,\n byteSize: asset.data.byteLength,\n };\n}\n\nasync function downloadReviewFigmaImageAsset({\n fetchOption,\n imageUrl,\n renderFormat,\n targetImageFormat,\n transformAsset,\n}: {\n fetchOption: typeof fetch | undefined;\n imageUrl: string;\n renderFormat: Extract<ReviewFigmaRenderFormat, 'png' | 'jpg'>;\n targetImageFormat: ReviewFigmaImageFormat;\n transformAsset: ReviewFigmaImageAssetTransformer | undefined;\n}) {\n const fetchImage = fetchOption ?? globalThis.fetch;\n if (!fetchImage) throw new Error('Figma image caching requires fetch.');\n\n const response = await fetchImage(imageUrl);\n if (!response.ok) {\n throw new Error(`Figma image download failed with ${response.status}`);\n }\n\n const sourceMimeType =\n normalizeImageMimeType(response.headers.get('content-type')) ??\n getReviewFigmaImageMimeType(renderFormat === 'jpg' ? 'jpg' : 'png');\n const sourceImageFormat =\n getReviewFigmaImageFormatFromMimeType(sourceMimeType) ??\n (renderFormat === 'jpg' ? 'jpg' : 'png');\n const sourceData = new Uint8Array(await response.arrayBuffer());\n const transformed = transformAsset\n ? await transformAsset({\n data: sourceData,\n imageFormat: sourceImageFormat,\n mimeType: sourceMimeType,\n targetFormat: targetImageFormat,\n })\n : null;\n const imageFormat = transformed?.imageFormat ?? sourceImageFormat;\n const mimeType =\n normalizeImageMimeType(transformed?.mimeType) ??\n getReviewFigmaImageMimeType(imageFormat);\n const data = createBufferFromImageData(transformed?.data ?? sourceData);\n\n return { data, imageFormat, mimeType };\n}\n\nexport async function deleteReviewFigmaImageAsset(\n assetDir: string,\n storageKey: string | undefined\n) {\n if (!storageKey) return;\n if (!isSafeReviewFigmaAssetStorageKey(storageKey)) return;\n await rm(path.join(assetDir, storageKey), { force: true }).catch(() => null);\n}\n\nfunction createBufferFromImageData(data: Uint8Array | ArrayBuffer) {\n return data instanceof ArrayBuffer\n ? Buffer.from(new Uint8Array(data))\n : Buffer.from(data);\n}\n\nexport function listImagesForTarget(\n images: ReviewFigmaImage[],\n target: ReviewFigmaImageTarget\n) {\n const targetKey = getReviewFigmaImageTargetKey(target);\n return images\n .filter((image) => getReviewFigmaImageTargetKey(image.target) === targetKey)\n .sort(compareReviewFigmaImages);\n}\n\nexport function reorderReviewFigmaImages(\n images: ReviewFigmaImage[],\n input: ReorderReviewFigmaImagesInput\n) {\n const targetKey = getReviewFigmaImageTargetKey(input.target);\n const orderById = new Map(input.imageIds.map((id, index) => [id, index]));\n const targetImages = listImagesForTarget(images, input.target);\n const nextTargetImages = targetImages\n .map((image) => ({\n ...image,\n order: orderById.get(image.id) ?? input.imageIds.length + image.order,\n updatedAt: orderById.has(image.id) ? new Date().toISOString() : image.updatedAt,\n }))\n .sort(compareReviewFigmaImages);\n const nextTargetImageById = new Map(\n nextTargetImages.map((image, index) => [image.id, { ...image, order: index }])\n );\n const allImages = images.map((image) =>\n getReviewFigmaImageTargetKey(image.target) === targetKey\n ? nextTargetImageById.get(image.id) ?? image\n : image\n );\n\n return {\n allImages,\n targetImages: listImagesForTarget(allImages, input.target),\n };\n}\n\nexport function updateReviewFigmaImage(\n images: ReviewFigmaImage[],\n id: string,\n patch: UpdateReviewFigmaImageInput\n) {\n const index = images.findIndex((image) => image.id === id);\n if (index < 0) return null;\n\n const nextImage: ReviewFigmaImage = {\n ...images[index],\n label:\n patch.label === undefined\n ? images[index].label\n : normalizeOptionalText(patch.label),\n order:\n typeof patch.order === 'number' && Number.isFinite(patch.order)\n ? patch.order\n : images[index].order,\n updatedAt: new Date().toISOString(),\n };\n const nextImages = [...images];\n nextImages[index] = nextImage;\n\n return { image: nextImage, images: nextImages };\n}\n\nexport async function readReviewFigmaImageStoreFile(\n dataFile: string\n): Promise<ReviewFigmaImageStoreFile> {\n try {\n const raw = await readFile(dataFile, 'utf8');\n const parsed = JSON.parse(raw) as Partial<ReviewFigmaImageStoreFile>;\n\n return {\n version: 1,\n images: Array.isArray(parsed.images)\n ? parsed.images.flatMap((image) => (isReviewFigmaImage(image) ? [image] : []))\n : [],\n };\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') {\n return { version: 1, images: [] };\n }\n throw error;\n }\n}\n\nexport async function writeReviewFigmaImageStoreFile(\n dataFile: string,\n data: ReviewFigmaImageStoreFile\n) {\n await mkdir(path.dirname(dataFile), { recursive: true });\n await writeFile(\n dataFile,\n `${JSON.stringify({ version: 1, images: data.images }, null, 2)}\\n`,\n 'utf8'\n );\n}\n\nfunction getNextImageOrder(\n images: ReviewFigmaImage[],\n target: ReviewFigmaImageTarget\n) {\n const targetImages = listImagesForTarget(images, target);\n return targetImages.length\n ? Math.max(...targetImages.map((image) => image.order)) + 1\n : 0;\n}\n\nfunction compareReviewFigmaImages(a: ReviewFigmaImage, b: ReviewFigmaImage) {\n return a.order - b.order || a.createdAt.localeCompare(b.createdAt);\n}\n\nfunction createReviewFigmaImageId() {\n return `figma_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction isReviewFigmaImage(value: unknown): value is ReviewFigmaImage {\n if (!value || typeof value !== 'object') return false;\n const image = value as ReviewFigmaImage;\n\n return (\n typeof image.id === 'string' &&\n typeof image.projectId === 'string' &&\n typeof image.figmaUrl === 'string' &&\n typeof image.fileKey === 'string' &&\n typeof image.nodeId === 'string' &&\n typeof image.imageUrl === 'string' &&\n typeof image.order === 'number' &&\n typeof image.createdAt === 'string' &&\n typeof image.updatedAt === 'string'\n );\n}\n\nexport function isNodeError(error: unknown): error is { code?: string } {\n if (!error || typeof error !== 'object') return false;\n return 'code' in error;\n}\n\nexport function normalizeOptionalText(value: string | null | undefined) {\n if (typeof value !== 'string') return undefined;\n return value.trim() || undefined;\n}\n"],"mappings":"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","names":["import_node_path","import_promises","import_node_path","cachedAsset","now","order","path","path","path"]}
1
+ {"version":3,"sources":["../src/vite.ts","../src/vite/figma-image-store.ts","../src/vite/figma-asset.ts","../src/figma/parse.ts","../src/figma/token.ts","../src/figma/render.ts","../src/figma/image.store.ts","../src/vite/figma-image-store.server.ts","../src/figma/image.snapshot.ts","../src/vite/figma-image-store.image.ts"],"sourcesContent":["import { type Plugin, type ResolvedConfig } from 'vite';\n\nexport * from './vite/figma-image-store';\n\ntype SourceLocatorPattern = string | RegExp;\n\nexport interface ReviewSourceLocatorOptions {\n enabled?: boolean;\n root?: string;\n include?: readonly SourceLocatorPattern[];\n exclude?: readonly SourceLocatorPattern[];\n filePath?: 'relative' | 'absolute';\n line?: boolean;\n column?: boolean;\n attributePrefix?: string;\n}\n\ntype RuntimeMatcher =\n | { type: 'path'; value: string }\n | { type: 'regex'; value: string; flags: string };\n\ntype RuntimeOptions = {\n enabled: boolean;\n root: string;\n include: RuntimeMatcher[];\n exclude: RuntimeMatcher[];\n filePath: 'relative' | 'absolute';\n line: boolean;\n column: boolean;\n fileAttribute: string;\n lineAttribute: string;\n columnAttribute: string;\n};\n\nconst VIRTUAL_JSX_DEV_RUNTIME_ID =\n '\\0@designfever/web-review-kit/source-locator/jsx-dev-runtime';\n\nconst REVIEW_SOURCE_ENV_DEFINE_KEYS = [\n ['__DF_WRK_REVIEW_SOURCE_ROOT__', 'VITE_REVIEW_SOURCE_ROOT'],\n ['__DF_WRK_REVIEW_SOURCE_EDITOR__', 'VITE_REVIEW_SOURCE_EDITOR'],\n [\n '__DF_WRK_REVIEW_SOURCE_URL_TEMPLATE__',\n 'VITE_REVIEW_SOURCE_URL_TEMPLATE',\n ],\n] as const;\n\ntype ReviewSourceEnvReplacements = Record<string, string>;\n\nconst createReviewSourceEnvReplacements = (\n env: ResolvedConfig['env'] = {}\n): ReviewSourceEnvReplacements => {\n return Object.fromEntries(\n REVIEW_SOURCE_ENV_DEFINE_KEYS.map(([defineKey, envKey]) => [\n defineKey,\n JSON.stringify(env[envKey] ?? ''),\n ])\n );\n};\n\nconst injectReviewSourceEnv = (\n code: string,\n replacements: ReviewSourceEnvReplacements\n) => {\n let nextCode = code;\n for (const [defineKey, value] of Object.entries(replacements)) {\n nextCode = nextCode\n .split(`typeof ${defineKey}`)\n .join(`typeof ${value}`)\n .split(`: ${defineKey}`)\n .join(`: ${value}`);\n }\n\n return nextCode === code ? null : nextCode;\n};\n\nexport const reviewSourceLocator = (\n options: ReviewSourceLocatorOptions = {}\n): Plugin => {\n let runtimeOptions = createRuntimeOptions(options);\n let sourceEnvReplacements = createReviewSourceEnvReplacements();\n\n return {\n name: 'df-web-review-kit-source-locator',\n enforce: 'pre',\n configResolved(config) {\n runtimeOptions = createRuntimeOptions(options, config);\n sourceEnvReplacements = createReviewSourceEnvReplacements(config.env);\n },\n resolveId(id, importer) {\n if (!runtimeOptions.enabled) return null;\n if (id !== 'react/jsx-dev-runtime') return null;\n if (importer === VIRTUAL_JSX_DEV_RUNTIME_ID) return null;\n\n return VIRTUAL_JSX_DEV_RUNTIME_ID;\n },\n load(id) {\n if (id !== VIRTUAL_JSX_DEV_RUNTIME_ID) return null;\n return createJsxDevRuntime(runtimeOptions);\n },\n transform(code) {\n const injectedCode = injectReviewSourceEnv(code, sourceEnvReplacements);\n return injectedCode ? { code: injectedCode, map: null } : null;\n },\n };\n};\n\nexport interface ReviewDataLocatorOptions {\n enabled?: boolean;\n root?: string;\n include?: readonly SourceLocatorPattern[];\n exclude?: readonly SourceLocatorPattern[];\n filePath?: 'relative' | 'absolute';\n /** 매칭할 component 이름 패턴. 기본은 `Section`으로 시작하는 이름. */\n componentPattern?: RegExp;\n fileAttribute?: string;\n lineAttribute?: string;\n}\n\n/**\n * page data 파일의 section 객체(`component: 'SectionXxx'`)에 출처 파일/라인을\n * `__wrkDataFile`/`__wrkDataLine` prop 으로 주입한다. 라인 보존을 위해 같은 줄에만 삽입한다.\n */\nexport const reviewDataLocator = (\n options: ReviewDataLocatorOptions = {}\n): Plugin => {\n let root = normalizePath(options.root ?? '');\n let enabled = options.enabled ?? false;\n let sourceEnvReplacements = createReviewSourceEnvReplacements();\n const include = (options.include ?? []).map(createRuntimeMatcher);\n const exclude = (options.exclude ?? ['node_modules', 'dist']).map(\n createRuntimeMatcher\n );\n const componentPattern = options.componentPattern ?? /Section[A-Za-z0-9_]*/;\n const fileKey = options.fileAttribute ?? '__wrkDataFile';\n const lineKey = options.lineAttribute ?? '__wrkDataLine';\n\n const componentSource = `(^|[\\\\n,{(\\\\[]\\\\s*)(component:\\\\s*)(['\"\\`])(${componentPattern.source})\\\\3`;\n\n return {\n name: 'df-web-review-kit-data-locator',\n enforce: 'pre',\n configResolved(config) {\n root = normalizePath(options.root ?? config.root ?? '');\n enabled = options.enabled ?? config.command === 'serve';\n sourceEnvReplacements = createReviewSourceEnvReplacements(config.env);\n },\n transform(code, id) {\n const envInjectedCode = injectReviewSourceEnv(\n code,\n sourceEnvReplacements\n );\n const inputCode = envInjectedCode ?? code;\n if (!enabled) return null;\n const file = normalizePath(id.split('?')[0]);\n const relativeFile =\n root && file.startsWith(root + '/') ? file.slice(root.length + 1) : file;\n if (\n include.length > 0 &&\n !include.some((m) => matchesPath(m, file, relativeFile))\n )\n return envInjectedCode ? { code: envInjectedCode, map: null } : null;\n if (exclude.some((m) => matchesPath(m, file, relativeFile))) {\n return envInjectedCode ? { code: envInjectedCode, map: null } : null;\n }\n\n const sourceFile =\n (options.filePath ?? 'relative') === 'absolute' ? file : relativeFile;\n const regex = new RegExp(componentSource, 'g');\n let changed = false;\n const out = inputCode.replace(\n regex,\n (\n _match,\n pre: string,\n comp: string,\n quote: string,\n name: string,\n offset: number\n ) => {\n const line = inputCode\n .slice(0, offset + pre.length)\n .split('\\n').length;\n changed = true;\n return `${pre}${JSON.stringify(fileKey)}: ${JSON.stringify(sourceFile)}, ${JSON.stringify(lineKey)}: ${line}, ${comp}${quote}${name}${quote}`;\n }\n );\n\n return changed || envInjectedCode ? { code: out, map: null } : null;\n },\n };\n};\n\nfunction matchesPath(\n matcher: RuntimeMatcher,\n absoluteFile: string,\n relativeFile: string\n) {\n if (matcher.type === 'regex') {\n const regex = new RegExp(matcher.value, matcher.flags);\n return regex.test(absoluteFile) || regex.test(relativeFile);\n }\n const target = matcher.value.startsWith('/') ? absoluteFile : relativeFile;\n return target === matcher.value || target.startsWith(matcher.value + '/') || target.includes('/' + matcher.value);\n}\n\nfunction createRuntimeOptions(\n options: ReviewSourceLocatorOptions,\n config?: ResolvedConfig\n): RuntimeOptions {\n const attributePrefix = (options.attributePrefix ?? 'data-wrk-source').replace(\n /-+$/,\n ''\n );\n const root = normalizePath(options.root ?? config?.root ?? '');\n const enabled = options.enabled ?? (config?.command === 'serve');\n\n return {\n enabled,\n root,\n include: (options.include ?? []).map(createRuntimeMatcher),\n exclude: (options.exclude ?? ['node_modules', 'dist']).map(\n createRuntimeMatcher\n ),\n filePath: options.filePath ?? 'relative',\n line: options.line ?? true,\n column: options.column ?? true,\n fileAttribute: `${attributePrefix}-file`,\n lineAttribute: `${attributePrefix}-line`,\n columnAttribute: `${attributePrefix}-column`,\n };\n}\n\nfunction createRuntimeMatcher(pattern: SourceLocatorPattern): RuntimeMatcher {\n if (pattern instanceof RegExp) {\n return { type: 'regex', value: pattern.source, flags: pattern.flags };\n }\n\n return { type: 'path', value: normalizePath(pattern).replace(/^\\.\\//, '') };\n}\n\nfunction normalizePath(value: string) {\n return value.replace(/\\\\/g, '/').replace(/\\/+$/, '');\n}\n\nfunction createJsxDevRuntime(options: RuntimeOptions) {\n return `\nimport { Fragment, jsxDEV as baseJsxDEV } from 'react/jsx-dev-runtime';\n\nconst OPTIONS = ${JSON.stringify(options)};\n\nexport { Fragment };\n\nexport function jsxDEV(type, props, key, isStaticChildren, source, self) {\n return baseJsxDEV(\n type,\n injectSourceProps(type, props, source),\n key,\n isStaticChildren,\n source,\n self\n );\n}\n\nfunction injectSourceProps(type, props, source) {\n if (typeof type !== 'string') return props;\n if (!source || typeof source.fileName !== 'string') return props;\n\n const sourceFile = getSourceFile(source.fileName);\n if (!sourceFile) return props;\n\n const nextProps = props ? { ...props } : {};\n if (nextProps[OPTIONS.fileAttribute] == null) {\n nextProps[OPTIONS.fileAttribute] = sourceFile;\n }\n if (OPTIONS.line && source.lineNumber != null && nextProps[OPTIONS.lineAttribute] == null) {\n nextProps[OPTIONS.lineAttribute] = String(source.lineNumber);\n }\n if (OPTIONS.column && source.columnNumber != null && nextProps[OPTIONS.columnAttribute] == null) {\n nextProps[OPTIONS.columnAttribute] = String(source.columnNumber);\n }\n\n return nextProps;\n}\n\nfunction getSourceFile(fileName) {\n const absoluteFile = normalizePath(fileName);\n const relativeFile = getRelativeFile(absoluteFile);\n\n if (OPTIONS.include.length > 0 && !matchesAny(OPTIONS.include, absoluteFile, relativeFile)) {\n return null;\n }\n if (matchesAny(OPTIONS.exclude, absoluteFile, relativeFile)) return null;\n\n return OPTIONS.filePath === 'absolute' ? absoluteFile : relativeFile;\n}\n\nfunction getRelativeFile(absoluteFile) {\n if (!OPTIONS.root) return absoluteFile;\n if (absoluteFile === OPTIONS.root) return '';\n if (absoluteFile.startsWith(OPTIONS.root + '/')) {\n return absoluteFile.slice(OPTIONS.root.length + 1);\n }\n\n return absoluteFile;\n}\n\nfunction matchesAny(patterns, absoluteFile, relativeFile) {\n return patterns.some((pattern) =>\n matchesPattern(pattern, absoluteFile, relativeFile)\n );\n}\n\nfunction matchesPattern(pattern, absoluteFile, relativeFile) {\n if (pattern.type === 'regex') {\n const regex = new RegExp(pattern.value, pattern.flags);\n return regex.test(absoluteFile) || regex.test(relativeFile);\n }\n\n const value = pattern.value;\n const target = isAbsolutePattern(value) ? absoluteFile : relativeFile;\n if (!value.includes('*')) {\n return target === value || target.startsWith(value + '/');\n }\n\n return globToRegExp(value).test(target);\n}\n\nfunction isAbsolutePattern(value) {\n return value.startsWith('/') || /^[a-zA-Z]:\\\\//.test(value);\n}\n\nfunction globToRegExp(value) {\n const source = escapeRegExp(value)\n .replace(/\\\\\\\\\\\\*\\\\\\\\\\\\*/g, '.*')\n .replace(/\\\\\\\\\\\\*/g, '[^/]*');\n\n return new RegExp('^' + source + '$');\n}\n\nfunction escapeRegExp(value) {\n return value.replace(/[|\\\\\\\\{}()[\\\\]^$+*?.]/g, '\\\\\\\\$&');\n}\n\nfunction normalizePath(value) {\n return value.replace(/\\\\\\\\/g, '/').replace(/\\\\/+$/, '');\n}\n`;\n}\n","import path from 'node:path';\nimport { loadEnv, type Plugin } from 'vite';\nimport type { ReviewFigmaImageFormat } from '../figma/image.types';\nimport {\n DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT,\n} from '../figma/image.store';\nimport {\n DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,\n readReviewFigmaToken,\n requireReviewFigmaToken,\n type ReviewFigmaTokenEnv,\n} from '../figma/token';\nimport {\n renderReviewFigmaImage,\n type ReviewFigmaImageRenderOptions,\n type ReviewFigmaRenderFormat,\n} from '../figma/render';\nimport {\n handleReviewFigmaImageStoreRequest,\n normalizeEndpoint,\n readJsonRequestBody,\n sendJson,\n sendReviewFigmaAsset,\n} from './figma-image-store.server';\n\nexport interface ReviewFigmaImageStorePluginOptions\n extends ReviewFigmaServerTokenOptions {\n enabled?: boolean;\n projectId?: string;\n endpoint?: string;\n dataFile?: string;\n assetDir?: string;\n assetEndpoint?: string;\n cacheAssets?: boolean;\n imageFormat?: ReviewFigmaImageFormat;\n renderFormat?: ReviewFigmaRenderFormat;\n renderScale?: number;\n useAbsoluteBounds?: boolean;\n apiBaseUrl?: string;\n fetch?: typeof fetch;\n transformAsset?: ReviewFigmaImageAssetTransformer;\n}\n\nexport interface ReviewFigmaServerTokenOptions {\n token?: string | null;\n env?: ReviewFigmaTokenEnv;\n envKey?: string;\n enabled?: boolean;\n}\n\nexport type ReviewFigmaServerImageRenderOptions =\n Omit<ReviewFigmaImageRenderOptions, 'token'> &\n ReviewFigmaServerTokenOptions;\n\nexport type ReviewFigmaImageAssetTransformInput = {\n data: Uint8Array;\n imageFormat: ReviewFigmaImageFormat;\n mimeType: string;\n targetFormat: ReviewFigmaImageFormat;\n};\n\nexport type ReviewFigmaImageAssetTransformResult = {\n data: Uint8Array | ArrayBuffer;\n imageFormat: ReviewFigmaImageFormat;\n mimeType?: string;\n};\n\nexport type ReviewFigmaImageAssetTransformer = (\n input: ReviewFigmaImageAssetTransformInput\n) =>\n | ReviewFigmaImageAssetTransformResult\n | null\n | undefined\n | Promise<ReviewFigmaImageAssetTransformResult | null | undefined>;\n\nexport {\n createReviewFigmaImageApiUrl,\n renderReviewFigmaImage,\n} from '../figma/render';\nexport {\n collectReviewFigmaReleaseSnapshot,\n createReviewFigmaImagesSnapshot,\n createReviewFigmaReleaseSnapshot,\n} from '../figma/image.snapshot';\nexport type {\n ReviewFigmaImageRenderOptions,\n ReviewFigmaRenderFormat,\n ReviewFigmaRenderedImage,\n} from '../figma/render';\nexport type {\n CollectReviewFigmaReleaseSnapshotOptions,\n CreateReviewFigmaImagesSnapshotOptions,\n CreateReviewFigmaReleaseSnapshotOptions,\n ReviewFigmaImagesSnapshot,\n ReviewFigmaReleaseSnapshot,\n} from '../figma/image.snapshot';\n\nexport const readReviewFigmaServerToken = (\n options: ReviewFigmaServerTokenOptions = {}\n) =>\n readReviewFigmaToken({\n token: options.token,\n env: options.env ?? getServerEnv(),\n envKey: options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,\n enabled: options.enabled,\n });\n\nexport const requireReviewFigmaServerToken = (\n options: ReviewFigmaServerTokenOptions = {}\n) =>\n requireReviewFigmaToken({\n token: options.token,\n env: options.env ?? getServerEnv(),\n envKey: options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,\n enabled: options.enabled,\n });\n\nexport const renderReviewFigmaServerImage = (\n options: ReviewFigmaServerImageRenderOptions\n) => {\n const { token, env, envKey, enabled, ...renderOptions } = options;\n const explicitToken = typeof token === 'string' ? token.trim() : token;\n\n return renderReviewFigmaImage({\n ...renderOptions,\n token:\n explicitToken ||\n requireReviewFigmaServerToken({ env, envKey, enabled }),\n });\n};\n\nexport const reviewFigmaImageStore = (\n options: ReviewFigmaImageStorePluginOptions = {}\n): Plugin => {\n let root = '';\n let dataFile = '';\n let assetDir = '';\n let env: ReviewFigmaTokenEnv = {};\n const enabled = options.enabled ?? true;\n const endpoint = normalizeEndpoint(\n options.endpoint ?? DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT\n );\n const assetEndpoint = normalizeEndpoint(\n options.assetEndpoint ?? `${endpoint}/assets`\n );\n\n return {\n name: 'df-web-review-kit-figma-image-store',\n apply: 'serve',\n configResolved(config) {\n root = config.root;\n dataFile = path.resolve(\n root,\n options.dataFile ?? '.df-review/figma-images.json'\n );\n assetDir = options.assetDir\n ? path.resolve(root, options.assetDir)\n : path.join(path.dirname(dataFile), 'figma-assets');\n env = {\n ...loadEnv(config.mode, config.envDir, ''),\n ...getServerEnv(),\n ...(options.env ?? {}),\n };\n },\n configureServer(server) {\n if (!enabled) return;\n\n server.middlewares.use(async (req, res, next) => {\n const requestUrl = new URL(req.url ?? '/', 'http://localhost');\n const pathname = requestUrl.pathname;\n if (pathname.startsWith(`${assetEndpoint}/`)) {\n await sendReviewFigmaAsset(res, assetDir, assetEndpoint, pathname);\n return;\n }\n if (pathname !== endpoint && !pathname.startsWith(`${endpoint}/`)) {\n next();\n return;\n }\n\n try {\n const response = await handleReviewFigmaImageStoreRequest({\n dataFile,\n assetDir,\n assetEndpoint,\n endpoint,\n options,\n env,\n pathname,\n requestUrl,\n method: req.method ?? 'GET',\n body: await readJsonRequestBody(req),\n requestToken: readRequestFigmaToken(req),\n });\n\n sendJson(res, response.status, response.body);\n } catch (error) {\n sendJson(res, 500, {\n error:\n error instanceof Error\n ? error.message\n : 'Figma image store request failed.',\n });\n }\n });\n },\n };\n};\n\nfunction readRequestFigmaToken(req: { headers?: Record<string, unknown> }) {\n const value = req.headers?.['x-figma-token'];\n const token = Array.isArray(value) ? value[0] : value;\n\n return typeof token === 'string' ? token.trim() || null : null;\n}\n\nfunction getServerEnv(): ReviewFigmaTokenEnv {\n const runtime = globalThis as typeof globalThis & {\n process?: {\n env?: ReviewFigmaTokenEnv;\n };\n };\n\n return runtime.process?.env ?? {};\n}\n","// Pure helpers for Figma image asset format conversion and storage-key handling.\n// Extracted from src/vite.ts to keep the plugin entry focused on request flow.\nimport type { ReviewFigmaImageFormat } from '../figma/image.types';\nimport type { ReviewFigmaRenderFormat } from '../figma/render';\n\nexport function parseReviewFigmaImageFormat(value: unknown) {\n return value === 'webp' || value === 'png' || value === 'jpg'\n ? value\n : undefined;\n}\n\nexport function getStoreRenderFormat(\n renderFormat: ReviewFigmaRenderFormat | undefined,\n imageFormat: ReviewFigmaImageFormat | undefined\n): Extract<ReviewFigmaRenderFormat, 'png' | 'jpg'> {\n if (renderFormat === 'jpg' || renderFormat === 'png') return renderFormat;\n if (imageFormat === 'jpg') return 'jpg';\n return 'png';\n}\n\nexport function getReviewFigmaImageFormatFromMimeType(\n mimeType: string\n): ReviewFigmaImageFormat | null {\n if (mimeType === 'image/webp') return 'webp';\n if (mimeType === 'image/png') return 'png';\n if (mimeType === 'image/jpeg') return 'jpg';\n return null;\n}\n\nexport function normalizeImageMimeType(value: string | null | undefined) {\n const mimeType = value?.split(';')[0]?.trim().toLowerCase();\n if (mimeType === 'image/jpg') return 'image/jpeg';\n if (\n mimeType === 'image/jpeg' ||\n mimeType === 'image/png' ||\n mimeType === 'image/webp'\n ) {\n return mimeType;\n }\n return null;\n}\n\nexport function createReviewFigmaAssetStorageKey(\n id: string,\n imageFormat: ReviewFigmaImageFormat\n) {\n return `${id}.${getReviewFigmaAssetExtension(imageFormat)}`;\n}\n\nexport function createReviewFigmaAssetUrl(\n assetEndpoint: string,\n storageKey: string\n) {\n return `${assetEndpoint}/${encodeURIComponent(storageKey)}`;\n}\n\nexport function getReviewFigmaAssetStorageKeyFromPathname(\n pathname: string,\n assetEndpoint: string\n) {\n try {\n const storageKey = decodeURIComponent(\n pathname.slice(assetEndpoint.length + 1)\n );\n return isSafeReviewFigmaAssetStorageKey(storageKey) ? storageKey : null;\n } catch {\n return null;\n }\n}\n\nexport function isSafeReviewFigmaAssetStorageKey(value: string) {\n return /^figma_[a-z0-9_]+\\.(webp|png|jpg)$/.test(value);\n}\n\nfunction getReviewFigmaAssetExtension(format: ReviewFigmaImageFormat) {\n return format === 'jpg' ? 'jpg' : format;\n}\n\nexport function getReviewFigmaAssetMimeType(storageKey: string) {\n if (storageKey.endsWith('.jpg')) return 'image/jpeg';\n if (storageKey.endsWith('.webp')) return 'image/webp';\n return 'image/png';\n}\n","export type ReviewFigmaNodeRef = {\n fileKey: string;\n nodeId: string;\n sourceUrl?: string;\n};\n\nexport const FIGMA_NODE_REF_SEPARATOR = '->';\n\nexport function parseReviewFigmaNodeRef(\n value: string | ReviewFigmaNodeRef\n): ReviewFigmaNodeRef | null {\n if (typeof value !== 'string') return normalizeReviewFigmaNodeRef(value);\n\n const input = value.trim();\n if (!input) return null;\n\n return parseReviewFigmaNodeRefValue(input) ?? parseReviewFigmaUrl(input);\n}\n\nexport function requireReviewFigmaNodeRef(\n value: string | ReviewFigmaNodeRef\n): ReviewFigmaNodeRef {\n const ref = parseReviewFigmaNodeRef(value);\n if (!ref) {\n throw new Error('A Figma node link or fileKey->nodeId value is required.');\n }\n return ref;\n}\n\nexport function createReviewFigmaNodeValue(ref: ReviewFigmaNodeRef) {\n return `${ref.fileKey}${FIGMA_NODE_REF_SEPARATOR}${ref.nodeId}`;\n}\n\nexport function createReviewFigmaFrameUrl(\n value: string | ReviewFigmaNodeRef\n) {\n const ref = parseReviewFigmaNodeRef(value);\n if (!ref) return null;\n\n return `https://www.figma.com/design/${encodeURIComponent(\n ref.fileKey\n )}?node-id=${encodeURIComponent(ref.nodeId.replace(/:/g, '-'))}`;\n}\n\nfunction parseReviewFigmaNodeRefValue(value: string) {\n const [fileKey, nodeId, extra] = value\n .split(FIGMA_NODE_REF_SEPARATOR)\n .map((part) => part.trim());\n\n if (!fileKey || !nodeId || extra !== undefined) return null;\n\n return normalizeReviewFigmaNodeRef({ fileKey, nodeId });\n}\n\nfunction parseReviewFigmaUrl(value: string) {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return null;\n }\n\n const fileKey = getFigmaFileKey(url);\n const nodeId = normalizeFigmaNodeId(url.searchParams.get('node-id'));\n if (!fileKey || !nodeId) return null;\n\n return { fileKey, nodeId, sourceUrl: value };\n}\n\nfunction getFigmaFileKey(url: URL) {\n if (!/(^|\\.)figma\\.com$/i.test(url.hostname)) return null;\n\n const parts = url.pathname.split('/').filter(Boolean);\n const fileKeyIndex = parts.findIndex((part) =>\n ['design', 'file', 'proto', 'board'].includes(part)\n );\n const fileKey = fileKeyIndex >= 0 ? parts[fileKeyIndex + 1] : null;\n\n return normalizeFigmaFileKey(fileKey);\n}\n\nfunction normalizeReviewFigmaNodeRef(\n value: ReviewFigmaNodeRef | null | undefined\n) {\n const fileKey = normalizeFigmaFileKey(value?.fileKey);\n const nodeId = normalizeFigmaNodeId(value?.nodeId);\n if (!fileKey || !nodeId) return null;\n const sourceUrl = normalizeOptionalString(value?.sourceUrl);\n\n return sourceUrl ? { fileKey, nodeId, sourceUrl } : { fileKey, nodeId };\n}\n\nfunction normalizeFigmaFileKey(value: string | null | undefined) {\n return normalizeOptionalString(value)?.replace(/[?#].*$/, '') ?? null;\n}\n\nfunction normalizeFigmaNodeId(value: string | null | undefined) {\n const nodeId = normalizeOptionalString(value);\n if (!nodeId) return null;\n if (nodeId.includes(':')) return nodeId;\n return nodeId.replace(/-/g, ':');\n}\n\nfunction normalizeOptionalString(value: string | null | undefined) {\n if (typeof value !== 'string') return null;\n return value.trim() || null;\n}\n","export const DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY = 'FIGMA_TOKEN';\nexport const REVIEW_FIGMA_TOKEN_MISSING_CODE = 'DFWR_FIGMA_TOKEN_MISSING';\n\nexport type ReviewFigmaTokenEnv = Record<\n string,\n string | null | undefined\n>;\n\nexport type ReviewFigmaTokenOptions = {\n token?: string | null;\n env?: ReviewFigmaTokenEnv;\n envKey?: string;\n enabled?: boolean;\n};\n\nexport class ReviewFigmaTokenError extends Error {\n readonly code = REVIEW_FIGMA_TOKEN_MISSING_CODE;\n readonly envKey: string;\n\n constructor(envKey = DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY) {\n super(\n `Figma image rendering requires server env ${envKey}. Set ${envKey} in the dev/server environment; do not expose it as VITE_${envKey}.`\n );\n this.name = 'ReviewFigmaTokenError';\n this.envKey = envKey;\n }\n}\n\nexport function readReviewFigmaToken(\n options: ReviewFigmaTokenOptions = {}\n): string | null {\n if (options.enabled === false) return null;\n\n const envKey = options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;\n return normalizeReviewFigmaToken(options.token ?? options.env?.[envKey]);\n}\n\nexport function requireReviewFigmaToken(\n options: ReviewFigmaTokenOptions = {}\n): string {\n const envKey = options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;\n const token = readReviewFigmaToken(options);\n\n if (!token) throw new ReviewFigmaTokenError(envKey);\n return token;\n}\n\nexport function isReviewFigmaTokenError(\n error: unknown\n): error is ReviewFigmaTokenError {\n if (!error || typeof error !== 'object') return false;\n\n return (\n error instanceof ReviewFigmaTokenError ||\n ('code' in error && error.code === REVIEW_FIGMA_TOKEN_MISSING_CODE)\n );\n}\n\nfunction normalizeReviewFigmaToken(value: string | null | undefined) {\n if (typeof value !== 'string') return null;\n return value.trim() || null;\n}\n","import {\n requireReviewFigmaNodeRef,\n type ReviewFigmaNodeRef,\n} from './parse';\nimport { requireReviewFigmaToken } from './token';\n\nexport type ReviewFigmaRenderFormat = 'png' | 'jpg' | 'svg' | 'pdf';\n\nexport type ReviewFigmaImageRenderOptions = {\n figmaUrl: string | ReviewFigmaNodeRef;\n token?: string | null;\n format?: ReviewFigmaRenderFormat;\n scale?: number;\n useAbsoluteBounds?: boolean;\n apiBaseUrl?: string;\n fetch?: typeof fetch;\n signal?: AbortSignal;\n};\n\nexport type ReviewFigmaRenderedImage = {\n fileKey: string;\n nodeId: string;\n figmaUrl?: string;\n imageUrl: string;\n renderFormat: ReviewFigmaRenderFormat;\n};\n\ntype FigmaImageResponse = {\n err?: string | null;\n images?: Record<string, string | null | undefined>;\n};\n\nconst DEFAULT_FIGMA_API_BASE_URL = 'https://api.figma.com';\n\nexport async function renderReviewFigmaImage(\n options: ReviewFigmaImageRenderOptions\n): Promise<ReviewFigmaRenderedImage> {\n const token = requireReviewFigmaToken({ token: options.token });\n const ref = requireReviewFigmaNodeRef(options.figmaUrl);\n const renderFormat = options.format ?? 'png';\n const requestUrl = createReviewFigmaImageApiUrl({\n apiBaseUrl: options.apiBaseUrl,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n format: renderFormat,\n scale: options.scale,\n useAbsoluteBounds: options.useAbsoluteBounds,\n });\n const fetchImage = options.fetch ?? globalThis.fetch;\n if (!fetchImage) throw new Error('Figma image rendering requires fetch.');\n\n const response = await fetchImage(requestUrl, {\n headers: {\n 'X-Figma-Token': token,\n },\n signal: options.signal,\n });\n const body = (await response.json().catch(() => null)) as\n | FigmaImageResponse\n | null;\n\n if (!response.ok) {\n throw new Error(\n body?.err || `Figma image render failed with ${response.status}`\n );\n }\n\n const imageUrl = body?.images?.[ref.nodeId];\n if (!imageUrl) {\n throw new Error(`Figma image render returned no URL for ${ref.nodeId}.`);\n }\n\n return {\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n figmaUrl: typeof options.figmaUrl === 'string'\n ? options.figmaUrl\n : options.figmaUrl.sourceUrl,\n imageUrl,\n renderFormat,\n };\n}\n\nexport function createReviewFigmaImageApiUrl({\n apiBaseUrl = DEFAULT_FIGMA_API_BASE_URL,\n fileKey,\n nodeId,\n format = 'png',\n scale,\n useAbsoluteBounds,\n}: {\n apiBaseUrl?: string;\n fileKey: string;\n nodeId: string;\n format?: ReviewFigmaRenderFormat;\n scale?: number;\n useAbsoluteBounds?: boolean;\n}) {\n const url = new URL(`/v1/images/${encodeURIComponent(fileKey)}`, apiBaseUrl);\n url.searchParams.set('ids', nodeId);\n url.searchParams.set('format', format);\n\n if (typeof scale === 'number' && Number.isFinite(scale) && scale > 0) {\n url.searchParams.set('scale', String(scale));\n }\n if (useAbsoluteBounds !== undefined) {\n url.searchParams.set('use_absolute_bounds', String(useAbsoluteBounds));\n }\n\n return url.toString();\n}\n","import type {\n AddReviewFigmaImageInput,\n ReorderReviewFigmaImagesInput,\n ReviewFigmaImage,\n ReviewFigmaImageAssetInput,\n ReviewFigmaImageFormat,\n ReviewFigmaImageStore,\n ReviewFigmaImageTarget,\n} from './image.types';\nimport { getReviewFigmaImageFormatFromMimeType } from '../vite/figma-asset';\nimport { parseReviewFigmaNodeRef } from './parse';\nimport {\n createReviewFigmaImageApiUrl,\n type ReviewFigmaRenderFormat,\n} from './render';\n\nexport const DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT =\n '/__dfwr/figma-images';\n\ntype ReviewFigmaImageTokenProvider =\n | string\n | null\n | undefined\n | (() => string | null | undefined);\n\nexport type ReviewFigmaImageStoreClientOptions = {\n endpoint?: string;\n fetch?: typeof fetch;\n token?: ReviewFigmaImageTokenProvider;\n clientRender?: boolean | ReviewFigmaImageClientRenderOptions;\n};\n\nexport type ReviewFigmaImageClientRenderOptions = {\n token?: string | null | (() => string | null | undefined);\n apiBaseUrl?: string;\n renderFormat?: Extract<ReviewFigmaRenderFormat, 'png' | 'jpg'>;\n renderScale?: number;\n useAbsoluteBounds?: boolean;\n convertToWebp?: boolean;\n webpQuality?: number;\n timeoutMs?: number;\n};\n\nexport type CreateReviewFigmaClientRenderedAssetOptions =\n ReviewFigmaImageClientRenderOptions & {\n figmaUrl: string;\n token: string;\n fetch?: typeof fetch;\n };\n\nexport function createReviewFigmaImageStoreClient(\n options: ReviewFigmaImageStoreClientOptions = {}\n): ReviewFigmaImageStore {\n const endpoint =\n options.endpoint ?? DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT;\n const request = createReviewFigmaImageStoreRequest(endpoint, options.fetch);\n\n return {\n listImages(target) {\n const url = `${endpoint}?target=${encodeURIComponent(\n JSON.stringify(target)\n )}`;\n return request<ReviewFigmaImage[]>(url);\n },\n async addImage(input) {\n const nextInput = await createClientRenderedAddImageInput(\n input,\n options.clientRender,\n options.fetch\n );\n return request<ReviewFigmaImage>(endpoint, {\n method: 'POST',\n body: JSON.stringify(nextInput),\n figmaToken: readReviewFigmaImageToken(\n options.token ?? getStoredReviewFigmaImageToken\n ),\n });\n },\n updateImage(id, patch) {\n return request<ReviewFigmaImage>(`${endpoint}/${encodeURIComponent(id)}`, {\n method: 'PATCH',\n body: JSON.stringify(patch),\n });\n },\n reorderImages(input) {\n return request<ReviewFigmaImage[]>(`${endpoint}/reorder`, {\n method: 'PATCH',\n body: JSON.stringify(input),\n });\n },\n deleteImage(id) {\n return request<void>(`${endpoint}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n });\n },\n };\n}\n\nasync function createClientRenderedAddImageInput(\n input: AddReviewFigmaImageInput,\n clientRender: ReviewFigmaImageStoreClientOptions['clientRender'],\n fetchOption: typeof fetch | undefined\n): Promise<AddReviewFigmaImageInput> {\n const options = normalizeClientRenderOptions(clientRender);\n if (!options) return input;\n\n const token = readClientRenderToken(options);\n if (!token) return input;\n\n try {\n const asset = await createReviewFigmaClientRenderedAsset({\n ...options,\n fetch: fetchOption,\n figmaUrl: input.figmaUrl,\n token,\n });\n return {\n ...input,\n imageFormat: asset.imageFormat,\n asset,\n };\n } catch {\n return input;\n }\n}\n\nfunction normalizeClientRenderOptions(\n clientRender: ReviewFigmaImageStoreClientOptions['clientRender']\n): ReviewFigmaImageClientRenderOptions | null {\n if (!clientRender) return null;\n if (clientRender === true) return {};\n return clientRender;\n}\n\nfunction readClientRenderToken(options: ReviewFigmaImageClientRenderOptions) {\n const token =\n typeof options.token === 'function' ? options.token() : options.token;\n return typeof token === 'string' ? token.trim() : '';\n}\n\nasync function createClientRenderedFigmaAsset(\n figmaUrl: string,\n token: string,\n options: ReviewFigmaImageClientRenderOptions,\n fetchOption: typeof fetch | undefined\n): Promise<ReviewFigmaImageAssetInput> {\n const ref = parseReviewFigmaNodeRef(figmaUrl);\n if (!ref) throw new Error('A Figma node link is required.');\n\n const requestFetch = fetchOption ?? globalThis.fetch;\n if (!requestFetch) throw new Error('Figma client rendering requires fetch.');\n\n const renderFormat = options.renderFormat ?? 'png';\n const response = await requestFetch(\n createReviewFigmaImageApiUrl({\n apiBaseUrl: options.apiBaseUrl,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n format: renderFormat,\n scale: options.renderScale,\n useAbsoluteBounds: options.useAbsoluteBounds,\n }),\n {\n headers: {\n 'X-Figma-Token': token,\n },\n }\n );\n const body = (await response.json().catch(() => null)) as\n | { err?: string | null; images?: Record<string, string | null | undefined> }\n | null;\n if (!response.ok) {\n throw new Error(body?.err || `Figma image render failed with ${response.status}`);\n }\n\n const imageUrl = body?.images?.[ref.nodeId];\n if (!imageUrl) throw new Error(`Figma image render returned no URL for ${ref.nodeId}.`);\n\n const imageResponse = await requestFetch(imageUrl);\n if (!imageResponse.ok) {\n throw new Error(`Figma image download failed with ${imageResponse.status}`);\n }\n\n const originalBlob = await imageResponse.blob();\n const originalMimeType =\n normalizeClientImageMimeType(originalBlob.type) ??\n getReviewFigmaImageMimeType(renderFormat === 'jpg' ? 'jpg' : 'png');\n const originalFormat =\n getReviewFigmaImageFormatFromMimeType(originalMimeType) ??\n (renderFormat === 'jpg' ? 'jpg' : 'png');\n const dimensions = await readImageBlobDimensions(originalBlob);\n const shouldConvertToWebp = options.convertToWebp ?? true;\n const convertedBlob = shouldConvertToWebp\n ? await convertImageBlobToWebp(\n originalBlob,\n options.webpQuality ?? 0.9,\n dimensions\n ).catch(() => null)\n : null;\n const finalBlob =\n convertedBlob?.type === 'image/webp' ? convertedBlob : originalBlob;\n const finalMimeType =\n normalizeClientImageMimeType(finalBlob.type) ?? originalMimeType;\n const finalFormat =\n getReviewFigmaImageFormatFromMimeType(finalMimeType) ?? originalFormat;\n\n return {\n dataUrl: await blobToDataUrl(finalBlob),\n imageFormat: finalFormat,\n mimeType: finalMimeType,\n byteSize: finalBlob.size,\n width: dimensions.width,\n height: dimensions.height,\n };\n}\n\nexport function createReviewFigmaClientRenderedAsset({\n fetch: fetchOption,\n figmaUrl,\n token,\n ...options\n}: CreateReviewFigmaClientRenderedAssetOptions): Promise<ReviewFigmaImageAssetInput> {\n return withTimeout(\n createClientRenderedFigmaAsset(figmaUrl, token, options, fetchOption),\n options.timeoutMs ?? 10000\n );\n}\n\nasync function readImageBlobDimensions(blob: Blob) {\n const image = await loadImageBlob(blob);\n return {\n width: image.naturalWidth || image.width,\n height: image.naturalHeight || image.height,\n };\n}\n\nasync function convertImageBlobToWebp(\n blob: Blob,\n quality: number,\n dimensions: { width: number; height: number }\n) {\n if (typeof document === 'undefined') return null;\n if (!dimensions.width || !dimensions.height) return null;\n\n const image = await loadImageBlob(blob);\n const canvas = document.createElement('canvas');\n canvas.width = dimensions.width;\n canvas.height = dimensions.height;\n const context = canvas.getContext('2d');\n if (!context) return null;\n\n context.drawImage(image, 0, 0);\n\n return new Promise<Blob | null>((resolve) => {\n canvas.toBlob(resolve, 'image/webp', quality);\n });\n}\n\nfunction loadImageBlob(blob: Blob) {\n return new Promise<HTMLImageElement>((resolve, reject) => {\n if (typeof Image === 'undefined' || typeof URL === 'undefined') {\n reject(new Error('Image decoding is unavailable.'));\n return;\n }\n\n const image = new Image();\n const objectUrl = URL.createObjectURL(blob);\n image.onload = () => {\n URL.revokeObjectURL(objectUrl);\n resolve(image);\n };\n image.onerror = () => {\n URL.revokeObjectURL(objectUrl);\n reject(new Error('Image decoding failed.'));\n };\n image.src = objectUrl;\n });\n}\n\nfunction blobToDataUrl(blob: Blob) {\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (typeof reader.result === 'string') {\n resolve(reader.result);\n return;\n }\n reject(new Error('Blob encoding failed.'));\n };\n reader.onerror = () => reject(reader.error ?? new Error('Blob encoding failed.'));\n reader.readAsDataURL(blob);\n });\n}\n\nfunction normalizeClientImageMimeType(value: string | null | undefined) {\n const mimeType = value?.split(';')[0]?.trim().toLowerCase();\n if (mimeType === 'image/jpg') return 'image/jpeg';\n if (\n mimeType === 'image/jpeg' ||\n mimeType === 'image/png' ||\n mimeType === 'image/webp'\n ) {\n return mimeType;\n }\n return null;\n}\n\nasync function withTimeout<T>(promise: Promise<T>, timeoutMs: number) {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<T>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new Error('Figma client rendering timed out.')),\n timeoutMs\n );\n }),\n ]);\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n}\n\nexport function getReviewFigmaImageTargetKey(target: ReviewFigmaImageTarget) {\n return JSON.stringify(normalizeReviewFigmaImageTarget(target));\n}\n\nexport function getReviewFigmaImageMimeType(\n format: ReviewFigmaImageFormat\n) {\n if (format === 'jpg') return 'image/jpeg';\n if (format === 'png') return 'image/png';\n return 'image/webp';\n}\n\ntype ReviewFigmaImageStoreRequestInit = RequestInit & {\n figmaToken?: string;\n};\n\nfunction createReviewFigmaImageStoreRequest(\n endpoint: string,\n fetchOption: typeof fetch | undefined\n) {\n return async <T>(\n input: string,\n init: ReviewFigmaImageStoreRequestInit = {}\n ) => {\n const requestFetch = fetchOption ?? globalThis.fetch;\n if (!requestFetch) throw new Error('Figma image store requires fetch.');\n const figmaToken = init.figmaToken ?? '';\n const { figmaToken: _figmaToken, ...requestInit } = init;\n\n const response = await requestFetch(input, {\n ...requestInit,\n headers: {\n 'Content-Type': 'application/json',\n ...(figmaToken ? { 'X-Figma-Token': figmaToken } : {}),\n ...(requestInit.headers ?? {}),\n },\n });\n const text = await response.text();\n const body = text ? JSON.parse(text) : null;\n\n if (!response.ok) {\n const message =\n typeof body?.error === 'string'\n ? body.error\n : `Figma image store request failed: ${response.status}`;\n throw new Error(message);\n }\n\n return body as T;\n };\n}\n\nfunction readReviewFigmaImageToken(provider: ReviewFigmaImageTokenProvider) {\n const token = typeof provider === 'function' ? provider() : provider;\n return typeof token === 'string' ? token.trim() : '';\n}\n\nfunction getStoredReviewFigmaImageToken() {\n if (typeof window === 'undefined') return '';\n\n try {\n return window.localStorage.getItem('figma-token') ?? '';\n } catch {\n return '';\n }\n}\n\nfunction normalizeReviewFigmaImageTarget(target: ReviewFigmaImageTarget) {\n if (target.type === 'figma-node') {\n return {\n type: target.type,\n projectId: target.projectId,\n fileKey: target.fileKey,\n nodeId: target.nodeId,\n };\n }\n\n return {\n type: target.type,\n projectId: target.projectId,\n pageUrl: target.pageUrl,\n slot: target.slot ?? '',\n viewport: target.viewport\n ? {\n label: target.viewport.label ?? '',\n width: target.viewport.width ?? null,\n height: target.viewport.height ?? null,\n scope: target.viewport.scope ?? '',\n }\n : null,\n };\n}\n\nexport type { ReorderReviewFigmaImagesInput };\n","import { readFile } from 'node:fs/promises';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport path from 'node:path';\nimport type {\n ReviewFigmaImage,\n ReviewFigmaImageTarget,\n AddReviewFigmaImageInput,\n} from '../figma/image.types';\nimport {\n getReviewFigmaImageTargetKey,\n type ReorderReviewFigmaImagesInput,\n} from '../figma/image.store';\nimport type { ReviewFigmaTokenEnv } from '../figma/token';\nimport { createReviewFigmaReleaseSnapshot } from '../figma/image.snapshot';\nimport {\n getReviewFigmaAssetMimeType,\n getReviewFigmaAssetStorageKeyFromPathname,\n parseReviewFigmaImageFormat,\n} from './figma-asset';\nimport type { ReviewFigmaImageStorePluginOptions } from './figma-image-store';\nimport {\n createReviewFigmaImage,\n deleteReviewFigmaImageAsset,\n isNodeError,\n listImagesForTarget,\n normalizeOptionalText,\n readReviewFigmaImageStoreFile,\n reorderReviewFigmaImages,\n updateReviewFigmaImage,\n writeReviewFigmaImageStoreFile,\n} from './figma-image-store.image';\n\ntype ReviewFigmaImageStoreResponse = {\n status: number;\n body: unknown;\n};\n\nexport async function handleReviewFigmaImageStoreRequest({\n dataFile,\n assetDir,\n assetEndpoint,\n endpoint,\n method,\n options,\n pathname,\n requestUrl,\n body,\n env,\n requestToken,\n}: {\n dataFile: string;\n assetDir: string;\n assetEndpoint: string;\n endpoint: string;\n method: string;\n options: ReviewFigmaImageStorePluginOptions;\n env: ReviewFigmaTokenEnv;\n pathname: string;\n requestUrl: URL;\n body: unknown;\n requestToken?: string | null;\n}): Promise<ReviewFigmaImageStoreResponse> {\n if (method === 'OPTIONS') return { status: 204, body: null };\n\n if (\n (method === 'GET' || method === 'POST') &&\n pathname === `${endpoint}/snapshot`\n ) {\n const input = parseReleaseSnapshotInput(body, requestUrl, options.projectId);\n if (!input) return jsonError(400, 'valid snapshot input is required.');\n if (options.projectId && input.projectId !== options.projectId) {\n return jsonError(403, 'snapshot project is not allowed.');\n }\n if (input.targets.some((target) => !isAllowedProjectTarget(target, options.projectId))) {\n return jsonError(403, 'snapshot target project is not allowed.');\n }\n\n const data = await readReviewFigmaImageStoreFile(dataFile);\n return {\n status: 200,\n body: createReviewFigmaReleaseSnapshot({\n images: data.images,\n projectId: input.projectId,\n releaseId: input.releaseId,\n label: input.label,\n targets: input.targets.length > 0 ? input.targets : undefined,\n }),\n };\n }\n\n if (method === 'GET' && pathname === endpoint) {\n const target = parseTargetParam(requestUrl.searchParams.get('target'));\n if (!target) return jsonError(400, 'target query is required.');\n if (!isAllowedProjectTarget(target, options.projectId)) {\n return { status: 200, body: [] };\n }\n\n const data = await readReviewFigmaImageStoreFile(dataFile);\n return { status: 200, body: listImagesForTarget(data.images, target) };\n }\n\n if (method === 'POST' && pathname === endpoint) {\n const input = parseAddImageInput(body);\n if (!input) return jsonError(400, 'valid add image input is required.');\n if (!isAllowedProjectTarget(input.target, options.projectId)) {\n return jsonError(403, 'target project is not allowed.');\n }\n\n const data = await readReviewFigmaImageStoreFile(dataFile);\n const image = await createReviewFigmaImage({\n assetDir,\n assetEndpoint,\n currentImages: data.images,\n env,\n input,\n options,\n requestToken,\n });\n data.images = [image, ...data.images];\n await writeReviewFigmaImageStoreFile(dataFile, data);\n\n return { status: 201, body: image };\n }\n\n if (method === 'PATCH' && pathname === `${endpoint}/reorder`) {\n const input = parseReorderImagesInput(body);\n if (!input) return jsonError(400, 'valid reorder input is required.');\n if (!isAllowedProjectTarget(input.target, options.projectId)) {\n return jsonError(403, 'target project is not allowed.');\n }\n\n const data = await readReviewFigmaImageStoreFile(dataFile);\n const images = reorderReviewFigmaImages(data.images, input);\n data.images = images.allImages;\n await writeReviewFigmaImageStoreFile(dataFile, data);\n\n return { status: 200, body: images.targetImages };\n }\n\n const id = getEndpointItemId(pathname, endpoint);\n if (id && method === 'PATCH') {\n const patch = parseUpdateImageInput(body);\n if (!patch) return jsonError(400, 'valid update patch is required.');\n\n const data = await readReviewFigmaImageStoreFile(dataFile);\n const result = updateReviewFigmaImage(data.images, id, patch);\n if (!result) return jsonError(404, `Figma image not found: ${id}`);\n if (!isAllowedProjectTarget(result.image.target, options.projectId)) {\n return jsonError(403, 'target project is not allowed.');\n }\n\n data.images = result.images;\n await writeReviewFigmaImageStoreFile(dataFile, data);\n\n return { status: 200, body: result.image };\n }\n\n if (id && method === 'DELETE') {\n const data = await readReviewFigmaImageStoreFile(dataFile);\n const image = data.images.find((item) => item.id === id);\n if (!image) return jsonError(404, `Figma image not found: ${id}`);\n if (!isAllowedProjectTarget(image.target, options.projectId)) {\n return jsonError(403, 'target project is not allowed.');\n }\n\n data.images = data.images.filter((item) => item.id !== id);\n await writeReviewFigmaImageStoreFile(dataFile, data);\n await deleteReviewFigmaImageAsset(assetDir, image.storageKey);\n\n return { status: 200, body: { ok: true } };\n }\n\n return jsonError(405, 'method not allowed.');\n}\n\nexport async function readJsonRequestBody(req: IncomingMessage) {\n if (req.method === 'GET' || req.method === 'DELETE') return null;\n\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);\n }\n const raw = Buffer.concat(chunks).toString('utf8').trim();\n if (!raw) return null;\n\n return JSON.parse(raw);\n}\n\nexport function sendJson(res: ServerResponse, status: number, body: unknown) {\n res.statusCode = status;\n res.setHeader('Content-Type', 'application/json; charset=utf-8');\n if (status === 204) {\n res.end();\n return;\n }\n res.end(JSON.stringify(body ?? null));\n}\n\nexport async function sendReviewFigmaAsset(\n res: ServerResponse,\n assetDir: string,\n assetEndpoint: string,\n pathname: string\n) {\n const storageKey = getReviewFigmaAssetStorageKeyFromPathname(pathname, assetEndpoint);\n if (!storageKey) {\n sendPlainText(res, 400, 'Invalid Figma image asset path.');\n return;\n }\n\n try {\n const data = await readFile(path.join(assetDir, storageKey));\n res.statusCode = 200;\n res.setHeader('Content-Type', getReviewFigmaAssetMimeType(storageKey));\n res.setHeader('Cache-Control', 'private, max-age=31536000, immutable');\n res.end(data);\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') {\n sendPlainText(res, 404, 'Figma image asset not found.');\n return;\n }\n sendPlainText(\n res,\n 500,\n error instanceof Error ? error.message : 'Figma image asset request failed.'\n );\n }\n}\n\nfunction sendPlainText(res: ServerResponse, status: number, body: string) {\n res.statusCode = status;\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\n res.end(body);\n}\n\nfunction parseTargetParam(value: string | null) {\n if (!value) return null;\n try {\n return parseReviewFigmaImageTarget(JSON.parse(value));\n } catch {\n return null;\n }\n}\n\nfunction parseAddImageInput(value: unknown): AddReviewFigmaImageInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Partial<AddReviewFigmaImageInput>;\n const target = parseReviewFigmaImageTarget(input.target);\n if (!target || typeof input.figmaUrl !== 'string') return null;\n\n return {\n target,\n figmaUrl: input.figmaUrl,\n label: typeof input.label === 'string' ? input.label : undefined,\n order: typeof input.order === 'number' ? input.order : undefined,\n imageFormat: parseReviewFigmaImageFormat(input.imageFormat),\n asset: parseAddImageAssetInput(input.asset),\n };\n}\n\nfunction parseAddImageAssetInput(\n value: unknown\n): AddReviewFigmaImageInput['asset'] {\n if (!value || typeof value !== 'object') return undefined;\n const input = value as Partial<NonNullable<AddReviewFigmaImageInput['asset']>>;\n const imageFormat = parseReviewFigmaImageFormat(input.imageFormat);\n if (\n !imageFormat ||\n typeof input.dataUrl !== 'string' ||\n typeof input.mimeType !== 'string'\n ) {\n return undefined;\n }\n\n return {\n dataUrl: input.dataUrl,\n imageFormat,\n mimeType: input.mimeType,\n byteSize: typeof input.byteSize === 'number' ? input.byteSize : undefined,\n width: typeof input.width === 'number' ? input.width : undefined,\n height: typeof input.height === 'number' ? input.height : undefined,\n };\n}\n\nfunction parseUpdateImageInput(value: unknown) {\n if (!value || typeof value !== 'object') return null;\n const input = value as { label?: unknown; order?: unknown };\n\n return {\n label: typeof input.label === 'string' ? input.label : undefined,\n order: typeof input.order === 'number' ? input.order : undefined,\n };\n}\n\nfunction parseReorderImagesInput(\n value: unknown\n): ReorderReviewFigmaImagesInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as ReorderReviewFigmaImagesInput;\n const target = parseReviewFigmaImageTarget(input.target);\n if (!target || !Array.isArray(input.imageIds)) return null;\n\n return {\n target,\n imageIds: input.imageIds.filter((id) => typeof id === 'string'),\n };\n}\n\nfunction parseReleaseSnapshotInput(\n value: unknown,\n requestUrl: URL,\n fallbackProjectId: string | undefined\n): {\n projectId: string;\n releaseId?: string;\n label?: string;\n targets: ReviewFigmaImageTarget[];\n} | null {\n const input = value && typeof value === 'object'\n ? (value as Partial<{ projectId: unknown; releaseId: unknown; label: unknown; targets: unknown }>)\n : null;\n const projectId = normalizeOptionalText(\n typeof input?.projectId === 'string'\n ? input.projectId\n : requestUrl.searchParams.get('projectId') ?? fallbackProjectId\n );\n if (!projectId) return null;\n\n return {\n projectId,\n releaseId: normalizeOptionalText(\n typeof input?.releaseId === 'string'\n ? input.releaseId\n : requestUrl.searchParams.get('releaseId')\n ),\n label: normalizeOptionalText(\n typeof input?.label === 'string'\n ? input.label\n : requestUrl.searchParams.get('label')\n ),\n targets: parseReleaseSnapshotTargets(input?.targets, requestUrl),\n };\n}\n\nfunction parseReleaseSnapshotTargets(value: unknown, requestUrl: URL) {\n const bodyTargets = Array.isArray(value)\n ? value.flatMap((target) => {\n const parsed = parseReviewFigmaImageTarget(target);\n return parsed ? [parsed] : [];\n })\n : [];\n const queryTargets = requestUrl.searchParams\n .getAll('target')\n .flatMap((target) => {\n const parsed = parseTargetParam(target);\n return parsed ? [parsed] : [];\n });\n const targetByKey = new Map(\n [...bodyTargets, ...queryTargets].map((target) => [\n getReviewFigmaImageTargetKey(target),\n target,\n ])\n );\n\n return Array.from(targetByKey.values());\n}\n\nfunction parseReviewFigmaImageTarget(\n value: unknown\n): ReviewFigmaImageTarget | null {\n if (!value || typeof value !== 'object') return null;\n const target = value as ReviewFigmaImageTarget;\n if (target.type === 'route') {\n if (\n typeof target.projectId !== 'string' ||\n typeof target.pageUrl !== 'string'\n ) {\n return null;\n }\n\n return {\n type: 'route',\n projectId: target.projectId,\n pageUrl: target.pageUrl,\n viewport:\n target.viewport && typeof target.viewport === 'object'\n ? target.viewport\n : undefined,\n slot: typeof target.slot === 'string' ? target.slot : undefined,\n };\n }\n\n if (target.type === 'figma-node') {\n if (\n typeof target.projectId !== 'string' ||\n typeof target.fileKey !== 'string' ||\n typeof target.nodeId !== 'string'\n ) {\n return null;\n }\n\n return {\n type: 'figma-node',\n projectId: target.projectId,\n fileKey: target.fileKey,\n nodeId: target.nodeId,\n };\n }\n\n return null;\n}\n\nexport function normalizeEndpoint(endpoint: string) {\n const normalized = endpoint.trim().replace(/\\/+$/, '');\n return normalized.startsWith('/') ? normalized : `/${normalized}`;\n}\n\nfunction getEndpointItemId(pathname: string, endpoint: string) {\n if (!pathname.startsWith(`${endpoint}/`)) return null;\n const value = pathname.slice(endpoint.length + 1);\n if (!value || value.includes('/')) return null;\n return decodeURIComponent(value);\n}\n\nfunction isAllowedProjectTarget(\n target: ReviewFigmaImageTarget,\n projectId: string | undefined\n) {\n return !projectId || target.projectId === projectId;\n}\n\nfunction jsonError(status: number, error: string): ReviewFigmaImageStoreResponse {\n return { status, body: { error } };\n}\n","import type {\n ReviewFigmaImage,\n ReviewFigmaImageStore,\n ReviewFigmaImageTarget,\n} from './image.types';\nimport { getReviewFigmaImageTargetKey } from './image.store';\n\nexport type ReviewFigmaImagesSnapshot = ReviewFigmaImage[];\n\nexport type ReviewFigmaReleaseSnapshot = {\n version: 1;\n projectId: string;\n releaseId?: string;\n label?: string;\n createdAt: string;\n figmaImagesSnapshot: ReviewFigmaImagesSnapshot;\n};\n\nexport type CreateReviewFigmaImagesSnapshotOptions = {\n projectId?: string;\n targets?: readonly ReviewFigmaImageTarget[];\n};\n\nexport type CreateReviewFigmaReleaseSnapshotOptions =\n CreateReviewFigmaImagesSnapshotOptions & {\n images: readonly ReviewFigmaImage[];\n projectId: string;\n releaseId?: string;\n label?: string;\n createdAt?: string;\n };\n\nexport type CollectReviewFigmaReleaseSnapshotOptions = Omit<\n CreateReviewFigmaReleaseSnapshotOptions,\n 'images'\n> & {\n store: ReviewFigmaImageStore;\n targets: readonly ReviewFigmaImageTarget[];\n};\n\nexport function createReviewFigmaImagesSnapshot(\n images: readonly ReviewFigmaImage[],\n options: CreateReviewFigmaImagesSnapshotOptions = {}\n): ReviewFigmaImagesSnapshot {\n const targetKeys = options.targets?.length\n ? new Set(options.targets.map(getReviewFigmaImageTargetKey))\n : null;\n\n return images\n .filter((image) => {\n if (options.projectId && image.projectId !== options.projectId) {\n return false;\n }\n if (\n targetKeys &&\n !targetKeys.has(getReviewFigmaImageTargetKey(image.target))\n ) {\n return false;\n }\n return true;\n })\n .map(cloneReviewFigmaImage)\n .sort(compareReviewFigmaSnapshotImages);\n}\n\nexport function createReviewFigmaReleaseSnapshot({\n images,\n projectId,\n releaseId,\n label,\n createdAt,\n targets,\n}: CreateReviewFigmaReleaseSnapshotOptions): ReviewFigmaReleaseSnapshot {\n return {\n version: 1,\n projectId,\n ...(releaseId ? { releaseId } : null),\n ...(label ? { label } : null),\n createdAt: createdAt ?? new Date().toISOString(),\n figmaImagesSnapshot: createReviewFigmaImagesSnapshot(images, {\n projectId,\n targets,\n }),\n };\n}\n\nexport async function collectReviewFigmaReleaseSnapshot({\n store,\n targets,\n ...snapshotOptions\n}: CollectReviewFigmaReleaseSnapshotOptions): Promise<ReviewFigmaReleaseSnapshot> {\n const imagesByTarget = await Promise.all(\n targets.map((target) => store.listImages(target))\n );\n\n return createReviewFigmaReleaseSnapshot({\n ...snapshotOptions,\n targets,\n images: dedupeReviewFigmaImages(imagesByTarget.flat()),\n });\n}\n\nfunction dedupeReviewFigmaImages(images: readonly ReviewFigmaImage[]) {\n return Array.from(new Map(images.map((image) => [image.id, image])).values());\n}\n\nfunction cloneReviewFigmaImage(image: ReviewFigmaImage): ReviewFigmaImage {\n return {\n ...image,\n target: cloneReviewFigmaImageTarget(image.target),\n };\n}\n\nfunction cloneReviewFigmaImageTarget(\n target: ReviewFigmaImageTarget\n): ReviewFigmaImageTarget {\n if (target.type === 'figma-node') {\n return {\n type: target.type,\n projectId: target.projectId,\n fileKey: target.fileKey,\n nodeId: target.nodeId,\n };\n }\n\n return {\n type: target.type,\n projectId: target.projectId,\n pageUrl: target.pageUrl,\n slot: target.slot,\n viewport: target.viewport\n ? {\n label: target.viewport.label,\n width: target.viewport.width,\n height: target.viewport.height,\n scope: target.viewport.scope,\n }\n : undefined,\n };\n}\n\nfunction compareReviewFigmaSnapshotImages(\n a: ReviewFigmaImage,\n b: ReviewFigmaImage\n) {\n return (\n a.projectId.localeCompare(b.projectId) ||\n getReviewFigmaImageTargetKey(a.target).localeCompare(\n getReviewFigmaImageTargetKey(b.target)\n ) ||\n a.order - b.order ||\n a.createdAt.localeCompare(b.createdAt) ||\n a.id.localeCompare(b.id)\n );\n}\n","import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type {\n ReviewFigmaImage,\n ReviewFigmaImageAssetInput,\n ReviewFigmaImageFormat,\n ReviewFigmaImageTarget,\n AddReviewFigmaImageInput,\n UpdateReviewFigmaImageInput,\n} from '../figma/image.types';\nimport {\n getReviewFigmaImageMimeType,\n getReviewFigmaImageTargetKey,\n type ReorderReviewFigmaImagesInput,\n} from '../figma/image.store';\nimport { parseReviewFigmaNodeRef } from '../figma/parse';\nimport {\n DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,\n ReviewFigmaTokenError,\n readReviewFigmaToken,\n type ReviewFigmaTokenEnv,\n} from '../figma/token';\nimport {\n renderReviewFigmaImage,\n type ReviewFigmaRenderFormat,\n} from '../figma/render';\nimport {\n createReviewFigmaAssetStorageKey,\n createReviewFigmaAssetUrl,\n getReviewFigmaImageFormatFromMimeType,\n getStoreRenderFormat,\n isSafeReviewFigmaAssetStorageKey,\n normalizeImageMimeType,\n} from './figma-asset';\nimport type { ReviewFigmaImageStorePluginOptions, ReviewFigmaImageAssetTransformer } from './figma-image-store';\n\nexport type ReviewFigmaImageStoreFile = {\n version: 1;\n images: ReviewFigmaImage[];\n};\n\nfunction requireReviewFigmaRequestToken({\n enabled,\n env,\n envKey,\n requestToken,\n token,\n}: {\n enabled?: boolean;\n env: ReviewFigmaTokenEnv;\n envKey?: string;\n requestToken?: string | null;\n token?: string | null;\n}) {\n const tokenEnvKey = envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;\n const figmaToken =\n readReviewFigmaToken({ token, env, envKey: tokenEnvKey, enabled }) ||\n readReviewFigmaToken({\n token: requestToken,\n env: {},\n envKey: tokenEnvKey,\n enabled,\n });\n\n if (!figmaToken) throw new ReviewFigmaTokenError(tokenEnvKey);\n return figmaToken;\n}\n\nasync function readReviewFigmaNodeName({\n apiBaseUrl,\n enabled,\n env,\n envKey,\n fetchOption,\n fileKey,\n nodeId,\n token,\n requestToken,\n}: {\n apiBaseUrl?: string;\n enabled?: boolean;\n env: ReviewFigmaTokenEnv;\n envKey?: string;\n fetchOption?: typeof fetch;\n fileKey: string;\n nodeId: string;\n token?: string | null;\n requestToken?: string | null;\n}) {\n const figmaToken = requireReviewFigmaRequestToken({\n enabled,\n env,\n envKey,\n requestToken,\n token,\n });\n const fetchNode = fetchOption ?? globalThis.fetch;\n if (!fetchNode) throw new Error('Figma node name lookup requires fetch.');\n\n type FigmaNodeResponse = {\n err?: string | null;\n nodes?: Record<string, { document?: { name?: string | null } | null } | null | undefined>;\n };\n\n const response = await fetchNode(\n createReviewFigmaNodeApiUrl({ apiBaseUrl, fileKey, nodeId }),\n {\n headers: {\n 'X-Figma-Token': figmaToken,\n },\n }\n );\n const body = (await response.json().catch(() => null)) as FigmaNodeResponse | null;\n\n if (!response.ok) {\n throw new Error(body?.err || `Figma node lookup failed with ${response.status}`);\n }\n\n const nodes = body?.nodes;\n const node = nodes?.[nodeId] ?? Object.values(nodes ?? {})[0];\n return normalizeOptionalText(node?.document?.name);\n}\n\nfunction createReviewFigmaNodeApiUrl({\n apiBaseUrl = 'https://api.figma.com',\n fileKey,\n nodeId,\n}: {\n apiBaseUrl?: string;\n fileKey: string;\n nodeId: string;\n}) {\n const url = new URL(\n `/v1/files/${encodeURIComponent(fileKey)}/nodes`,\n apiBaseUrl\n );\n url.searchParams.set('ids', nodeId);\n return url.toString();\n}\n\nexport async function createReviewFigmaImage({\n assetDir,\n assetEndpoint,\n currentImages,\n env,\n input,\n options,\n requestToken,\n}: {\n assetDir: string;\n assetEndpoint: string;\n currentImages: ReviewFigmaImage[];\n env: ReviewFigmaTokenEnv;\n input: AddReviewFigmaImageInput;\n options: ReviewFigmaImageStorePluginOptions;\n requestToken?: string | null;\n}): Promise<ReviewFigmaImage> {\n const ref = parseReviewFigmaNodeRef(input.figmaUrl);\n if (!ref) {\n throw new Error('A Figma node copy link or fileKey->nodeId value is required.');\n }\n\n const id = createReviewFigmaImageId();\n const explicitLabel = normalizeOptionalText(input.label);\n if (input.asset) {\n const cachedAsset = await cacheReviewFigmaProvidedImageAsset({\n assetDir,\n assetEndpoint,\n id,\n asset: input.asset,\n options,\n });\n const now = new Date().toISOString();\n const order =\n typeof input.order === 'number' && Number.isFinite(input.order)\n ? input.order\n : getNextImageOrder(currentImages, input.target);\n\n return {\n id,\n projectId: input.target.projectId,\n target: input.target,\n figmaUrl: input.figmaUrl,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n imageUrl: cachedAsset.imageUrl,\n imageFormat: cachedAsset.imageFormat,\n mimeType: cachedAsset.mimeType,\n label: explicitLabel,\n order,\n storageKey: cachedAsset.storageKey,\n width: input.asset.width,\n height: input.asset.height,\n byteSize: cachedAsset.byteSize,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n const nodeLabelPromise = explicitLabel\n ? Promise.resolve(undefined)\n : readReviewFigmaNodeName({\n apiBaseUrl: options.apiBaseUrl,\n enabled: options.enabled,\n env,\n envKey: options.envKey,\n fetchOption: options.fetch,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n token: options.token,\n requestToken,\n }).catch(() => undefined);\n const targetImageFormat = input.imageFormat ?? options.imageFormat ?? 'webp';\n const renderFormat = getStoreRenderFormat(options.renderFormat, targetImageFormat);\n const rendered = await renderReviewFigmaImage({\n figmaUrl: input.figmaUrl,\n format: renderFormat,\n scale: options.renderScale,\n useAbsoluteBounds: options.useAbsoluteBounds,\n apiBaseUrl: options.apiBaseUrl,\n fetch: options.fetch,\n token:\n requireReviewFigmaRequestToken({\n enabled: options.enabled,\n env,\n envKey: options.envKey,\n requestToken,\n token: options.token,\n }),\n });\n const cachedAsset = await cacheReviewFigmaImageAsset({\n assetDir,\n assetEndpoint,\n id,\n imageUrl: rendered.imageUrl,\n options,\n renderFormat,\n targetImageFormat,\n });\n const imageFormat =\n cachedAsset?.imageFormat ?? (renderFormat === 'jpg' ? 'jpg' : 'png');\n const now = new Date().toISOString();\n const order =\n typeof input.order === 'number' && Number.isFinite(input.order)\n ? input.order\n : getNextImageOrder(currentImages, input.target);\n const nodeLabel = await nodeLabelPromise;\n\n return {\n id,\n projectId: input.target.projectId,\n target: input.target,\n figmaUrl: input.figmaUrl,\n fileKey: rendered.fileKey,\n nodeId: rendered.nodeId,\n imageUrl: cachedAsset?.imageUrl ?? rendered.imageUrl,\n imageFormat,\n mimeType: cachedAsset?.mimeType ?? getReviewFigmaImageMimeType(imageFormat),\n label: explicitLabel ?? nodeLabel,\n order,\n storageKey: cachedAsset?.storageKey,\n byteSize: cachedAsset?.byteSize,\n createdAt: now,\n updatedAt: now,\n };\n}\n\ntype CachedReviewFigmaImageAsset = {\n imageUrl: string;\n imageFormat: ReviewFigmaImageFormat;\n mimeType: string;\n storageKey: string;\n byteSize: number;\n};\n\nasync function cacheReviewFigmaProvidedImageAsset({\n assetDir,\n assetEndpoint,\n id,\n asset,\n options,\n}: {\n assetDir: string;\n assetEndpoint: string;\n id: string;\n asset: ReviewFigmaImageAssetInput;\n options: ReviewFigmaImageStorePluginOptions;\n}): Promise<CachedReviewFigmaImageAsset> {\n const decodedAsset = decodeReviewFigmaImageAsset(asset);\n const storageKey = createReviewFigmaAssetStorageKey(\n id,\n decodedAsset.imageFormat\n );\n\n await mkdir(assetDir, { recursive: true });\n await writeFile(path.join(assetDir, storageKey), decodedAsset.data);\n\n return {\n imageUrl: createReviewFigmaAssetUrl(assetEndpoint, storageKey),\n imageFormat: decodedAsset.imageFormat,\n mimeType: decodedAsset.mimeType,\n storageKey,\n byteSize: decodedAsset.data.byteLength,\n };\n}\n\nfunction decodeReviewFigmaImageAsset(asset: ReviewFigmaImageAssetInput) {\n const mimeType = normalizeImageMimeType(asset.mimeType);\n if (!mimeType) throw new Error('Unsupported Figma image asset MIME type.');\n\n const imageFormat =\n getReviewFigmaImageFormatFromMimeType(mimeType) ?? asset.imageFormat;\n const match = /^data:([^;,]+);base64,([a-zA-Z0-9+/=\\s]+)$/.exec(\n asset.dataUrl\n );\n if (!match) throw new Error('Valid Figma image asset data URL is required.');\n\n const dataUrlMimeType = normalizeImageMimeType(match[1]);\n if (dataUrlMimeType && dataUrlMimeType !== mimeType) {\n throw new Error('Figma image asset MIME type mismatch.');\n }\n\n return {\n data: Buffer.from(match[2].replace(/\\s/g, ''), 'base64'),\n imageFormat,\n mimeType,\n };\n}\n\nasync function cacheReviewFigmaImageAsset({\n assetDir,\n assetEndpoint,\n id,\n imageUrl,\n options,\n renderFormat,\n targetImageFormat,\n}: {\n assetDir: string;\n assetEndpoint: string;\n id: string;\n imageUrl: string;\n options: ReviewFigmaImageStorePluginOptions;\n renderFormat: Extract<ReviewFigmaRenderFormat, 'png' | 'jpg'>;\n targetImageFormat: ReviewFigmaImageFormat;\n}): Promise<CachedReviewFigmaImageAsset | null> {\n if (options.cacheAssets === false) return null;\n\n const asset = await downloadReviewFigmaImageAsset({\n fetchOption: options.fetch,\n imageUrl,\n renderFormat,\n targetImageFormat,\n transformAsset: options.transformAsset,\n });\n const storageKey = createReviewFigmaAssetStorageKey(id, asset.imageFormat);\n\n await mkdir(assetDir, { recursive: true });\n await writeFile(path.join(assetDir, storageKey), asset.data);\n\n return {\n imageUrl: createReviewFigmaAssetUrl(assetEndpoint, storageKey),\n imageFormat: asset.imageFormat,\n mimeType: asset.mimeType,\n storageKey,\n byteSize: asset.data.byteLength,\n };\n}\n\nasync function downloadReviewFigmaImageAsset({\n fetchOption,\n imageUrl,\n renderFormat,\n targetImageFormat,\n transformAsset,\n}: {\n fetchOption: typeof fetch | undefined;\n imageUrl: string;\n renderFormat: Extract<ReviewFigmaRenderFormat, 'png' | 'jpg'>;\n targetImageFormat: ReviewFigmaImageFormat;\n transformAsset: ReviewFigmaImageAssetTransformer | undefined;\n}) {\n const fetchImage = fetchOption ?? globalThis.fetch;\n if (!fetchImage) throw new Error('Figma image caching requires fetch.');\n\n const response = await fetchImage(imageUrl);\n if (!response.ok) {\n throw new Error(`Figma image download failed with ${response.status}`);\n }\n\n const sourceMimeType =\n normalizeImageMimeType(response.headers.get('content-type')) ??\n getReviewFigmaImageMimeType(renderFormat === 'jpg' ? 'jpg' : 'png');\n const sourceImageFormat =\n getReviewFigmaImageFormatFromMimeType(sourceMimeType) ??\n (renderFormat === 'jpg' ? 'jpg' : 'png');\n const sourceData = new Uint8Array(await response.arrayBuffer());\n const transformed = transformAsset\n ? await transformAsset({\n data: sourceData,\n imageFormat: sourceImageFormat,\n mimeType: sourceMimeType,\n targetFormat: targetImageFormat,\n })\n : null;\n const imageFormat = transformed?.imageFormat ?? sourceImageFormat;\n const mimeType =\n normalizeImageMimeType(transformed?.mimeType) ??\n getReviewFigmaImageMimeType(imageFormat);\n const data = createBufferFromImageData(transformed?.data ?? sourceData);\n\n return { data, imageFormat, mimeType };\n}\n\nexport async function deleteReviewFigmaImageAsset(\n assetDir: string,\n storageKey: string | undefined\n) {\n if (!storageKey) return;\n if (!isSafeReviewFigmaAssetStorageKey(storageKey)) return;\n await rm(path.join(assetDir, storageKey), { force: true }).catch(() => null);\n}\n\nfunction createBufferFromImageData(data: Uint8Array | ArrayBuffer) {\n return data instanceof ArrayBuffer\n ? Buffer.from(new Uint8Array(data))\n : Buffer.from(data);\n}\n\nexport function listImagesForTarget(\n images: ReviewFigmaImage[],\n target: ReviewFigmaImageTarget\n) {\n const targetKey = getReviewFigmaImageTargetKey(target);\n return images\n .filter((image) => getReviewFigmaImageTargetKey(image.target) === targetKey)\n .sort(compareReviewFigmaImages);\n}\n\nexport function reorderReviewFigmaImages(\n images: ReviewFigmaImage[],\n input: ReorderReviewFigmaImagesInput\n) {\n const targetKey = getReviewFigmaImageTargetKey(input.target);\n const orderById = new Map(input.imageIds.map((id, index) => [id, index]));\n const targetImages = listImagesForTarget(images, input.target);\n const nextTargetImages = targetImages\n .map((image) => ({\n ...image,\n order: orderById.get(image.id) ?? input.imageIds.length + image.order,\n updatedAt: orderById.has(image.id) ? new Date().toISOString() : image.updatedAt,\n }))\n .sort(compareReviewFigmaImages);\n const nextTargetImageById = new Map(\n nextTargetImages.map((image, index) => [image.id, { ...image, order: index }])\n );\n const allImages = images.map((image) =>\n getReviewFigmaImageTargetKey(image.target) === targetKey\n ? nextTargetImageById.get(image.id) ?? image\n : image\n );\n\n return {\n allImages,\n targetImages: listImagesForTarget(allImages, input.target),\n };\n}\n\nexport function updateReviewFigmaImage(\n images: ReviewFigmaImage[],\n id: string,\n patch: UpdateReviewFigmaImageInput\n) {\n const index = images.findIndex((image) => image.id === id);\n if (index < 0) return null;\n\n const nextImage: ReviewFigmaImage = {\n ...images[index],\n label:\n patch.label === undefined\n ? images[index].label\n : normalizeOptionalText(patch.label),\n order:\n typeof patch.order === 'number' && Number.isFinite(patch.order)\n ? patch.order\n : images[index].order,\n updatedAt: new Date().toISOString(),\n };\n const nextImages = [...images];\n nextImages[index] = nextImage;\n\n return { image: nextImage, images: nextImages };\n}\n\nexport async function readReviewFigmaImageStoreFile(\n dataFile: string\n): Promise<ReviewFigmaImageStoreFile> {\n try {\n const raw = await readFile(dataFile, 'utf8');\n const parsed = JSON.parse(raw) as Partial<ReviewFigmaImageStoreFile>;\n\n return {\n version: 1,\n images: Array.isArray(parsed.images)\n ? parsed.images.flatMap((image) => (isReviewFigmaImage(image) ? [image] : []))\n : [],\n };\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') {\n return { version: 1, images: [] };\n }\n throw error;\n }\n}\n\nexport async function writeReviewFigmaImageStoreFile(\n dataFile: string,\n data: ReviewFigmaImageStoreFile\n) {\n await mkdir(path.dirname(dataFile), { recursive: true });\n await writeFile(\n dataFile,\n `${JSON.stringify({ version: 1, images: data.images }, null, 2)}\\n`,\n 'utf8'\n );\n}\n\nfunction getNextImageOrder(\n images: ReviewFigmaImage[],\n target: ReviewFigmaImageTarget\n) {\n const targetImages = listImagesForTarget(images, target);\n return targetImages.length\n ? Math.max(...targetImages.map((image) => image.order)) + 1\n : 0;\n}\n\nfunction compareReviewFigmaImages(a: ReviewFigmaImage, b: ReviewFigmaImage) {\n return a.order - b.order || a.createdAt.localeCompare(b.createdAt);\n}\n\nfunction createReviewFigmaImageId() {\n return `figma_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction isReviewFigmaImage(value: unknown): value is ReviewFigmaImage {\n if (!value || typeof value !== 'object') return false;\n const image = value as ReviewFigmaImage;\n\n return (\n typeof image.id === 'string' &&\n typeof image.projectId === 'string' &&\n typeof image.figmaUrl === 'string' &&\n typeof image.fileKey === 'string' &&\n typeof image.nodeId === 'string' &&\n typeof image.imageUrl === 'string' &&\n typeof image.order === 'number' &&\n typeof image.createdAt === 'string' &&\n typeof image.updatedAt === 'string'\n );\n}\n\nexport function isNodeError(error: unknown): error is { code?: string } {\n if (!error || typeof error !== 'object') return false;\n return 'code' in error;\n}\n\nexport function normalizeOptionalText(value: string | null | undefined) {\n if (typeof value !== 'string') return undefined;\n return value.trim() || undefined;\n}\n"],"mappings":"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","names":["import_node_path","import_promises","import_node_path","cachedAsset","now","order","path","path","path"]}